Merge "Add setting/experiment for small battery devices to have all forced app standby enabled except for when the device is charging."
diff --git a/Android.bp b/Android.bp
index 19c0580..641ce5f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -689,7 +689,10 @@
" $(in) " +
"&& $(location soong_zip) -jar -o $(out) -C $(genDir)/$(in) -D $(genDir)/$(in)",
- srcs: ["core/proto/**/*.proto"],
+ srcs: [
+ "core/proto/**/*.proto",
+ "libs/incident/**/*.proto",
+ ],
output_extension: "srcjar",
}
diff --git a/Android.mk b/Android.mk
index 298b85d..2254008 100644
--- a/Android.mk
+++ b/Android.mk
@@ -331,6 +331,8 @@
$(framework_docs_LOCAL_DROIDDOC_OPTIONS) \
-referenceonly \
-api $(INTERNAL_PLATFORM_API_FILE) \
+ -privateApi $(INTERNAL_PLATFORM_PRIVATE_API_FILE) \
+ -privateDexApi $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE) \
-removedApi $(INTERNAL_PLATFORM_REMOVED_API_FILE) \
-nodocs
@@ -340,7 +342,9 @@
include $(BUILD_DROIDDOC)
-$(INTERNAL_PLATFORM_API_FILE): $(full_target)
+$(full_target): .KATI_IMPLICIT_OUTPUTS := $(INTERNAL_PLATFORM_API_FILE) \
+ $(INTERNAL_PLATFORM_PRIVATE_API_FILE) \
+ $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE)
$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE))
# ==== the system api stubs ===================================
@@ -365,6 +369,8 @@
-referenceonly \
-showAnnotation android.annotation.SystemApi \
-api $(INTERNAL_PLATFORM_SYSTEM_API_FILE) \
+ -privateApi $(INTERNAL_PLATFORM_SYSTEM_PRIVATE_API_FILE) \
+ -privateDexApi $(INTERNAL_PLATFORM_SYSTEM_PRIVATE_DEX_API_FILE) \
-removedApi $(INTERNAL_PLATFORM_SYSTEM_REMOVED_API_FILE) \
-exactApi $(INTERNAL_PLATFORM_SYSTEM_EXACT_API_FILE) \
-nodocs
@@ -375,7 +381,9 @@
include $(BUILD_DROIDDOC)
-$(INTERNAL_PLATFORM_SYSTEM_API_FILE): $(full_target)
+$(full_target): .KATI_IMPLICIT_OUTPUTS := $(INTERNAL_PLATFORM_SYSTEM_API_FILE) \
+ $(INTERNAL_PLATFORM_SYSTEM_PRIVATE_API_FILE) \
+ $(INTERNAL_PLATFORM_SYSTEM_PRIVATE_DEX_API_FILE)
$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_SYSTEM_API_FILE))
# ==== the test api stubs ===================================
@@ -781,6 +789,7 @@
LOCAL_SOURCE_FILES_ALL_GENERATED := true
LOCAL_SRC_FILES := \
cmds/am/proto/instrumentation_data.proto \
+ cmds/statsd/src/perfetto/perfetto_config.proto \
$(call all-proto-files-under, core/proto) \
$(call all-proto-files-under, libs/incident/proto) \
$(call all-proto-files-under, cmds/statsd/src)
@@ -797,7 +806,8 @@
store_unknown_fields = true
LOCAL_JAVA_LIBRARIES := core-oj core-libart
LOCAL_SRC_FILES := \
- $(call all-proto-files-under, core/proto)
+ $(call all-proto-files-under, core/proto) \
+ $(call all-proto-files-under, libs/incident/proto/android/os)
include $(BUILD_STATIC_JAVA_LIBRARY)
@@ -809,7 +819,8 @@
LOCAL_PROTOC_FLAGS := \
-Iexternal/protobuf/src
LOCAL_SRC_FILES := \
- $(call all-proto-files-under, core/proto)
+ $(call all-proto-files-under, core/proto) \
+ $(call all-proto-files-under, libs/incident/proto/android/os)
include $(BUILD_STATIC_JAVA_LIBRARY)
# Include subdirectory makefiles
diff --git a/apct-tests/perftests/core/AndroidManifest.xml b/apct-tests/perftests/core/AndroidManifest.xml
index bd0b944..132a2f9 100644
--- a/apct-tests/perftests/core/AndroidManifest.xml
+++ b/apct-tests/perftests/core/AndroidManifest.xml
@@ -10,7 +10,11 @@
<application>
<uses-library android:name="android.test.runner" />
- <activity android:name="android.perftests.utils.StubActivity" />
+ <activity android:name="android.perftests.utils.StubActivity">
+ <intent-filter>
+ <action android:name="com.android.perftests.core.PERFTEST" />
+ </intent-filter>
+ </activity>
<service android:name="android.os.SomeService" android:exported="false" android:process=":some_service" />
</application>
diff --git a/apct-tests/perftests/core/src/android/os/PackageManagerPerfTest.java b/apct-tests/perftests/core/src/android/os/PackageManagerPerfTest.java
new file mode 100644
index 0000000..145fbcd
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/os/PackageManagerPerfTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.os;
+
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class PackageManagerPerfTest {
+ private static final String PERMISSION_NAME_EXISTS =
+ "com.android.perftests.core.TestPermission";
+ private static final String PERMISSION_NAME_DOESNT_EXIST =
+ "com.android.perftests.core.TestBadPermission";
+ private static final ComponentName TEST_ACTIVITY =
+ new ComponentName("com.android.perftests.core", "android.perftests.utils.StubActivity");
+
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Test
+ public void testCheckPermissionExists() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
+ final String packageName = TEST_ACTIVITY.getPackageName();
+
+ while (state.keepRunning()) {
+ int ret = pm.checkPermission(PERMISSION_NAME_EXISTS, packageName);
+ }
+ }
+
+ @Test
+ public void testCheckPermissionDoesntExist() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
+ final String packageName = TEST_ACTIVITY.getPackageName();
+
+ while (state.keepRunning()) {
+ int ret = pm.checkPermission(PERMISSION_NAME_DOESNT_EXIST, packageName);
+ }
+ }
+
+ @Test
+ public void testQueryIntentActivities() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
+ final Intent intent = new Intent("com.android.perftests.core.PERFTEST");
+
+ while (state.keepRunning()) {
+ pm.queryIntentActivities(intent, 0);
+ }
+ }
+
+ @Test
+ public void testGetPackageInfo() throws Exception {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
+ final String packageName = TEST_ACTIVITY.getPackageName();
+
+ while (state.keepRunning()) {
+ pm.getPackageInfo(packageName, 0);
+ }
+ }
+
+ @Test
+ public void testGetApplicationInfo() throws Exception {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
+ final String packageName = TEST_ACTIVITY.getPackageName();
+
+ while (state.keepRunning()) {
+ pm.getApplicationInfo(packageName, 0);
+ }
+ }
+
+ @Test
+ public void testGetActivityInfo() throws Exception {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
+
+ while (state.keepRunning()) {
+ pm.getActivityInfo(TEST_ACTIVITY, 0);
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/os/PermissionTest.java b/apct-tests/perftests/core/src/android/os/PermissionTest.java
deleted file mode 100644
index d292e7d..0000000
--- a/apct-tests/perftests/core/src/android/os/PermissionTest.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package android.os;
-
-import static android.content.pm.PackageManager.PERMISSION_DENIED;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-
-import android.content.Context;
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.Assert;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-@LargeTest
-public class PermissionTest {
- private static final String PERMISSION_HAS_NAME = "com.android.perftests.core.TestPermission";
- private static final String PERMISSION_DOESNT_HAVE_NAME =
- "com.android.perftests.core.TestBadPermission";
- private static final String THIS_PACKAGE_NAME = "com.android.perftests.core";
-
- @Rule
- public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
-
- @Test
- public void testHasPermission() {
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- final Context context = InstrumentationRegistry.getTargetContext();
- while (state.keepRunning()) {
- int ret = context.getPackageManager().checkPermission(PERMISSION_HAS_NAME,
- THIS_PACKAGE_NAME);
- }
- }
-
- @Test
- public void testDoesntHavePermission() {
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- final Context context = InstrumentationRegistry.getTargetContext();
-
- while (state.keepRunning()) {
- int ret = context.getPackageManager().checkPermission(PERMISSION_DOESNT_HAVE_NAME,
- THIS_PACKAGE_NAME);
- }
- }
-
-}
diff --git a/api/current.txt b/api/current.txt
index 86f1a3b..3d8c5a6 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2796,6 +2796,7 @@
field public static final int GLOBAL_ACTION_POWER_DIALOG = 6; // 0x6
field public static final int GLOBAL_ACTION_QUICK_SETTINGS = 5; // 0x5
field public static final int GLOBAL_ACTION_RECENTS = 3; // 0x3
+ field public static final int GLOBAL_ACTION_TAKE_SCREENSHOT = 9; // 0x9
field public static final int GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN = 7; // 0x7
field public static final java.lang.String SERVICE_INTERFACE = "android.accessibilityservice.AccessibilityService";
field public static final java.lang.String SERVICE_META_DATA = "android.accessibilityservice";
@@ -6672,6 +6673,7 @@
field public static final int PERMISSION_POLICY_PROMPT = 0; // 0x0
field public static final java.lang.String POLICY_DISABLE_CAMERA = "policy_disable_camera";
field public static final java.lang.String POLICY_DISABLE_SCREEN_CAPTURE = "policy_disable_screen_capture";
+ field public static final java.lang.String POLICY_MANDATORY_BACKUPS = "policy_mandatory_backups";
field public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2
field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1
field public static final int SKIP_SETUP_WIZARD = 1; // 0x1
@@ -7252,6 +7254,7 @@
public static class NetworkStats.Bucket {
ctor public NetworkStats.Bucket();
+ method public int getDefaultNetwork();
method public long getEndTimeStamp();
method public int getMetered();
method public int getRoaming();
@@ -7263,6 +7266,9 @@
method public long getTxBytes();
method public long getTxPackets();
method public int getUid();
+ field public static final int DEFAULT_NETWORK_ALL = -1; // 0xffffffff
+ field public static final int DEFAULT_NETWORK_NO = 1; // 0x1
+ field public static final int DEFAULT_NETWORK_YES = 2; // 0x2
field public static final int METERED_ALL = -1; // 0xffffffff
field public static final int METERED_NO = 1; // 0x1
field public static final int METERED_YES = 2; // 0x2
@@ -15233,18 +15239,26 @@
method public void writeToParcel(android.os.Parcel, int);
field public static final int BLOB = 33; // 0x21
field public static final android.os.Parcelable.Creator<android.hardware.HardwareBuffer> CREATOR;
+ field public static final int DS_24UI8 = 50; // 0x32
+ field public static final int DS_FP32UI8 = 52; // 0x34
+ field public static final int D_16 = 48; // 0x30
+ field public static final int D_24 = 49; // 0x31
+ field public static final int D_FP32 = 51; // 0x33
field public static final int RGBA_1010102 = 43; // 0x2b
field public static final int RGBA_8888 = 1; // 0x1
field public static final int RGBA_FP16 = 22; // 0x16
field public static final int RGBX_8888 = 2; // 0x2
field public static final int RGB_565 = 4; // 0x4
field public static final int RGB_888 = 3; // 0x3
+ field public static final int S_UI8 = 53; // 0x35
field public static final long USAGE_CPU_READ_OFTEN = 3L; // 0x3L
field public static final long USAGE_CPU_READ_RARELY = 2L; // 0x2L
field public static final long USAGE_CPU_WRITE_OFTEN = 48L; // 0x30L
field public static final long USAGE_CPU_WRITE_RARELY = 32L; // 0x20L
field public static final long USAGE_GPU_COLOR_OUTPUT = 512L; // 0x200L
+ field public static final long USAGE_GPU_CUBE_MAP = 33554432L; // 0x2000000L
field public static final long USAGE_GPU_DATA_BUFFER = 16777216L; // 0x1000000L
+ field public static final long USAGE_GPU_MIPMAP_COMPLETE = 67108864L; // 0x4000000L
field public static final long USAGE_GPU_SAMPLED_IMAGE = 256L; // 0x100L
field public static final long USAGE_PROTECTED_CONTENT = 16384L; // 0x4000L
field public static final long USAGE_SENSOR_DIRECT_DATA = 8388608L; // 0x800000L
@@ -21737,7 +21751,13 @@
field public static final int CHANNEL_OUT_STEREO = 12; // 0xc
field public static final int CHANNEL_OUT_SURROUND = 1052; // 0x41c
field public static final android.os.Parcelable.Creator<android.media.AudioFormat> CREATOR;
+ field public static final int ENCODING_AAC_ELD = 15; // 0xf
+ field public static final int ENCODING_AAC_HE_V1 = 11; // 0xb
+ field public static final int ENCODING_AAC_HE_V2 = 12; // 0xc
+ field public static final int ENCODING_AAC_LC = 10; // 0xa
+ field public static final int ENCODING_AAC_XHE = 16; // 0x10
field public static final int ENCODING_AC3 = 5; // 0x5
+ field public static final int ENCODING_AC4 = 17; // 0x11
field public static final int ENCODING_DEFAULT = 1; // 0x1
field public static final int ENCODING_DOLBY_TRUEHD = 14; // 0xe
field public static final int ENCODING_DTS = 7; // 0x7
@@ -21745,6 +21765,7 @@
field public static final int ENCODING_E_AC3 = 6; // 0x6
field public static final int ENCODING_IEC61937 = 13; // 0xd
field public static final int ENCODING_INVALID = 0; // 0x0
+ field public static final int ENCODING_MP3 = 9; // 0x9
field public static final int ENCODING_PCM_16BIT = 2; // 0x2
field public static final int ENCODING_PCM_8BIT = 3; // 0x3
field public static final int ENCODING_PCM_FLOAT = 4; // 0x4
@@ -21787,6 +21808,7 @@
method public boolean isBluetoothScoOn();
method public boolean isMicrophoneMute();
method public boolean isMusicActive();
+ method public boolean isOffloadedPlaybackSupported(android.media.AudioFormat);
method public boolean isSpeakerphoneOn();
method public boolean isStreamMute(int);
method public boolean isVolumeFixed();
@@ -22087,6 +22109,7 @@
method public int reloadStaticData();
method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
method public deprecated void removeOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener);
+ method public void removeStreamEventCallback();
method public int setAuxEffectSendLevel(float);
method public int setBufferSizeInFrames(int);
method public int setLoopPoints(int, int, int);
@@ -22100,6 +22123,7 @@
method public boolean setPreferredDevice(android.media.AudioDeviceInfo);
method protected deprecated void setState(int);
method public deprecated int setStereoVolume(float, float);
+ method public void setStreamEventCallback(java.util.concurrent.Executor, android.media.AudioTrack.StreamEventCallback);
method public int setVolume(float);
method public void stop() throws java.lang.IllegalStateException;
method public int write(byte[], int, int);
@@ -22135,6 +22159,7 @@
method public android.media.AudioTrack.Builder setAudioAttributes(android.media.AudioAttributes) throws java.lang.IllegalArgumentException;
method public android.media.AudioTrack.Builder setAudioFormat(android.media.AudioFormat) throws java.lang.IllegalArgumentException;
method public android.media.AudioTrack.Builder setBufferSizeInBytes(int) throws java.lang.IllegalArgumentException;
+ method public android.media.AudioTrack.Builder setOffloadedPlayback(boolean);
method public android.media.AudioTrack.Builder setPerformanceMode(int);
method public android.media.AudioTrack.Builder setSessionId(int) throws java.lang.IllegalArgumentException;
method public android.media.AudioTrack.Builder setTransferMode(int) throws java.lang.IllegalArgumentException;
@@ -22150,6 +22175,12 @@
method public default void onRoutingChanged(android.media.AudioRouting);
}
+ public static abstract class AudioTrack.StreamEventCallback {
+ method public void onStreamDataRequest(android.media.AudioTrack);
+ method public void onStreamPresentationEnd(android.media.AudioTrack);
+ method public void onTearDown(android.media.AudioTrack);
+ }
+
public class CamcorderProfile {
method public static android.media.CamcorderProfile get(int);
method public static android.media.CamcorderProfile get(int, int);
@@ -37910,7 +37941,6 @@
}
public static final class FieldClassification.Match {
- method public java.lang.String getAlgorithm();
method public java.lang.String getRemoteId();
method public float getScore();
}
diff --git a/api/system-current.txt b/api/system-current.txt
index f35984a..66b6d99 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1777,9 +1777,9 @@
method public android.hardware.radio.ProgramSelector.Identifier[] getAllIds(int);
method public long getFirstId(int);
method public android.hardware.radio.ProgramSelector.Identifier getPrimaryId();
- method public int getProgramType();
+ method public deprecated int getProgramType();
method public android.hardware.radio.ProgramSelector.Identifier[] getSecondaryIds();
- method public long[] getVendorIds();
+ method public deprecated long[] getVendorIds();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.hardware.radio.ProgramSelector> CREATOR;
field public static final int IDENTIFIER_TYPE_AMFM_FREQUENCY = 1; // 0x1
@@ -1787,27 +1787,31 @@
field public static final int IDENTIFIER_TYPE_DAB_FREQUENCY = 8; // 0x8
field public static final int IDENTIFIER_TYPE_DAB_SCID = 7; // 0x7
field public static final int IDENTIFIER_TYPE_DAB_SIDECC = 5; // 0x5
+ field public static final int IDENTIFIER_TYPE_DAB_SID_EXT = 5; // 0x5
field public static final int IDENTIFIER_TYPE_DRMO_FREQUENCY = 10; // 0xa
- field public static final int IDENTIFIER_TYPE_DRMO_MODULATION = 11; // 0xb
+ field public static final deprecated int IDENTIFIER_TYPE_DRMO_MODULATION = 11; // 0xb
field public static final int IDENTIFIER_TYPE_DRMO_SERVICE_ID = 9; // 0x9
field public static final int IDENTIFIER_TYPE_HD_STATION_ID_EXT = 3; // 0x3
- field public static final int IDENTIFIER_TYPE_HD_SUBCHANNEL = 4; // 0x4
+ field public static final int IDENTIFIER_TYPE_HD_STATION_NAME = 10004; // 0x2714
+ field public static final deprecated int IDENTIFIER_TYPE_HD_SUBCHANNEL = 4; // 0x4
field public static final int IDENTIFIER_TYPE_INVALID = 0; // 0x0
field public static final int IDENTIFIER_TYPE_RDS_PI = 2; // 0x2
field public static final int IDENTIFIER_TYPE_SXM_CHANNEL = 13; // 0xd
field public static final int IDENTIFIER_TYPE_SXM_SERVICE_ID = 12; // 0xc
- field public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_END = 1999; // 0x7cf
- field public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_START = 1000; // 0x3e8
- field public static final int PROGRAM_TYPE_AM = 1; // 0x1
- field public static final int PROGRAM_TYPE_AM_HD = 3; // 0x3
- field public static final int PROGRAM_TYPE_DAB = 5; // 0x5
- field public static final int PROGRAM_TYPE_DRMO = 6; // 0x6
- field public static final int PROGRAM_TYPE_FM = 2; // 0x2
- field public static final int PROGRAM_TYPE_FM_HD = 4; // 0x4
- field public static final int PROGRAM_TYPE_INVALID = 0; // 0x0
- field public static final int PROGRAM_TYPE_SXM = 7; // 0x7
- field public static final int PROGRAM_TYPE_VENDOR_END = 1999; // 0x7cf
- field public static final int PROGRAM_TYPE_VENDOR_START = 1000; // 0x3e8
+ field public static final int IDENTIFIER_TYPE_VENDOR_END = 1999; // 0x7cf
+ field public static final deprecated int IDENTIFIER_TYPE_VENDOR_PRIMARY_END = 1999; // 0x7cf
+ field public static final deprecated int IDENTIFIER_TYPE_VENDOR_PRIMARY_START = 1000; // 0x3e8
+ field public static final int IDENTIFIER_TYPE_VENDOR_START = 1000; // 0x3e8
+ field public static final deprecated int PROGRAM_TYPE_AM = 1; // 0x1
+ field public static final deprecated int PROGRAM_TYPE_AM_HD = 3; // 0x3
+ field public static final deprecated int PROGRAM_TYPE_DAB = 5; // 0x5
+ field public static final deprecated int PROGRAM_TYPE_DRMO = 6; // 0x6
+ field public static final deprecated int PROGRAM_TYPE_FM = 2; // 0x2
+ field public static final deprecated int PROGRAM_TYPE_FM_HD = 4; // 0x4
+ field public static final deprecated int PROGRAM_TYPE_INVALID = 0; // 0x0
+ field public static final deprecated int PROGRAM_TYPE_SXM = 7; // 0x7
+ field public static final deprecated int PROGRAM_TYPE_VENDOR_END = 1999; // 0x7cf
+ field public static final deprecated int PROGRAM_TYPE_VENDOR_START = 1000; // 0x3e8
}
public static final class ProgramSelector.Identifier implements android.os.Parcelable {
@@ -1822,7 +1826,7 @@
public static abstract class ProgramSelector.IdentifierType implements java.lang.annotation.Annotation {
}
- public static abstract class ProgramSelector.ProgramType implements java.lang.annotation.Annotation {
+ public static abstract deprecated class ProgramSelector.ProgramType implements java.lang.annotation.Annotation {
}
public class RadioManager {
@@ -2860,9 +2864,18 @@
method public void onTetheringStarted();
}
+ public final class IpSecManager {
+ method public android.net.IpSecManager.IpSecTunnelInterface createIpSecTunnelInterface(java.net.InetAddress, java.net.InetAddress, android.net.Network) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
+ }
+
+ public static final class IpSecManager.IpSecTunnelInterface implements java.lang.AutoCloseable {
+ method public void close();
+ method public java.lang.String getInterfaceName();
+ }
+
public static class IpSecTransform.Builder {
+ method public android.net.IpSecTransform buildTunnelModeTransform(java.net.InetAddress, android.net.IpSecManager.SecurityParameterIndex) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
method public android.net.IpSecTransform.Builder setNattKeepalive(int);
- method public android.net.IpSecTransform.Builder setUnderlyingNetwork(android.net.Network);
}
public class NetworkKey implements android.os.Parcelable {
@@ -3862,20 +3875,10 @@
public abstract class AutofillFieldClassificationService extends android.app.Service {
method public android.os.IBinder onBind(android.content.Intent);
- method public java.util.List<java.lang.String> onGetAvailableAlgorithms();
- method public java.lang.String onGetDefaultAlgorithm();
- method public android.service.autofill.AutofillFieldClassificationService.Scores onGetScores(java.lang.String, android.os.Bundle, java.util.List<android.view.autofill.AutofillValue>, java.util.List<java.lang.String>);
+ method public float[][] onGetScores(java.lang.String, android.os.Bundle, java.util.List<android.view.autofill.AutofillValue>, java.util.List<java.lang.String>);
field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutofillFieldClassificationService";
- }
-
- public static final class AutofillFieldClassificationService.Scores implements android.os.Parcelable {
- ctor public AutofillFieldClassificationService.Scores(java.lang.String, int, int);
- ctor public AutofillFieldClassificationService.Scores(android.os.Parcel);
- method public int describeContents();
- method public java.lang.String getAlgorithm();
- method public float[][] getScores();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.service.autofill.AutofillFieldClassificationService.Scores> CREATOR;
+ field public static final java.lang.String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS = "android.autofill.field_classification.available_algorithms";
+ field public static final java.lang.String SERVICE_META_DATA_KEY_DEFAULT_ALGORITHM = "android.autofill.field_classification.default_algorithm";
}
}
diff --git a/cmds/incident_helper/src/TextParserBase.h b/cmds/incident_helper/src/TextParserBase.h
index c41612d..1667966 100644
--- a/cmds/incident_helper/src/TextParserBase.h
+++ b/cmds/incident_helper/src/TextParserBase.h
@@ -68,4 +68,4 @@
virtual status_t Parse(const int in, const int out) const;
};
-#endif // TEXT_PARSER_BASE_H
\ No newline at end of file
+#endif // TEXT_PARSER_BASE_H
diff --git a/cmds/incidentd/Android.mk b/cmds/incidentd/Android.mk
index 8420bc8..368a70b 100644
--- a/cmds/incidentd/Android.mk
+++ b/cmds/incidentd/Android.mk
@@ -134,6 +134,7 @@
libcutils \
libincident \
liblog \
+ libprotobuf-cpp-lite \
libprotoutil \
libselinux \
libservices \
diff --git a/cmds/incidentd/README.md b/cmds/incidentd/README.md
index ad0fa08..71c6deb 100644
--- a/cmds/incidentd/README.md
+++ b/cmds/incidentd/README.md
@@ -12,8 +12,8 @@
```
root$ mmm -j frameworks/base/cmds/incidentd && \
-adb push $OUT/data/nativetest/incidentd_test/* /data/nativetest/incidentd_test/ && \
-adb shell /data/nativetest/incidentd_test/incidentd_test 2>/dev/null
+adb push $OUT/data/nativetest/incidentd_test/* /data/nativetest/ && \
+adb shell /data/nativetest/incidentd_test 2>/dev/null
```
Run the test via AndroidTest.xml
diff --git a/cmds/incidentd/incidentd.rc b/cmds/incidentd/incidentd.rc
index 66667dc..1bd1468 100644
--- a/cmds/incidentd/incidentd.rc
+++ b/cmds/incidentd/incidentd.rc
@@ -19,4 +19,4 @@
on post-fs-data
# Create directory for incidentd
- mkdir /data/misc/incidents 0770 root root
+ mkdir /data/misc/incidents 0770 incidentd incidentd
diff --git a/cmds/incidentd/src/FdBuffer.cpp b/cmds/incidentd/src/FdBuffer.cpp
index 30dd339..0fff4e6 100644
--- a/cmds/incidentd/src/FdBuffer.cpp
+++ b/cmds/incidentd/src/FdBuffer.cpp
@@ -63,12 +63,14 @@
int64_t remainingTime = (mStartTime + timeout) - uptimeMillis();
if (remainingTime <= 0) {
+ if (DEBUG) ALOGD("timed out due to long read");
mTimedOut = true;
break;
}
int count = poll(&pfds, 1, remainingTime);
if (count == 0) {
+ if (DEBUG) ALOGD("timed out due to block calling poll");
mTimedOut = true;
break;
} else if (count < 0) {
@@ -129,6 +131,7 @@
int64_t remainingTime = (mStartTime + timeoutMs) - uptimeMillis();
if (remainingTime <= 0) {
+ if (DEBUG) ALOGD("timed out due to long read");
mTimedOut = true;
break;
}
@@ -136,6 +139,7 @@
// wait for any pfds to be ready to perform IO
int count = poll(pfds, 3, remainingTime);
if (count == 0) {
+ if (DEBUG) ALOGD("timed out due to block calling poll");
mTimedOut = true;
break;
} else if (count < 0) {
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp
index c4b54bb..1d5ab59 100644
--- a/cmds/incidentd/src/IncidentService.cpp
+++ b/cmds/incidentd/src/IncidentService.cpp
@@ -43,24 +43,44 @@
String16 const USAGE_STATS_PERMISSION("android.permission.PACKAGE_USAGE_STATS");
static Status
-checkIncidentPermissions()
+checkIncidentPermissions(const IncidentReportArgs& args)
{
+ uid_t callingUid = IPCThreadState::self()->getCallingUid();
+ if (callingUid == AID_ROOT || callingUid == AID_SHELL) {
+ // root doesn't have permission.DUMP if don't do this!
+ return Status::ok();
+ }
+
+ // checking calling permission.
if (!checkCallingPermission(DUMP_PERMISSION)) {
ALOGW("Calling pid %d and uid %d does not have permission: android.permission.DUMP",
- IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid());
+ IPCThreadState::self()->getCallingPid(), callingUid);
return Status::fromExceptionCode(Status::EX_SECURITY,
"Calling process does not have permission: android.permission.DUMP");
}
if (!checkCallingPermission(USAGE_STATS_PERMISSION)) {
ALOGW("Calling pid %d and uid %d does not have permission: android.permission.USAGE_STATS",
- IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid());
+ IPCThreadState::self()->getCallingPid(), callingUid);
return Status::fromExceptionCode(Status::EX_SECURITY,
"Calling process does not have permission: android.permission.USAGE_STATS");
}
+
+ // checking calling request uid permission.
+ switch (args.dest()) {
+ case DEST_LOCAL:
+ if (callingUid != AID_SHELL || callingUid != AID_ROOT) {
+ return Status::fromExceptionCode(Status::EX_SECURITY,
+ "Calling process does not have permission to get local data.");
+ }
+ case DEST_EXPLICIT:
+ if (callingUid != AID_SHELL || callingUid != AID_ROOT ||
+ callingUid != AID_STATSD || callingUid != AID_SYSTEM) {
+ return Status::fromExceptionCode(Status::EX_SECURITY,
+ "Calling process does not have permission to get explicit data.");
+ }
+ }
return Status::ok();
}
-
-
// ================================================================================
ReportRequestQueue::ReportRequestQueue()
{
@@ -71,7 +91,7 @@
}
void
-ReportRequestQueue::addRequest(const sp<ReportRequest>& request)
+ReportRequestQueue::addRequest(const sp<ReportRequest>& request)
{
unique_lock<mutex> lock(mLock);
mQueue.push_back(request);
@@ -196,7 +216,7 @@
{
ALOGI("reportIncident");
- Status status = checkIncidentPermissions();
+ Status status = checkIncidentPermissions(args);
if (!status.isOk()) {
return status;
}
@@ -212,7 +232,7 @@
{
ALOGI("reportIncidentToStream");
- Status status = checkIncidentPermissions();
+ Status status = checkIncidentPermissions(args);
if (!status.isOk()) {
return status;
}
diff --git a/cmds/incidentd/src/Reporter.cpp b/cmds/incidentd/src/Reporter.cpp
index 34930aa..bd559d6 100644
--- a/cmds/incidentd/src/Reporter.cpp
+++ b/cmds/incidentd/src/Reporter.cpp
@@ -251,7 +251,7 @@
// Override umask. Not super critical. If it fails go on with life.
chmod(filename, 0660);
- if (chown(filename, AID_SYSTEM, AID_SYSTEM)) {
+ if (chown(filename, AID_INCIDENTD, AID_INCIDENTD)) {
ALOGE("Unable to change ownership of incident file %s: %s\n", filename, strerror(errno));
status_t err = -errno;
unlink(mFilename.c_str());
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index 61d16f8..0827785 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -19,6 +19,7 @@
#include "Section.h"
#include <errno.h>
+#include <sys/prctl.h>
#include <unistd.h>
#include <wait.h>
@@ -30,7 +31,6 @@
#include <log/log_event_list.h>
#include <log/logprint.h>
#include <log/log_read.h>
-#include <private/android_filesystem_config.h> // for AID_NOBODY
#include <private/android_logger.h>
#include "FdBuffer.h"
@@ -55,26 +55,20 @@
fork_execute_incident_helper(const int id, const char* name, Fpipe& p2cPipe, Fpipe& c2pPipe)
{
const char* ihArgs[] { INCIDENT_HELPER, "-s", String8::format("%d", id).string(), NULL };
-
// fork used in multithreaded environment, avoid adding unnecessary code in child process
pid_t pid = fork();
if (pid == 0) {
- // child process executes incident helper as nobody
- if (setgid(AID_NOBODY) == -1) {
- ALOGW("%s can't change gid: %s", name, strerror(errno));
- _exit(EXIT_FAILURE);
- }
- if (setuid(AID_NOBODY) == -1) {
- ALOGW("%s can't change uid: %s", name, strerror(errno));
- _exit(EXIT_FAILURE);
- }
-
- if (dup2(p2cPipe.readFd(), STDIN_FILENO) != 0 || !p2cPipe.close() ||
- dup2(c2pPipe.writeFd(), STDOUT_FILENO) != 1 || !c2pPipe.close()) {
+ if (TEMP_FAILURE_RETRY(dup2(p2cPipe.readFd(), STDIN_FILENO)) != 0
+ || !p2cPipe.close()
+ || TEMP_FAILURE_RETRY(dup2(c2pPipe.writeFd(), STDOUT_FILENO)) != 1
+ || !c2pPipe.close()) {
ALOGW("%s can't setup stdin and stdout for incident helper", name);
_exit(EXIT_FAILURE);
}
+ /* make sure the child dies when incidentd dies */
+ prctl(PR_SET_PDEATHSIG, SIGKILL);
+
execv(INCIDENT_HELPER, const_cast<char**>(ihArgs));
ALOGW("%s failed in incident helper process: %s", name, strerror(errno));
@@ -87,11 +81,23 @@
}
// ================================================================================
+static status_t statusCode(int status) {
+ if (WIFSIGNALED(status)) {
+ ALOGD("return by signal: %s", strerror(WTERMSIG(status)));
+ return -WTERMSIG(status);
+ } else if (WIFEXITED(status) && WEXITSTATUS(status) > 0) {
+ ALOGD("return by exit: %s", strerror(WEXITSTATUS(status)));
+ return -WEXITSTATUS(status);
+ }
+ return NO_ERROR;
+}
+
static status_t kill_child(pid_t pid) {
int status;
+ ALOGD("try to kill child process %d", pid);
kill(pid, SIGKILL);
if (waitpid(pid, &status, 0) == -1) return -1;
- return WIFEXITED(status) == 0 ? NO_ERROR : -WEXITSTATUS(status);
+ return statusCode(status);
}
static status_t wait_child(pid_t pid) {
@@ -104,7 +110,7 @@
nanosleep(&WAIT_INTERVAL_NS, NULL);
}
if (!died) return kill_child(pid);
- return WIFEXITED(status) == 0 ? NO_ERROR : -WEXITSTATUS(status);
+ return statusCode(status);
}
// ================================================================================
static const Privacy*
@@ -275,9 +281,9 @@
status_t readStatus = buffer.readProcessedDataInStream(fd, p2cPipe.writeFd(), c2pPipe.readFd(),
this->timeoutMs, mIsSysfs);
if (readStatus != NO_ERROR || buffer.timedOut()) {
- ALOGW("FileSection '%s' failed to read data from incident helper: %s, timedout: %s, kill: %s",
- this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false",
- strerror(-kill_child(pid)));
+ ALOGW("FileSection '%s' failed to read data from incident helper: %s, timedout: %s",
+ this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false");
+ kill_child(pid);
return readStatus;
}
@@ -543,10 +549,10 @@
close(cmdPipe.writeFd());
status_t readStatus = buffer.read(ihPipe.readFd(), this->timeoutMs);
if (readStatus != NO_ERROR || buffer.timedOut()) {
- ALOGW("CommandSection '%s' failed to read data from incident helper: %s, "
- "timedout: %s, kill command: %s, kill incident helper: %s",
- this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false",
- strerror(-kill_child(cmdPid)), strerror(-kill_child(ihPid)));
+ ALOGW("CommandSection '%s' failed to read data from incident helper: %s, timedout: %s",
+ this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false");
+ kill_child(cmdPid);
+ kill_child(ihPid);
return readStatus;
}
diff --git a/cmds/incidentd/src/io_util.cpp b/cmds/incidentd/src/io_util.cpp
index af4a35c..90f543e 100644
--- a/cmds/incidentd/src/io_util.cpp
+++ b/cmds/incidentd/src/io_util.cpp
@@ -23,7 +23,7 @@
status_t write_all(int fd, uint8_t const* buf, size_t size)
{
while (size > 0) {
- ssize_t amt = ::write(fd, buf, size);
+ ssize_t amt = TEMP_FAILURE_RETRY(::write(fd, buf, size));
if (amt < 0) {
return -errno;
}
diff --git a/cmds/incidentd/src/report_directory.cpp b/cmds/incidentd/src/report_directory.cpp
index 65030b3..20111d8 100644
--- a/cmds/incidentd/src/report_directory.cpp
+++ b/cmds/incidentd/src/report_directory.cpp
@@ -58,26 +58,9 @@
goto done;
}
} else {
- if (mkdir(dir, 0770)) {
- ALOGE("No incident reports today. "
- "Unable to create incident report dir %s: %s", dir,
- strerror(errno));
- err = -errno;
- goto done;
- }
- if (chmod(dir, 0770)) {
- ALOGE("No incident reports today. "
- "Unable to set permissions for incident report dir %s: %s", dir,
- strerror(errno));
- err = -errno;
- goto done;
- }
- if (chown(dir, AID_SYSTEM, AID_SYSTEM)) {
- ALOGE("No incident reports today. Unable to change ownership of dir %s: %s\n",
- dir, strerror(errno));
- err = -errno;
- goto done;
- }
+ ALOGE("No such directory %s, something wrong.", dir);
+ err = -1;
+ goto done;
}
if (!last) {
*d++ = '/';
@@ -97,8 +80,7 @@
err = BAD_VALUE;
goto done;
}
- if ((st.st_uid != AID_SYSTEM && st.st_uid != AID_ROOT) ||
- (st.st_gid != AID_SYSTEM && st.st_gid != AID_ROOT)) {
+ if (st.st_uid != AID_INCIDENTD || st.st_gid != AID_INCIDENTD) {
ALOGE("No incident reports today. Owner is %d and group is %d on report directory %s",
st.st_uid, st.st_gid, directory);
err = BAD_VALUE;
diff --git a/cmds/incidentd/tests/Reporter_test.cpp b/cmds/incidentd/tests/Reporter_test.cpp
index 531c9f2..c494bd6 100644
--- a/cmds/incidentd/tests/Reporter_test.cpp
+++ b/cmds/incidentd/tests/Reporter_test.cpp
@@ -17,6 +17,7 @@
#include "Reporter.h"
#include <android/os/BnIncidentReportStatusListener.h>
+#include <frameworks/base/libs/incident/proto/android/os/header.pb.h>
#include <android-base/file.h>
#include <android-base/test_utils.h>
@@ -29,6 +30,7 @@
using namespace android;
using namespace android::base;
using namespace android::binder;
+using namespace android::os;
using namespace std;
using ::testing::StrEq;
using ::testing::Test;
@@ -141,7 +143,8 @@
IncidentReportArgs args1, args2;
args1.addSection(1);
args2.addSection(2);
- std::vector<uint8_t> header {'a', 'b', 'c', 'd', 'e'};
+ IncidentHeaderProto header;
+ header.set_alert_id(12);
args2.addHeader(header);
sp<ReportRequest> r1 = new ReportRequest(args1, l, tf.fd);
sp<ReportRequest> r2 = new ReportRequest(args2, l, tf.fd);
@@ -153,7 +156,7 @@
string result;
ReadFileToString(tf.path, &result);
- EXPECT_THAT(result, StrEq("\n\x5" "abcde"));
+ EXPECT_THAT(result, StrEq("\n\x2" "\b\f"));
EXPECT_EQ(l->startInvoked, 2);
EXPECT_EQ(l->finishInvoked, 2);
@@ -164,13 +167,16 @@
TEST_F(ReporterTest, RunReportToGivenDirectory) {
IncidentReportArgs args;
- args.addHeader({'1', '2', '3'});
- args.addHeader({'a', 'b', 'c', 'd'});
+ IncidentHeaderProto header1, header2;
+ header1.set_alert_id(12);
+ header2.set_reason("abcd");
+ args.addHeader(header1);
+ args.addHeader(header2);
sp<ReportRequest> r = new ReportRequest(args, l, -1);
reporter->batch.add(r);
ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport());
vector<string> results = InspectFiles();
ASSERT_EQ((int)results.size(), 1);
- EXPECT_EQ(results[0], "\n\x3" "123\n\x4" "abcd");
+ EXPECT_EQ(results[0], "\n\x2" "\b\f\n\x6" "\x12\x4" "abcd");
}
diff --git a/cmds/incidentd/tests/Section_test.cpp b/cmds/incidentd/tests/Section_test.cpp
index cbfb896..2cfd7df 100644
--- a/cmds/incidentd/tests/Section_test.cpp
+++ b/cmds/incidentd/tests/Section_test.cpp
@@ -18,6 +18,7 @@
#include <android-base/file.h>
#include <android-base/test_utils.h>
+#include <frameworks/base/libs/incident/proto/android/os/header.pb.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <string.h>
@@ -34,6 +35,7 @@
using namespace android::base;
using namespace android::binder;
+using namespace android::os;
using namespace std;
using ::testing::StrEq;
using ::testing::internal::CaptureStdout;
@@ -66,15 +68,9 @@
args1.addSection(2);
args2.setAll(true);
- vector<uint8_t> head1;
- head1.push_back('a');
- head1.push_back('x');
- head1.push_back('e');
-
- vector<uint8_t> head2;
- head2.push_back('p');
- head2.push_back('u');
- head2.push_back('p');
+ IncidentHeaderProto head1, head2;
+ head1.set_reason("axe");
+ head2.set_reason("pup");
args1.addHeader(head1);
args1.addHeader(head2);
@@ -87,10 +83,10 @@
string content;
CaptureStdout();
ASSERT_EQ(NO_ERROR, hs.Execute(&requests));
- EXPECT_THAT(GetCapturedStdout(), StrEq("\n\x3" "axe\n\x03pup"));
+ EXPECT_THAT(GetCapturedStdout(), StrEq("\n\x5" "\x12\x3" "axe\n\x05\x12\x03pup"));
EXPECT_TRUE(ReadFileToString(output2.path, &content));
- EXPECT_THAT(content, StrEq("\n\x03pup"));
+ EXPECT_THAT(content, StrEq("\n\x05\x12\x03pup"));
}
TEST(SectionTest, FileSection) {
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 5eff548..01f4a84 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -34,6 +34,7 @@
src/config/ConfigKey.cpp \
src/config/ConfigListener.cpp \
src/config/ConfigManager.cpp \
+ src/external/Perfetto.cpp \
src/external/StatsPuller.cpp \
src/external/StatsCompanionServicePuller.cpp \
src/external/SubsystemSleepStatePuller.cpp \
@@ -57,6 +58,7 @@
src/metrics/MetricsManager.cpp \
src/metrics/metrics_manager_util.cpp \
src/packages/UidMap.cpp \
+ src/perfetto/perfetto_config.proto \
src/storage/StorageManager.cpp \
src/StatsLogProcessor.cpp \
src/StatsService.cpp \
@@ -209,6 +211,7 @@
LOCAL_SRC_FILES := \
src/stats_log.proto \
src/statsd_config.proto \
+ src/perfetto/perfetto_config.proto \
src/atoms.proto
LOCAL_PROTOC_OPTIMIZE_TYPE := lite
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
index f10b2cf..e34aed3 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
@@ -18,7 +18,9 @@
#include "Log.h"
#include "AnomalyTracker.h"
+#include "external/Perfetto.h"
#include "guardrail/StatsdStats.h"
+#include "frameworks/base/libs/incident/proto/android/os/header.pb.h"
#include <android/os/IIncidentManager.h>
#include <android/os/IncidentReportArgs.h>
@@ -239,7 +241,7 @@
}
break;
case Subscription::SubscriberInformationCase::kPerfettoDetails:
- ALOGW("Perfetto reports not implemented.");
+ CollectPerfettoTraceAndUploadToDropbox(subscription.perfetto_details());
break;
default:
break;
@@ -253,10 +255,10 @@
for (const auto section : incidentdSections) {
incidentReport.addSection(section);
}
- int64_t alertId = mAlert.id();
- std::vector<uint8_t> header;
- uint8_t* src = static_cast<uint8_t*>(static_cast<void*>(&alertId));
- header.insert(header.end(), src, src + sizeof(int64_t));
+ android::os::IncidentHeaderProto header;
+ header.set_alert_id(mAlert.id());
+ header.mutable_config_key()->set_uid(mConfigKey.GetUid());
+ header.mutable_config_key()->set_id(mConfigKey.GetId());
incidentReport.addHeader(header);
service->reportIncident(incidentReport);
} else {
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 7d513cd..ef99c9f 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -84,6 +84,7 @@
AppStartChanged app_start_changed = 48;
AppStartCancelChanged app_start_cancel_changed = 49;
AppStartFullyDrawnChanged app_start_fully_drawn_changed = 50;
+ LmkEventOccurred lmk_event_occurred = 51;
// TODO: Reorder the numbering so that the most frequent occur events occur in the first 15.
}
@@ -101,6 +102,11 @@
CpuTimePerUidFreq cpu_time_per_uid_freq = 10010;
WifiActivityEnergyInfo wifi_activity_energy_info = 10011;
ModemActivityInfo modem_activity_info = 10012;
+ MemoryStat memory_stat = 10013;
+ CpuSuspendTime cpu_suspend_time = 10014;
+ CpuIdleTime cpu_idle_time = 10015;
+ CpuActiveTime cpu_active_time = 10016;
+ CpuClusterTime cpu_cluster_time = 10017;
}
}
@@ -1177,3 +1183,91 @@
// product of current(mA), voltage(V) and time(ms)
optional uint64 energy_used = 10;
}
+
+/*
+ * Logs the memory stats for a process
+ */
+message MemoryStat {
+ // The uid if available. -1 means not available.
+ optional int32 uid = 1;
+
+ // The app package name.
+ optional string pkg_name = 2;
+
+ // # of page-faults
+ optional int64 pgfault = 3;
+
+ // # of major page-faults
+ optional int64 pgmajfault = 4;
+
+ // RSS+CACHE(+SWAP)
+ optional int64 usage_in_bytes = 5;
+}
+
+/*
+ * Logs the event when LMKD kills a process to reduce memory pressure
+ * Logged from:
+ * system/core/lmkd/lmkd.c
+ */
+message LmkEventOccurred {
+ // The uid if available. -1 means not available.
+ optional int32 uid = 1;
+
+ // The app package name.
+ optional string pkg_name = 2;
+
+ // oom adj score.
+ optional int32 oom_score = 3;
+
+ // Used as start/stop boundaries for the event
+ enum State {
+ UNKNOWN = 0;
+ START = 1;
+ END = 2;
+ }
+ optional State state = 4;
+}
+
+/*
+ * Cpu syspend time for cpu power calculation.
+ */
+message CpuSuspendTime {
+ optional uint64 time = 1;
+}
+
+/*
+ * Cpu idle time for cpu power calculation.
+ */
+message CpuIdleTime {
+ optional uint64 time = 1;
+}
+
+/*
+ * Reads from /proc/uid_concurrent_active_time which has the format:
+ * active: X (X is # cores)
+ * [uid0]: [time-0] [time-1] [time-2] ... (# entries = # cores)
+ * [uid1]: [time-0] [time-1] [time-2] ... ...
+ * ...
+ * Time-N means the CPU time a UID spent running concurrently with N other processes.
+ * The file contains a monotonically increasing count of time for a single boot.
+ */
+message CpuActiveTime {
+ optional uint64 uid = 1;
+ optional uint64 idx = 2;
+ optional uint64 time_ms = 3;
+}
+
+/**
+ * Reads from /proc/uid_concurrent_policy_time which has the format:
+ * policy0: X policy4: Y (there are X cores on policy0, Y cores on policy4)
+ * [uid0]: [time-0-0] [time-0-1] ... [time-1-0] [time-1-1] ...
+ * [uid1]: [time-0-0] [time-0-1] ... [time-1-0] [time-1-1] ...
+ * ...
+ * Time-X-Y means the time a UID spent on clusterX running concurrently with Y other processes.
+ * The file contains a monotonically increasing count of time for a single boot.
+ */
+message CpuClusterTime {
+ optional uint64 uid = 1;
+ optional uint64 idx = 2;
+ optional uint64 time_ms = 3;
+}
\ No newline at end of file
diff --git a/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp
index a61afb4..a751273 100644
--- a/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp
+++ b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp
@@ -34,7 +34,7 @@
namespace os {
namespace statsd {
-static const string sProcFile = "/proc/uid_cputime/show_uid_stat";
+static const string sProcFile = "/proc/uid_time_in_state";
static const int kLineBufferSize = 1024;
/**
diff --git a/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.cpp b/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.cpp
new file mode 100644
index 0000000..7a2d199
--- /dev/null
+++ b/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.cpp
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+#define DEBUG true // STOPSHIP if true
+#include "Log.h"
+
+#include <fstream>
+
+#include "KernelUidCpuActiveTimeReader.h"
+#include "guardrail/StatsdStats.h"
+#include "logd/LogEvent.h"
+#include "statslog.h"
+
+using std::make_shared;
+using std::shared_ptr;
+using std::ifstream;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+static const string sProcFile = "/proc/uid_concurrent_active_time";
+static const int kLineBufferSize = 1024;
+
+/**
+ * Reads /proc/uid_concurrent_active_time which has the format:
+ * active: X (X is # cores)
+ * [uid0]: [time-0] [time-1] [time-2] ... (# entries = # cores)
+ * [uid1]: [time-0] [time-1] [time-2] ... ...
+ * ...
+ * Time-N means the CPU time a UID spent running concurrently with N other processes.
+ * The file contains a monotonically increasing count of time for a single boot.
+ */
+KernelUidCpuActiveTimeReader::KernelUidCpuActiveTimeReader() : StatsPuller(android::util::CPU_ACTIVE_TIME) {
+}
+
+bool KernelUidCpuActiveTimeReader::PullInternal(vector<shared_ptr<LogEvent>>* data) {
+ data->clear();
+
+ ifstream fin;
+ fin.open(sProcFile);
+ if (!fin.good()) {
+ VLOG("Failed to read pseudo file %s", sProcFile.c_str());
+ return false;
+ }
+
+ uint64_t timestamp = time(nullptr) * NS_PER_SEC;
+ char buf[kLineBufferSize];
+ char* pch;
+ while (!fin.eof()) {
+ fin.getline(buf, kLineBufferSize);
+ pch = strtok(buf, " :");
+ if (pch == NULL) break;
+ uint64_t uid = std::stoull(pch);
+ pch = strtok(NULL, " ");
+ uint64_t timeMs;
+ int idx = 0;
+ do {
+ timeMs = std::stoull(pch);
+ auto ptr = make_shared<LogEvent>(mTagId, timestamp);
+ ptr->write(uid);
+ ptr->write(idx);
+ ptr->write(timeMs);
+ ptr->init();
+ data->push_back(ptr);
+ VLOG("uid %lld, freq idx %d, active time %lld", (long long)uid, idx, (long long)timeMs);
+ idx++;
+ pch = strtok(NULL, " ");
+ } while (pch != NULL);
+ }
+ return true;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.h b/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.h
new file mode 100644
index 0000000..fcae35f
--- /dev/null
+++ b/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.h
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <utils/String16.h>
+#include "StatsPuller.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class KernelUidCpuActiveTimeReader : public StatsPuller {
+ public:
+ KernelUidCpuActiveTimeReader();
+ bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.cpp b/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.cpp
new file mode 100644
index 0000000..7426e74
--- /dev/null
+++ b/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.cpp
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+#define DEBUG true // STOPSHIP if true
+#include "Log.h"
+
+#include <fstream>
+#include "KernelUidCpuClusterTimeReader.h"
+#include "guardrail/StatsdStats.h"
+#include "logd/LogEvent.h"
+#include "statslog.h"
+
+using std::make_shared;
+using std::shared_ptr;
+using std::ifstream;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+static const string sProcFile = "/proc/uid_concurrent_policy_time";
+static const int kLineBufferSize = 1024;
+
+/**
+ * Reads /proc/uid_concurrent_policy_time which has the format:
+ * policy0: X policy4: Y (there are X cores on policy0, Y cores on policy4)
+ * [uid0]: [time-0-0] [time-0-1] ... [time-1-0] [time-1-1] ...
+ * [uid1]: [time-0-0] [time-0-1] ... [time-1-0] [time-1-1] ...
+ * ...
+ * Time-X-Y means the time a UID spent on clusterX running concurrently with Y other processes.
+ * The file contains a monotonically increasing count of time for a single boot.
+ */
+KernelUidCpuClusterTimeReader::KernelUidCpuClusterTimeReader() : StatsPuller(android::util::CPU_CLUSTER_TIME) {
+}
+
+bool KernelUidCpuClusterTimeReader::PullInternal(vector<shared_ptr<LogEvent>>* data) {
+ data->clear();
+
+ ifstream fin;
+ fin.open(sProcFile);
+ if (!fin.good()) {
+ VLOG("Failed to read pseudo file %s", sProcFile.c_str());
+ return false;
+ }
+
+ uint64_t timestamp = time(nullptr) * NS_PER_SEC;
+ char buf[kLineBufferSize];
+ char* pch;
+ while (!fin.eof()) {
+ fin.getline(buf, kLineBufferSize);
+ pch = strtok(buf, " :");
+ if (pch == NULL) break;
+ uint64_t uid = std::stoull(pch);
+ pch = strtok(NULL, " ");
+ uint64_t timeMs;
+ int idx = 0;
+ do {
+ timeMs = std::stoull(pch);
+ auto ptr = make_shared<LogEvent>(mTagId, timestamp);
+ ptr->write(uid);
+ ptr->write(idx);
+ ptr->write(timeMs);
+ ptr->init();
+ data->push_back(ptr);
+ VLOG("uid %lld, freq idx %d, cluster time %lld", (long long)uid, idx, (long long)timeMs);
+ idx++;
+ pch = strtok(NULL, " ");
+ } while (pch != NULL);
+ }
+ return true;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.h b/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.h
new file mode 100644
index 0000000..90236ae
--- /dev/null
+++ b/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.h
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <utils/String16.h>
+#include "StatsPuller.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+/**
+ * Reads /proc/uid_cputime/show_uid_stat which has the line format:
+ *
+ * uid: user_time_micro_seconds system_time_micro_seconds
+ *
+ * This provides the time a UID's processes spent executing in user-space and kernel-space.
+ * The file contains a monotonically increasing count of time for a single boot.
+ */
+class KernelUidCpuClusterTimeReader : public StatsPuller {
+ public:
+ KernelUidCpuClusterTimeReader();
+ bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/Perfetto.cpp b/cmds/statsd/src/external/Perfetto.cpp
new file mode 100644
index 0000000..f7b33e7
--- /dev/null
+++ b/cmds/statsd/src/external/Perfetto.cpp
@@ -0,0 +1,111 @@
+/*
+ * 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.
+ */
+
+#include "Log.h"
+
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert
+
+#include <android-base/unique_fd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <string>
+
+namespace {
+const char kDropboxTag[] = "perfetto";
+}
+
+namespace android {
+namespace os {
+namespace statsd {
+
+bool CollectPerfettoTraceAndUploadToDropbox(const PerfettoDetails& config) {
+ ALOGD("Starting trace collection through perfetto");
+
+ if (!config.has_trace_config()) {
+ ALOGE("The perfetto trace config is empty, aborting");
+ return false;
+ }
+
+ android::base::unique_fd readPipe;
+ android::base::unique_fd writePipe;
+ if (!android::base::Pipe(&readPipe, &writePipe)) {
+ ALOGE("pipe() failed while calling the Perfetto client: %s", strerror(errno));
+ return false;
+ }
+
+ pid_t pid = fork();
+ if (pid < 0) {
+ ALOGE("fork() failed while calling the Perfetto client: %s", strerror(errno));
+ return false;
+ }
+
+ if (pid == 0) {
+ // Child process.
+
+ // No malloc calls or library calls after this point. Remember that even
+ // ALOGx (aka android_printLog()) can use dynamic memory for vsprintf().
+
+ writePipe.reset(); // Close the write end (owned by the main process).
+
+ // Replace stdin with |readPipe| so the main process can write into it.
+ if (dup2(readPipe.get(), STDIN_FILENO) < 0) _exit(1);
+ execl("/system/bin/perfetto", "perfetto", "--background", "--config", "-", "--dropbox",
+ kDropboxTag, nullptr);
+
+ // execl() doesn't return in case of success, if we get here something failed.
+ _exit(1);
+ }
+
+ // Main process.
+
+ readPipe.reset(); // Close the read end (owned by the child process).
+
+ // Using fopen() because fwrite() has the right logic to chunking write()
+ // over a pipe (see __sfvwrite()).
+ FILE* writePipeStream = fdopen(writePipe.get(), "wb");
+ if (!writePipeStream) {
+ ALOGE("fdopen() failed while calling the Perfetto client: %s", strerror(errno));
+ return false;
+ }
+
+ std::string cfgProto = config.trace_config().SerializeAsString();
+ size_t bytesWritten = fwrite(cfgProto.data(), 1, cfgProto.size(), writePipeStream);
+ fclose(writePipeStream);
+ if (bytesWritten != cfgProto.size() || cfgProto.size() == 0) {
+ ALOGE("fwrite() failed (ret: %zd) while calling the Perfetto client: %s", bytesWritten,
+ strerror(errno));
+ return false;
+ }
+
+ // This does NOT wait for the full duration of the trace. It just waits until the process
+ // has read the config from stdin and detached.
+ int childStatus = 0;
+ waitpid(pid, &childStatus, 0);
+ if (!WIFEXITED(childStatus) || WEXITSTATUS(childStatus) != 0) {
+ ALOGE("Child process failed (0x%x) while calling the Perfetto client", childStatus);
+ return false;
+ }
+
+ ALOGD("CollectPerfettoTraceAndUploadToDropbox() succeeded");
+ return true;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/Perfetto.h b/cmds/statsd/src/external/Perfetto.h
new file mode 100644
index 0000000..e2e0253
--- /dev/null
+++ b/cmds/statsd/src/external/Perfetto.h
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+using android::os::StatsLogEventWrapper;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class PerfettoDetails; // Declared in statsd_config.pb.h
+
+// Starts the collection of a Perfetto trace with the given |config|.
+// The trace is uploaded to Dropbox by the perfetto cmdline util once done.
+// This method returns immediately after passing the config and does NOT wait
+// for the full duration of the trace.
+bool CollectPerfettoTraceAndUploadToDropbox(const PerfettoDetails& config);
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
index bb2e8c0..79f1a5d 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
@@ -64,6 +64,8 @@
mPullers.insert({android::util::CPU_TIME_PER_FREQ, make_shared<StatsCompanionServicePuller>(android::util::CPU_TIME_PER_FREQ)});
mPullers.insert({android::util::CPU_TIME_PER_UID, make_shared<CpuTimePerUidPuller>()});
mPullers.insert({android::util::CPU_TIME_PER_UID_FREQ, make_shared<CpuTimePerUidFreqPuller>()});
+ mPullers.insert({android::util::CPU_SUSPEND_TIME, make_shared<StatsCompanionServicePuller>(android::util::CPU_SUSPEND_TIME)});
+ mPullers.insert({android::util::CPU_IDLE_TIME, make_shared<StatsCompanionServicePuller>(android::util::CPU_IDLE_TIME)});
mStatsCompanionService = StatsService::getStatsCompanionService();
}
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index c7550f7..e985873 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -285,8 +285,15 @@
interval.start = value;
interval.startUpdated = true;
} else {
+ // Generally we expect value to be monotonically increasing.
+ // If not, there was a reset event. We take the absolute value as
+ // diff in this case.
if (interval.startUpdated) {
- interval.sum += (value - interval.start);
+ if (value > interval.start) {
+ interval.sum += (value - interval.start);
+ } else {
+ interval.sum += value;
+ }
interval.startUpdated = false;
} else {
VLOG("No start for matching end %ld", value);
diff --git a/cmds/statsd/src/perfetto/perfetto_config.proto b/cmds/statsd/src/perfetto/perfetto_config.proto
new file mode 100644
index 0000000..dc868f9
--- /dev/null
+++ b/cmds/statsd/src/perfetto/perfetto_config.proto
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+
+package perfetto.protos;
+
+message DataSourceConfig {
+ message FtraceConfig {
+ repeated string event_names = 1;
+ }
+
+ optional string name = 1;
+
+ optional uint32 target_buffer = 2;
+
+ optional FtraceConfig ftrace_config = 100;
+}
+
+message TraceConfig {
+ message BufferConfig {
+ optional uint32 size_kb = 1;
+
+ enum OptimizeFor {
+ DEFAULT = 0;
+ ONE_SHOT_READ = 1;
+
+ }
+ optional OptimizeFor optimize_for = 3;
+
+ enum FillPolicy {
+ UNSPECIFIED = 0;
+ RING_BUFFER = 1;
+ }
+ optional FillPolicy fill_policy = 4;
+ }
+ repeated BufferConfig buffers = 1;
+
+ message DataSource {
+ optional protos.DataSourceConfig config = 1;
+
+ repeated string producer_name_filter = 2;
+ }
+ repeated DataSource data_sources = 2;
+
+ optional uint32 duration_ms = 3;
+}
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 6247854..cd60ee7 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -22,6 +22,8 @@
option java_package = "com.android.internal.os";
option java_outer_classname = "StatsdConfigProto";
+import "frameworks/base/cmds/statsd/src/perfetto/perfetto_config.proto";
+
enum Position {
POSITION_UNKNOWN = 0;
FIRST = 1;
@@ -272,7 +274,7 @@
}
message PerfettoDetails {
- optional int32 perfetto_stuff = 1;
+ optional perfetto.protos.TraceConfig trace_config = 1;
}
message Subscription {
diff --git a/cmds/statsd/tools/dogfood/Android.mk b/cmds/statsd/tools/dogfood/Android.mk
index a4c0800..32a85b1 100644
--- a/cmds/statsd/tools/dogfood/Android.mk
+++ b/cmds/statsd/tools/dogfood/Android.mk
@@ -19,13 +19,10 @@
LOCAL_PACKAGE_NAME := StatsdDogfood
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_SRC_FILES += ../../src/stats_log.proto \
- ../../src/atoms.proto \
- ../../src/statsd_config.proto
-LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/../../src/
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_STATIC_JAVA_LIBRARIES := platformprotoslite
+LOCAL_STATIC_JAVA_LIBRARIES := platformprotoslite \
+ statsdprotolite
LOCAL_PROTOC_OPTIMIZE_TYPE := lite
LOCAL_PRIVILEGED_MODULE := true
diff --git a/cmds/statsd/tools/loadtest/Android.mk b/cmds/statsd/tools/loadtest/Android.mk
index 0a0fd66..f5722c2 100644
--- a/cmds/statsd/tools/loadtest/Android.mk
+++ b/cmds/statsd/tools/loadtest/Android.mk
@@ -21,6 +21,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SRC_FILES += ../../src/stats_log.proto \
../../src/atoms.proto \
+ ../../src/perfetto/perfetto_config.proto \
../../src/statsd_config.proto
LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/../../src/
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 97dcb90..0a4541b 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -363,6 +363,11 @@
*/
public static final int GLOBAL_ACTION_LOCK_SCREEN = 8;
+ /**
+ * Action to take a screenshot
+ */
+ public static final int GLOBAL_ACTION_TAKE_SCREENSHOT = 9;
+
private static final String LOG_TAG = "AccessibilityService";
/**
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 57f9f67..7ca6802 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1326,6 +1326,25 @@
}
/**
+ * Retrieve the human readable mode.
+ * @hide
+ */
+ public static String modeToString(int mode) {
+ switch (mode) {
+ case MODE_ALLOWED:
+ return "allow";
+ case MODE_IGNORED:
+ return "ignore";
+ case MODE_ERRORED:
+ return "deny";
+ case MODE_DEFAULT:
+ return "default";
+ default:
+ return "mode=" + mode;
+ }
+ }
+
+ /**
* Retrieve whether the op allows itself to be reset.
* @hide
*/
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index b469de5..c5a58f2 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -17,6 +17,7 @@
package android.app;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
@@ -458,7 +459,8 @@
*
* @see Context#startActivity(Intent, Bundle)
*/
- public Activity startActivitySync(Intent intent, @Nullable Bundle options) {
+ @NonNull
+ public Activity startActivitySync(@NonNull Intent intent, @Nullable Bundle options) {
validateNotAppThread();
synchronized (mSync) {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 0be5564..d465e0d 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1157,9 +1157,17 @@
public static final String POLICY_DISABLE_SCREEN_CAPTURE = "policy_disable_screen_capture";
/**
+ * Constant to indicate the feature of mandatory backups. Used as argument to
+ * {@link #createAdminSupportIntent(String)}.
+ * @see #setMandatoryBackupTransport(ComponentName, ComponentName)
+ */
+ public static final String POLICY_MANDATORY_BACKUPS = "policy_mandatory_backups";
+
+ /**
* A String indicating a specific restricted feature. Can be a user restriction from the
* {@link UserManager}, e.g. {@link UserManager#DISALLOW_ADJUST_VOLUME}, or one of the values
- * {@link #POLICY_DISABLE_CAMERA} or {@link #POLICY_DISABLE_SCREEN_CAPTURE}.
+ * {@link #POLICY_DISABLE_CAMERA}, {@link #POLICY_DISABLE_SCREEN_CAPTURE} or
+ * {@link #POLICY_MANDATORY_BACKUPS}.
* @see #createAdminSupportIntent(String)
* @hide
*/
@@ -6806,7 +6814,8 @@
* @param restriction Indicates for which feature the dialog should be displayed. Can be a
* user restriction from {@link UserManager}, e.g.
* {@link UserManager#DISALLOW_ADJUST_VOLUME}, or one of the constants
- * {@link #POLICY_DISABLE_CAMERA} or {@link #POLICY_DISABLE_SCREEN_CAPTURE}.
+ * {@link #POLICY_DISABLE_CAMERA}, {@link #POLICY_DISABLE_SCREEN_CAPTURE} or
+ * {@link #POLICY_MANDATORY_BACKUPS}.
* @return Intent An intent to be used to start the dialog-activity if the restriction is
* set by an admin, or null if the restriction does not exist or no admin set it.
*/
diff --git a/core/java/android/app/slice/ISliceManager.aidl b/core/java/android/app/slice/ISliceManager.aidl
index 5f0e542..4461b16 100644
--- a/core/java/android/app/slice/ISliceManager.aidl
+++ b/core/java/android/app/slice/ISliceManager.aidl
@@ -29,4 +29,6 @@
void unpinSlice(String pkg, in Uri uri);
boolean hasSliceAccess(String pkg);
SliceSpec[] getPinnedSpecs(in Uri uri, String pkg);
+ int checkSlicePermission(in Uri uri, String pkg, int pid, int uid);
+ void grantPermissionFromUser(in Uri uri, String pkg, String callingPkg, boolean allSlices);
}
diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java
index 6093a4a..5bd3440 100644
--- a/core/java/android/app/slice/Slice.java
+++ b/core/java/android/app/slice/Slice.java
@@ -156,6 +156,13 @@
*/
public static final String HINT_SEE_MORE = "see_more";
/**
+ * A hint to tell the system that this slice cares about the return value of
+ * {@link SliceProvider#getBindingPackage} and should not cache the result
+ * for multiple apps.
+ * @hide
+ */
+ public static final String HINT_CALLER_NEEDED = "caller_needed";
+ /**
* Key to retrieve an extra added to an intent when a control is changed.
*/
public static final String EXTRA_TOGGLE_STATE = "android.app.slice.extra.TOGGLE_STATE";
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index 74864cb..09c420c 100644
--- a/core/java/android/app/slice/SliceManager.java
+++ b/core/java/android/app/slice/SliceManager.java
@@ -53,12 +53,34 @@
private static final String TAG = "SliceManager";
+ /**
+ * @hide
+ */
+ public static final String ACTION_REQUEST_SLICE_PERMISSION =
+ "android.intent.action.REQUEST_SLICE_PERMISSION";
+
private final ISliceManager mService;
private final Context mContext;
private final ArrayMap<Pair<Uri, SliceCallback>, ISliceListener> mListenerLookup =
new ArrayMap<>();
/**
+ * Permission denied.
+ * @hide
+ */
+ public static final int PERMISSION_DENIED = -1;
+ /**
+ * Permission granted.
+ * @hide
+ */
+ public static final int PERMISSION_GRANTED = 0;
+ /**
+ * Permission just granted by the user, and should be granted uri permission as well.
+ * @hide
+ */
+ public static final int PERMISSION_USER_GRANTED = 1;
+
+ /**
* @hide
*/
public SliceManager(Context context, Handler handler) throws ServiceNotFoundException {
@@ -284,7 +306,7 @@
extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri);
extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
new ArrayList<>(supportedSpecs));
- final Bundle res = provider.call(resolver.getPackageName(), SliceProvider.METHOD_SLICE,
+ final Bundle res = provider.call(mContext.getPackageName(), SliceProvider.METHOD_SLICE,
null, extras);
Bundle.setDefusable(res, true);
if (res == null) {
@@ -342,7 +364,7 @@
extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
new ArrayList<>(supportedSpecs));
- final Bundle res = provider.call(resolver.getPackageName(),
+ final Bundle res = provider.call(mContext.getPackageName(),
SliceProvider.METHOD_MAP_INTENT, null, extras);
if (res == null) {
return null;
@@ -358,6 +380,45 @@
}
/**
+ * Does the permission check to see if a caller has access to a specific slice.
+ * @hide
+ */
+ public void enforceSlicePermission(Uri uri, String pkg, int pid, int uid) {
+ try {
+ if (pkg == null) {
+ throw new SecurityException("No pkg specified");
+ }
+ int result = mService.checkSlicePermission(uri, pkg, pid, uid);
+ if (result == PERMISSION_DENIED) {
+ throw new SecurityException("User " + uid + " does not have slice permission for "
+ + uri + ".");
+ }
+ if (result == PERMISSION_USER_GRANTED) {
+ // We just had a user grant of this permission and need to grant this to the app
+ // permanently.
+ mContext.grantUriPermission(pkg, uri.buildUpon().path("").build(),
+ Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
+ | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Called by SystemUI to grant a slice permission after a dialog is shown.
+ * @hide
+ */
+ public void grantPermissionFromUser(Uri uri, String pkg, boolean allSlices) {
+ try {
+ mService.grantPermissionFromUser(uri, pkg, mContext.getPackageName(), allSlices);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Class that listens to changes in {@link Slice}s.
*/
public interface SliceCallback {
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index aa41f14..8ffacf5 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -15,13 +15,19 @@
*/
package android.app.slice;
-import android.Manifest.permission;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.PendingIntent;
+import android.content.ComponentName;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentValues;
+import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ProviderInfo;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
@@ -129,9 +135,41 @@
* @hide
*/
public static final String EXTRA_SLICE_DESCENDANTS = "slice_descendants";
+ /**
+ * @hide
+ */
+ public static final String EXTRA_PKG = "pkg";
+ /**
+ * @hide
+ */
+ public static final String EXTRA_PROVIDER_PKG = "provider_pkg";
+ /**
+ * @hide
+ */
+ public static final String EXTRA_OVERRIDE_PKG = "override_pkg";
private static final boolean DEBUG = false;
+ private String mBindingPkg;
+ private SliceManager mSliceManager;
+
+ /**
+ * Return the package name of the caller that initiated the binding request
+ * currently happening. The returned package will have been
+ * verified to belong to the calling UID. Returns {@code null} if not
+ * currently performing an {@link #onBindSlice(Uri, List)}.
+ * @hide
+ */
+ public final @Nullable String getBindingPackage() {
+ return mBindingPkg;
+ }
+
+ @Override
+ public void attachInfo(Context context, ProviderInfo info) {
+ super.attachInfo(context, info);
+ mSliceManager = context.getSystemService(SliceManager.class);
+ }
+
/**
* Implemented to create a slice. Will be called on the main thread.
* <p>
@@ -262,28 +300,27 @@
public Bundle call(String method, String arg, Bundle extras) {
if (method.equals(METHOD_SLICE)) {
Uri uri = extras.getParcelable(EXTRA_BIND_URI);
- if (!UserHandle.isSameApp(Binder.getCallingUid(), Process.myUid())) {
- getContext().enforceUriPermission(uri, permission.BIND_SLICE,
- permission.BIND_SLICE, Binder.getCallingPid(), Binder.getCallingUid(),
- Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
- "Slice binding requires the permission BIND_SLICE");
- }
List<SliceSpec> supportedSpecs = extras.getParcelableArrayList(EXTRA_SUPPORTED_SPECS);
- Slice s = handleBindSlice(uri, supportedSpecs);
+ String callingPackage = getCallingPackage();
+ if (extras.containsKey(EXTRA_OVERRIDE_PKG)) {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("Only the system can override calling pkg");
+ }
+ callingPackage = extras.getString(EXTRA_OVERRIDE_PKG);
+ }
+ Slice s = handleBindSlice(uri, supportedSpecs, callingPackage);
Bundle b = new Bundle();
b.putParcelable(EXTRA_SLICE, s);
return b;
} else if (method.equals(METHOD_MAP_INTENT)) {
- getContext().enforceCallingPermission(permission.BIND_SLICE,
- "Slice binding requires the permission BIND_SLICE");
Intent intent = extras.getParcelable(EXTRA_INTENT);
if (intent == null) return null;
Uri uri = onMapIntentToUri(intent);
List<SliceSpec> supportedSpecs = extras.getParcelableArrayList(EXTRA_SUPPORTED_SPECS);
Bundle b = new Bundle();
if (uri != null) {
- Slice s = handleBindSlice(uri, supportedSpecs);
+ Slice s = handleBindSlice(uri, supportedSpecs, getCallingPackage());
b.putParcelable(EXTRA_SLICE, s);
} else {
b.putParcelable(EXTRA_SLICE, null);
@@ -291,20 +328,14 @@
return b;
} else if (method.equals(METHOD_PIN)) {
Uri uri = extras.getParcelable(EXTRA_BIND_URI);
- if (!UserHandle.isSameApp(Binder.getCallingUid(), Process.myUid())) {
- getContext().enforceUriPermission(uri, permission.BIND_SLICE,
- permission.BIND_SLICE, Binder.getCallingPid(), Binder.getCallingUid(),
- Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
- "Slice binding requires the permission BIND_SLICE");
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("Only the system can pin/unpin slices");
}
handlePinSlice(uri);
} else if (method.equals(METHOD_UNPIN)) {
Uri uri = extras.getParcelable(EXTRA_BIND_URI);
- if (!UserHandle.isSameApp(Binder.getCallingUid(), Process.myUid())) {
- getContext().enforceUriPermission(uri, permission.BIND_SLICE,
- permission.BIND_SLICE, Binder.getCallingPid(), Binder.getCallingUid(),
- Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
- "Slice binding requires the permission BIND_SLICE");
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("Only the system can pin/unpin slices");
}
handleUnpinSlice(uri);
} else if (method.equals(METHOD_GET_DESCENDANTS)) {
@@ -370,14 +401,27 @@
}
}
- private Slice handleBindSlice(Uri sliceUri, List<SliceSpec> supportedSpecs) {
+ private Slice handleBindSlice(Uri sliceUri, List<SliceSpec> supportedSpecs,
+ String callingPkg) {
+ // This can be removed once Slice#bindSlice is removed and everyone is using
+ // SliceManager#bindSlice.
+ String pkg = callingPkg != null ? callingPkg
+ : getContext().getPackageManager().getNameForUid(Binder.getCallingUid());
+ if (!UserHandle.isSameApp(Binder.getCallingUid(), Process.myUid())) {
+ try {
+ mSliceManager.enforceSlicePermission(sliceUri, pkg,
+ Binder.getCallingPid(), Binder.getCallingUid());
+ } catch (SecurityException e) {
+ return createPermissionSlice(getContext(), sliceUri, pkg);
+ }
+ }
if (Looper.myLooper() == Looper.getMainLooper()) {
- return onBindSliceStrict(sliceUri, supportedSpecs);
+ return onBindSliceStrict(sliceUri, supportedSpecs, pkg);
} else {
CountDownLatch latch = new CountDownLatch(1);
Slice[] output = new Slice[1];
Handler.getMain().post(() -> {
- output[0] = onBindSliceStrict(sliceUri, supportedSpecs);
+ output[0] = onBindSliceStrict(sliceUri, supportedSpecs, pkg);
latch.countDown();
});
try {
@@ -389,15 +433,66 @@
}
}
- private Slice onBindSliceStrict(Uri sliceUri, List<SliceSpec> supportedSpecs) {
+ /**
+ * @hide
+ */
+ public static Slice createPermissionSlice(Context context, Uri sliceUri,
+ String callingPackage) {
+ return new Slice.Builder(sliceUri)
+ .addAction(createPermissionIntent(context, sliceUri, callingPackage),
+ new Slice.Builder(sliceUri.buildUpon().appendPath("permission").build())
+ .addText(getPermissionString(context, callingPackage), null)
+ .build())
+ .addHints(Slice.HINT_LIST_ITEM)
+ .build();
+ }
+
+ /**
+ * @hide
+ */
+ public static PendingIntent createPermissionIntent(Context context, Uri sliceUri,
+ String callingPackage) {
+ Intent intent = new Intent(SliceManager.ACTION_REQUEST_SLICE_PERMISSION);
+ intent.setComponent(new ComponentName("com.android.systemui",
+ "com.android.systemui.SlicePermissionActivity"));
+ intent.putExtra(EXTRA_BIND_URI, sliceUri);
+ intent.putExtra(EXTRA_PKG, callingPackage);
+ intent.putExtra(EXTRA_PROVIDER_PKG, context.getPackageName());
+ // Unique pending intent.
+ intent.setData(sliceUri.buildUpon().appendQueryParameter("package", callingPackage)
+ .build());
+
+ return PendingIntent.getActivity(context, 0, intent, 0);
+ }
+
+ /**
+ * @hide
+ */
+ public static CharSequence getPermissionString(Context context, String callingPackage) {
+ PackageManager pm = context.getPackageManager();
+ try {
+ return context.getString(
+ com.android.internal.R.string.slices_permission_request,
+ pm.getApplicationInfo(callingPackage, 0).loadLabel(pm),
+ context.getApplicationInfo().loadLabel(pm));
+ } catch (NameNotFoundException e) {
+ // This shouldn't be possible since the caller is verified.
+ throw new RuntimeException("Unknown calling app", e);
+ }
+ }
+
+ private Slice onBindSliceStrict(Uri sliceUri, List<SliceSpec> supportedSpecs,
+ String callingPackage) {
ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
try {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyDeath()
.build());
+ mBindingPkg = callingPackage;
return onBindSlice(sliceUri, supportedSpecs);
} finally {
+ mBindingPkg = null;
StrictMode.setThreadPolicy(oldPolicy);
}
}
diff --git a/core/java/android/app/usage/NetworkStats.java b/core/java/android/app/usage/NetworkStats.java
index 2e44a63..da36157 100644
--- a/core/java/android/app/usage/NetworkStats.java
+++ b/core/java/android/app/usage/NetworkStats.java
@@ -227,6 +227,30 @@
*/
public static final int ROAMING_YES = 0x2;
+ /** @hide */
+ @IntDef(prefix = { "DEFAULT_NETWORK_" }, value = {
+ DEFAULT_NETWORK_ALL,
+ DEFAULT_NETWORK_NO,
+ DEFAULT_NETWORK_YES
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DefaultNetwork {}
+
+ /**
+ * Combined usage for this network regardless of whether it was the active default network.
+ */
+ public static final int DEFAULT_NETWORK_ALL = -1;
+
+ /**
+ * Usage that occurs while this network is not the active default network.
+ */
+ public static final int DEFAULT_NETWORK_NO = 0x1;
+
+ /**
+ * Usage that occurs while this network is the active default network.
+ */
+ public static final int DEFAULT_NETWORK_YES = 0x2;
+
/**
* Special TAG value for total data across all tags
*/
@@ -235,6 +259,7 @@
private int mUid;
private int mTag;
private int mState;
+ private int mDefaultNetwork;
private int mMetered;
private int mRoaming;
private long mBeginTimeStamp;
@@ -286,6 +311,15 @@
return 0;
}
+ private static @DefaultNetwork int convertDefaultNetwork(int defaultNetwork) {
+ switch (defaultNetwork) {
+ case android.net.NetworkStats.DEFAULT_NETWORK_ALL : return DEFAULT_NETWORK_ALL;
+ case android.net.NetworkStats.DEFAULT_NETWORK_NO: return DEFAULT_NETWORK_NO;
+ case android.net.NetworkStats.DEFAULT_NETWORK_YES: return DEFAULT_NETWORK_YES;
+ }
+ return 0;
+ }
+
public Bucket() {
}
@@ -351,6 +385,21 @@
}
/**
+ * Default network state. One of the following values:<p/>
+ * <ul>
+ * <li>{@link #DEFAULT_NETWORK_ALL}</li>
+ * <li>{@link #DEFAULT_NETWORK_NO}</li>
+ * <li>{@link #DEFAULT_NETWORK_YES}</li>
+ * </ul>
+ * <p>Indicates whether the network usage occurred on the system default network for this
+ * type of traffic, or whether the application chose to send this traffic on a network that
+ * was not the one selected by the system.
+ */
+ public @DefaultNetwork int getDefaultNetwork() {
+ return mDefaultNetwork;
+ }
+
+ /**
* Start timestamp of the bucket's time interval. Defined in terms of "Unix time", see
* {@link java.lang.System#currentTimeMillis}.
* @return Start of interval.
@@ -551,6 +600,8 @@
bucketOut.mUid = Bucket.convertUid(mRecycledSummaryEntry.uid);
bucketOut.mTag = Bucket.convertTag(mRecycledSummaryEntry.tag);
bucketOut.mState = Bucket.convertState(mRecycledSummaryEntry.set);
+ bucketOut.mDefaultNetwork = Bucket.convertDefaultNetwork(
+ mRecycledSummaryEntry.defaultNetwork);
bucketOut.mMetered = Bucket.convertMetered(mRecycledSummaryEntry.metered);
bucketOut.mRoaming = Bucket.convertRoaming(mRecycledSummaryEntry.roaming);
bucketOut.mBeginTimeStamp = mStartTimeStamp;
@@ -600,6 +651,7 @@
bucketOut.mUid = Bucket.convertUid(getUid());
bucketOut.mTag = Bucket.convertTag(mTag);
bucketOut.mState = Bucket.STATE_ALL;
+ bucketOut.mDefaultNetwork = Bucket.DEFAULT_NETWORK_ALL;
bucketOut.mMetered = Bucket.METERED_ALL;
bucketOut.mRoaming = Bucket.ROAMING_ALL;
bucketOut.mBeginTimeStamp = mRecycledHistoryEntry.bucketStart;
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index 853b003..90b514e 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -60,10 +60,11 @@
* {@link #queryDetailsForUid} <p />
* {@link #queryDetails} <p />
* These queries do not aggregate over time but do aggregate over state, metered and roaming.
- * Therefore there can be multiple buckets for a particular key but all Bucket's state is going to
- * be {@link NetworkStats.Bucket#STATE_ALL}, all Bucket's metered is going to be
- * {@link NetworkStats.Bucket#METERED_ALL}, and all Bucket's roaming is going to be
- * {@link NetworkStats.Bucket#ROAMING_ALL}.
+ * Therefore there can be multiple buckets for a particular key. However, all Buckets will have
+ * {@code state} {@link NetworkStats.Bucket#STATE_ALL},
+ * {@code defaultNetwork} {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL},
+ * {@code metered } {@link NetworkStats.Bucket#METERED_ALL},
+ * {@code roaming} {@link NetworkStats.Bucket#ROAMING_ALL}.
* <p />
* <b>NOTE:</b> Calling {@link #querySummaryForDevice} or accessing stats for apps other than the
* calling app requires the permission {@link android.Manifest.permission#PACKAGE_USAGE_STATS},
@@ -136,7 +137,9 @@
* roaming. This means the bucket's start and end timestamp are going to be the same as the
* 'startTime' and 'endTime' parameters. State is going to be
* {@link NetworkStats.Bucket#STATE_ALL}, uid {@link NetworkStats.Bucket#UID_ALL},
- * tag {@link NetworkStats.Bucket#TAG_NONE}, metered {@link NetworkStats.Bucket#METERED_ALL},
+ * tag {@link NetworkStats.Bucket#TAG_NONE},
+ * default network {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL},
+ * metered {@link NetworkStats.Bucket#METERED_ALL},
* and roaming {@link NetworkStats.Bucket#ROAMING_ALL}.
*
* @param networkType As defined in {@link ConnectivityManager}, e.g.
@@ -209,10 +212,10 @@
/**
* Query network usage statistics summaries. Result filtered to include only uids belonging to
* calling user. Result is aggregated over time, hence all buckets will have the same start and
- * end timestamps. Not aggregated over state, uid, metered, or roaming. This means buckets'
- * start and end timestamps are going to be the same as the 'startTime' and 'endTime'
- * parameters. State, uid, metered, and roaming are going to vary, and tag is going to be the
- * same.
+ * end timestamps. Not aggregated over state, uid, default network, metered, or roaming. This
+ * means buckets' start and end timestamps are going to be the same as the 'startTime' and
+ * 'endTime' parameters. State, uid, metered, and roaming are going to vary, and tag is going to
+ * be the same.
*
* @param networkType As defined in {@link ConnectivityManager}, e.g.
* {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
@@ -258,9 +261,10 @@
* belonging to calling user. Result is aggregated over state but not aggregated over time.
* This means buckets' start and end timestamps are going to be between 'startTime' and
* 'endTime' parameters. State is going to be {@link NetworkStats.Bucket#STATE_ALL}, uid the
- * same as the 'uid' parameter and tag the same as 'tag' parameter. metered is going to be
- * {@link NetworkStats.Bucket#METERED_ALL}, and roaming is going to be
- * {@link NetworkStats.Bucket#ROAMING_ALL}.
+ * same as the 'uid' parameter and tag the same as 'tag' parameter.
+ * defaultNetwork is going to be {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL},
+ * metered is going to be {@link NetworkStats.Bucket#METERED_ALL}, and
+ * roaming is going to be {@link NetworkStats.Bucket#ROAMING_ALL}.
* <p>Only includes buckets that atomically occur in the inclusive time range. Doesn't
* interpolate across partial buckets. Since bucket length is in the order of hours, this
* method cannot be used to measure data usage on a fine grained time scale.
@@ -301,9 +305,10 @@
* metered, nor roaming. This means buckets' start and end timestamps are going to be between
* 'startTime' and 'endTime' parameters. State is going to be
* {@link NetworkStats.Bucket#STATE_ALL}, uid will vary,
- * tag {@link NetworkStats.Bucket#TAG_NONE}, metered is going to be
- * {@link NetworkStats.Bucket#METERED_ALL}, and roaming is going to be
- * {@link NetworkStats.Bucket#ROAMING_ALL}.
+ * tag {@link NetworkStats.Bucket#TAG_NONE},
+ * default network is going to be {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL},
+ * metered is going to be {@link NetworkStats.Bucket#METERED_ALL},
+ * and roaming is going to be {@link NetworkStats.Bucket#ROAMING_ALL}.
* <p>Only includes buckets that atomically occur in the inclusive time range. Doesn't
* interpolate across partial buckets. Since bucket length is in the order of hours, this
* method cannot be used to measure data usage on a fine grained time scale.
diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java
index 5cd0981..bd978e3 100644
--- a/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -176,6 +176,12 @@
public abstract void setActiveAdminApps(Set<String> adminApps, int userId);
/**
+ * Called by DevicePolicyManagerService during boot to inform that admin data is loaded and
+ * pushed to UsageStatsService.
+ */
+ public abstract void onAdminDataAvailable();
+
+ /**
* Return usage stats.
*
* @param obfuscateInstantApps whether instant app package names need to be obfuscated in the
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 15e119b..84cbdb4 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -26,7 +26,6 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
-import android.os.Build;
import android.os.Environment;
import android.os.Parcel;
import android.os.Parcelable;
@@ -1593,11 +1592,6 @@
return (privateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0;
}
- /** @hide */
- public boolean isTargetingDeprecatedSdkVersion() {
- return targetSdkVersion < Build.VERSION.MIN_SUPPORTED_TARGET_SDK_INT;
- }
-
/**
* Returns whether or not this application was installed as a virtual preload.
*/
diff --git a/core/java/android/hardware/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java
index 7866b52..9aa3f40 100644
--- a/core/java/android/hardware/HardwareBuffer.java
+++ b/core/java/android/hardware/HardwareBuffer.java
@@ -25,11 +25,11 @@
import dalvik.annotation.optimization.FastNative;
import dalvik.system.CloseGuard;
+import libcore.util.NativeAllocationRegistry;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import libcore.util.NativeAllocationRegistry;
-
/**
* HardwareBuffer wraps a native <code>AHardwareBuffer</code> object, which is a low-level object
* representing a memory buffer accessible by various hardware units. HardwareBuffer allows sharing
@@ -42,18 +42,25 @@
public final class HardwareBuffer implements Parcelable, AutoCloseable {
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = { "RGB", "BLOB" }, value = {
+ @IntDef(prefix = { "RGB", "BLOB", "D_", "DS_", "S_" }, value = {
RGBA_8888,
RGBA_FP16,
RGBA_1010102,
RGBX_8888,
RGB_888,
RGB_565,
- BLOB
+ BLOB,
+ D_16,
+ D_24,
+ DS_24UI8,
+ D_FP32,
+ DS_FP32UI8,
+ S_UI8,
})
public @interface Format {
}
+ @Format
/** Format: 8 bits each red, green, blue, alpha */
public static final int RGBA_8888 = 1;
/** Format: 8 bits each red, green, blue, alpha, alpha is always 0xFF */
@@ -68,6 +75,18 @@
public static final int RGBA_1010102 = 0x2b;
/** Format: opaque format used for raw data transfer; must have a height of 1 */
public static final int BLOB = 0x21;
+ /** Format: 16 bits depth */
+ public static final int D_16 = 0x30;
+ /** Format: 24 bits depth */
+ public static final int D_24 = 0x31;
+ /** Format: 24 bits depth, 8 bits stencil */
+ public static final int DS_24UI8 = 0x32;
+ /** Format: 32 bits depth */
+ public static final int D_FP32 = 0x33;
+ /** Format: 32 bits depth, 8 bits stencil */
+ public static final int DS_FP32UI8 = 0x34;
+ /** Format: 8 bits stencil */
+ public static final int S_UI8 = 0x35;
// Note: do not rename, this field is used by native code
private long mNativeObject;
@@ -82,9 +101,11 @@
@LongDef(flag = true, value = {USAGE_CPU_READ_RARELY, USAGE_CPU_READ_OFTEN,
USAGE_CPU_WRITE_RARELY, USAGE_CPU_WRITE_OFTEN, USAGE_GPU_SAMPLED_IMAGE,
USAGE_GPU_COLOR_OUTPUT, USAGE_PROTECTED_CONTENT, USAGE_VIDEO_ENCODE,
- USAGE_GPU_DATA_BUFFER, USAGE_SENSOR_DIRECT_DATA})
+ USAGE_GPU_DATA_BUFFER, USAGE_SENSOR_DIRECT_DATA, USAGE_GPU_CUBE_MAP,
+ USAGE_GPU_MIPMAP_COMPLETE})
public @interface Usage {};
+ @Usage
/** Usage: The buffer will sometimes be read by the CPU */
public static final long USAGE_CPU_READ_RARELY = 2;
/** Usage: The buffer will often be read by the CPU */
@@ -107,6 +128,10 @@
public static final long USAGE_SENSOR_DIRECT_DATA = 1 << 23;
/** Usage: The buffer will be used as a shader storage or uniform buffer object */
public static final long USAGE_GPU_DATA_BUFFER = 1 << 24;
+ /** Usage: The buffer will be used as a cube map texture */
+ public static final long USAGE_GPU_CUBE_MAP = 1 << 25;
+ /** Usage: The buffer contains a complete mipmap hierarchy */
+ public static final long USAGE_GPU_MIPMAP_COMPLETE = 1 << 26;
// The approximate size of a native AHardwareBuffer object.
private static final long NATIVE_HARDWARE_BUFFER_SIZE = 232;
@@ -118,15 +143,9 @@
*
* @param width The width in pixels of the buffer
* @param height The height in pixels of the buffer
- * @param format The format of each pixel, one of {@link #RGBA_8888}, {@link #RGBA_FP16},
- * {@link #RGBX_8888}, {@link #RGB_565}, {@link #RGB_888}, {@link #RGBA_1010102}, {@link #BLOB}
+ * @param format The @Format of each pixel
* @param layers The number of layers in the buffer
- * @param usage Flags describing how the buffer will be used, one of
- * {@link #USAGE_CPU_READ_RARELY}, {@link #USAGE_CPU_READ_OFTEN},
- * {@link #USAGE_CPU_WRITE_RARELY}, {@link #USAGE_CPU_WRITE_OFTEN},
- * {@link #USAGE_GPU_SAMPLED_IMAGE}, {@link #USAGE_GPU_COLOR_OUTPUT},
- * {@link #USAGE_GPU_DATA_BUFFER}, {@link #USAGE_PROTECTED_CONTENT},
- * {@link #USAGE_SENSOR_DIRECT_DATA}, {@link #USAGE_VIDEO_ENCODE}
+ * @param usage The @Usage flags describing how the buffer will be used
* @return A <code>HardwareBuffer</code> instance if successful, or throws an
* IllegalArgumentException if the dimensions passed are invalid (either zero, negative, or
* too large to allocate), if the format is not supported, if the requested number of layers
@@ -154,7 +173,7 @@
if (nativeObject == 0) {
throw new IllegalArgumentException("Unable to create a HardwareBuffer, either the " +
"dimensions passed were too large, too many image layers were requested, " +
- "or an invalid set of usage flags was passed");
+ "or an invalid set of usage flags or invalid format was passed");
}
return new HardwareBuffer(nativeObject);
}
@@ -206,8 +225,7 @@
}
/**
- * Returns the format of this buffer, one of {@link #RGBA_8888}, {@link #RGBA_FP16},
- * {@link #RGBX_8888}, {@link #RGB_565}, {@link #RGB_888}, {@link #RGBA_1010102}, {@link #BLOB}.
+ * Returns the @Format of this buffer.
*/
@Format
public int getFormat() {
@@ -338,6 +356,12 @@
case RGB_565:
case RGB_888:
case BLOB:
+ case D_16:
+ case D_24:
+ case DS_24UI8:
+ case D_FP32:
+ case DS_FP32UI8:
+ case S_UI8:
return true;
}
return false;
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index f1ffb89..4455d45 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -16,9 +16,8 @@
package android.hardware.camera2.impl;
-import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
-import android.graphics.ImageFormat;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
@@ -31,7 +30,6 @@
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.InputConfiguration;
import android.hardware.camera2.params.OutputConfiguration;
-import android.hardware.camera2.params.ReprocessFormatsMap;
import android.hardware.camera2.params.SessionConfiguration;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.hardware.camera2.utils.SubmitInfo;
@@ -1798,34 +1796,36 @@
case ERROR_CAMERA_DISCONNECTED:
CameraDeviceImpl.this.mDeviceHandler.post(mCallOnDisconnected);
break;
- default:
- Log.e(TAG, "Unknown error from camera device: " + errorCode);
- // no break
- case ERROR_CAMERA_DEVICE:
- case ERROR_CAMERA_SERVICE:
- mInError = true;
- final int publicErrorCode = (errorCode == ERROR_CAMERA_DEVICE) ?
- StateCallback.ERROR_CAMERA_DEVICE :
- StateCallback.ERROR_CAMERA_SERVICE;
- Runnable r = new Runnable() {
- @Override
- public void run() {
- if (!CameraDeviceImpl.this.isClosed()) {
- mDeviceCallback.onError(CameraDeviceImpl.this, publicErrorCode);
- }
- }
- };
- CameraDeviceImpl.this.mDeviceHandler.post(r);
- break;
case ERROR_CAMERA_REQUEST:
case ERROR_CAMERA_RESULT:
case ERROR_CAMERA_BUFFER:
onCaptureErrorLocked(errorCode, resultExtras);
break;
+ case ERROR_CAMERA_DEVICE:
+ scheduleNotifyError(StateCallback.ERROR_CAMERA_DEVICE);
+ break;
+ case ERROR_CAMERA_DISABLED:
+ scheduleNotifyError(StateCallback.ERROR_CAMERA_DISABLED);
+ break;
+ default:
+ Log.e(TAG, "Unknown error from camera device: " + errorCode);
+ scheduleNotifyError(StateCallback.ERROR_CAMERA_SERVICE);
}
}
}
+ private void scheduleNotifyError(int code) {
+ mInError = true;
+ CameraDeviceImpl.this.mDeviceHandler.post(obtainRunnable(
+ CameraDeviceCallbacks::notifyError, this, code));
+ }
+
+ private void notifyError(int code) {
+ if (!CameraDeviceImpl.this.isClosed()) {
+ mDeviceCallback.onError(CameraDeviceImpl.this, code);
+ }
+ }
+
@Override
public void onRepeatingRequestError(long lastFrameNumber, int repeatingRequestId) {
if (DEBUG) {
diff --git a/core/java/android/hardware/radio/ProgramSelector.java b/core/java/android/hardware/radio/ProgramSelector.java
index 3556751..0cf7605 100644
--- a/core/java/android/hardware/radio/ProgramSelector.java
+++ b/core/java/android/hardware/radio/ProgramSelector.java
@@ -59,24 +59,56 @@
*/
@SystemApi
public final class ProgramSelector implements Parcelable {
+ /** Invalid program type.
+ * @deprecated use {@link ProgramIdentifier} instead
+ */
+ @Deprecated
public static final int PROGRAM_TYPE_INVALID = 0;
- /** Analogue AM radio (with or without RDS). */
+ /** Analogue AM radio (with or without RDS).
+ * @deprecated use {@link ProgramIdentifier} instead
+ */
+ @Deprecated
public static final int PROGRAM_TYPE_AM = 1;
- /** analogue FM radio (with or without RDS). */
+ /** analogue FM radio (with or without RDS).
+ * @deprecated use {@link ProgramIdentifier} instead
+ */
+ @Deprecated
public static final int PROGRAM_TYPE_FM = 2;
- /** AM HD Radio. */
+ /** AM HD Radio.
+ * @deprecated use {@link ProgramIdentifier} instead
+ */
+ @Deprecated
public static final int PROGRAM_TYPE_AM_HD = 3;
- /** FM HD Radio. */
+ /** FM HD Radio.
+ * @deprecated use {@link ProgramIdentifier} instead
+ */
+ @Deprecated
public static final int PROGRAM_TYPE_FM_HD = 4;
- /** Digital audio broadcasting. */
+ /** Digital audio broadcasting.
+ * @deprecated use {@link ProgramIdentifier} instead
+ */
+ @Deprecated
public static final int PROGRAM_TYPE_DAB = 5;
- /** Digital Radio Mondiale. */
+ /** Digital Radio Mondiale.
+ * @deprecated use {@link ProgramIdentifier} instead
+ */
+ @Deprecated
public static final int PROGRAM_TYPE_DRMO = 6;
- /** SiriusXM Satellite Radio. */
+ /** SiriusXM Satellite Radio.
+ * @deprecated use {@link ProgramIdentifier} instead
+ */
+ @Deprecated
public static final int PROGRAM_TYPE_SXM = 7;
- /** Vendor-specific, not synced across devices. */
+ /** Vendor-specific, not synced across devices.
+ * @deprecated use {@link ProgramIdentifier} instead
+ */
+ @Deprecated
public static final int PROGRAM_TYPE_VENDOR_START = 1000;
+ /** @deprecated use {@link ProgramIdentifier} instead */
+ @Deprecated
public static final int PROGRAM_TYPE_VENDOR_END = 1999;
+ /** @deprecated use {@link ProgramIdentifier} instead */
+ @Deprecated
@IntDef(prefix = { "PROGRAM_TYPE_" }, value = {
PROGRAM_TYPE_INVALID,
PROGRAM_TYPE_AM,
@@ -112,18 +144,46 @@
*
* The subchannel index is 0-based (where 0 is MPS and 1..7 are SPS),
* as opposed to HD Radio standard (where it's 1-based).
+ *
+ * @deprecated use IDENTIFIER_TYPE_HD_STATION_ID_EXT instead
*/
+ @Deprecated
public static final int IDENTIFIER_TYPE_HD_SUBCHANNEL = 4;
/**
- * 24bit compound primary identifier for DAB.
+ * 64bit additional identifier for HD Radio.
+ *
+ * Due to Station ID abuse, some HD_STATION_ID_EXT identifiers may be not
+ * globally unique. To provide a best-effort solution, a short version of
+ * station name may be carried as additional identifier and may be used
+ * by the tuner hardware to double-check tuning.
+ *
+ * The name is limited to the first 8 A-Z0-9 characters (lowercase letters
+ * must be converted to uppercase). Encoded in little-endian ASCII:
+ * the first character of the name is the LSB.
+ *
+ * For example: "Abc" is encoded as 0x434241.
+ */
+ public static final int IDENTIFIER_TYPE_HD_STATION_NAME = 10004;
+ /**
+ * @see {@link IDENTIFIER_TYPE_DAB_SID_EXT}
+ */
+ public static final int IDENTIFIER_TYPE_DAB_SIDECC = 5;
+ /**
+ * 28bit compound primary identifier for Digital Audio Broadcasting.
*
* Consists of (from the LSB):
* - 16bit: SId;
- * - 8bit: ECC code.
+ * - 8bit: ECC code;
+ * - 4bit: SCIdS.
+ *
+ * SCIdS (Service Component Identifier within the Service) value
+ * of 0 represents the main service, while 1 and above represents
+ * secondary services.
+ *
* The remaining bits should be set to zeros when writing on the chip side
* and ignored when read.
*/
- public static final int IDENTIFIER_TYPE_DAB_SIDECC = 5;
+ public static final int IDENTIFIER_TYPE_DAB_SID_EXT = IDENTIFIER_TYPE_DAB_SIDECC;
/** 16bit */
public static final int IDENTIFIER_TYPE_DAB_ENSEMBLE = 6;
/** 12bit */
@@ -134,7 +194,11 @@
public static final int IDENTIFIER_TYPE_DRMO_SERVICE_ID = 9;
/** kHz */
public static final int IDENTIFIER_TYPE_DRMO_FREQUENCY = 10;
- /** 1: AM, 2:FM */
+ /**
+ * 1: AM, 2:FM
+ * @deprecated use {@link IDENTIFIER_TYPE_DRMO_FREQUENCY} instead
+ */
+ @Deprecated
public static final int IDENTIFIER_TYPE_DRMO_MODULATION = 11;
/** 32bit */
public static final int IDENTIFIER_TYPE_SXM_SERVICE_ID = 12;
@@ -148,14 +212,29 @@
* type between VENDOR_START and VENDOR_END (eg. identifier type 1015 must
* not be used in any program type other than 1015).
*/
- public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_START = PROGRAM_TYPE_VENDOR_START;
- public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_END = PROGRAM_TYPE_VENDOR_END;
+ public static final int IDENTIFIER_TYPE_VENDOR_START = PROGRAM_TYPE_VENDOR_START;
+ /**
+ * @see {@link IDENTIFIER_TYPE_VENDOR_START}
+ */
+ public static final int IDENTIFIER_TYPE_VENDOR_END = PROGRAM_TYPE_VENDOR_END;
+ /**
+ * @deprecated use {@link IDENTIFIER_TYPE_VENDOR_START} instead
+ */
+ @Deprecated
+ public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_START = IDENTIFIER_TYPE_VENDOR_START;
+ /**
+ * @deprecated use {@link IDENTIFIER_TYPE_VENDOR_END} instead
+ */
+ @Deprecated
+ public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_END = IDENTIFIER_TYPE_VENDOR_END;
@IntDef(prefix = { "IDENTIFIER_TYPE_" }, value = {
IDENTIFIER_TYPE_INVALID,
IDENTIFIER_TYPE_AMFM_FREQUENCY,
IDENTIFIER_TYPE_RDS_PI,
IDENTIFIER_TYPE_HD_STATION_ID_EXT,
IDENTIFIER_TYPE_HD_SUBCHANNEL,
+ IDENTIFIER_TYPE_HD_STATION_NAME,
+ IDENTIFIER_TYPE_DAB_SID_EXT,
IDENTIFIER_TYPE_DAB_SIDECC,
IDENTIFIER_TYPE_DAB_ENSEMBLE,
IDENTIFIER_TYPE_DAB_SCID,
@@ -166,7 +245,7 @@
IDENTIFIER_TYPE_SXM_SERVICE_ID,
IDENTIFIER_TYPE_SXM_CHANNEL,
})
- @IntRange(from = IDENTIFIER_TYPE_VENDOR_PRIMARY_START, to = IDENTIFIER_TYPE_VENDOR_PRIMARY_END)
+ @IntRange(from = IDENTIFIER_TYPE_VENDOR_START, to = IDENTIFIER_TYPE_VENDOR_END)
@Retention(RetentionPolicy.SOURCE)
public @interface IdentifierType {}
@@ -205,7 +284,9 @@
* Type of a radio technology.
*
* @return program type.
+ * @deprecated use {@link getPrimaryId} instead
*/
+ @Deprecated
public @ProgramType int getProgramType() {
return mProgramType;
}
@@ -273,7 +354,10 @@
* preserving elements order.
*
* @return an array of vendor identifiers, must not be modified.
+ * @deprecated for HAL 1.x compatibility;
+ * HAL 2.x uses standard primary/secondary lists for vendor IDs
*/
+ @Deprecated
public @NonNull long[] getVendorIds() {
return mVendorIds;
}
@@ -427,6 +511,10 @@
private final long mValue;
public Identifier(@IdentifierType int type, long value) {
+ if (type == IDENTIFIER_TYPE_HD_STATION_NAME) {
+ // see getType
+ type = IDENTIFIER_TYPE_HD_SUBCHANNEL;
+ }
mType = type;
mValue = value;
}
@@ -437,6 +525,13 @@
* @return type of an identifier.
*/
public @IdentifierType int getType() {
+ if (mType == IDENTIFIER_TYPE_HD_SUBCHANNEL && mValue > 10) {
+ /* HD_SUBCHANNEL and HD_STATION_NAME use the same identifier type, but they differ
+ * in possible values: sub channel is 0-7, station name is greater than ASCII space
+ * code (32).
+ */
+ return IDENTIFIER_TYPE_HD_STATION_NAME;
+ }
return mType;
}
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index 2cda58c..f04f03f6 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.content.Context;
@@ -625,6 +626,133 @@
}
/**
+ * This class represents an IpSecTunnelInterface
+ *
+ * <p>IpSecTunnelInterface objects track tunnel interfaces that serve as
+ * local endpoints for IPsec tunnels.
+ *
+ * <p>Creating an IpSecTunnelInterface creates a device to which IpSecTransforms may be
+ * applied to provide IPsec security to packets sent through the tunnel. While a tunnel
+ * cannot be used in standalone mode within Android, the higher layers may use the tunnel
+ * to create Network objects which are accessible to the Android system.
+ * @hide
+ */
+ @SystemApi
+ public static final class IpSecTunnelInterface implements AutoCloseable {
+ private final IIpSecService mService;
+ private final InetAddress mRemoteAddress;
+ private final InetAddress mLocalAddress;
+ private final Network mUnderlyingNetwork;
+ private final CloseGuard mCloseGuard = CloseGuard.get();
+ private String mInterfaceName;
+ private int mResourceId = INVALID_RESOURCE_ID;
+
+ /** Get the underlying SPI held by this object. */
+ public String getInterfaceName() {
+ return mInterfaceName;
+ }
+
+ /**
+ * Add an address to the IpSecTunnelInterface
+ *
+ * <p>Add an address which may be used as the local inner address for
+ * tunneled traffic.
+ *
+ * @param address the local address for traffic inside the tunnel
+ * @throws IOException if the address could not be added
+ * @hide
+ */
+ public void addAddress(LinkAddress address) throws IOException {
+ }
+
+ /**
+ * Remove an address from the IpSecTunnelInterface
+ *
+ * <p>Remove an address which was previously added to the IpSecTunnelInterface
+ *
+ * @param address to be removed
+ * @throws IOException if the address could not be removed
+ * @hide
+ */
+ public void removeAddress(LinkAddress address) throws IOException {
+ }
+
+ private IpSecTunnelInterface(@NonNull IIpSecService service,
+ @NonNull InetAddress localAddress, @NonNull InetAddress remoteAddress,
+ @NonNull Network underlyingNetwork)
+ throws ResourceUnavailableException, IOException {
+ mService = service;
+ mLocalAddress = localAddress;
+ mRemoteAddress = remoteAddress;
+ mUnderlyingNetwork = underlyingNetwork;
+ // TODO: Call IpSecService
+ }
+
+ /**
+ * Delete an IpSecTunnelInterface
+ *
+ * <p>Calling close will deallocate the IpSecTunnelInterface and all of its system
+ * resources. Any packets bound for this interface either inbound or outbound will
+ * all be lost.
+ */
+ @Override
+ public void close() {
+ // try {
+ // TODO: Call IpSecService
+ mResourceId = INVALID_RESOURCE_ID;
+ // } catch (RemoteException e) {
+ // throw e.rethrowFromSystemServer();
+ // }
+ mCloseGuard.close();
+ }
+
+ /** Check that the Interface was closed properly. */
+ @Override
+ protected void finalize() throws Throwable {
+ if (mCloseGuard != null) {
+ mCloseGuard.warnIfOpen();
+ }
+ close();
+ }
+ }
+
+ /**
+ * Create a new IpSecTunnelInterface as a local endpoint for tunneled IPsec traffic.
+ *
+ * @param localAddress The local addres of the tunnel
+ * @param remoteAddress The local addres of the tunnel
+ * @param underlyingNetwork the {@link Network} that will carry traffic for this tunnel.
+ * This network should almost certainly be a network such as WiFi with an L2 address.
+ * @return a new {@link IpSecManager#IpSecTunnelInterface} with the specified properties
+ * @throws IOException indicating that the socket could not be opened or bound
+ * @throws ResourceUnavailableException indicating that too many encapsulation sockets are open
+ * @hide
+ */
+ @SystemApi
+ public IpSecTunnelInterface createIpSecTunnelInterface(@NonNull InetAddress localAddress,
+ @NonNull InetAddress remoteAddress, @NonNull Network underlyingNetwork)
+ throws ResourceUnavailableException, IOException {
+ return new IpSecTunnelInterface(mService, localAddress, remoteAddress, underlyingNetwork);
+ }
+
+ /**
+ * Apply a transform to the IpSecTunnelInterface
+ *
+ * @param tunnel The {@link IpSecManager#IpSecTunnelInterface} that will use the supplied
+ * transform.
+ * @param direction the direction, {@link DIRECTION_OUT} or {@link #DIRECTION_IN} in which
+ * the transform will be used.
+ * @param transform an {@link IpSecTransform} created in tunnel mode
+ * @throws IOException indicating that the transform could not be applied due to a lower
+ * layer failure.
+ * @hide
+ */
+ @SystemApi
+ void applyTunnelModeTransform(IpSecTunnelInterface tunnel, int direction,
+ IpSecTransform transform) throws IOException {
+ // TODO: call IpSecService
+ }
+ /**
* Construct an instance of IpSecManager within an application context.
*
* @param context the application context for this manager
diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java
index 7b9b483..be6026f 100644
--- a/core/java/android/net/IpSecTransform.java
+++ b/core/java/android/net/IpSecTransform.java
@@ -300,21 +300,6 @@
}
/**
- * Set the {@link Network} which will carry tunneled traffic.
- *
- * <p>Restricts the transformed traffic to a particular {@link Network}. This is required
- * for tunnel mode, otherwise tunneled traffic would be sent on the default network.
- *
- * @hide
- */
- @SystemApi
- public IpSecTransform.Builder setUnderlyingNetwork(@NonNull Network net) {
- Preconditions.checkNotNull(net);
- mConfig.setNetwork(net);
- return this;
- }
-
- /**
* Add UDP encapsulation to an IPv4 transform.
*
* <p>This allows IPsec traffic to pass through a NAT.
@@ -415,6 +400,7 @@
* @throws IOException indicating other errors
* @hide
*/
+ @SystemApi
public IpSecTransform buildTunnelModeTransform(
@NonNull InetAddress sourceAddress,
@NonNull IpSecManager.SecurityParameterIndex spi)
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index fd118f3..ce2de85 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -130,6 +130,7 @@
proto.write(NetworkIdentityProto.NETWORK_ID, mNetworkId);
proto.write(NetworkIdentityProto.ROAMING, mRoaming);
proto.write(NetworkIdentityProto.METERED, mMetered);
+ proto.write(NetworkIdentityProto.DEFAULT_NETWORK, mDefaultNetwork);
proto.end(start);
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index dc271d8..49879a8 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -35,6 +35,7 @@
import android.view.Display;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.location.gnssmetrics.GnssMetrics;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatteryStatsHelper;
@@ -683,6 +684,14 @@
public abstract long[] getCpuFreqTimes(int which);
public abstract long[] getScreenOffCpuFreqTimes(int which);
+ /**
+ * Returns cpu active time of an uid.
+ */
+ public abstract long getCpuActiveTime();
+ /**
+ * Returns cpu times of an uid on each cluster
+ */
+ public abstract long[] getCpuClusterTimes();
/**
* Returns cpu times of an uid at a particular process state.
@@ -1500,6 +1509,10 @@
public static final int STATE2_WIFI_SIGNAL_STRENGTH_SHIFT = 4;
public static final int STATE2_WIFI_SIGNAL_STRENGTH_MASK =
0x7 << STATE2_WIFI_SIGNAL_STRENGTH_SHIFT;
+ // Values for NUM_GPS_SIGNAL_QUALITY_LEVELS
+ public static final int STATE2_GPS_SIGNAL_QUALITY_SHIFT = 7;
+ public static final int STATE2_GPS_SIGNAL_QUALITY_MASK =
+ 0x1 << STATE2_GPS_SIGNAL_QUALITY_SHIFT;
public static final int STATE2_POWER_SAVE_FLAG = 1<<31;
public static final int STATE2_VIDEO_ON_FLAG = 1<<30;
@@ -2088,6 +2101,23 @@
*/
public abstract int getNumConnectivityChange(int which);
+
+ /**
+ * Returns the time in microseconds that the phone has been running with
+ * the given GPS signal quality level
+ *
+ * {@hide}
+ */
+ public abstract long getGpsSignalQualityTime(int strengthBin,
+ long elapsedRealtimeUs, int which);
+
+ /**
+ * Returns the GPS battery drain in mA-ms
+ *
+ * {@hide}
+ */
+ public abstract long getGpsBatteryDrainMaMs();
+
/**
* Returns the time in microseconds that the phone has been on while the device was
* running on battery.
@@ -2312,6 +2342,9 @@
WIFI_SUPPL_STATE_NAMES, WIFI_SUPPL_STATE_SHORT_NAMES),
new BitDescription(HistoryItem.STATE2_CAMERA_FLAG, "camera", "ca"),
new BitDescription(HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG, "ble_scan", "bles"),
+ new BitDescription(HistoryItem.STATE2_GPS_SIGNAL_QUALITY_MASK,
+ HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT, "gps_signal_quality", "Gss",
+ new String[] { "poor", "good"}, new String[] { "poor", "good"}),
};
public static final String[] HISTORY_EVENT_NAMES = new String[] {
@@ -4732,6 +4765,43 @@
pw.print(prefix);
sb.setLength(0);
sb.append(prefix);
+ sb.append(" GPS Statistics:");
+ pw.println(sb.toString());
+
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" GPS signal quality (Top 4 Average CN0):");
+ final String[] gpsSignalQualityDescription = new String[]{
+ "poor (less than 20 dBHz): ",
+ "good (greater than 20 dBHz): "};
+ final int numGpsSignalQualityBins = Math.min(GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS,
+ gpsSignalQualityDescription.length);
+ for (int i=0; i<numGpsSignalQualityBins; i++) {
+ final long time = getGpsSignalQualityTime(i, rawRealtime, which);
+ sb.append("\n ");
+ sb.append(prefix);
+ sb.append(" ");
+ sb.append(gpsSignalQualityDescription[i]);
+ formatTimeMs(sb, time/1000);
+ sb.append("(");
+ sb.append(formatRatioLocked(time, whichBatteryRealtime));
+ sb.append(") ");
+ }
+ pw.println(sb.toString());
+
+ final long gpsBatteryDrainMaMs = getGpsBatteryDrainMaMs();
+ if (gpsBatteryDrainMaMs > 0) {
+ pw.print(prefix);
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" Battery Drain (mAh): ");
+ sb.append(Double.toString(((double) gpsBatteryDrainMaMs)/(3600 * 1000)));
+ pw.println(sb.toString());
+ }
+
+ pw.print(prefix);
+ sb.setLength(0);
+ sb.append(prefix);
sb.append(" CONNECTIVITY POWER SUMMARY END");
pw.println(sb.toString());
pw.println("");
diff --git a/core/java/android/os/IPermissionController.aidl b/core/java/android/os/IPermissionController.aidl
index 5e8590a..3de953a 100644
--- a/core/java/android/os/IPermissionController.aidl
+++ b/core/java/android/os/IPermissionController.aidl
@@ -22,4 +22,5 @@
boolean checkPermission(String permission, int pid, int uid);
String[] getPackagesForUid(int uid);
boolean isRuntimePermission(String permission);
+ int getPackageUid(String packageName, int flags);
}
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index e6cf5e6..7654e9b 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -151,6 +151,12 @@
*/
public static final int OTA_UPDATE_UID = 1061;
+ /**
+ * Defines the UID used for incidentd.
+ * @hide
+ */
+ public static final int INCIDENTD_UID = 1067;
+
/** {@hide} */
public static final int NOBODY_UID = 9999;
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 673a8ba..57db9d1 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -61,6 +61,7 @@
import java.util.Locale;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
@@ -101,6 +102,9 @@
private static final String ACTION_EUICC_FACTORY_RESET =
"com.android.internal.action.EUICC_FACTORY_RESET";
+ /** used in {@link #wipeEuiccData} as package name of callback intent */
+ private static final String PACKAGE_NAME_WIPING_EUICC_DATA_CALLBACK = "android";
+
/**
* The recovery image uses this file to identify the location (i.e. blocks)
* of an OTA package on the /data partition. The block map file is
@@ -751,7 +755,7 @@
// Block until the ordered broadcast has completed.
condition.block();
- wipeEuiccData(context, wipeEuicc);
+ wipeEuiccData(context, wipeEuicc, PACKAGE_NAME_WIPING_EUICC_DATA_CALLBACK);
String shutdownArg = null;
if (shutdown) {
@@ -767,19 +771,29 @@
bootCommand(context, shutdownArg, "--wipe_data", reasonArg, localeArg);
}
- private static void wipeEuiccData(Context context, final boolean isWipeEuicc) {
+ /**
+ * Returns whether wipe Euicc data successfully or not.
+ *
+ * @param isWipeEuicc whether we want to wipe Euicc data or not
+ * @param packageName the package name of the caller app.
+ *
+ * @hide
+ */
+ public static boolean wipeEuiccData(
+ Context context, final boolean isWipeEuicc, final String packageName) {
ContentResolver cr = context.getContentResolver();
if (Settings.Global.getInt(cr, Settings.Global.EUICC_PROVISIONED, 0) == 0) {
// If the eUICC isn't provisioned, there's no reason to either wipe or retain profiles,
// as there's nothing to wipe nor retain.
Log.d(TAG, "Skipping eUICC wipe/retain as it is not provisioned");
- return;
+ return true;
}
EuiccManager euiccManager = (EuiccManager) context.getSystemService(
Context.EUICC_SERVICE);
if (euiccManager != null && euiccManager.isEnabled()) {
CountDownLatch euiccFactoryResetLatch = new CountDownLatch(1);
+ final AtomicBoolean wipingSucceeded = new AtomicBoolean(false);
BroadcastReceiver euiccWipeFinishReceiver = new BroadcastReceiver() {
@Override
@@ -801,6 +815,7 @@
} else {
Log.d(TAG, "Successfully retained euicc data.");
}
+ wipingSucceeded.set(true /* newValue */);
}
euiccFactoryResetLatch.countDown();
}
@@ -808,7 +823,7 @@
};
Intent intent = new Intent(ACTION_EUICC_FACTORY_RESET);
- intent.setPackage("android");
+ intent.setPackage(packageName);
PendingIntent callbackIntent = PendingIntent.getBroadcastAsUser(
context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT, UserHandle.SYSTEM);
IntentFilter filterConsent = new IntentFilter();
@@ -839,8 +854,8 @@
} else {
Log.e(TAG, "Timeout retaining eUICC data.");
}
+ return false;
}
- context.getApplicationContext().unregisterReceiver(euiccWipeFinishReceiver);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
if (isWipeEuicc) {
@@ -848,8 +863,13 @@
} else {
Log.e(TAG, "Retaining eUICC data interrupted", e);
}
+ return false;
+ } finally {
+ context.getApplicationContext().unregisterReceiver(euiccWipeFinishReceiver);
}
+ return wipingSucceeded.get();
}
+ return false;
}
/** {@hide} */
diff --git a/core/java/android/os/connectivity/GpsBatteryStats.aidl b/core/java/android/os/connectivity/GpsBatteryStats.aidl
new file mode 100644
index 0000000..7b96d1a
--- /dev/null
+++ b/core/java/android/os/connectivity/GpsBatteryStats.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.connectivity;
+
+/** {@hide} */
+parcelable GpsBatteryStats;
\ No newline at end of file
diff --git a/core/java/android/os/connectivity/GpsBatteryStats.java b/core/java/android/os/connectivity/GpsBatteryStats.java
new file mode 100644
index 0000000..f2ac5ef
--- /dev/null
+++ b/core/java/android/os/connectivity/GpsBatteryStats.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.os.connectivity;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.location.gnssmetrics.GnssMetrics;
+
+import java.util.Arrays;
+
+/**
+ * API for GPS power stats
+ *
+ * @hide
+ */
+public final class GpsBatteryStats implements Parcelable {
+
+ private long mLoggingDurationMs;
+ private long mEnergyConsumedMaMs;
+ private long[] mTimeInGpsSignalQualityLevel;
+
+ public static final Parcelable.Creator<GpsBatteryStats> CREATOR = new
+ Parcelable.Creator<GpsBatteryStats>() {
+ public GpsBatteryStats createFromParcel(Parcel in) {
+ return new GpsBatteryStats(in);
+ }
+
+ public GpsBatteryStats[] newArray(int size) {
+ return new GpsBatteryStats[size];
+ }
+ };
+
+ public GpsBatteryStats() {
+ initialize();
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeLong(mLoggingDurationMs);
+ out.writeLong(mEnergyConsumedMaMs);
+ out.writeLongArray(mTimeInGpsSignalQualityLevel);
+ }
+
+ public void readFromParcel(Parcel in) {
+ mLoggingDurationMs = in.readLong();
+ mEnergyConsumedMaMs = in.readLong();
+ in.readLongArray(mTimeInGpsSignalQualityLevel);
+ }
+
+ public long getLoggingDurationMs() {
+ return mLoggingDurationMs;
+ }
+
+ public long getEnergyConsumedMaMs() {
+ return mEnergyConsumedMaMs;
+ }
+
+ public long[] getTimeInGpsSignalQualityLevel() {
+ return mTimeInGpsSignalQualityLevel;
+ }
+
+ public void setLoggingDurationMs(long t) {
+ mLoggingDurationMs = t;
+ return;
+ }
+
+ public void setEnergyConsumedMaMs(long e) {
+ mEnergyConsumedMaMs = e;
+ return;
+ }
+
+ public void setTimeInGpsSignalQualityLevel(long[] t) {
+ mTimeInGpsSignalQualityLevel = Arrays.copyOfRange(t, 0,
+ Math.min(t.length, GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS));
+ return;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ private GpsBatteryStats(Parcel in) {
+ initialize();
+ readFromParcel(in);
+ }
+
+ private void initialize() {
+ mLoggingDurationMs = 0;
+ mEnergyConsumedMaMs = 0;
+ mTimeInGpsSignalQualityLevel = new long[GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS];
+ return;
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index c9e8621..60ce42b 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -16,10 +16,12 @@
package android.provider;
+import static android.provider.SettingsValidators.ANY_INTEGER_VALIDATOR;
import static android.provider.SettingsValidators.ANY_STRING_VALIDATOR;
import static android.provider.SettingsValidators.BOOLEAN_VALIDATOR;
import static android.provider.SettingsValidators.COMPONENT_NAME_VALIDATOR;
import static android.provider.SettingsValidators.LENIENT_IP_ADDRESS_VALIDATOR;
+import static android.provider.SettingsValidators.LOCALE_VALIDATOR;
import static android.provider.SettingsValidators.NON_NEGATIVE_INTEGER_VALIDATOR;
import static android.provider.SettingsValidators.PACKAGE_NAME_VALIDATOR;
import static android.provider.SettingsValidators.URI_VALIDATOR;
@@ -4008,6 +4010,9 @@
* Keys we no longer back up under the current schema, but want to continue to
* process when restoring historical backup datasets.
*
+ * All settings in {@link LEGACY_RESTORE_SETTINGS} array *must* have a non-null validator,
+ * otherwise they won't be restored.
+ *
* @hide
*/
public static final String[] LEGACY_RESTORE_SETTINGS = {
@@ -4116,6 +4121,9 @@
/**
* These are all public system settings
*
+ * All settings in {@link SETTINGS_TO_BACKUP} array *must* have a non-null validator,
+ * otherwise they won't be restored.
+ *
* @hide
*/
public static final Map<String, Validator> VALIDATORS = new ArrayMap<>();
@@ -4391,8 +4399,8 @@
public static final String WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON =
Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON;
- private static final Validator WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON_VALIDATOR =
- BOOLEAN_VALIDATOR;
+ private static final Validator WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON_VALIDATOR =
+ BOOLEAN_VALIDATOR;
/**
* @deprecated Use
@@ -4402,6 +4410,9 @@
public static final String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY =
Global.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY;
+ private static final Validator WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY_VALIDATOR =
+ NON_NEGATIVE_INTEGER_VALIDATOR;
+
/**
* @deprecated Use {@link android.provider.Settings.Global#WIFI_NUM_OPEN_NETWORKS_KEPT}
* instead
@@ -4409,6 +4420,9 @@
@Deprecated
public static final String WIFI_NUM_OPEN_NETWORKS_KEPT = Global.WIFI_NUM_OPEN_NETWORKS_KEPT;
+ private static final Validator WIFI_NUM_OPEN_NETWORKS_KEPT_VALIDATOR =
+ NON_NEGATIVE_INTEGER_VALIDATOR;
+
/**
* @deprecated Use {@link android.provider.Settings.Global#WIFI_ON} instead
*/
@@ -5190,6 +5204,8 @@
@Deprecated
public static final String ALLOW_MOCK_LOCATION = "mock_location";
+ private static final Validator ALLOW_MOCK_LOCATION_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* On Android 8.0 (API level 26) and higher versions of the platform,
* a 64-bit number (expressed as a hexadecimal string), unique to
@@ -5284,6 +5300,8 @@
@TestApi
public static final String AUTOFILL_SERVICE = "autofill_service";
+ private static final Validator AUTOFILL_SERVICE_VALIDATOR = COMPONENT_NAME_VALIDATOR;
+
/**
* Boolean indicating if Autofill supports field classification.
*
@@ -5370,9 +5388,38 @@
* List of input methods that are currently enabled. This is a string
* containing the IDs of all enabled input methods, each ID separated
* by ':'.
+ *
+ * Format like "ime0;subtype0;subtype1;subtype2:ime1:ime2;subtype0"
+ * where imeId is ComponentName and subtype is int32.
*/
public static final String ENABLED_INPUT_METHODS = "enabled_input_methods";
+ private static final Validator ENABLED_INPUT_METHODS_VALIDATOR = new Validator() {
+ @Override
+ public boolean validate(String value) {
+ if (value == null) {
+ return false;
+ }
+ String[] inputMethods = value.split(":");
+ boolean valid = true;
+ for (String inputMethod : inputMethods) {
+ if (inputMethod.length() == 0) {
+ return false;
+ }
+ String[] subparts = inputMethod.split(";");
+ for (String subpart : subparts) {
+ // allow either a non negative integer or a ComponentName
+ valid |= (NON_NEGATIVE_INTEGER_VALIDATOR.validate(subpart)
+ || COMPONENT_NAME_VALIDATOR.validate(subpart));
+ }
+ if (!valid) {
+ return false;
+ }
+ }
+ return valid;
+ }
+ };
+
/**
* List of system input methods that are currently disabled. This is a string
* containing the IDs of all disabled input methods, each ID separated
@@ -5388,6 +5435,8 @@
*/
public static final String SHOW_IME_WITH_HARD_KEYBOARD = "show_ime_with_hard_keyboard";
+ private static final Validator SHOW_IME_WITH_HARD_KEYBOARD_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* Host name and port for global http proxy. Uses ':' seperator for
* between host and port.
@@ -5677,6 +5726,8 @@
*/
public static final String ACCESSIBILITY_ENABLED = "accessibility_enabled";
+ private static final Validator ACCESSIBILITY_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* Setting specifying if the accessibility shortcut is enabled.
* @hide
@@ -5684,6 +5735,8 @@
public static final String ACCESSIBILITY_SHORTCUT_ENABLED =
"accessibility_shortcut_enabled";
+ private static final Validator ACCESSIBILITY_SHORTCUT_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* Setting specifying if the accessibility shortcut is enabled.
* @hide
@@ -5691,6 +5744,9 @@
public static final String ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN =
"accessibility_shortcut_on_lock_screen";
+ private static final Validator ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN_VALIDATOR =
+ BOOLEAN_VALIDATOR;
+
/**
* Setting specifying if the accessibility shortcut dialog has been shown to this user.
* @hide
@@ -5698,6 +5754,9 @@
public static final String ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN =
"accessibility_shortcut_dialog_shown";
+ private static final Validator ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN_VALIDATOR =
+ BOOLEAN_VALIDATOR;
+
/**
* Setting specifying the accessibility service to be toggled via the accessibility
* shortcut. Must be its flattened {@link ComponentName}.
@@ -5706,6 +5765,9 @@
public static final String ACCESSIBILITY_SHORTCUT_TARGET_SERVICE =
"accessibility_shortcut_target_service";
+ private static final Validator ACCESSIBILITY_SHORTCUT_TARGET_SERVICE_VALIDATOR =
+ COMPONENT_NAME_VALIDATOR;
+
/**
* Setting specifying the accessibility service or feature to be toggled via the
* accessibility button in the navigation bar. This is either a flattened
@@ -5716,17 +5778,32 @@
public static final String ACCESSIBILITY_BUTTON_TARGET_COMPONENT =
"accessibility_button_target_component";
+ private static final Validator ACCESSIBILITY_BUTTON_TARGET_COMPONENT_VALIDATOR =
+ new Validator() {
+ @Override
+ public boolean validate(String value) {
+ // technically either ComponentName or class name, but there's proper value
+ // validation at callsites, so allow any non-null string
+ return value != null;
+ }
+ };
+
/**
* If touch exploration is enabled.
*/
public static final String TOUCH_EXPLORATION_ENABLED = "touch_exploration_enabled";
+ private static final Validator TOUCH_EXPLORATION_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* List of the enabled accessibility providers.
*/
public static final String ENABLED_ACCESSIBILITY_SERVICES =
"enabled_accessibility_services";
+ private static final Validator ENABLED_ACCESSIBILITY_SERVICES_VALIDATOR =
+ new SettingsValidators.ComponentNameListValidator(":");
+
/**
* List of the accessibility services to which the user has granted
* permission to put the device into touch exploration mode.
@@ -5736,6 +5813,9 @@
public static final String TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES =
"touch_exploration_granted_accessibility_services";
+ private static final Validator TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES_VALIDATOR =
+ new SettingsValidators.ComponentNameListValidator(":");
+
/**
* Uri of the slice that's presented on the keyguard.
* Defaults to a slice with the date and next alarm.
@@ -5754,6 +5834,8 @@
@Deprecated
public static final String ACCESSIBILITY_SPEAK_PASSWORD = "speak_password";
+ private static final Validator ACCESSIBILITY_SPEAK_PASSWORD_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* Whether to draw text with high contrast while in accessibility mode.
*
@@ -5762,6 +5844,9 @@
public static final String ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED =
"high_text_contrast_enabled";
+ private static final Validator ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED_VALIDATOR =
+ BOOLEAN_VALIDATOR;
+
/**
* Setting that specifies whether the display magnification is enabled via a system-wide
* triple tap gesture. Display magnifications allows the user to zoom in the display content
@@ -5774,6 +5859,9 @@
public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED =
"accessibility_display_magnification_enabled";
+ private static final Validator ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED_VALIDATOR =
+ BOOLEAN_VALIDATOR;
+
/**
* Setting that specifies whether the display magnification is enabled via a shortcut
* affordance within the system's navigation area. Display magnifications allows the user to
@@ -5785,6 +5873,9 @@
public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED =
"accessibility_display_magnification_navbar_enabled";
+ private static final Validator ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED_VALIDATOR
+ = BOOLEAN_VALIDATOR;
+
/**
* Setting that specifies what the display magnification scale is.
* Display magnifications allows the user to zoom in the display
@@ -5798,6 +5889,9 @@
public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE =
"accessibility_display_magnification_scale";
+ private static final Validator ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE_VALIDATOR =
+ new SettingsValidators.InclusiveFloatRangeValidator(1.0f, Float.MAX_VALUE);
+
/**
* Unused mangnification setting
*
@@ -5850,6 +5944,9 @@
public static final String ACCESSIBILITY_CAPTIONING_ENABLED =
"accessibility_captioning_enabled";
+ private static final Validator ACCESSIBILITY_CAPTIONING_ENABLED_VALIDATOR =
+ BOOLEAN_VALIDATOR;
+
/**
* Setting that specifies the language for captions as a locale string,
* e.g. en_US.
@@ -5860,6 +5957,8 @@
public static final String ACCESSIBILITY_CAPTIONING_LOCALE =
"accessibility_captioning_locale";
+ private static final Validator ACCESSIBILITY_CAPTIONING_LOCALE_VALIDATOR = LOCALE_VALIDATOR;
+
/**
* Integer property that specifies the preset style for captions, one
* of:
@@ -5874,6 +5973,10 @@
public static final String ACCESSIBILITY_CAPTIONING_PRESET =
"accessibility_captioning_preset";
+ private static final Validator ACCESSIBILITY_CAPTIONING_PRESET_VALIDATOR =
+ new SettingsValidators.DiscreteValueValidator(new String[]{"-1", "0", "1", "2",
+ "3", "4"});
+
/**
* Integer property that specifes the background color for captions as a
* packed 32-bit color.
@@ -5884,6 +5987,9 @@
public static final String ACCESSIBILITY_CAPTIONING_BACKGROUND_COLOR =
"accessibility_captioning_background_color";
+ private static final Validator ACCESSIBILITY_CAPTIONING_BACKGROUND_COLOR_VALIDATOR =
+ ANY_INTEGER_VALIDATOR;
+
/**
* Integer property that specifes the foreground color for captions as a
* packed 32-bit color.
@@ -5894,6 +6000,9 @@
public static final String ACCESSIBILITY_CAPTIONING_FOREGROUND_COLOR =
"accessibility_captioning_foreground_color";
+ private static final Validator ACCESSIBILITY_CAPTIONING_FOREGROUND_COLOR_VALIDATOR =
+ ANY_INTEGER_VALIDATOR;
+
/**
* Integer property that specifes the edge type for captions, one of:
* <ul>
@@ -5908,6 +6017,9 @@
public static final String ACCESSIBILITY_CAPTIONING_EDGE_TYPE =
"accessibility_captioning_edge_type";
+ private static final Validator ACCESSIBILITY_CAPTIONING_EDGE_TYPE_VALIDATOR =
+ new SettingsValidators.DiscreteValueValidator(new String[]{"0", "1", "2"});
+
/**
* Integer property that specifes the edge color for captions as a
* packed 32-bit color.
@@ -5919,6 +6031,9 @@
public static final String ACCESSIBILITY_CAPTIONING_EDGE_COLOR =
"accessibility_captioning_edge_color";
+ private static final Validator ACCESSIBILITY_CAPTIONING_EDGE_COLOR_VALIDATOR =
+ ANY_INTEGER_VALIDATOR;
+
/**
* Integer property that specifes the window color for captions as a
* packed 32-bit color.
@@ -5929,6 +6044,9 @@
public static final String ACCESSIBILITY_CAPTIONING_WINDOW_COLOR =
"accessibility_captioning_window_color";
+ private static final Validator ACCESSIBILITY_CAPTIONING_WINDOW_COLOR_VALIDATOR =
+ ANY_INTEGER_VALIDATOR;
+
/**
* String property that specifies the typeface for captions, one of:
* <ul>
@@ -5944,6 +6062,10 @@
public static final String ACCESSIBILITY_CAPTIONING_TYPEFACE =
"accessibility_captioning_typeface";
+ private static final Validator ACCESSIBILITY_CAPTIONING_TYPEFACE_VALIDATOR =
+ new SettingsValidators.DiscreteValueValidator(new String[]{"DEFAULT",
+ "MONOSPACE", "SANS_SERIF", "SERIF"});
+
/**
* Floating point property that specifies font scaling for captions.
*
@@ -5952,12 +6074,18 @@
public static final String ACCESSIBILITY_CAPTIONING_FONT_SCALE =
"accessibility_captioning_font_scale";
+ private static final Validator ACCESSIBILITY_CAPTIONING_FONT_SCALE_VALIDATOR =
+ new SettingsValidators.InclusiveFloatRangeValidator(0.5f, 2.0f);
+
/**
* Setting that specifies whether display color inversion is enabled.
*/
public static final String ACCESSIBILITY_DISPLAY_INVERSION_ENABLED =
"accessibility_display_inversion_enabled";
+ private static final Validator ACCESSIBILITY_DISPLAY_INVERSION_ENABLED_VALIDATOR =
+ BOOLEAN_VALIDATOR;
+
/**
* Setting that specifies whether display color space adjustment is
* enabled.
@@ -5967,15 +6095,24 @@
public static final String ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED =
"accessibility_display_daltonizer_enabled";
+ private static final Validator ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED_VALIDATOR =
+ BOOLEAN_VALIDATOR;
+
/**
* Integer property that specifies the type of color space adjustment to
- * perform. Valid values are defined in AccessibilityManager.
+ * perform. Valid values are defined in AccessibilityManager:
+ * - AccessibilityManager.DALTONIZER_DISABLED = -1
+ * - AccessibilityManager.DALTONIZER_SIMULATE_MONOCHROMACY = 0
+ * - AccessibilityManager.DALTONIZER_CORRECT_DEUTERANOMALY = 12
*
* @hide
*/
public static final String ACCESSIBILITY_DISPLAY_DALTONIZER =
"accessibility_display_daltonizer";
+ private static final Validator ACCESSIBILITY_DISPLAY_DALTONIZER_VALIDATOR =
+ new SettingsValidators.DiscreteValueValidator(new String[] {"-1", "0", "12"});
+
/**
* Setting that specifies whether automatic click when the mouse pointer stops moving is
* enabled.
@@ -5985,6 +6122,9 @@
public static final String ACCESSIBILITY_AUTOCLICK_ENABLED =
"accessibility_autoclick_enabled";
+ private static final Validator ACCESSIBILITY_AUTOCLICK_ENABLED_VALIDATOR =
+ BOOLEAN_VALIDATOR;
+
/**
* Integer setting specifying amount of time in ms the mouse pointer has to stay still
* before performing click when {@link #ACCESSIBILITY_AUTOCLICK_ENABLED} is set.
@@ -5995,6 +6135,9 @@
public static final String ACCESSIBILITY_AUTOCLICK_DELAY =
"accessibility_autoclick_delay";
+ private static final Validator ACCESSIBILITY_AUTOCLICK_DELAY_VALIDATOR =
+ NON_NEGATIVE_INTEGER_VALIDATOR;
+
/**
* Whether or not larger size icons are used for the pointer of mouse/trackpad for
* accessibility.
@@ -6004,12 +6147,18 @@
public static final String ACCESSIBILITY_LARGE_POINTER_ICON =
"accessibility_large_pointer_icon";
+ private static final Validator ACCESSIBILITY_LARGE_POINTER_ICON_VALIDATOR =
+ BOOLEAN_VALIDATOR;
+
/**
* The timeout for considering a press to be a long press in milliseconds.
* @hide
*/
public static final String LONG_PRESS_TIMEOUT = "long_press_timeout";
+ private static final Validator LONG_PRESS_TIMEOUT_VALIDATOR =
+ NON_NEGATIVE_INTEGER_VALIDATOR;
+
/**
* The duration in milliseconds between the first tap's up event and the second tap's
* down event for an interaction to be considered part of the same multi-press.
@@ -6063,16 +6212,22 @@
*/
public static final String TTS_DEFAULT_RATE = "tts_default_rate";
+ private static final Validator TTS_DEFAULT_RATE_VALIDATOR = NON_NEGATIVE_INTEGER_VALIDATOR;
+
/**
* Default text-to-speech engine pitch. 100 = 1x
*/
public static final String TTS_DEFAULT_PITCH = "tts_default_pitch";
+ private static final Validator TTS_DEFAULT_PITCH_VALIDATOR = NON_NEGATIVE_INTEGER_VALIDATOR;
+
/**
* Default text-to-speech engine.
*/
public static final String TTS_DEFAULT_SYNTH = "tts_default_synth";
+ private static final Validator TTS_DEFAULT_SYNTH_VALIDATOR = PACKAGE_NAME_VALIDATOR;
+
/**
* Default text-to-speech language.
*
@@ -6120,11 +6275,33 @@
*/
public static final String TTS_DEFAULT_LOCALE = "tts_default_locale";
+ private static final Validator TTS_DEFAULT_LOCALE_VALIDATOR = new Validator() {
+ @Override
+ public boolean validate(String value) {
+ if (value == null || value.length() == 0) {
+ return false;
+ }
+ String[] ttsLocales = value.split(",");
+ boolean valid = true;
+ for (String ttsLocale : ttsLocales) {
+ String[] parts = ttsLocale.split(":");
+ valid |= ((parts.length == 2)
+ && (parts[0].length() > 0)
+ && ANY_STRING_VALIDATOR.validate(parts[0])
+ && LOCALE_VALIDATOR.validate(parts[1]));
+ }
+ return valid;
+ }
+ };
+
/**
* Space delimited list of plugin packages that are enabled.
*/
public static final String TTS_ENABLED_PLUGINS = "tts_enabled_plugins";
+ private static final Validator TTS_ENABLED_PLUGINS_VALIDATOR =
+ new SettingsValidators.PackageNameListValidator(" ");
+
/**
* @deprecated Use {@link android.provider.Settings.Global#WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON}
* instead.
@@ -6133,8 +6310,8 @@
public static final String WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON =
Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON;
- private static final Validator WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON_VALIDATOR =
- BOOLEAN_VALIDATOR;
+ private static final Validator WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON_VALIDATOR =
+ BOOLEAN_VALIDATOR;
/**
* @deprecated Use {@link android.provider.Settings.Global#WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY}
@@ -6144,6 +6321,9 @@
public static final String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY =
Global.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY;
+ private static final Validator WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY_VALIDATOR =
+ NON_NEGATIVE_INTEGER_VALIDATOR;
+
/**
* @deprecated Use {@link android.provider.Settings.Global#WIFI_NUM_OPEN_NETWORKS_KEPT}
* instead.
@@ -6152,6 +6332,9 @@
public static final String WIFI_NUM_OPEN_NETWORKS_KEPT =
Global.WIFI_NUM_OPEN_NETWORKS_KEPT;
+ private static final Validator WIFI_NUM_OPEN_NETWORKS_KEPT_VALIDATOR =
+ NON_NEGATIVE_INTEGER_VALIDATOR;
+
/**
* @deprecated Use {@link android.provider.Settings.Global#WIFI_ON}
* instead.
@@ -6310,6 +6493,9 @@
public static final String PREFERRED_TTY_MODE =
"preferred_tty_mode";
+ private static final Validator PREFERRED_TTY_MODE_VALIDATOR =
+ new SettingsValidators.DiscreteValueValidator(new String[]{"0", "1", "2", "3"});
+
/**
* Whether the enhanced voice privacy mode is enabled.
* 0 = normal voice privacy
@@ -6318,6 +6504,8 @@
*/
public static final String ENHANCED_VOICE_PRIVACY_ENABLED = "enhanced_voice_privacy_enabled";
+ private static final Validator ENHANCED_VOICE_PRIVACY_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* Whether the TTY mode mode is enabled.
* 0 = disabled
@@ -6326,6 +6514,8 @@
*/
public static final String TTY_MODE_ENABLED = "tty_mode_enabled";
+ private static final Validator TTY_MODE_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* Controls whether settings backup is enabled.
* Type: int ( 0 = disabled, 1 = enabled )
@@ -6496,24 +6686,32 @@
*/
public static final String MOUNT_PLAY_NOTIFICATION_SND = "mount_play_not_snd";
+ private static final Validator MOUNT_PLAY_NOTIFICATION_SND_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* Whether or not UMS auto-starts on UMS host detection. (0 = false, 1 = true)
* @hide
*/
public static final String MOUNT_UMS_AUTOSTART = "mount_ums_autostart";
+ private static final Validator MOUNT_UMS_AUTOSTART_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* Whether or not a notification is displayed on UMS host detection. (0 = false, 1 = true)
* @hide
*/
public static final String MOUNT_UMS_PROMPT = "mount_ums_prompt";
+ private static final Validator MOUNT_UMS_PROMPT_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* Whether or not a notification is displayed while UMS is enabled. (0 = false, 1 = true)
* @hide
*/
public static final String MOUNT_UMS_NOTIFY_ENABLED = "mount_ums_notify_enabled";
+ private static final Validator MOUNT_UMS_NOTIFY_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* If nonzero, ANRs in invisible background processes bring up a dialog.
* Otherwise, the process will be silently killed.
@@ -6531,6 +6729,9 @@
public static final String SHOW_FIRST_CRASH_DIALOG_DEV_OPTION =
"show_first_crash_dialog_dev_option";
+ private static final Validator SHOW_FIRST_CRASH_DIALOG_DEV_OPTION_VALIDATOR =
+ BOOLEAN_VALIDATOR;
+
/**
* The {@link ComponentName} string of the service to be used as the voice recognition
* service.
@@ -6556,6 +6757,8 @@
*/
public static final String SELECTED_SPELL_CHECKER = "selected_spell_checker";
+ private static final Validator SELECTED_SPELL_CHECKER_VALIDATOR = COMPONENT_NAME_VALIDATOR;
+
/**
* The {@link ComponentName} string of the selected subtype of the selected spell checker
* service which is one of the services managed by the text service manager.
@@ -6565,13 +6768,18 @@
public static final String SELECTED_SPELL_CHECKER_SUBTYPE =
"selected_spell_checker_subtype";
+ private static final Validator SELECTED_SPELL_CHECKER_SUBTYPE_VALIDATOR =
+ COMPONENT_NAME_VALIDATOR;
+
/**
- * The {@link ComponentName} string whether spell checker is enabled or not.
+ * Whether spell checker is enabled or not.
*
* @hide
*/
public static final String SPELL_CHECKER_ENABLED = "spell_checker_enabled";
+ private static final Validator SPELL_CHECKER_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* What happens when the user presses the Power button while in-call
* and the screen is on.<br/>
@@ -6583,6 +6791,9 @@
*/
public static final String INCALL_POWER_BUTTON_BEHAVIOR = "incall_power_button_behavior";
+ private static final Validator INCALL_POWER_BUTTON_BEHAVIOR_VALIDATOR =
+ new SettingsValidators.DiscreteValueValidator(new String[]{"1", "2"});
+
/**
* INCALL_POWER_BUTTON_BEHAVIOR value for "turn off screen".
* @hide
@@ -6638,12 +6849,16 @@
*/
public static final String WAKE_GESTURE_ENABLED = "wake_gesture_enabled";
+ private static final Validator WAKE_GESTURE_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* Whether the device should doze if configured.
* @hide
*/
public static final String DOZE_ENABLED = "doze_enabled";
+ private static final Validator DOZE_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* Whether doze should be always on.
* @hide
@@ -6656,6 +6871,8 @@
*/
public static final String DOZE_PULSE_ON_PICK_UP = "doze_pulse_on_pick_up";
+ private static final Validator DOZE_PULSE_ON_PICK_UP_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* Whether the device should pulse on long press gesture.
* @hide
@@ -6668,6 +6885,8 @@
*/
public static final String DOZE_PULSE_ON_DOUBLE_TAP = "doze_pulse_on_double_tap";
+ private static final Validator DOZE_PULSE_ON_DOUBLE_TAP_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* The current night mode that has been selected by the user. Owned
* and controlled by UiModeManagerService. Constants are as per
@@ -6682,6 +6901,8 @@
*/
public static final String SCREENSAVER_ENABLED = "screensaver_enabled";
+ private static final Validator SCREENSAVER_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* The user's chosen screensaver components.
*
@@ -6691,6 +6912,9 @@
*/
public static final String SCREENSAVER_COMPONENTS = "screensaver_components";
+ private static final Validator SCREENSAVER_COMPONENTS_VALIDATOR =
+ new SettingsValidators.ComponentNameListValidator(",");
+
/**
* If screensavers are enabled, whether the screensaver should be automatically launched
* when the device is inserted into a (desk) dock.
@@ -6698,6 +6922,8 @@
*/
public static final String SCREENSAVER_ACTIVATE_ON_DOCK = "screensaver_activate_on_dock";
+ private static final Validator SCREENSAVER_ACTIVATE_ON_DOCK_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* If screensavers are enabled, whether the screensaver should be automatically launched
* when the screen times out when not on battery.
@@ -6705,6 +6931,8 @@
*/
public static final String SCREENSAVER_ACTIVATE_ON_SLEEP = "screensaver_activate_on_sleep";
+ private static final Validator SCREENSAVER_ACTIVATE_ON_SLEEP_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* If screensavers are enabled, the default screensaver component.
* @hide
@@ -6717,6 +6945,9 @@
*/
public static final String NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component";
+ private static final Validator NFC_PAYMENT_DEFAULT_COMPONENT_VALIDATOR =
+ COMPONENT_NAME_VALIDATOR;
+
/**
* Whether NFC payment is handled by the foreground application or a default.
* @hide
@@ -6814,6 +7045,9 @@
public static final String ENABLED_NOTIFICATION_ASSISTANT =
"enabled_notification_assistant";
+ private static final Validator ENABLED_NOTIFICATION_ASSISTANT_VALIDATOR =
+ new SettingsValidators.ComponentNameListValidator(":");
+
/**
* Read only list of the service components that the current user has explicitly allowed to
* see all of the user's notifications, separated by ':'.
@@ -6825,6 +7059,9 @@
@Deprecated
public static final String ENABLED_NOTIFICATION_LISTENERS = "enabled_notification_listeners";
+ private static final Validator ENABLED_NOTIFICATION_LISTENERS_VALIDATOR =
+ new SettingsValidators.ComponentNameListValidator(":");
+
/**
* Read only list of the packages that the current user has explicitly allowed to
* manage do not disturb, separated by ':'.
@@ -6837,6 +7074,9 @@
public static final String ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES =
"enabled_notification_policy_access_packages";
+ private static final Validator ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES_VALIDATOR =
+ new SettingsValidators.PackageNameListValidator(":");
+
/**
* Defines whether managed profile ringtones should be synced from it's parent profile
* <p>
@@ -6850,6 +7090,8 @@
@RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
public static final String SYNC_PARENT_SOUNDS = "sync_parent_sounds";
+ private static final Validator SYNC_PARENT_SOUNDS_VALIDATOR = BOOLEAN_VALIDATOR;
+
/** @hide */
public static final String IMMERSIVE_MODE_CONFIRMATIONS = "immersive_mode_confirmations";
@@ -6936,12 +7178,17 @@
*/
public static final String SLEEP_TIMEOUT = "sleep_timeout";
+ private static final Validator SLEEP_TIMEOUT_VALIDATOR =
+ new SettingsValidators.InclusiveIntegerRangeValidator(-1, Integer.MAX_VALUE);
+
/**
* Controls whether double tap to wake is enabled.
* @hide
*/
public static final String DOUBLE_TAP_TO_WAKE = "double_tap_to_wake";
+ private static final Validator DOUBLE_TAP_TO_WAKE_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* The current assistant component. It could be a voice interaction service,
* or an activity that handles ACTION_ASSIST, or empty which means using the default
@@ -6958,6 +7205,8 @@
*/
public static final String CAMERA_GESTURE_DISABLED = "camera_gesture_disabled";
+ private static final Validator CAMERA_GESTURE_DISABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* Whether the camera launch gesture to double tap the power button when the screen is off
* should be disabled.
@@ -6967,6 +7216,9 @@
public static final String CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED =
"camera_double_tap_power_gesture_disabled";
+ private static final Validator CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED_VALIDATOR =
+ BOOLEAN_VALIDATOR;
+
/**
* Whether the camera double twist gesture to flip between front and back mode should be
* enabled.
@@ -6976,6 +7228,9 @@
public static final String CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED =
"camera_double_twist_to_flip_enabled";
+ private static final Validator CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED_VALIDATOR =
+ BOOLEAN_VALIDATOR;
+
/**
* Whether or not the smart camera lift trigger that launches the camera when the user moves
* the phone into a position for taking photos should be enabled.
@@ -6998,6 +7253,9 @@
*/
public static final String ASSIST_GESTURE_ENABLED = "assist_gesture_enabled";
+ private static final Validator ASSIST_GESTURE_ENABLED_VALIDATOR =
+ BOOLEAN_VALIDATOR;
+
/**
* Sensitivity control for the assist gesture.
*
@@ -7005,6 +7263,9 @@
*/
public static final String ASSIST_GESTURE_SENSITIVITY = "assist_gesture_sensitivity";
+ private static final Validator ASSIST_GESTURE_SENSITIVITY_VALIDATOR =
+ new SettingsValidators.InclusiveFloatRangeValidator(0.0f, 1.0f);
+
/**
* Whether the assist gesture should silence alerts.
*
@@ -7013,6 +7274,9 @@
public static final String ASSIST_GESTURE_SILENCE_ALERTS_ENABLED =
"assist_gesture_silence_alerts_enabled";
+ private static final Validator ASSIST_GESTURE_SILENCE_ALERTS_ENABLED_VALIDATOR =
+ BOOLEAN_VALIDATOR;
+
/**
* Whether the assist gesture should wake the phone.
*
@@ -7021,6 +7285,9 @@
public static final String ASSIST_GESTURE_WAKE_ENABLED =
"assist_gesture_wake_enabled";
+ private static final Validator ASSIST_GESTURE_WAKE_ENABLED_VALIDATOR =
+ BOOLEAN_VALIDATOR;
+
/**
* Whether Assist Gesture Deferred Setup has been completed
*
@@ -7028,6 +7295,8 @@
*/
public static final String ASSIST_GESTURE_SETUP_COMPLETE = "assist_gesture_setup_complete";
+ private static final Validator ASSIST_GESTURE_SETUP_COMPLETE_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* Control whether Night display is currently activated.
* @hide
@@ -7040,6 +7309,8 @@
*/
public static final String NIGHT_DISPLAY_AUTO_MODE = "night_display_auto_mode";
+ private static final Validator NIGHT_DISPLAY_AUTO_MODE_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* Control the color temperature of Night Display, represented in Kelvin.
* @hide
@@ -7047,6 +7318,9 @@
public static final String NIGHT_DISPLAY_COLOR_TEMPERATURE =
"night_display_color_temperature";
+ private static final Validator NIGHT_DISPLAY_COLOR_TEMPERATURE_VALIDATOR =
+ NON_NEGATIVE_INTEGER_VALIDATOR;
+
/**
* Custom time when Night display is scheduled to activate.
* Represented as milliseconds from midnight (e.g. 79200000 == 10pm).
@@ -7055,6 +7329,9 @@
public static final String NIGHT_DISPLAY_CUSTOM_START_TIME =
"night_display_custom_start_time";
+ private static final Validator NIGHT_DISPLAY_CUSTOM_START_TIME_VALIDATOR =
+ NON_NEGATIVE_INTEGER_VALIDATOR;
+
/**
* Custom time when Night display is scheduled to deactivate.
* Represented as milliseconds from midnight (e.g. 21600000 == 6am).
@@ -7062,6 +7339,9 @@
*/
public static final String NIGHT_DISPLAY_CUSTOM_END_TIME = "night_display_custom_end_time";
+ private static final Validator NIGHT_DISPLAY_CUSTOM_END_TIME_VALIDATOR =
+ NON_NEGATIVE_INTEGER_VALIDATOR;
+
/**
* A String representing the LocalDateTime when Night display was last activated. Use to
* decide whether to apply the current activated state after a reboot or user change. In
@@ -7079,6 +7359,9 @@
*/
public static final String ENABLED_VR_LISTENERS = "enabled_vr_listeners";
+ private static final Validator ENABLED_VR_LISTENERS_VALIDATOR =
+ new SettingsValidators.ComponentNameListValidator(":");
+
/**
* Behavior of the display while in VR mode.
*
@@ -7088,6 +7371,9 @@
*/
public static final String VR_DISPLAY_MODE = "vr_display_mode";
+ private static final Validator VR_DISPLAY_MODE_VALIDATOR =
+ new SettingsValidators.DiscreteValueValidator(new String[]{"0", "1"});
+
/**
* Lower the display persistence while the system is in VR mode.
*
@@ -7144,6 +7430,9 @@
public static final String AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN =
"automatic_storage_manager_days_to_retain";
+ private static final Validator AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN_VALIDATOR =
+ NON_NEGATIVE_INTEGER_VALIDATOR;
+
/**
* Default number of days of information for the automatic storage manager to retain.
*
@@ -7185,12 +7474,30 @@
public static final String SYSTEM_NAVIGATION_KEYS_ENABLED =
"system_navigation_keys_enabled";
+ private static final Validator SYSTEM_NAVIGATION_KEYS_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* Holds comma separated list of ordering of QS tiles.
* @hide
*/
public static final String QS_TILES = "sysui_qs_tiles";
+ private static final Validator QS_TILES_VALIDATOR = new Validator() {
+ @Override
+ public boolean validate(String value) {
+ if (value == null) {
+ return false;
+ }
+ String[] tiles = value.split(",");
+ boolean valid = true;
+ for (String tile : tiles) {
+ // tile can be any non-empty string as specified by OEM
+ valid |= ((tile.length() > 0) && ANY_STRING_VALIDATOR.validate(tile));
+ }
+ return valid;
+ }
+ };
+
/**
* Specifies whether the web action API is enabled.
*
@@ -7226,18 +7533,38 @@
*/
public static final String NOTIFICATION_BADGING = "notification_badging";
+ private static final Validator NOTIFICATION_BADGING_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* Comma separated list of QS tiles that have been auto-added already.
* @hide
*/
public static final String QS_AUTO_ADDED_TILES = "qs_auto_tiles";
+ private static final Validator QS_AUTO_ADDED_TILES_VALIDATOR = new Validator() {
+ @Override
+ public boolean validate(String value) {
+ if (value == null) {
+ return false;
+ }
+ String[] tiles = value.split(",");
+ boolean valid = true;
+ for (String tile : tiles) {
+ // tile can be any non-empty string as specified by OEM
+ valid |= ((tile.length() > 0) && ANY_STRING_VALIDATOR.validate(tile));
+ }
+ return valid;
+ }
+ };
+
/**
* Whether the Lockdown button should be shown in the power menu.
* @hide
*/
public static final String LOCKDOWN_IN_POWER_MENU = "lockdown_in_power_menu";
+ private static final Validator LOCKDOWN_IN_POWER_MENU_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* Backup manager behavioral parameters.
* This is encoded as a key=value list, separated by commas. Ex:
@@ -7277,8 +7604,6 @@
public static final String[] SETTINGS_TO_BACKUP = {
BUGREPORT_IN_POWER_MENU, // moved to global
ALLOW_MOCK_LOCATION,
- PARENTAL_CONTROL_ENABLED,
- PARENTAL_CONTROL_REDIRECT_URL,
USB_MASS_STORAGE_ENABLED, // moved to global
ACCESSIBILITY_DISPLAY_INVERSION_ENABLED,
ACCESSIBILITY_DISPLAY_DALTONIZER,
@@ -7310,12 +7635,9 @@
ACCESSIBILITY_CAPTIONING_TYPEFACE,
ACCESSIBILITY_CAPTIONING_FONT_SCALE,
ACCESSIBILITY_CAPTIONING_WINDOW_COLOR,
- TTS_USE_DEFAULTS,
TTS_DEFAULT_RATE,
TTS_DEFAULT_PITCH,
TTS_DEFAULT_SYNTH,
- TTS_DEFAULT_LANG,
- TTS_DEFAULT_COUNTRY,
TTS_ENABLED_PLUGINS,
TTS_DEFAULT_LOCALE,
SHOW_IME_WITH_HARD_KEYBOARD,
@@ -7371,7 +7693,158 @@
SHOW_FIRST_CRASH_DIALOG_DEV_OPTION,
};
- /** @hide */
+ /**
+ * All settings in {@link SETTINGS_TO_BACKUP} array *must* have a non-null validator,
+ * otherwise they won't be restored.
+ *
+ * @hide
+ */
+ public static final Map<String, Validator> VALIDATORS = new ArrayMap<>();
+ static {
+ VALIDATORS.put(BUGREPORT_IN_POWER_MENU, BUGREPORT_IN_POWER_MENU_VALIDATOR);
+ VALIDATORS.put(ALLOW_MOCK_LOCATION, ALLOW_MOCK_LOCATION_VALIDATOR);
+ VALIDATORS.put(USB_MASS_STORAGE_ENABLED, USB_MASS_STORAGE_ENABLED_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_DISPLAY_INVERSION_ENABLED,
+ ACCESSIBILITY_DISPLAY_INVERSION_ENABLED_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_DISPLAY_DALTONIZER,
+ ACCESSIBILITY_DISPLAY_DALTONIZER_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
+ ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
+ ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED,
+ ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED_VALIDATOR);
+ VALIDATORS.put(AUTOFILL_SERVICE, AUTOFILL_SERVICE_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
+ ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE_VALIDATOR);
+ VALIDATORS.put(ENABLED_ACCESSIBILITY_SERVICES,
+ ENABLED_ACCESSIBILITY_SERVICES_VALIDATOR);
+ VALIDATORS.put(ENABLED_VR_LISTENERS, ENABLED_VR_LISTENERS_VALIDATOR);
+ VALIDATORS.put(ENABLED_INPUT_METHODS, ENABLED_INPUT_METHODS_VALIDATOR);
+ VALIDATORS.put(TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
+ TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES_VALIDATOR);
+ VALIDATORS.put(TOUCH_EXPLORATION_ENABLED, TOUCH_EXPLORATION_ENABLED_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_ENABLED, ACCESSIBILITY_ENABLED_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
+ ACCESSIBILITY_SHORTCUT_TARGET_SERVICE_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_BUTTON_TARGET_COMPONENT,
+ ACCESSIBILITY_BUTTON_TARGET_COMPONENT_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN,
+ ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_SHORTCUT_ENABLED,
+ ACCESSIBILITY_SHORTCUT_ENABLED_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN,
+ ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_SPEAK_PASSWORD, ACCESSIBILITY_SPEAK_PASSWORD_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED,
+ ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_CAPTIONING_PRESET,
+ ACCESSIBILITY_CAPTIONING_PRESET_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_CAPTIONING_ENABLED,
+ ACCESSIBILITY_CAPTIONING_ENABLED_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_CAPTIONING_LOCALE,
+ ACCESSIBILITY_CAPTIONING_LOCALE_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_CAPTIONING_BACKGROUND_COLOR,
+ ACCESSIBILITY_CAPTIONING_BACKGROUND_COLOR_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_CAPTIONING_FOREGROUND_COLOR,
+ ACCESSIBILITY_CAPTIONING_FOREGROUND_COLOR_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_CAPTIONING_EDGE_TYPE,
+ ACCESSIBILITY_CAPTIONING_EDGE_TYPE_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_CAPTIONING_EDGE_COLOR,
+ ACCESSIBILITY_CAPTIONING_EDGE_COLOR_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_CAPTIONING_TYPEFACE,
+ ACCESSIBILITY_CAPTIONING_TYPEFACE_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_CAPTIONING_FONT_SCALE,
+ ACCESSIBILITY_CAPTIONING_FONT_SCALE_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_CAPTIONING_WINDOW_COLOR,
+ ACCESSIBILITY_CAPTIONING_WINDOW_COLOR_VALIDATOR);
+ VALIDATORS.put(TTS_DEFAULT_RATE, TTS_DEFAULT_RATE_VALIDATOR);
+ VALIDATORS.put(TTS_DEFAULT_PITCH, TTS_DEFAULT_PITCH_VALIDATOR);
+ VALIDATORS.put(TTS_DEFAULT_SYNTH, TTS_DEFAULT_SYNTH_VALIDATOR);
+ VALIDATORS.put(TTS_ENABLED_PLUGINS, TTS_ENABLED_PLUGINS_VALIDATOR);
+ VALIDATORS.put(TTS_DEFAULT_LOCALE, TTS_DEFAULT_LOCALE_VALIDATOR);
+ VALIDATORS.put(SHOW_IME_WITH_HARD_KEYBOARD, SHOW_IME_WITH_HARD_KEYBOARD_VALIDATOR);
+ VALIDATORS.put(WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,
+ WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON_VALIDATOR);
+ VALIDATORS.put(WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY,
+ WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY_VALIDATOR);
+ VALIDATORS.put(WIFI_NUM_OPEN_NETWORKS_KEPT, WIFI_NUM_OPEN_NETWORKS_KEPT_VALIDATOR);
+ VALIDATORS.put(SELECTED_SPELL_CHECKER, SELECTED_SPELL_CHECKER_VALIDATOR);
+ VALIDATORS.put(SELECTED_SPELL_CHECKER_SUBTYPE,
+ SELECTED_SPELL_CHECKER_SUBTYPE_VALIDATOR);
+ VALIDATORS.put(SPELL_CHECKER_ENABLED, SPELL_CHECKER_ENABLED_VALIDATOR);
+ VALIDATORS.put(MOUNT_PLAY_NOTIFICATION_SND, MOUNT_PLAY_NOTIFICATION_SND_VALIDATOR);
+ VALIDATORS.put(MOUNT_UMS_AUTOSTART, MOUNT_UMS_AUTOSTART_VALIDATOR);
+ VALIDATORS.put(MOUNT_UMS_PROMPT, MOUNT_UMS_PROMPT_VALIDATOR);
+ VALIDATORS.put(MOUNT_UMS_NOTIFY_ENABLED, MOUNT_UMS_NOTIFY_ENABLED_VALIDATOR);
+ VALIDATORS.put(SLEEP_TIMEOUT, SLEEP_TIMEOUT_VALIDATOR);
+ VALIDATORS.put(DOUBLE_TAP_TO_WAKE, DOUBLE_TAP_TO_WAKE_VALIDATOR);
+ VALIDATORS.put(WAKE_GESTURE_ENABLED, WAKE_GESTURE_ENABLED_VALIDATOR);
+ VALIDATORS.put(LONG_PRESS_TIMEOUT, LONG_PRESS_TIMEOUT_VALIDATOR);
+ VALIDATORS.put(CAMERA_GESTURE_DISABLED, CAMERA_GESTURE_DISABLED_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_AUTOCLICK_ENABLED,
+ ACCESSIBILITY_AUTOCLICK_ENABLED_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_AUTOCLICK_DELAY, ACCESSIBILITY_AUTOCLICK_DELAY_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_LARGE_POINTER_ICON,
+ ACCESSIBILITY_LARGE_POINTER_ICON_VALIDATOR);
+ VALIDATORS.put(PREFERRED_TTY_MODE, PREFERRED_TTY_MODE_VALIDATOR);
+ VALIDATORS.put(ENHANCED_VOICE_PRIVACY_ENABLED,
+ ENHANCED_VOICE_PRIVACY_ENABLED_VALIDATOR);
+ VALIDATORS.put(TTY_MODE_ENABLED, TTY_MODE_ENABLED_VALIDATOR);
+ VALIDATORS.put(INCALL_POWER_BUTTON_BEHAVIOR, INCALL_POWER_BUTTON_BEHAVIOR_VALIDATOR);
+ VALIDATORS.put(NIGHT_DISPLAY_CUSTOM_START_TIME,
+ NIGHT_DISPLAY_CUSTOM_START_TIME_VALIDATOR);
+ VALIDATORS.put(NIGHT_DISPLAY_CUSTOM_END_TIME, NIGHT_DISPLAY_CUSTOM_END_TIME_VALIDATOR);
+ VALIDATORS.put(NIGHT_DISPLAY_COLOR_TEMPERATURE,
+ NIGHT_DISPLAY_COLOR_TEMPERATURE_VALIDATOR);
+ VALIDATORS.put(NIGHT_DISPLAY_AUTO_MODE, NIGHT_DISPLAY_AUTO_MODE_VALIDATOR);
+ VALIDATORS.put(SYNC_PARENT_SOUNDS, SYNC_PARENT_SOUNDS_VALIDATOR);
+ VALIDATORS.put(CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED,
+ CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED_VALIDATOR);
+ VALIDATORS.put(CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED,
+ CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED_VALIDATOR);
+ VALIDATORS.put(SYSTEM_NAVIGATION_KEYS_ENABLED,
+ SYSTEM_NAVIGATION_KEYS_ENABLED_VALIDATOR);
+ VALIDATORS.put(QS_TILES, QS_TILES_VALIDATOR);
+ VALIDATORS.put(DOZE_ENABLED, DOZE_ENABLED_VALIDATOR);
+ VALIDATORS.put(DOZE_PULSE_ON_PICK_UP, DOZE_PULSE_ON_PICK_UP_VALIDATOR);
+ VALIDATORS.put(DOZE_PULSE_ON_DOUBLE_TAP, DOZE_PULSE_ON_DOUBLE_TAP_VALIDATOR);
+ VALIDATORS.put(NFC_PAYMENT_DEFAULT_COMPONENT, NFC_PAYMENT_DEFAULT_COMPONENT_VALIDATOR);
+ VALIDATORS.put(AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN,
+ AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN_VALIDATOR);
+ VALIDATORS.put(ASSIST_GESTURE_ENABLED, ASSIST_GESTURE_ENABLED_VALIDATOR);
+ VALIDATORS.put(ASSIST_GESTURE_SENSITIVITY, ASSIST_GESTURE_SENSITIVITY_VALIDATOR);
+ VALIDATORS.put(ASSIST_GESTURE_SETUP_COMPLETE, ASSIST_GESTURE_SETUP_COMPLETE_VALIDATOR);
+ VALIDATORS.put(ASSIST_GESTURE_SILENCE_ALERTS_ENABLED,
+ ASSIST_GESTURE_SILENCE_ALERTS_ENABLED_VALIDATOR);
+ VALIDATORS.put(ASSIST_GESTURE_WAKE_ENABLED, ASSIST_GESTURE_WAKE_ENABLED_VALIDATOR);
+ VALIDATORS.put(VR_DISPLAY_MODE, VR_DISPLAY_MODE_VALIDATOR);
+ VALIDATORS.put(NOTIFICATION_BADGING, NOTIFICATION_BADGING_VALIDATOR);
+ VALIDATORS.put(QS_AUTO_ADDED_TILES, QS_AUTO_ADDED_TILES_VALIDATOR);
+ VALIDATORS.put(SCREENSAVER_ENABLED, SCREENSAVER_ENABLED_VALIDATOR);
+ VALIDATORS.put(SCREENSAVER_COMPONENTS, SCREENSAVER_COMPONENTS_VALIDATOR);
+ VALIDATORS.put(SCREENSAVER_ACTIVATE_ON_DOCK, SCREENSAVER_ACTIVATE_ON_DOCK_VALIDATOR);
+ VALIDATORS.put(SCREENSAVER_ACTIVATE_ON_SLEEP, SCREENSAVER_ACTIVATE_ON_SLEEP_VALIDATOR);
+ VALIDATORS.put(LOCKDOWN_IN_POWER_MENU, LOCKDOWN_IN_POWER_MENU_VALIDATOR);
+ VALIDATORS.put(SHOW_FIRST_CRASH_DIALOG_DEV_OPTION,
+ SHOW_FIRST_CRASH_DIALOG_DEV_OPTION_VALIDATOR);
+ VALIDATORS.put(ENABLED_NOTIFICATION_LISTENERS,
+ ENABLED_NOTIFICATION_LISTENERS_VALIDATOR); //legacy restore setting
+ VALIDATORS.put(ENABLED_NOTIFICATION_ASSISTANT,
+ ENABLED_NOTIFICATION_ASSISTANT_VALIDATOR); //legacy restore setting
+ VALIDATORS.put(ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
+ ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES_VALIDATOR); //legacy restore setting
+ }
+
+ /**
+ * Keys we no longer back up under the current schema, but want to continue to
+ * process when restoring historical backup datasets.
+ *
+ * All settings in {@link LEGACY_RESTORE_SETTINGS} array *must* have a non-null validator,
+ * otherwise they won't be restored.
+ *
+ * @hide
+ */
public static final String[] LEGACY_RESTORE_SETTINGS = {
ENABLED_NOTIFICATION_LISTENERS,
ENABLED_NOTIFICATION_ASSISTANT,
@@ -8706,6 +9179,9 @@
public static final String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY =
"wifi_networks_available_repeat_delay";
+ private static final Validator WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY_VALIDATOR =
+ NON_NEGATIVE_INTEGER_VALIDATOR;
+
/**
* 802.11 country code in ISO 3166 format
* @hide
@@ -8735,6 +9211,9 @@
*/
public static final String WIFI_NUM_OPEN_NETWORKS_KEPT = "wifi_num_open_networks_kept";
+ private static final Validator WIFI_NUM_OPEN_NETWORKS_KEPT_VALIDATOR =
+ NON_NEGATIVE_INTEGER_VALIDATOR;
+
/**
* Whether the Wi-Fi should be on. Only the Wi-Fi service should touch this.
*/
@@ -10789,7 +11268,15 @@
LOCATION_GLOBAL_KILL_SWITCH,
};
- /** @hide */
+ /**
+ * Keys we no longer back up under the current schema, but want to continue to
+ * process when restoring historical backup datasets.
+ *
+ * All settings in {@link LEGACY_RESTORE_SETTINGS} array *must* have a non-null validator,
+ * otherwise they won't be restored.
+ *
+ * @hide
+ */
public static final String[] LEGACY_RESTORE_SETTINGS = {
};
diff --git a/core/java/android/provider/SettingsValidators.java b/core/java/android/provider/SettingsValidators.java
index 22ef3b6..84c9e88 100644
--- a/core/java/android/provider/SettingsValidators.java
+++ b/core/java/android/provider/SettingsValidators.java
@@ -21,6 +21,8 @@
import com.android.internal.util.ArrayUtils;
+import java.util.Locale;
+
/**
* This class provides both interface for validation and common validators
* used to ensure Settings have meaningful values.
@@ -50,6 +52,18 @@
}
};
+ public static final Validator ANY_INTEGER_VALIDATOR = new Validator() {
+ @Override
+ public boolean validate(String value) {
+ try {
+ Integer.parseInt(value);
+ return true;
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ }
+ };
+
public static final Validator URI_VALIDATOR = new Validator() {
@Override
public boolean validate(String value) {
@@ -80,7 +94,10 @@
// and underscores ('_'). However, individual package name parts may only
// start with letters.
// (https://developer.android.com/guide/topics/manifest/manifest-element.html#package)
- String[] subparts = value.split(".");
+ if (value == null) {
+ return false;
+ }
+ String[] subparts = value.split("\\.");
boolean isValidPackageName = true;
for (String subpart : subparts) {
isValidPackageName |= isSubpartValidForPackageName(subpart);
@@ -106,10 +123,29 @@
@Override
public boolean validate(String value) {
+ if (value == null) {
+ return false;
+ }
return value.length() <= MAX_IPV6_LENGTH;
}
};
+ public static final Validator LOCALE_VALIDATOR = new Validator() {
+ @Override
+ public boolean validate(String value) {
+ if (value == null) {
+ return false;
+ }
+ Locale[] validLocales = Locale.getAvailableLocales();
+ for (Locale locale : validLocales) {
+ if (value.equals(locale.toString())) {
+ return true;
+ }
+ }
+ return false;
+ }
+ };
+
public interface Validator {
boolean validate(String value);
}
@@ -166,4 +202,48 @@
}
}
}
+
+ public static final class ComponentNameListValidator implements Validator {
+ private final String mSeparator;
+
+ public ComponentNameListValidator(String separator) {
+ mSeparator = separator;
+ }
+
+ @Override
+ public boolean validate(String value) {
+ if (value == null) {
+ return false;
+ }
+ String[] elements = value.split(mSeparator);
+ for (String element : elements) {
+ if (!COMPONENT_NAME_VALIDATOR.validate(element)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ public static final class PackageNameListValidator implements Validator {
+ private final String mSeparator;
+
+ public PackageNameListValidator(String separator) {
+ mSeparator = separator;
+ }
+
+ @Override
+ public boolean validate(String value) {
+ if (value == null) {
+ return false;
+ }
+ String[] elements = value.split(mSeparator);
+ for (String element : elements) {
+ if (!PACKAGE_NAME_VALIDATOR.validate(element)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
}
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index f409e5b..72afbb8 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -73,6 +73,7 @@
public static final int KM_TAG_USER_AUTH_TYPE = KM_ENUM | 504;
public static final int KM_TAG_AUTH_TIMEOUT = KM_UINT | 505;
public static final int KM_TAG_ALLOW_WHILE_ON_BODY = KM_BOOL | 506;
+ public static final int KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED = KM_BOOL | 507;
public static final int KM_TAG_ALL_APPLICATIONS = KM_BOOL | 600;
public static final int KM_TAG_APPLICATION_ID = KM_BYTES | 601;
diff --git a/core/java/android/service/autofill/AutofillFieldClassificationService.java b/core/java/android/service/autofill/AutofillFieldClassificationService.java
index 18f6dab..4e4caf0 100644
--- a/core/java/android/service/autofill/AutofillFieldClassificationService.java
+++ b/core/java/android/service/autofill/AutofillFieldClassificationService.java
@@ -15,9 +15,6 @@
*/
package android.service.autofill;
-import static android.view.autofill.AutofillManager.EXTRA_AVAILABLE_ALGORITHMS;
-import static android.view.autofill.AutofillManager.EXTRA_DEFAULT_ALGORITHM;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -58,9 +55,7 @@
private static final String TAG = "AutofillFieldClassificationService";
- private static final int MSG_GET_AVAILABLE_ALGORITHMS = 1;
- private static final int MSG_GET_DEFAULT_ALGORITHM = 2;
- private static final int MSG_GET_SCORES = 3;
+ private static final int MSG_GET_SCORES = 1;
/**
* The {@link Intent} action that must be declared as handled by a service
@@ -69,6 +64,20 @@
public static final String SERVICE_INTERFACE =
"android.service.autofill.AutofillFieldClassificationService";
+ /**
+ * Manifest metadata key for the resource string containing the name of the default field
+ * classification algorithm.
+ */
+ public static final String SERVICE_META_DATA_KEY_DEFAULT_ALGORITHM =
+ "android.autofill.field_classification.default_algorithm";
+ /**
+ * Manifest metadata key for the resource string array containing the names of all field
+ * classification algorithms provided by the service.
+ */
+ public static final String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS =
+ "android.autofill.field_classification.available_algorithms";
+
+
/** {@hide} **/
public static final String EXTRA_SCORES = "scores";
@@ -79,21 +88,6 @@
final Bundle data = new Bundle();
final RemoteCallback callback;
switch (action) {
- case MSG_GET_AVAILABLE_ALGORITHMS:
- callback = (RemoteCallback) msg.obj;
- final List<String> availableAlgorithms = onGetAvailableAlgorithms();
- String[] asArray = null;
- if (availableAlgorithms != null) {
- asArray = new String[availableAlgorithms.size()];
- availableAlgorithms.toArray(asArray);
- }
- data.putStringArray(EXTRA_AVAILABLE_ALGORITHMS, asArray);
- break;
- case MSG_GET_DEFAULT_ALGORITHM:
- callback = (RemoteCallback) msg.obj;
- final String defaultAlgorithm = onGetDefaultAlgorithm();
- data.putString(EXTRA_DEFAULT_ALGORITHM, defaultAlgorithm);
- break;
case MSG_GET_SCORES:
final SomeArgs args = (SomeArgs) msg.obj;
callback = (RemoteCallback) args.arg1;
@@ -103,9 +97,9 @@
final List<AutofillValue> actualValues = ((List<AutofillValue>) args.arg4);
@SuppressWarnings("unchecked")
final String[] userDataValues = (String[]) args.arg5;
- final Scores scores = onGetScores(algorithmName, algorithmArgs, actualValues,
+ final float[][] scores = onGetScores(algorithmName, algorithmArgs, actualValues,
Arrays.asList(userDataValues));
- data.putParcelable(EXTRA_SCORES, scores);
+ data.putParcelable(EXTRA_SCORES, new Scores(scores));
break;
default:
Log.w(TAG, "Handling unknown message: " + action);
@@ -134,27 +128,6 @@
}
/**
- * Gets the name of all available algorithms.
- *
- * @throws UnsupportedOperationException if not implemented by service.
- */
- // TODO(b/70939974): rename to onGetAvailableAlgorithms if not removed
- @NonNull
- public List<String> onGetAvailableAlgorithms() {
- throw new UnsupportedOperationException("Must be implemented by external service");
- }
-
- /**
- * Gets the default algorithm that's used when an algorithm is not specified or is invalid.
- *
- * @throws UnsupportedOperationException if not implemented by service.
- */
- @NonNull
- public String onGetDefaultAlgorithm() {
- throw new UnsupportedOperationException("Must be implemented by external service");
- }
-
- /**
* Calculates field classification scores in a batch.
*
* <p>See {@link AutofillFieldClassificationService} for more info about field classification
@@ -165,13 +138,14 @@
* @param args optional arguments to be passed to the algorithm.
* @param actualValues values entered by the user.
* @param userDataValues values predicted from the user data.
- * @return the calculated scores and the algorithm used.
+ * @return the calculated scores, with the first dimension representing actual values and the
+ * second dimension values from {@link UserData}.
*
* {@hide}
*/
@Nullable
@SystemApi
- public Scores onGetScores(@Nullable String algorithm,
+ public float[][] onGetScores(@Nullable String algorithm,
@Nullable Bundle args, @NonNull List<AutofillValue> actualValues,
@NonNull List<String> userDataValues) {
throw new UnsupportedOperationException("Must be implemented by external service");
@@ -179,17 +153,6 @@
private final class AutofillFieldClassificationServiceWrapper
extends IAutofillFieldClassificationService.Stub {
-
- @Override
- public void getAvailableAlgorithms(RemoteCallback callback) throws RemoteException {
- mHandlerCaller.obtainMessageO(MSG_GET_AVAILABLE_ALGORITHMS, callback).sendToTarget();
- }
-
- @Override
- public void getDefaultAlgorithm(RemoteCallback callback) throws RemoteException {
- mHandlerCaller.obtainMessageO(MSG_GET_DEFAULT_ALGORITHM, callback).sendToTarget();
- }
-
@Override
public void getScores(RemoteCallback callback, String algorithmName, Bundle algorithmArgs,
List<AutofillValue> actualValues, String[] userDataValues)
@@ -200,52 +163,27 @@
}
}
-
- // TODO(b/70939974): it might be simpler to remove this class and return the float[][] directly,
- // ignoring the request if the algorithm name is invalid.
/**
- * Represents field classification scores used in a batch calculation.
+ * Helper class used to encapsulate a float[][] in a Parcelable.
*
* {@hide}
*/
- @SystemApi
public static final class Scores implements Parcelable {
- private final String mAlgorithmName;
- private final float[][] mScores;
+ public final float[][] scores;
- /* @hide */
- public Scores(String algorithmName, int size1, int size2) {
- mAlgorithmName = algorithmName;
- mScores = new float[size1][size2];
- }
-
- public Scores(Parcel parcel) {
- mAlgorithmName = parcel.readString();
+ private Scores(Parcel parcel) {
final int size1 = parcel.readInt();
final int size2 = parcel.readInt();
- mScores = new float[size1][size2];
+ scores = new float[size1][size2];
for (int i = 0; i < size1; i++) {
for (int j = 0; j < size2; j++) {
- mScores[i][j] = parcel.readFloat();
+ scores[i][j] = parcel.readFloat();
}
}
}
- /**
- * Gets the name of algorithm used to calculate the score.
- */
- @NonNull
- public String getAlgorithm() {
- return mAlgorithmName;
- }
-
- /**
- * Gets the resulting scores, with the 1st dimension representing actual values and the 2nd
- * dimension values from {@link UserData}.
- */
- @NonNull
- public float[][] getScores() {
- return mScores;
+ private Scores(float[][] scores) {
+ this.scores = scores;
}
@Override
@@ -255,20 +193,18 @@
@Override
public void writeToParcel(Parcel parcel, int flags) {
- parcel.writeString(mAlgorithmName);
- int size1 = mScores.length;
- int size2 = mScores[0].length;
+ int size1 = scores.length;
+ int size2 = scores[0].length;
parcel.writeInt(size1);
parcel.writeInt(size2);
for (int i = 0; i < size1; i++) {
for (int j = 0; j < size2; j++) {
- parcel.writeFloat(mScores[i][j]);
+ parcel.writeFloat(scores[i][j]);
}
}
}
public static final Creator<Scores> CREATOR = new Creator<Scores>() {
-
@Override
public Scores createFromParcel(Parcel parcel) {
return new Scores(parcel);
@@ -278,7 +214,6 @@
public Scores[] newArray(int size) {
return new Scores[size];
}
-
};
}
}
diff --git a/core/java/android/service/autofill/FieldClassification.java b/core/java/android/service/autofill/FieldClassification.java
index 5361803..cd1efd6 100644
--- a/core/java/android/service/autofill/FieldClassification.java
+++ b/core/java/android/service/autofill/FieldClassification.java
@@ -105,21 +105,16 @@
/**
* Represents the score of a {@link UserData} entry for the field.
- *
- * <p>The score is calculated by the given {@link #getAlgorithm() algorithm} and
- * the entry is identified by {@link #getRemoteId()}.
*/
public static final class Match {
private final String mRemoteId;
private final float mScore;
- private final String mAlgorithm;
/** @hide */
- public Match(String remoteId, float score, String algorithm) {
+ public Match(String remoteId, float score) {
mRemoteId = Preconditions.checkNotNull(remoteId);
mScore = score;
- mAlgorithm = algorithm;
}
/**
@@ -150,38 +145,22 @@
return mScore;
}
- /**
- * Gets the algorithm used to calculate this score.
- *
- * <p>Typically, this is either the algorithm set by
- * {@link UserData.Builder#setFieldClassificationAlgorithm(String, android.os.Bundle)},
- * or the
- * {@link android.view.autofill.AutofillManager#getDefaultFieldClassificationAlgorithm()}.
- */
- @NonNull
- public String getAlgorithm() {
- return mAlgorithm;
- }
-
@Override
public String toString() {
if (!sDebug) return super.toString();
final StringBuilder string = new StringBuilder("Match: remoteId=");
Helper.appendRedacted(string, mRemoteId);
- return string.append(", score=").append(mScore)
- .append(", algorithm=").append(mAlgorithm)
- .toString();
+ return string.append(", score=").append(mScore).toString();
}
private void writeToParcel(@NonNull Parcel parcel) {
parcel.writeString(mRemoteId);
parcel.writeFloat(mScore);
- parcel.writeString(mAlgorithm);
}
private static Match readFromParcel(@NonNull Parcel parcel) {
- return new Match(parcel.readString(), parcel.readFloat(), parcel.readString());
+ return new Match(parcel.readString(), parcel.readFloat());
}
}
}
diff --git a/core/java/android/service/autofill/IAutofillFieldClassificationService.aidl b/core/java/android/service/autofill/IAutofillFieldClassificationService.aidl
index d8e829d..398557d 100644
--- a/core/java/android/service/autofill/IAutofillFieldClassificationService.aidl
+++ b/core/java/android/service/autofill/IAutofillFieldClassificationService.aidl
@@ -27,8 +27,6 @@
* @hide
*/
oneway interface IAutofillFieldClassificationService {
- void getAvailableAlgorithms(in RemoteCallback callback);
- void getDefaultAlgorithm(in RemoteCallback callback);
void getScores(in RemoteCallback callback, String algorithmName, in Bundle algorithmArgs,
- in List<AutofillValue> actualValues, in String[] userDataValues);
+ in List<AutofillValue> actualValues, in String[] userDataValues);
}
diff --git a/core/java/android/service/autofill/UserData.java b/core/java/android/service/autofill/UserData.java
index 2f9225a..9017848 100644
--- a/core/java/android/service/autofill/UserData.java
+++ b/core/java/android/service/autofill/UserData.java
@@ -155,12 +155,9 @@
* <p>The currently available algorithms can be retrieve through
* {@link AutofillManager#getAvailableFieldClassificationAlgorithms()}.
*
- * <p><b>Note: </b>The available algorithms in the Android System can change dinamically,
- * so it's not guaranteed that the algorithm set here is the one that will be effectually
- * used. If the algorithm set here is not available at runtime, the
- * {@link AutofillManager#getDefaultFieldClassificationAlgorithm()} is used instead.
- * You can verify which algorithm was used by calling
- * {@link FieldClassification.Match#getAlgorithm()}.
+ * <p>If not set, the
+ * {@link AutofillManager#getDefaultFieldClassificationAlgorithm() default algorithm} is
+ * used instead.
*
* @param name name of the algorithm or {@code null} to used default.
* @param args optional arguments to the algorithm.
diff --git a/core/java/android/text/style/BackgroundColorSpan.java b/core/java/android/text/style/BackgroundColorSpan.java
index de05f50..4f471a8 100644
--- a/core/java/android/text/style/BackgroundColorSpan.java
+++ b/core/java/android/text/style/BackgroundColorSpan.java
@@ -16,24 +16,48 @@
package android.text.style;
+import android.annotation.ColorInt;
+import android.annotation.NonNull;
import android.os.Parcel;
import android.text.ParcelableSpan;
import android.text.TextPaint;
import android.text.TextUtils;
+/**
+ * Changes the background color of the text to which the span is attached.
+ * <p>
+ * For example, to set a green background color for a text you would create a {@link
+ * android.text.SpannableStringBuilder} based on the text and set the span.
+ * <pre>{@code
+ * SpannableString string = new SpannableString("Text with a background color span");
+ *string.setSpan(new BackgroundColorSpan(color), 12, 28, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * }</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/backgroundcolorspan.png" />
+ * <figcaption>Set a background color for the text.</figcaption>
+ */
public class BackgroundColorSpan extends CharacterStyle
implements UpdateAppearance, ParcelableSpan {
private final int mColor;
- public BackgroundColorSpan(int color) {
+ /**
+ * Creates a {@link BackgroundColorSpan} from a color integer.
+ * <p>
+ *
+ * @param color color integer that defines the background color
+ * @see android.content.res.Resources#getColor(int, Resources.Theme)
+ */
+ public BackgroundColorSpan(@ColorInt int color) {
mColor = color;
}
- public BackgroundColorSpan(Parcel src) {
+ /**
+ * Creates a {@link BackgroundColorSpan} from a parcel.
+ */
+ public BackgroundColorSpan(@NonNull Parcel src) {
mColor = src.readInt();
}
-
+
public int getSpanTypeId() {
return getSpanTypeIdInternal();
}
@@ -42,26 +66,40 @@
public int getSpanTypeIdInternal() {
return TextUtils.BACKGROUND_COLOR_SPAN;
}
-
+
public int describeContents() {
return 0;
}
- public void writeToParcel(Parcel dest, int flags) {
+ /**
+ * Flatten this object into a Parcel.
+ *
+ * @param dest The Parcel in which the object should be written.
+ * @param flags Additional flags about how the object should be written.
+ */
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
writeToParcelInternal(dest, flags);
}
/** @hide */
- public void writeToParcelInternal(Parcel dest, int flags) {
+ public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
dest.writeInt(mColor);
}
+ /**
+ * @return the background color of this span.
+ * @see BackgroundColorSpan#BackgroundColorSpan(int)
+ */
+ @ColorInt
public int getBackgroundColor() {
return mColor;
}
+ /**
+ * Updates the background color of the TextPaint.
+ */
@Override
- public void updateDrawState(TextPaint ds) {
- ds.bgColor = mColor;
+ public void updateDrawState(@NonNull TextPaint textPaint) {
+ textPaint.bgColor = mColor;
}
}
diff --git a/core/java/android/text/style/ForegroundColorSpan.java b/core/java/android/text/style/ForegroundColorSpan.java
index 2bc6d54..08ab2a1 100644
--- a/core/java/android/text/style/ForegroundColorSpan.java
+++ b/core/java/android/text/style/ForegroundColorSpan.java
@@ -17,24 +17,48 @@
package android.text.style;
import android.annotation.ColorInt;
+import android.annotation.NonNull;
import android.os.Parcel;
import android.text.ParcelableSpan;
import android.text.TextPaint;
import android.text.TextUtils;
+/**
+ * Changes the color of the text to which the span is attached.
+ * <p>
+ * For example, to set a green text color you would create a {@link
+ * android.text.SpannableStringBuilder} based on the text and set the span.
+ * <pre>{@code
+ * SpannableString string = new SpannableString("Text with a foreground color span");
+ *string.setSpan(new ForegroundColorSpan(color), 12, 28, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * }</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/foregroundcolorspan.png" />
+ * <figcaption>Set a text color.</figcaption>
+ */
public class ForegroundColorSpan extends CharacterStyle
implements UpdateAppearance, ParcelableSpan {
private final int mColor;
+ /**
+ * Creates a {@link ForegroundColorSpan} from a color integer.
+ * <p>
+ * To get the color integer associated with a particular color resource ID, use
+ * {@link android.content.res.Resources#getColor(int, Resources.Theme)}
+ *
+ * @param color color integer that defines the text color
+ */
public ForegroundColorSpan(@ColorInt int color) {
mColor = color;
}
- public ForegroundColorSpan(Parcel src) {
+ /**
+ * Creates a {@link ForegroundColorSpan} from a parcel.
+ */
+ public ForegroundColorSpan(@NonNull Parcel src) {
mColor = src.readInt();
}
-
+
public int getSpanTypeId() {
return getSpanTypeIdInternal();
}
@@ -48,22 +72,35 @@
return 0;
}
- public void writeToParcel(Parcel dest, int flags) {
+ /**
+ * Flatten this object into a Parcel.
+ *
+ * @param dest The Parcel in which the object should be written.
+ * @param flags Additional flags about how the object should be written.
+ */
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
writeToParcelInternal(dest, flags);
}
/** @hide */
- public void writeToParcelInternal(Parcel dest, int flags) {
+ public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
dest.writeInt(mColor);
}
+ /**
+ * @return the foreground color of this span.
+ * @see ForegroundColorSpan#ForegroundColorSpan(int)
+ */
@ColorInt
public int getForegroundColor() {
return mColor;
}
+ /**
+ * Updates the color of the TextPaint to the foreground color.
+ */
@Override
- public void updateDrawState(TextPaint ds) {
- ds.setColor(mColor);
+ public void updateDrawState(@NonNull TextPaint textPaint) {
+ textPaint.setColor(mColor);
}
}
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
index ce8998f..5a09dab 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
@@ -16,6 +16,7 @@
package android.util.apk;
+import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256;
import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_DSA_WITH_SHA256;
import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_ECDSA_WITH_SHA256;
import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_ECDSA_WITH_SHA512;
@@ -42,6 +43,7 @@
import java.io.RandomAccessFile;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
+import java.security.DigestException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
@@ -105,7 +107,8 @@
*/
public static X509Certificate[][] verify(String apkFile)
throws SignatureNotFoundException, SecurityException, IOException {
- return verify(apkFile, true);
+ VerifiedSigner vSigner = verify(apkFile, true);
+ return vSigner.certs;
}
/**
@@ -119,10 +122,11 @@
*/
public static X509Certificate[][] plsCertsNoVerifyOnlyCerts(String apkFile)
throws SignatureNotFoundException, SecurityException, IOException {
- return verify(apkFile, false);
+ VerifiedSigner vSigner = verify(apkFile, false);
+ return vSigner.certs;
}
- private static X509Certificate[][] verify(String apkFile, boolean verifyIntegrity)
+ private static VerifiedSigner verify(String apkFile, boolean verifyIntegrity)
throws SignatureNotFoundException, SecurityException, IOException {
try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) {
return verify(apk, verifyIntegrity);
@@ -138,7 +142,7 @@
* verify.
* @throws IOException if an I/O error occurs while reading the APK file.
*/
- private static X509Certificate[][] verify(RandomAccessFile apk, boolean verifyIntegrity)
+ private static VerifiedSigner verify(RandomAccessFile apk, boolean verifyIntegrity)
throws SignatureNotFoundException, SecurityException, IOException {
SignatureInfo signatureInfo = findSignature(apk);
return verify(apk, signatureInfo, verifyIntegrity);
@@ -163,7 +167,7 @@
* @param signatureInfo APK Signature Scheme v2 Block and information relevant for verifying it
* against the APK file.
*/
- private static X509Certificate[][] verify(
+ private static VerifiedSigner verify(
RandomAccessFile apk,
SignatureInfo signatureInfo,
boolean doVerifyIntegrity) throws SecurityException, IOException {
@@ -207,7 +211,14 @@
ApkSigningBlockUtils.verifyIntegrity(contentDigests, apk, signatureInfo);
}
- return signerCerts.toArray(new X509Certificate[signerCerts.size()][]);
+ byte[] verityRootHash = null;
+ if (contentDigests.containsKey(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)) {
+ verityRootHash = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256);
+ }
+
+ return new VerifiedSigner(
+ signerCerts.toArray(new X509Certificate[signerCerts.size()][]),
+ verityRootHash);
}
private static X509Certificate[] verifySigner(
@@ -383,6 +394,24 @@
return;
}
+ static byte[] getVerityRootHash(String apkPath)
+ throws IOException, SignatureNotFoundException, SecurityException {
+ try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
+ SignatureInfo signatureInfo = findSignature(apk);
+ VerifiedSigner vSigner = verify(apk, false);
+ return vSigner.verityRootHash;
+ }
+ }
+
+ static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory)
+ throws IOException, SignatureNotFoundException, SecurityException, DigestException,
+ NoSuchAlgorithmException {
+ try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
+ SignatureInfo signatureInfo = findSignature(apk);
+ return ApkSigningBlockUtils.generateApkVerity(apkPath, bufferFactory, signatureInfo);
+ }
+ }
+
private static boolean isSupportedSignatureAlgorithm(int sigAlgorithm) {
switch (sigAlgorithm) {
case SIGNATURE_RSA_PSS_WITH_SHA256:
@@ -400,4 +429,20 @@
return false;
}
}
+
+ /**
+ * Verified APK Signature Scheme v2 signer.
+ *
+ * @hide for internal use only.
+ */
+ public static class VerifiedSigner {
+ public final X509Certificate[][] certs;
+ public final byte[] verityRootHash;
+
+ public VerifiedSigner(X509Certificate[][] certs, byte[] verityRootHash) {
+ this.certs = certs;
+ this.verityRootHash = verityRootHash;
+ }
+
+ }
}
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
index c9e67fe..1b04eb2 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
@@ -16,6 +16,7 @@
package android.util.apk;
+import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256;
import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_DSA_WITH_SHA256;
import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_ECDSA_WITH_SHA256;
import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_ECDSA_WITH_SHA512;
@@ -43,6 +44,7 @@
import java.io.RandomAccessFile;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
+import java.security.DigestException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
@@ -211,6 +213,10 @@
ApkSigningBlockUtils.verifyIntegrity(contentDigests, apk, signatureInfo);
}
+ if (contentDigests.containsKey(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)) {
+ result.verityRootHash = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256);
+ }
+
return result;
}
@@ -499,6 +505,24 @@
return new VerifiedProofOfRotation(certs, flagsList);
}
+ static byte[] getVerityRootHash(String apkPath)
+ throws IOException, SignatureNotFoundException, SecurityException {
+ try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
+ SignatureInfo signatureInfo = findSignature(apk);
+ VerifiedSigner vSigner = verify(apk, false);
+ return vSigner.verityRootHash;
+ }
+ }
+
+ static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory)
+ throws IOException, SignatureNotFoundException, SecurityException, DigestException,
+ NoSuchAlgorithmException {
+ try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
+ SignatureInfo signatureInfo = findSignature(apk);
+ return ApkSigningBlockUtils.generateApkVerity(apkPath, bufferFactory, signatureInfo);
+ }
+ }
+
private static boolean isSupportedSignatureAlgorithm(int sigAlgorithm) {
switch (sigAlgorithm) {
case SIGNATURE_RSA_PSS_WITH_SHA256:
@@ -541,6 +565,8 @@
public final X509Certificate[] certs;
public final VerifiedProofOfRotation por;
+ public byte[] verityRootHash;
+
public VerifiedSigner(X509Certificate[] certs, VerifiedProofOfRotation por) {
this.certs = certs;
this.por = por;
diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java
index 555c474..a2a7616 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -36,7 +36,9 @@
import java.io.IOException;
import java.io.InputStream;
+import java.security.DigestException;
import java.security.GeneralSecurityException;
+import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.util.ArrayList;
@@ -367,4 +369,51 @@
// v2 didn't work, try jarsigner
return verifyV1Signature(apkPath, false);
}
+
+ /**
+ * @return the verity root hash in the Signing Block.
+ */
+ public static byte[] getVerityRootHash(String apkPath)
+ throws IOException, SignatureNotFoundException, SecurityException {
+ // first try v3
+ try {
+ return ApkSignatureSchemeV3Verifier.getVerityRootHash(apkPath);
+ } catch (SignatureNotFoundException e) {
+ // try older version
+ }
+ return ApkSignatureSchemeV2Verifier.getVerityRootHash(apkPath);
+ }
+
+ /**
+ * Generates the Merkle tree and verity metadata to the buffer allocated by the {@code
+ * ByteBufferFactory}.
+ *
+ * @return the verity root hash of the generated Merkle tree.
+ */
+ public static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory)
+ throws IOException, SignatureNotFoundException, SecurityException, DigestException,
+ NoSuchAlgorithmException {
+ // first try v3
+ try {
+ return ApkSignatureSchemeV3Verifier.generateApkVerity(apkPath, bufferFactory);
+ } catch (SignatureNotFoundException e) {
+ // try older version
+ }
+ return ApkSignatureSchemeV2Verifier.generateApkVerity(apkPath, bufferFactory);
+ }
+
+ /**
+ * Result of a successful APK verification operation.
+ */
+ public static class Result {
+ public final Certificate[][] certs;
+ public final Signature[] sigs;
+ public final int signatureSchemeVersion;
+
+ public Result(Certificate[][] certs, Signature[] sigs, int signingVersion) {
+ this.certs = certs;
+ this.sigs = sigs;
+ this.signatureSchemeVersion = signingVersion;
+ }
+ }
}
diff --git a/core/java/android/util/apk/ApkSigningBlockUtils.java b/core/java/android/util/apk/ApkSigningBlockUtils.java
index 9d53847..4146f6f 100644
--- a/core/java/android/util/apk/ApkSigningBlockUtils.java
+++ b/core/java/android/util/apk/ApkSigningBlockUtils.java
@@ -306,6 +306,26 @@
}
/**
+ * Generates the fsverity header and hash tree to be used by kernel for the given apk. This
+ * method does not check whether the root hash exists in the Signing Block or not.
+ *
+ * <p>The output is stored in the {@link ByteBuffer} created by the given {@link
+ * ByteBufferFactory}.
+ *
+ * @return the root hash of the generated hash tree.
+ */
+ public static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory,
+ SignatureInfo signatureInfo)
+ throws IOException, SignatureNotFoundException, SecurityException, DigestException,
+ NoSuchAlgorithmException {
+ try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
+ ApkVerityBuilder.ApkVerityResult result = ApkVerityBuilder.generateApkVerity(apk,
+ signatureInfo, bufferFactory);
+ return result.rootHash;
+ }
+ }
+
+ /**
* Returns the ZIP End of Central Directory (EoCD) and its offset in the file.
*
* @throws IOException if an I/O error occurs while reading the file.
diff --git a/core/java/android/util/apk/ApkVerityBuilder.java b/core/java/android/util/apk/ApkVerityBuilder.java
index 7412ef4..a0d5e4c 100644
--- a/core/java/android/util/apk/ApkVerityBuilder.java
+++ b/core/java/android/util/apk/ApkVerityBuilder.java
@@ -164,11 +164,11 @@
}
private void fillUpLastOutputChunk() {
- int extra = (int) (BUFFER_SIZE - mOutput.position() % BUFFER_SIZE);
- if (extra == 0) {
+ int lastBlockSize = (int) (mOutput.position() % BUFFER_SIZE);
+ if (lastBlockSize == 0) {
return;
}
- mOutput.put(ByteBuffer.allocate(extra));
+ mOutput.put(ByteBuffer.allocate(BUFFER_SIZE - lastBlockSize));
}
}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index f54561b..4b24a71 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -33,7 +33,6 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcelable;
-import android.os.RemoteCallback;
import android.os.RemoteException;
import android.service.autofill.AutofillService;
import android.service.autofill.FillEventHistory;
@@ -58,8 +57,6 @@
import java.util.Collections;
import java.util.List;
import java.util.Objects;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
// TODO: use java.lang.ref.Cleaner once Android supports Java 9
import sun.misc.Cleaner;
@@ -177,11 +174,6 @@
public static final String EXTRA_RESTORE_SESSION_TOKEN =
"android.view.autofill.extra.RESTORE_SESSION_TOKEN";
- /** @hide */
- public static final String EXTRA_AVAILABLE_ALGORITHMS = "available_algorithms";
- /** @hide */
- public static final String EXTRA_DEFAULT_ALGORITHM = "default_algorithm";
-
private static final String SESSION_ID_TAG = "android:sessionId";
private static final String STATE_TAG = "android:state";
private static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData";
@@ -1174,22 +1166,10 @@
* and it's ignored if the caller currently doesn't have an enabled autofill service for
* the user.
*/
- // TODO(b/70939974): refactor this method to be "purely" sync by getting the info from the
- // the ExtService manifest (instead of calling the service)
@Nullable
public String getDefaultFieldClassificationAlgorithm() {
- final SyncRemoteCallbackListener<String> listener =
- new SyncRemoteCallbackListener<String>() {
-
- @Override
- String getResult(Bundle result) {
- return result == null ? null : result.getString(EXTRA_DEFAULT_ALGORITHM);
- }
- };
-
try {
- mService.getDefaultFieldClassificationAlgorithm(new RemoteCallback(listener));
- return listener.getResult(FC_SERVICE_TIMEOUT);
+ return mService.getDefaultFieldClassificationAlgorithm();
} catch (RemoteException e) {
e.rethrowFromSystemServer();
return null;
@@ -1204,29 +1184,12 @@
* and it returns an empty list if the caller currently doesn't have an enabled autofill service
* for the user.
*/
- // TODO(b/70939974): refactor this method to be "purely" sync by getting the info from the
- // the ExtService manifest (instead of calling the service)
@NonNull
public List<String> getAvailableFieldClassificationAlgorithms() {
- final SyncRemoteCallbackListener<List<String>> listener =
- new SyncRemoteCallbackListener<List<String>>() {
-
- @Override
- List<String> getResult(Bundle result) {
- List<String> algorithms = null;
- if (result != null) {
- final String[] asArray = result.getStringArray(EXTRA_AVAILABLE_ALGORITHMS);
- if (asArray != null) {
- algorithms = Arrays.asList(asArray);
- }
- }
- return algorithms != null ? algorithms : Collections.emptyList();
- }
- };
-
+ final String[] algorithms;
try {
- mService.getAvailableFieldClassificationAlgorithms(new RemoteCallback(listener));
- return listener.getResult(FC_SERVICE_TIMEOUT);
+ algorithms = mService.getAvailableFieldClassificationAlgorithms();
+ return algorithms != null ? Arrays.asList(algorithms) : Collections.emptyList();
} catch (RemoteException e) {
e.rethrowFromSystemServer();
return null;
@@ -2322,36 +2285,4 @@
}
}
}
-
- private abstract static class SyncRemoteCallbackListener<T>
- implements RemoteCallback.OnResultListener {
-
- private final CountDownLatch mLatch = new CountDownLatch(1);
- private T mResult;
-
- @Override
- public void onResult(Bundle result) {
- if (sVerbose) Log.w(TAG, "SyncRemoteCallbackListener.onResult(): " + result);
- mResult = getResult(result);
- mLatch.countDown();
- }
-
- T getResult(int timeoutMs) {
- T result = null;
- try {
- if (mLatch.await(timeoutMs, TimeUnit.MILLISECONDS)) {
- result = mResult;
- } else {
- Log.w(TAG, "SyncRemoteCallbackListener not called in " + timeoutMs + "ms");
- }
- } catch (InterruptedException e) {
- Log.w(TAG, "SyncRemoteCallbackListener interrupted: " + e);
- Thread.currentThread().interrupt();
- }
- if (sVerbose) Log.w(TAG, "SyncRemoteCallbackListener: returning " + result);
- return result;
- }
-
- abstract T getResult(Bundle result);
- }
}
diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
index 41672e7..1a11fbb 100644
--- a/core/java/android/view/autofill/IAutoFillManager.aidl
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -59,6 +59,6 @@
void setUserData(in UserData userData);
boolean isFieldClassificationEnabled();
ComponentName getAutofillServiceComponentName();
- void getAvailableFieldClassificationAlgorithms(in RemoteCallback callback);
- void getDefaultFieldClassificationAlgorithm(in RemoteCallback callback);
+ String[] getAvailableFieldClassificationAlgorithms();
+ String getDefaultFieldClassificationAlgorithm();
}
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 388180d..e2d1ad5 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -23,6 +23,7 @@
import android.os.ParcelFileDescriptor;
import android.os.WorkSource;
import android.os.connectivity.CellularBatteryStats;
+import android.os.connectivity.GpsBatteryStats;
import android.os.health.HealthStatsParceler;
import android.telephony.DataConnectionRealTimeInfo;
import android.telephony.ModemActivityInfo;
@@ -91,6 +92,7 @@
void noteVibratorOff(int uid);
void noteStartGps(int uid);
void noteStopGps(int uid);
+ void noteGpsSignalQuality(int signalLevel);
void noteScreenState(int state);
void noteScreenBrightness(int brightness);
void noteUserActivity(int uid, int event);
@@ -140,6 +142,9 @@
/** {@hide} */
CellularBatteryStats getCellularBatteryStats();
+ /** {@hide} */
+ GpsBatteryStats getGpsBatteryStats();
+
HealthStatsParceler takeUidSnapshot(int uid);
HealthStatsParceler[] takeUidSnapshots(in int[] uid);
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index 15dc6f5..5a59e70 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -665,14 +665,14 @@
/**
* Calculate the baseline power usage for the device when it is in suspend and idle.
- * The device is drawing POWER_CPU_IDLE power at its lowest power state.
- * The device is drawing POWER_CPU_IDLE + POWER_CPU_AWAKE power when a wakelock is held.
+ * The device is drawing POWER_CPU_SUSPEND power at its lowest power state.
+ * The device is drawing POWER_CPU_SUSPEND + POWER_CPU_IDLE power when a wakelock is held.
*/
private void addIdleUsage() {
final double suspendPowerMaMs = (mTypeBatteryRealtimeUs / 1000) *
- mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE);
+ mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_SUSPEND);
final double idlePowerMaMs = (mTypeBatteryUptimeUs / 1000) *
- mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_AWAKE);
+ mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE);
final double totalPowerMah = (suspendPowerMaMs + idlePowerMaMs) / (60 * 60 * 1000);
if (DEBUG && totalPowerMah != 0) {
Log.d(TAG, "Suspend: time=" + (mTypeBatteryRealtimeUs / 1000)
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index ac5dbc4..799e3e8 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -34,6 +34,7 @@
import android.os.BatteryStats;
import android.os.Build;
import android.os.connectivity.CellularBatteryStats;
+import android.os.connectivity.GpsBatteryStats;
import android.os.FileUtils;
import android.os.Handler;
import android.os.IBatteryPropertiesRegistrar;
@@ -78,6 +79,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.location.gnssmetrics.GnssMetrics;
import com.android.internal.net.NetworkStatsFactory;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
@@ -153,11 +155,11 @@
MAX_HISTORY_BUFFER = 96*1024; // 96KB
MAX_MAX_HISTORY_BUFFER = 128*1024; // 128KB
} else {
- MAX_HISTORY_ITEMS = 2000;
- MAX_MAX_HISTORY_ITEMS = 3000;
- MAX_WAKELOCKS_PER_UID = 100;
- MAX_HISTORY_BUFFER = 256*1024; // 256KB
- MAX_MAX_HISTORY_BUFFER = 320*1024; // 256KB
+ MAX_HISTORY_ITEMS = 4000;
+ MAX_MAX_HISTORY_ITEMS = 6000;
+ MAX_WAKELOCKS_PER_UID = 200;
+ MAX_HISTORY_BUFFER = 512*1024; // 512KB
+ MAX_MAX_HISTORY_BUFFER = 640*1024; // 640KB
}
}
@@ -198,6 +200,12 @@
protected KernelUidCpuFreqTimeReader mKernelUidCpuFreqTimeReader =
new KernelUidCpuFreqTimeReader();
@VisibleForTesting
+ protected KernelUidCpuActiveTimeReader mKernelUidCpuActiveTimeReader =
+ new KernelUidCpuActiveTimeReader();
+ @VisibleForTesting
+ protected KernelUidCpuClusterTimeReader mKernelUidCpuClusterTimeReader =
+ new KernelUidCpuClusterTimeReader();
+ @VisibleForTesting
protected KernelSingleUidTimeReader mKernelSingleUidTimeReader;
private final KernelMemoryBandwidthStats mKernelMemoryBandwidthStats
@@ -666,6 +674,10 @@
int mCameraOnNesting;
StopwatchTimer mCameraOnTimer;
+ int mGpsSignalQualityBin = -1;
+ final StopwatchTimer[] mGpsSignalQualityTimer =
+ new StopwatchTimer[GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS];
+
int mPhoneSignalStrengthBin = -1;
int mPhoneSignalStrengthBinRaw = -1;
final StopwatchTimer[] mPhoneSignalStrengthsTimer =
@@ -3880,6 +3892,8 @@
}
mKernelUidCpuTimeReader.removeUid(isolatedUid);
mKernelUidCpuFreqTimeReader.removeUid(isolatedUid);
+ mKernelUidCpuActiveTimeReader.removeUid(isolatedUid);
+ mKernelUidCpuClusterTimeReader.removeUid(isolatedUid);
}
public int mapUid(int uid) {
@@ -4575,10 +4589,37 @@
if (DEBUG_HISTORY) Slog.v(TAG, "Stop GPS to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtime, uptime);
+ stopAllGpsSignalQualityTimersLocked(-1);
+ mGpsSignalQualityBin = -1;
}
getUidStatsLocked(uid).noteStopGps(elapsedRealtime);
}
+ public void noteGpsSignalQualityLocked(int signalLevel) {
+ if (mGpsNesting == 0) {
+ return;
+ }
+ if (signalLevel < 0 || signalLevel >= GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS) {
+ stopAllGpsSignalQualityTimersLocked(-1);
+ return;
+ }
+ final long elapsedRealtime = mClocks.elapsedRealtime();
+ final long uptime = mClocks.uptimeMillis();
+ if (mGpsSignalQualityBin != signalLevel) {
+ if (mGpsSignalQualityBin >= 0) {
+ mGpsSignalQualityTimer[mGpsSignalQualityBin].stopRunningLocked(elapsedRealtime);
+ }
+ if(!mGpsSignalQualityTimer[signalLevel].isRunningLocked()) {
+ mGpsSignalQualityTimer[signalLevel].startRunningLocked(elapsedRealtime);
+ }
+ mHistoryCur.states2 = (mHistoryCur.states2&~HistoryItem.STATE2_GPS_SIGNAL_QUALITY_MASK)
+ | (signalLevel << HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
+ mGpsSignalQualityBin = signalLevel;
+ }
+ return;
+ }
+
public void noteScreenStateLocked(int state) {
state = mPretendScreenOff ? Display.STATE_OFF : state;
@@ -4912,6 +4953,18 @@
mDailyPackageChanges.add(pc);
}
+ void stopAllGpsSignalQualityTimersLocked(int except) {
+ final long elapsedRealtime = mClocks.elapsedRealtime();
+ for (int i = 0; i < GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
+ if (i == except) {
+ continue;
+ }
+ while (mGpsSignalQualityTimer[i].isRunningLocked()) {
+ mGpsSignalQualityTimer[i].stopRunningLocked(elapsedRealtime);
+ }
+ }
+ }
+
public void notePhoneOnLocked() {
if (!mPhoneOn) {
final long elapsedRealtime = mClocks.elapsedRealtime();
@@ -6123,6 +6176,20 @@
return val;
}
+ @Override public long getGpsSignalQualityTime(int strengthBin,
+ long elapsedRealtimeUs, int which) {
+ if (strengthBin < 0 || strengthBin >= GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS) {
+ return 0;
+ }
+ return mGpsSignalQualityTimer[strengthBin].getTotalTimeLocked(
+ elapsedRealtimeUs, which);
+ }
+
+ @Override public long getGpsBatteryDrainMaMs() {
+ //TODO: Add GPS power computation (b/67213967)
+ return 0;
+ }
+
@Override public long getPhoneOnTime(long elapsedRealtimeUs, int which) {
return mPhoneOnTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
}
@@ -6479,9 +6546,11 @@
LongSamplingCounter mUserCpuTime;
LongSamplingCounter mSystemCpuTime;
LongSamplingCounter[][] mCpuClusterSpeedTimesUs;
+ LongSamplingCounter mCpuActiveTimeMs;
LongSamplingCounterArray mCpuFreqTimeMs;
LongSamplingCounterArray mScreenOffCpuFreqTimeMs;
+ LongSamplingCounterArray mCpuClusterTimesMs;
LongSamplingCounterArray[] mProcStateTimeMs;
LongSamplingCounterArray[] mProcStateScreenOffTimeMs;
@@ -6551,6 +6620,8 @@
mUserCpuTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
mSystemCpuTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
+ mCpuActiveTimeMs = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
+ mCpuClusterTimesMs = new LongSamplingCounterArray(mBsi.mOnBatteryTimeBase);
mWakelockStats = mBsi.new OverflowArrayMap<Wakelock>(uid) {
@Override public Wakelock instantiateObject() {
@@ -6598,6 +6669,17 @@
}
@Override
+ public long getCpuActiveTime() {
+ return mCpuActiveTimeMs.getCountLocked(STATS_SINCE_CHARGED);
+ }
+
+ @Override
+ public long[] getCpuClusterTimes() {
+ return nullIfAllZeros(mCpuClusterTimesMs, STATS_SINCE_CHARGED);
+ }
+
+
+ @Override
public long[] getCpuFreqTimes(int which, int procState) {
if (which < 0 || which >= NUM_PROCESS_STATE) {
return null;
@@ -7660,6 +7742,9 @@
mScreenOffCpuFreqTimeMs.reset(false);
}
+ mCpuActiveTimeMs.reset(false);
+ mCpuClusterTimesMs.reset(false);
+
if (mProcStateTimeMs != null) {
for (LongSamplingCounterArray counters : mProcStateTimeMs) {
if (counters != null) {
@@ -7864,6 +7949,8 @@
if (mScreenOffCpuFreqTimeMs != null) {
mScreenOffCpuFreqTimeMs.detach();
}
+ mCpuActiveTimeMs.detach();
+ mCpuClusterTimesMs.detach();
if (mProcStateTimeMs != null) {
for (LongSamplingCounterArray counters : mProcStateTimeMs) {
@@ -8139,6 +8226,10 @@
LongSamplingCounterArray.writeToParcel(out, mCpuFreqTimeMs);
LongSamplingCounterArray.writeToParcel(out, mScreenOffCpuFreqTimeMs);
+
+ mCpuActiveTimeMs.writeToParcel(out);
+ mCpuClusterTimesMs.writeToParcel(out);
+
if (mProcStateTimeMs != null) {
out.writeInt(mProcStateTimeMs.length);
for (LongSamplingCounterArray counters : mProcStateTimeMs) {
@@ -8456,6 +8547,9 @@
mScreenOffCpuFreqTimeMs = LongSamplingCounterArray.readFromParcel(
in, mBsi.mOnBatteryScreenOffTimeBase);
+ mCpuActiveTimeMs = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in);
+ mCpuClusterTimesMs = new LongSamplingCounterArray(mBsi.mOnBatteryTimeBase, in);
+
int length = in.readInt();
if (length == NUM_PROCESS_STATE) {
mProcStateTimeMs = new LongSamplingCounterArray[length];
@@ -9822,6 +9916,10 @@
mWifiSignalStrengthsTimer[i] = new StopwatchTimer(mClocks, null, -800-i, null,
mOnBatteryTimeBase);
}
+ for (int i=0; i< GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
+ mGpsSignalQualityTimer[i] = new StopwatchTimer(mClocks, null, -1000-i, null,
+ mOnBatteryTimeBase);
+ }
mAudioOnTimer = new StopwatchTimer(mClocks, null, -7, null, mOnBatteryTimeBase);
mVideoOnTimer = new StopwatchTimer(mClocks, null, -8, null, mOnBatteryTimeBase);
mFlashlightOnTimer = new StopwatchTimer(mClocks, null, -9, null, mOnBatteryTimeBase);
@@ -10511,6 +10609,9 @@
mWifiSignalStrengthsTimer[i].reset(false);
}
mWifiMulticastWakelockTimer.reset(false);
+ for (int i=0; i< GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
+ mGpsSignalQualityTimer[i].reset(false);
+ }
mWifiActivity.reset(false);
mBluetoothActivity.reset(false);
mModemActivity.reset(false);
@@ -11437,6 +11538,8 @@
if (!mOnBatteryInternal) {
mKernelUidCpuTimeReader.readDelta(null);
mKernelUidCpuFreqTimeReader.readDelta(null);
+ mKernelUidCpuActiveTimeReader.readDelta(null);
+ mKernelUidCpuClusterTimeReader.readDelta(null);
for (int cluster = mKernelCpuSpeedReaders.length - 1; cluster >= 0; --cluster) {
mKernelCpuSpeedReaders[cluster].readDelta();
}
@@ -11453,6 +11556,8 @@
updateClusterSpeedTimes(updatedUids);
}
readKernelUidCpuFreqTimesLocked(partialTimersToConsider);
+ readKernelUidCpuActiveTimesLocked();
+ readKernelUidCpuClusterTimesLocked();
}
/**
@@ -11764,6 +11869,64 @@
}
}
+ /**
+ * Take a snapshot of the cpu active times spent by each uid and update the corresponding
+ * counters.
+ */
+ @VisibleForTesting
+ public void readKernelUidCpuActiveTimesLocked() {
+ final long startTimeMs = mClocks.uptimeMillis();
+ mKernelUidCpuActiveTimeReader.readDelta((uid, cpuActiveTimesUs) -> {
+ uid = mapUid(uid);
+ if (Process.isIsolated(uid)) {
+ mKernelUidCpuActiveTimeReader.removeUid(uid);
+ Slog.w(TAG, "Got active times for an isolated uid with no mapping: " + uid);
+ return;
+ }
+ if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
+ Slog.w(TAG, "Got active times for an invalid user's uid " + uid);
+ mKernelUidCpuActiveTimeReader.removeUid(uid);
+ return;
+ }
+ final Uid u = getUidStatsLocked(uid);
+ u.mCpuActiveTimeMs.addCountLocked(cpuActiveTimesUs);
+ });
+
+ final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs;
+ if (DEBUG_ENERGY_CPU || elapsedTimeMs >= 100) {
+ Slog.d(TAG, "Reading cpu active times took " + elapsedTimeMs + "ms");
+ }
+ }
+
+ /**
+ * Take a snapshot of the cpu cluster times spent by each uid and update the corresponding
+ * counters.
+ */
+ @VisibleForTesting
+ public void readKernelUidCpuClusterTimesLocked() {
+ final long startTimeMs = mClocks.uptimeMillis();
+ mKernelUidCpuClusterTimeReader.readDelta((uid, cpuClusterTimesUs) -> {
+ uid = mapUid(uid);
+ if (Process.isIsolated(uid)) {
+ mKernelUidCpuClusterTimeReader.removeUid(uid);
+ Slog.w(TAG, "Got cluster times for an isolated uid with no mapping: " + uid);
+ return;
+ }
+ if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
+ Slog.w(TAG, "Got cluster times for an invalid user's uid " + uid);
+ mKernelUidCpuClusterTimeReader.removeUid(uid);
+ return;
+ }
+ final Uid u = getUidStatsLocked(uid);
+ u.mCpuClusterTimesMs.addCountLocked(cpuClusterTimesUs);
+ });
+
+ final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs;
+ if (DEBUG_ENERGY_CPU || elapsedTimeMs >= 100) {
+ Slog.d(TAG, "Reading cpu cluster times took " + elapsedTimeMs + "ms");
+ }
+ }
+
boolean setChargingLocked(boolean charging) {
if (mCharging != charging) {
mCharging = charging;
@@ -12367,6 +12530,21 @@
return s;
}
+ /*@hide */
+ public GpsBatteryStats getGpsBatteryStats() {
+ GpsBatteryStats s = new GpsBatteryStats();
+ final int which = STATS_SINCE_CHARGED;
+ final long rawRealTime = SystemClock.elapsedRealtime() * 1000;
+ s.setLoggingDurationMs(computeBatteryRealtime(rawRealTime, which) / 1000);
+ s.setEnergyConsumedMaMs(getGpsBatteryDrainMaMs());
+ long[] time = new long[GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS];
+ for (int i=0; i<time.length; i++) {
+ time[i] = getGpsSignalQualityTime(i, rawRealTime, which) / 1000;
+ }
+ s.setTimeInGpsSignalQualityLevel(time);
+ return s;
+ }
+
@Override
public LevelStepTracker getChargeLevelStepTracker() {
return mChargeStepTracker;
@@ -13044,6 +13222,9 @@
for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
mWifiSignalStrengthsTimer[i].readSummaryFromParcelLocked(in);
}
+ for (int i=0; i<GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
+ mGpsSignalQualityTimer[i].readSummaryFromParcelLocked(in);
+ }
mWifiActivity.readSummaryFromParcel(in);
mBluetoothActivity.readSummaryFromParcel(in);
mModemActivity.readSummaryFromParcel(in);
@@ -13249,6 +13430,10 @@
in, mOnBatteryTimeBase);
u.mScreenOffCpuFreqTimeMs = LongSamplingCounterArray.readSummaryFromParcelLocked(
in, mOnBatteryScreenOffTimeBase);
+
+ u.mCpuActiveTimeMs.readSummaryFromParcelLocked(in);
+ u.mCpuClusterTimesMs.readSummaryFromParcelLocked(in);
+
int length = in.readInt();
if (length == Uid.NUM_PROCESS_STATE) {
u.mProcStateTimeMs = new LongSamplingCounterArray[length];
@@ -13482,6 +13667,9 @@
for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
mWifiSignalStrengthsTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
}
+ for (int i=0; i< GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
+ mGpsSignalQualityTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ }
mWifiActivity.writeSummaryToParcel(out);
mBluetoothActivity.writeSummaryToParcel(out);
mModemActivity.writeSummaryToParcel(out);
@@ -13725,6 +13913,9 @@
LongSamplingCounterArray.writeSummaryToParcelLocked(out, u.mCpuFreqTimeMs);
LongSamplingCounterArray.writeSummaryToParcelLocked(out, u.mScreenOffCpuFreqTimeMs);
+ u.mCpuActiveTimeMs.writeSummaryFromParcelLocked(out);
+ u.mCpuClusterTimesMs.writeSummaryToParcelLocked(out);
+
if (u.mProcStateTimeMs != null) {
out.writeInt(u.mProcStateTimeMs.length);
for (LongSamplingCounterArray counters : u.mProcStateTimeMs) {
@@ -13954,7 +14145,10 @@
mWifiSignalStrengthsTimer[i] = new StopwatchTimer(mClocks, null, -800-i,
null, mOnBatteryTimeBase, in);
}
-
+ for (int i=0; i<GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
+ mGpsSignalQualityTimer[i] = new StopwatchTimer(mClocks, null, -1000-i,
+ null, mOnBatteryTimeBase, in);
+ }
mWifiActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
NUM_WIFI_TX_LEVELS, in);
mBluetoothActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
@@ -14154,6 +14348,9 @@
for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
mWifiSignalStrengthsTimer[i].writeToParcel(out, uSecRealtime);
}
+ for (int i=0; i< GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
+ mGpsSignalQualityTimer[i].writeToParcel(out, uSecRealtime);
+ }
mWifiActivity.writeToParcel(out, 0);
mBluetoothActivity.writeToParcel(out, 0);
mModemActivity.writeToParcel(out, 0);
@@ -14348,6 +14545,10 @@
pr.println("*** Wifi signal strength #" + i + ":");
mWifiSignalStrengthsTimer[i].logState(pr, " ");
}
+ for (int i=0; i<GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
+ pr.println("*** GPS signal quality #" + i + ":");
+ mGpsSignalQualityTimer[i].logState(pr, " ");
+ }
pr.println("*** Flashlight timer:");
mFlashlightOnTimer.logState(pr, " ");
pr.println("*** Camera timer:");
diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java
index bb743c1..a34e7f5 100644
--- a/core/java/com/android/internal/os/CpuPowerCalculator.java
+++ b/core/java/com/android/internal/os/CpuPowerCalculator.java
@@ -31,8 +31,7 @@
@Override
public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
- long rawUptimeUs, int statsType) {
-
+ long rawUptimeUs, int statsType) {
app.cpuTimeMs = (u.getUserCpuTimeUs(statsType) + u.getSystemCpuTimeUs(statsType)) / 1000;
final int numClusters = mProfile.getNumCpuClusters();
@@ -42,7 +41,7 @@
for (int speed = 0; speed < speedsForCluster; speed++) {
final long timeUs = u.getTimeAtCpuSpeed(cluster, speed, statsType);
final double cpuSpeedStepPower = timeUs *
- mProfile.getAveragePowerForCpu(cluster, speed);
+ mProfile.getAveragePowerForCpuCore(cluster, speed);
if (DEBUG) {
Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster + " step #"
+ speed + " timeUs=" + timeUs + " power="
@@ -51,6 +50,25 @@
cpuPowerMaUs += cpuSpeedStepPower;
}
}
+ cpuPowerMaUs += u.getCpuActiveTime() * mProfile.getAveragePower(
+ PowerProfile.POWER_CPU_ACTIVE);
+ long[] cpuClusterTimes = u.getCpuClusterTimes();
+ if (cpuClusterTimes != null) {
+ if (cpuClusterTimes.length == numClusters) {
+ for (int i = 0; i < numClusters; i++) {
+ double power = cpuClusterTimes[i] * mProfile.getAveragePowerForCpuCluster(i);
+ cpuPowerMaUs += power;
+ if (DEBUG) {
+ Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + i + " clusterTimeUs="
+ + cpuClusterTimes[i] + " power="
+ + BatteryStatsHelper.makemAh(power / MICROSEC_IN_HR));
+ }
+ }
+ } else {
+ Log.w(TAG, "UID " + u.getUid() + " CPU cluster # mismatch: Power Profile # "
+ + numClusters + " actual # " + cpuClusterTimes.length);
+ }
+ }
app.cpuPowerMah = cpuPowerMaUs / MICROSEC_IN_HR;
if (DEBUG && (app.cpuTimeMs != 0 || app.cpuPowerMah != 0)) {
diff --git a/core/java/com/android/internal/os/KernelUidCpuActiveTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuActiveTimeReader.java
new file mode 100644
index 0000000..cb96c5c
--- /dev/null
+++ b/core/java/com/android/internal/os/KernelUidCpuActiveTimeReader.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import android.annotation.Nullable;
+import android.os.StrictMode;
+import android.os.SystemClock;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.TimeUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+
+/**
+ * Reads /proc/uid_concurrent_active_time which has the format:
+ * active: X (X is # cores)
+ * [uid0]: [time-0] [time-1] [time-2] ... (# entries = # cores)
+ * [uid1]: [time-0] [time-1] [time-2] ... ...
+ * ...
+ * Time-N means the CPU time a UID spent running concurrently with N other processes.
+ * The file contains a monotonically increasing count of time for a single boot. This class
+ * maintains the previous results of a call to {@link #readDelta} in order to provide a
+ * proper delta.
+ */
+public class KernelUidCpuActiveTimeReader {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "KernelUidCpuActiveTimeReader";
+ private static final String UID_TIMES_PROC_FILE = "/proc/uid_concurrent_active_time";
+
+ private int mCoreCount;
+ private long mLastTimeReadMs;
+ private long mNowTimeMs;
+ private SparseArray<long[]> mLastUidCpuActiveTimeMs = new SparseArray<>();
+
+ public interface Callback {
+ void onUidCpuActiveTime(int uid, long cpuActiveTimeMs);
+ }
+
+ public void readDelta(@Nullable Callback cb) {
+ final int oldMask = StrictMode.allowThreadDiskReadsMask();
+ try (BufferedReader reader = new BufferedReader(new FileReader(UID_TIMES_PROC_FILE))) {
+ mNowTimeMs = SystemClock.elapsedRealtime();
+ readDeltaInternal(reader, cb);
+ mLastTimeReadMs = mNowTimeMs;
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e);
+ } finally {
+ StrictMode.setThreadPolicyMask(oldMask);
+ }
+ }
+
+ public void removeUid(int uid) {
+ mLastUidCpuActiveTimeMs.delete(uid);
+ }
+
+ public void removeUidsInRange(int startUid, int endUid) {
+ if (endUid < startUid) {
+ Slog.w(TAG, "End UID " + endUid + " is smaller than start UID " + startUid);
+ return;
+ }
+ mLastUidCpuActiveTimeMs.put(startUid, null);
+ mLastUidCpuActiveTimeMs.put(endUid, null);
+ final int firstIndex = mLastUidCpuActiveTimeMs.indexOfKey(startUid);
+ final int lastIndex = mLastUidCpuActiveTimeMs.indexOfKey(endUid);
+ mLastUidCpuActiveTimeMs.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
+ }
+
+ @VisibleForTesting
+ public void readDeltaInternal(BufferedReader reader, @Nullable Callback cb) throws IOException {
+ String line = reader.readLine();
+ if (line == null || !line.startsWith("active:")) {
+ Slog.e(TAG, String.format("Malformed proc file: %s ", UID_TIMES_PROC_FILE));
+ return;
+ }
+ if (mCoreCount == 0) {
+ mCoreCount = Integer.parseInt(line.substring(line.indexOf(' ')+1));
+ }
+ while ((line = reader.readLine()) != null) {
+ final int index = line.indexOf(' ');
+ final int uid = Integer.parseInt(line.substring(0, index - 1), 10);
+ readTimesForUid(uid, line.substring(index + 1), cb);
+ }
+ }
+
+ private void readTimesForUid(int uid, String line, @Nullable Callback cb) {
+ long[] lastActiveTime = mLastUidCpuActiveTimeMs.get(uid);
+ if (lastActiveTime == null) {
+ lastActiveTime = new long[mCoreCount];
+ mLastUidCpuActiveTimeMs.put(uid, lastActiveTime);
+ }
+ final String[] timesStr = line.split(" ");
+ if (timesStr.length != mCoreCount) {
+ Slog.e(TAG, String.format("# readings don't match # cores, readings: %d, CPU cores: %d",
+ timesStr.length, mCoreCount));
+ return;
+ }
+ long sumDeltas = 0;
+ final long[] curActiveTime = new long[mCoreCount];
+ boolean notify = false;
+ for (int i = 0; i < mCoreCount; i++) {
+ // Times read will be in units of 10ms
+ curActiveTime[i] = Long.parseLong(timesStr[i], 10) * 10;
+ long delta = curActiveTime[i] - lastActiveTime[i];
+ if (delta < 0 || curActiveTime[i] < 0) {
+ if (DEBUG) {
+ final StringBuilder sb = new StringBuilder();
+ sb.append(String.format("Malformed cpu active time for UID=%d\n", uid));
+ sb.append(String.format("data=(%d,%d)\n", lastActiveTime[i], curActiveTime[i]));
+ sb.append("times=(");
+ TimeUtils.formatDuration(mLastTimeReadMs, sb);
+ sb.append(",");
+ TimeUtils.formatDuration(mNowTimeMs, sb);
+ sb.append(")");
+ Slog.e(TAG, sb.toString());
+ }
+ return;
+ }
+ notify |= delta > 0;
+ sumDeltas += delta / (i + 1);
+ }
+ if (notify) {
+ System.arraycopy(curActiveTime, 0, lastActiveTime, 0, mCoreCount);
+ if (cb != null) {
+ cb.onUidCpuActiveTime(uid, sumDeltas);
+ }
+ }
+ }
+}
diff --git a/core/java/com/android/internal/os/KernelUidCpuClusterTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuClusterTimeReader.java
new file mode 100644
index 0000000..85153bc
--- /dev/null
+++ b/core/java/com/android/internal/os/KernelUidCpuClusterTimeReader.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import android.annotation.Nullable;
+import android.os.StrictMode;
+import android.os.SystemClock;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.TimeUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Reads /proc/uid_concurrent_policy_time which has the format:
+ * policy0: X policy4: Y (there are X cores on policy0, Y cores on policy4)
+ * [uid0]: [time-0-0] [time-0-1] ... [time-1-0] [time-1-1] ...
+ * [uid1]: [time-0-0] [time-0-1] ... [time-1-0] [time-1-1] ...
+ * ...
+ * Time-X-Y means the time a UID spent on clusterX running concurrently with Y other processes.
+ * The file contains a monotonically increasing count of time for a single boot. This class
+ * maintains the previous results of a call to {@link #readDelta} in order to provide a proper
+ * delta.
+ */
+public class KernelUidCpuClusterTimeReader {
+
+ private static final boolean DEBUG = false;
+ private static final String TAG = "KernelUidCpuClusterTimeReader";
+ private static final String UID_TIMES_PROC_FILE = "/proc/uid_concurrent_policy_time";
+
+ // mCoreOnCluster[i] is the # of cores on cluster i
+ private int[] mCoreOnCluster;
+ private int mCores;
+ private long mLastTimeReadMs;
+ private long mNowTimeMs;
+ private SparseArray<long[]> mLastUidPolicyTimeMs = new SparseArray<>();
+
+ public interface Callback {
+ /**
+ * @param uid
+ * @param cpuActiveTimeMs the first dimension is cluster, the second dimension is the # of
+ * processes running concurrently with this uid.
+ */
+ void onUidCpuPolicyTime(int uid, long[] cpuActiveTimeMs);
+ }
+
+ public void readDelta(@Nullable Callback cb) {
+ final int oldMask = StrictMode.allowThreadDiskReadsMask();
+ try (BufferedReader reader = new BufferedReader(new FileReader(UID_TIMES_PROC_FILE))) {
+ mNowTimeMs = SystemClock.elapsedRealtime();
+ readDeltaInternal(reader, cb);
+ mLastTimeReadMs = mNowTimeMs;
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e);
+ } finally {
+ StrictMode.setThreadPolicyMask(oldMask);
+ }
+ }
+
+ public void removeUid(int uid) {
+ mLastUidPolicyTimeMs.delete(uid);
+ }
+
+ public void removeUidsInRange(int startUid, int endUid) {
+ if (endUid < startUid) {
+ Slog.w(TAG, "End UID " + endUid + " is smaller than start UID " + startUid);
+ return;
+ }
+ mLastUidPolicyTimeMs.put(startUid, null);
+ mLastUidPolicyTimeMs.put(endUid, null);
+ final int firstIndex = mLastUidPolicyTimeMs.indexOfKey(startUid);
+ final int lastIndex = mLastUidPolicyTimeMs.indexOfKey(endUid);
+ mLastUidPolicyTimeMs.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
+ }
+
+ @VisibleForTesting
+ public void readDeltaInternal(BufferedReader reader, @Nullable Callback cb) throws IOException {
+ String line = reader.readLine();
+ if (line == null || !line.startsWith("policy")) {
+ Slog.e(TAG, String.format("Malformed proc file: %s ", UID_TIMES_PROC_FILE));
+ return;
+ }
+ if (mCoreOnCluster == null) {
+ List<Integer> list = new ArrayList<>();
+ String[] policies = line.split(" ");
+
+ if (policies.length == 0 || policies.length % 2 != 0) {
+ Slog.e(TAG, String.format("Malformed proc file: %s ", UID_TIMES_PROC_FILE));
+ return;
+ }
+
+ for (int i = 0; i < policies.length; i+=2) {
+ list.add(Integer.parseInt(policies[i+1]));
+ }
+
+ mCoreOnCluster = new int[list.size()];
+ for(int i=0;i<list.size();i++){
+ mCoreOnCluster[i] = list.get(i);
+ mCores += mCoreOnCluster[i];
+ }
+ }
+ while ((line = reader.readLine()) != null) {
+ final int index = line.indexOf(' ');
+ final int uid = Integer.parseInt(line.substring(0, index - 1), 10);
+ readTimesForUid(uid, line.substring(index + 1), cb);
+ }
+ }
+
+ private void readTimesForUid(int uid, String line, @Nullable Callback cb) {
+ long[] lastPolicyTime = mLastUidPolicyTimeMs.get(uid);
+ if (lastPolicyTime == null) {
+ lastPolicyTime = new long[mCores];
+ mLastUidPolicyTimeMs.put(uid, lastPolicyTime);
+ }
+ final String[] timeStr = line.split(" ");
+ if (timeStr.length != mCores) {
+ Slog.e(TAG, String.format("# readings don't match # cores, readings: %d, # CPU cores: %d",
+ timeStr.length, mCores));
+ return;
+ }
+ final long[] deltaPolicyTime = new long[mCores];
+ final long[] currPolicyTime = new long[mCores];
+ boolean notify = false;
+ for (int i = 0; i < mCores; i++) {
+ // Times read will be in units of 10ms
+ currPolicyTime[i] = Long.parseLong(timeStr[i], 10) * 10;
+ deltaPolicyTime[i] = currPolicyTime[i] - lastPolicyTime[i];
+ if (deltaPolicyTime[i] < 0 || currPolicyTime[i] < 0) {
+ if (DEBUG) {
+ final StringBuilder sb = new StringBuilder();
+ sb.append(String.format("Malformed cpu policy time for UID=%d\n", uid));
+ sb.append(String.format("data=(%d,%d)\n", lastPolicyTime[i], currPolicyTime[i]));
+ sb.append("times=(");
+ TimeUtils.formatDuration(mLastTimeReadMs, sb);
+ sb.append(",");
+ TimeUtils.formatDuration(mNowTimeMs, sb);
+ sb.append(")");
+ Slog.e(TAG, sb.toString());
+ }
+ return;
+ }
+ notify |= deltaPolicyTime[i] > 0;
+ }
+ if (notify) {
+ System.arraycopy(currPolicyTime, 0, lastPolicyTime, 0, mCores);
+ if (cb != null) {
+ final long[] times = new long[mCoreOnCluster.length];
+ int core = 0;
+ for (int i = 0; i < mCoreOnCluster.length; i++) {
+ for (int j = 0; j < mCoreOnCluster[i]; j++) {
+ times[i] += deltaPolicyTime[core++] / (j+1);
+ }
+ }
+ cb.onUidCpuPolicyTime(uid, times);
+ }
+ }
+ }
+}
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index 872b465..fcbbcd0 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -21,6 +21,7 @@
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
@@ -43,23 +44,25 @@
public static final String POWER_NONE = "none";
/**
- * Power consumption when CPU is in power collapse mode.
+ * POWER_CPU_SUSPEND: Power consumption when CPU is in power collapse mode.
+ * POWER_CPU_IDLE: Power consumption when CPU is awake (when a wake lock is held). This should
+ * be zero on devices that can go into full CPU power collapse even when a wake
+ * lock is held. Otherwise, this is the power consumption in addition to
+ * POWER_CPU_SUSPEND due to a wake lock being held but with no CPU activity.
+ * POWER_CPU_ACTIVE: Power consumption when CPU is running, excluding power consumed by clusters
+ * and cores.
+ *
+ * CPU Power Equation (assume two clusters):
+ * Total power = POWER_CPU_SUSPEND (always added)
+ * + POWER_CPU_IDLE (skip this and below if in power collapse mode)
+ * + POWER_CPU_ACTIVE (skip this and below if CPU is not running, but a wakelock
+ * is held)
+ * + cluster_power.cluster0 + cluster_power.cluster1 (skip cluster not running)
+ * + core_power.cluster0 * num running cores in cluster 0
+ * + core_power.cluster1 * num running cores in cluster 1
*/
+ public static final String POWER_CPU_SUSPEND = "cpu.suspend";
public static final String POWER_CPU_IDLE = "cpu.idle";
-
- /**
- * Power consumption when CPU is awake (when a wake lock is held). This
- * should be 0 on devices that can go into full CPU power collapse even
- * when a wake lock is held. Otherwise, this is the power consumption in
- * addition to POWER_CPU_IDLE due to a wake lock being held but with no
- * CPU activity.
- */
- public static final String POWER_CPU_AWAKE = "cpu.awake";
-
- /**
- * Power consumption when CPU is in power collapse mode.
- */
- @Deprecated
public static final String POWER_CPU_ACTIVE = "cpu.active";
/**
@@ -182,9 +185,6 @@
*/
public static final String POWER_CAMERA = "camera.avg";
- @Deprecated
- public static final String POWER_CPU_SPEEDS = "cpu.speeds";
-
/**
* Power consumed by wif batched scaning. Broken down into bins by
* Channels Scanned per Hour. May do 1-720 scans per hour of 1-100 channels
@@ -197,7 +197,15 @@
*/
public static final String POWER_BATTERY_CAPACITY = "battery.capacity";
- static final HashMap<String, Object> sPowerMap = new HashMap<>();
+ /**
+ * A map from Power Use Item to its power consumption.
+ */
+ static final HashMap<String, Double> sPowerItemMap = new HashMap<>();
+ /**
+ * A map from Power Use Item to an array of its power consumption
+ * (for items with variable power e.g. CPU).
+ */
+ static final HashMap<String, Double[]> sPowerArrayMap = new HashMap<>();
private static final String TAG_DEVICE = "device";
private static final String TAG_ITEM = "item";
@@ -207,23 +215,32 @@
private static final Object sLock = new Object();
+ @VisibleForTesting
public PowerProfile(Context context) {
- // Read the XML file for the given profile (normally only one per
- // device)
+ this(context, false);
+ }
+
+ /**
+ * For PowerProfileTest
+ */
+ @VisibleForTesting
+ public PowerProfile(Context context, boolean forTest) {
+ // Read the XML file for the given profile (normally only one per device)
synchronized (sLock) {
- if (sPowerMap.size() == 0) {
- readPowerValuesFromXml(context);
+ if (sPowerItemMap.size() == 0 && sPowerArrayMap.size() == 0) {
+ readPowerValuesFromXml(context, forTest);
}
initCpuClusters();
}
}
- private void readPowerValuesFromXml(Context context) {
- int id = com.android.internal.R.xml.power_profile;
+ private void readPowerValuesFromXml(Context context, boolean forTest) {
+ final int id = forTest ? com.android.internal.R.xml.power_profile_test :
+ com.android.internal.R.xml.power_profile;
final Resources resources = context.getResources();
XmlResourceParser parser = resources.getXml(id);
boolean parsingArray = false;
- ArrayList<Double> array = new ArrayList<Double>();
+ ArrayList<Double> array = new ArrayList<>();
String arrayName = null;
try {
@@ -237,7 +254,7 @@
if (parsingArray && !element.equals(TAG_ARRAYITEM)) {
// Finish array
- sPowerMap.put(arrayName, array.toArray(new Double[array.size()]));
+ sPowerArrayMap.put(arrayName, array.toArray(new Double[array.size()]));
parsingArray = false;
}
if (element.equals(TAG_ARRAY)) {
@@ -255,7 +272,7 @@
} catch (NumberFormatException nfe) {
}
if (element.equals(TAG_ITEM)) {
- sPowerMap.put(name, value);
+ sPowerItemMap.put(name, value);
} else if (parsingArray) {
array.add(value);
}
@@ -263,7 +280,7 @@
}
}
if (parsingArray) {
- sPowerMap.put(arrayName, array.toArray(new Double[array.size()]));
+ sPowerArrayMap.put(arrayName, array.toArray(new Double[array.size()]));
}
} catch (XmlPullParserException e) {
throw new RuntimeException(e);
@@ -300,52 +317,56 @@
String key = configResIdKeys[i];
// if we already have some of these parameters in power_profile.xml, ignore the
// value in config.xml
- if ((sPowerMap.containsKey(key) && (Double) sPowerMap.get(key) > 0)) {
+ if ((sPowerItemMap.containsKey(key) && sPowerItemMap.get(key) > 0)) {
continue;
}
int value = resources.getInteger(configResIds[i]);
if (value > 0) {
- sPowerMap.put(key, (double) value);
+ sPowerItemMap.put(key, (double) value);
}
}
}
private CpuClusterKey[] mCpuClusters;
- private static final String POWER_CPU_CLUSTER_CORE_COUNT = "cpu.clusters.cores";
- private static final String POWER_CPU_CLUSTER_SPEED_PREFIX = "cpu.speeds.cluster";
- private static final String POWER_CPU_CLUSTER_ACTIVE_PREFIX = "cpu.active.cluster";
+ private static final String CPU_PER_CLUSTER_CORE_COUNT = "cpu.clusters.cores";
+ private static final String CPU_CLUSTER_POWER_COUNT = "cpu.cluster_power.cluster";
+ private static final String CPU_CORE_SPEED_PREFIX = "cpu.core_speeds.cluster";
+ private static final String CPU_CORE_POWER_PREFIX = "cpu.core_power.cluster";
- @SuppressWarnings("deprecation")
private void initCpuClusters() {
- // Figure out how many CPU clusters we're dealing with
- final Object obj = sPowerMap.get(POWER_CPU_CLUSTER_CORE_COUNT);
- if (obj == null || !(obj instanceof Double[])) {
+ if (sPowerArrayMap.containsKey(CPU_PER_CLUSTER_CORE_COUNT)) {
+ final Double[] data = sPowerArrayMap.get(CPU_PER_CLUSTER_CORE_COUNT);
+ mCpuClusters = new CpuClusterKey[data.length];
+ for (int cluster = 0; cluster < data.length; cluster++) {
+ int numCpusInCluster = (int) Math.round(data[cluster]);
+ mCpuClusters[cluster] = new CpuClusterKey(
+ CPU_CORE_SPEED_PREFIX + cluster, CPU_CLUSTER_POWER_COUNT + cluster,
+ CPU_CORE_POWER_PREFIX + cluster, numCpusInCluster);
+ }
+ } else {
// Default to single.
mCpuClusters = new CpuClusterKey[1];
- mCpuClusters[0] = new CpuClusterKey(POWER_CPU_SPEEDS, POWER_CPU_ACTIVE, 1);
-
- } else {
- final Double[] array = (Double[]) obj;
- mCpuClusters = new CpuClusterKey[array.length];
- for (int cluster = 0; cluster < array.length; cluster++) {
- int numCpusInCluster = (int) Math.round(array[cluster]);
- mCpuClusters[cluster] = new CpuClusterKey(
- POWER_CPU_CLUSTER_SPEED_PREFIX + cluster,
- POWER_CPU_CLUSTER_ACTIVE_PREFIX + cluster,
- numCpusInCluster);
+ int numCpus = 1;
+ if (sPowerItemMap.containsKey(CPU_PER_CLUSTER_CORE_COUNT)) {
+ numCpus = (int) Math.round(sPowerItemMap.get(CPU_PER_CLUSTER_CORE_COUNT));
}
+ mCpuClusters[0] = new CpuClusterKey(CPU_CORE_SPEED_PREFIX + 0,
+ CPU_CLUSTER_POWER_COUNT + 0, CPU_CORE_POWER_PREFIX + 0, numCpus);
}
}
public static class CpuClusterKey {
- private final String timeKey;
- private final String powerKey;
+ private final String freqKey;
+ private final String clusterPowerKey;
+ private final String corePowerKey;
private final int numCpus;
- private CpuClusterKey(String timeKey, String powerKey, int numCpus) {
- this.timeKey = timeKey;
- this.powerKey = powerKey;
+ private CpuClusterKey(String freqKey, String clusterPowerKey,
+ String corePowerKey, int numCpus) {
+ this.freqKey = freqKey;
+ this.clusterPowerKey = clusterPowerKey;
+ this.corePowerKey = corePowerKey;
this.numCpus = numCpus;
}
}
@@ -354,21 +375,30 @@
return mCpuClusters.length;
}
- public int getNumCoresInCpuCluster(int index) {
- return mCpuClusters[index].numCpus;
+ public int getNumCoresInCpuCluster(int cluster) {
+ return mCpuClusters[cluster].numCpus;
}
- public int getNumSpeedStepsInCpuCluster(int index) {
- Object value = sPowerMap.get(mCpuClusters[index].timeKey);
- if (value != null && value instanceof Double[]) {
- return ((Double[])value).length;
+ public int getNumSpeedStepsInCpuCluster(int cluster) {
+ if (cluster < 0 || cluster >= mCpuClusters.length) {
+ return 0; // index out of bound
+ }
+ if (sPowerArrayMap.containsKey(mCpuClusters[cluster].freqKey)) {
+ return sPowerArrayMap.get(mCpuClusters[cluster].freqKey).length;
}
return 1; // Only one speed
}
- public double getAveragePowerForCpu(int cluster, int step) {
+ public double getAveragePowerForCpuCluster(int cluster) {
if (cluster >= 0 && cluster < mCpuClusters.length) {
- return getAveragePower(mCpuClusters[cluster].powerKey, step);
+ return getAveragePower(mCpuClusters[cluster].clusterPowerKey);
+ }
+ return 0;
+ }
+
+ public double getAveragePowerForCpuCore(int cluster, int step) {
+ if (cluster >= 0 && cluster < mCpuClusters.length) {
+ return getAveragePower(mCpuClusters[cluster].corePowerKey, step);
}
return 0;
}
@@ -379,14 +409,10 @@
* @return the number of memory bandwidth buckets.
*/
public int getNumElements(String key) {
- if (sPowerMap.containsKey(key)) {
- Object data = sPowerMap.get(key);
- if (data instanceof Double[]) {
- final Double[] values = (Double[]) data;
- return values.length;
- } else {
- return 1;
- }
+ if (sPowerItemMap.containsKey(key)) {
+ return 1;
+ } else if (sPowerArrayMap.containsKey(key)) {
+ return sPowerArrayMap.get(key).length;
}
return 0;
}
@@ -399,13 +425,10 @@
* @return the average current in milliAmps.
*/
public double getAveragePowerOrDefault(String type, double defaultValue) {
- if (sPowerMap.containsKey(type)) {
- Object data = sPowerMap.get(type);
- if (data instanceof Double[]) {
- return ((Double[])data)[0];
- } else {
- return (Double) sPowerMap.get(type);
- }
+ if (sPowerItemMap.containsKey(type)) {
+ return sPowerItemMap.get(type);
+ } else if (sPowerArrayMap.containsKey(type)) {
+ return sPowerArrayMap.get(type)[0];
} else {
return defaultValue;
}
@@ -429,19 +452,16 @@
* @return the average current in milliAmps.
*/
public double getAveragePower(String type, int level) {
- if (sPowerMap.containsKey(type)) {
- Object data = sPowerMap.get(type);
- if (data instanceof Double[]) {
- final Double[] values = (Double[]) data;
- if (values.length > level && level >= 0) {
- return values[level];
- } else if (level < 0 || values.length == 0) {
- return 0;
- } else {
- return values[values.length - 1];
- }
+ if (sPowerItemMap.containsKey(type)) {
+ return sPowerItemMap.get(type);
+ } else if (sPowerArrayMap.containsKey(type)) {
+ final Double[] values = sPowerArrayMap.get(type);
+ if (values.length > level && level >= 0) {
+ return values[level];
+ } else if (level < 0 || values.length == 0) {
+ return 0;
} else {
- return (Double) data;
+ return values[values.length - 1];
}
} else {
return 0;
diff --git a/core/java/com/android/internal/os/WakelockPowerCalculator.java b/core/java/com/android/internal/os/WakelockPowerCalculator.java
index c7897b2..486b584 100644
--- a/core/java/com/android/internal/os/WakelockPowerCalculator.java
+++ b/core/java/com/android/internal/os/WakelockPowerCalculator.java
@@ -26,7 +26,7 @@
private long mTotalAppWakelockTimeMs = 0;
public WakelockPowerCalculator(PowerProfile profile) {
- mPowerWakelock = profile.getAveragePower(PowerProfile.POWER_CPU_AWAKE);
+ mPowerWakelock = profile.getAveragePower(PowerProfile.POWER_CPU_IDLE);
}
@Override
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 85655a5..e097362a 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -115,7 +115,7 @@
/**
* Notifies the status bar that a new rotation suggestion is available.
*/
- void onProposedRotationChanged(int rotation);
+ void onProposedRotationChanged(int rotation, boolean isValid);
/**
* Set whether the top app currently hides the statusbar.
diff --git a/core/java/com/android/internal/util/ConcurrentUtils.java b/core/java/com/android/internal/util/ConcurrentUtils.java
index e35f9f4..e08eb58 100644
--- a/core/java/com/android/internal/util/ConcurrentUtils.java
+++ b/core/java/com/android/internal/util/ConcurrentUtils.java
@@ -18,11 +18,13 @@
import android.os.Process;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -86,4 +88,27 @@
}
}
+ /**
+ * Waits for {@link CountDownLatch#countDown()} to be called on the {@param countDownLatch}.
+ * <p>If {@link CountDownLatch#countDown()} doesn't occur within {@param timeoutMs}, this
+ * method will throw {@code IllegalStateException}
+ * <p>If {@code InterruptedException} occurs, this method will interrupt the current thread
+ * and throw {@code IllegalStateException}
+ *
+ * @param countDownLatch the CountDownLatch which {@link CountDownLatch#countDown()} is
+ * being waited on.
+ * @param timeoutMs the maximum time waited for {@link CountDownLatch#countDown()}
+ * @param description a short description of the operation
+ */
+ public static void waitForCountDownNoInterrupt(CountDownLatch countDownLatch, long timeoutMs,
+ String description) {
+ try {
+ if (!countDownLatch.await(timeoutMs, TimeUnit.MILLISECONDS)) {
+ throw new IllegalStateException(description + " timed out.");
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new IllegalStateException(description + " interrupted.");
+ }
+ }
}
diff --git a/core/java/com/android/internal/util/DumpUtils.java b/core/java/com/android/internal/util/DumpUtils.java
index 66b777e..2b51033 100644
--- a/core/java/com/android/internal/util/DumpUtils.java
+++ b/core/java/com/android/internal/util/DumpUtils.java
@@ -102,6 +102,7 @@
case android.os.Process.ROOT_UID:
case android.os.Process.SYSTEM_UID:
case android.os.Process.SHELL_UID:
+ case android.os.Process.INCIDENTD_UID:
return true;
}
diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java
index f7ea787..7fd94c6 100644
--- a/core/java/com/android/internal/util/ScreenshotHelper.java
+++ b/core/java/com/android/internal/util/ScreenshotHelper.java
@@ -1,5 +1,6 @@
package com.android.internal.util;
+import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -32,8 +33,18 @@
mContext = context;
}
+ /**
+ * Request a screenshot be taken.
+ *
+ * @param screenshotType The type of screenshot, for example either
+ * {@link android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN}
+ * or {@link android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION}
+ * @param hasStatus {@code true} if the status bar is currently showing. {@code false} if not.
+ * @param hasNav {@code true} if the navigation bar is currently showing. {@code false} if not.
+ * @param handler A handler used in case the screenshot times out
+ */
public void takeScreenshot(final int screenshotType, final boolean hasStatus,
- final boolean hasNav, Handler handler) {
+ final boolean hasNav, @NonNull Handler handler) {
synchronized (mScreenshotLock) {
if (mScreenshotConnection != null) {
return;
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 7f4b384..dcb81fa 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -66,10 +66,6 @@
static SkPixelRef* refSkPixelRef(JNIEnv*, jobject bitmap);
static SkRegion* getNativeRegion(JNIEnv*, jobject region);
- // Given the 'native' long held by the Rasterizer.java object, return a
- // ref to its SkRasterizer* (or NULL).
- static sk_sp<SkRasterizer> refNativeRasterizer(jlong rasterizerHandle);
-
/*
* LegacyBitmapConfig is the old enum in Skia that matched the enum int values
* in Bitmap.Config. Skia no longer supports this config, but has replaced it
diff --git a/core/jni/android/opengl/util.cpp b/core/jni/android/opengl/util.cpp
index f3aeb32..888db32 100644
--- a/core/jni/android/opengl/util.cpp
+++ b/core/jni/android/opengl/util.cpp
@@ -622,7 +622,7 @@
// ---------------------------------------------------------------------------
-static int checkFormat(SkColorType colorType, int format, int type)
+static int checkInternalFormat(SkColorType colorType, int format, int type)
{
switch(colorType) {
case kN32_SkColorType:
@@ -651,6 +651,20 @@
return -1;
}
+// The internal format is no longer the same as pixel format, per Table 2 in
+// https://www.khronos.org/registry/OpenGL-Refpages/es3.1/html/glTexImage2D.xhtml
+static int getPixelFormatFromInternalFormat(uint32_t internalFormat) {
+ switch (internalFormat) {
+ // For sized internal format.
+ case GL_RGBA16F:
+ return GL_RGBA;
+ // Base internal formats and pixel formats are still the same, see Table 1 in
+ // https://www.khronos.org/registry/OpenGL-Refpages/es3.1/html/glTexImage2D.xhtml
+ default:
+ return internalFormat;
+ }
+}
+
static int getInternalFormat(SkColorType colorType)
{
switch(colorType) {
@@ -716,7 +730,7 @@
if (type < 0) {
type = getType(colorType);
}
- int err = checkFormat(colorType, internalformat, type);
+ int err = checkInternalFormat(colorType, internalformat, type);
if (err)
return err;
const int w = bitmap.width();
@@ -725,7 +739,8 @@
if (internalformat == GL_PALETTE8_RGBA8_OES) {
err = -1;
} else {
- glTexImage2D(target, level, internalformat, w, h, border, internalformat, type, p);
+ glTexImage2D(target, level, internalformat, w, h, border,
+ getPixelFormatFromInternalFormat(internalformat), type, p);
}
return err;
}
@@ -737,12 +752,13 @@
SkBitmap bitmap;
GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
SkColorType colorType = bitmap.colorType();
+ int internalFormat = getInternalFormat(colorType);
if (format < 0) {
- format = getInternalFormat(colorType);
+ format = getPixelFormatFromInternalFormat(internalFormat);
if (format == GL_PALETTE8_RGBA8_OES)
return -1; // glCompressedTexSubImage2D() not supported
}
- int err = checkFormat(colorType, format, type);
+ int err = checkInternalFormat(colorType, internalFormat, type);
if (err)
return err;
const int w = bitmap.width();
diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h
index 092aaf6..c79f5bd 100644
--- a/core/jni/android_media_AudioFormat.h
+++ b/core/jni/android_media_AudioFormat.h
@@ -33,6 +33,9 @@
#define ENCODING_AAC_HE_V2 12
#define ENCODING_IEC61937 13
#define ENCODING_DOLBY_TRUEHD 14
+#define ENCODING_AAC_ELD 15
+#define ENCODING_AAC_XHE 16
+#define ENCODING_AC4 17
#define ENCODING_INVALID 0
#define ENCODING_DEFAULT 1
@@ -71,6 +74,12 @@
return AUDIO_FORMAT_DOLBY_TRUEHD;
case ENCODING_IEC61937:
return AUDIO_FORMAT_IEC61937;
+ case ENCODING_AAC_ELD:
+ return AUDIO_FORMAT_AAC_ELD;
+ case ENCODING_AAC_XHE:
+ return AUDIO_FORMAT_AAC; // FIXME temporary value, needs addition of xHE-AAC
+ case ENCODING_AC4:
+ return AUDIO_FORMAT_AC4;
case ENCODING_DEFAULT:
return AUDIO_FORMAT_DEFAULT;
default:
@@ -114,6 +123,13 @@
return ENCODING_IEC61937;
case AUDIO_FORMAT_DOLBY_TRUEHD:
return ENCODING_DOLBY_TRUEHD;
+ case AUDIO_FORMAT_AAC_ELD:
+ return ENCODING_AAC_ELD;
+ // FIXME needs addition of AUDIO_FORMAT_AAC_XHE
+ //case AUDIO_FORMAT_AAC_XHE:
+ // return ENCODING_AAC_XHE;
+ case AUDIO_FORMAT_AC4:
+ return ENCODING_AC4;
case AUDIO_FORMAT_DEFAULT:
return ENCODING_DEFAULT;
default:
@@ -121,6 +137,25 @@
}
}
+// This function converts Java channel masks to a native channel mask.
+// validity should be checked with audio_is_output_channel().
+static inline audio_channel_mask_t nativeChannelMaskFromJavaChannelMasks(
+ jint channelPositionMask, jint channelIndexMask)
+{
+ // 0 is the java android.media.AudioFormat.CHANNEL_INVALID value
+ if (channelIndexMask != 0) { // channel index mask takes priority
+ // To convert to a native channel mask, the Java channel index mask
+ // requires adding the index representation.
+ return audio_channel_mask_from_representation_and_bits(
+ AUDIO_CHANNEL_REPRESENTATION_INDEX,
+ channelIndexMask);
+ }
+ // To convert to a native channel mask, the Java channel position mask
+ // requires a shift by 2 to skip the two deprecated channel
+ // configurations "default" and "mono".
+ return (audio_channel_mask_t)((uint32_t)channelPositionMask >> 2);
+}
+
static inline audio_channel_mask_t outChannelMaskToNative(int channelMask)
{
switch (channelMask) {
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 7ec68ed..2be9471 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -1770,6 +1770,24 @@
(audio_devices_t)device);
}
+static jboolean
+android_media_AudioSystem_isOffloadSupported(JNIEnv *env, jobject thiz,
+ jint encoding, jint sampleRate, jint channelMask, jint channelIndexMask)
+{
+ audio_offload_info_t format = AUDIO_INFO_INITIALIZER;
+ format.format = (audio_format_t) audioFormatToNative(encoding);
+ format.sample_rate = (uint32_t) sampleRate;
+ format.channel_mask = nativeChannelMaskFromJavaChannelMasks(channelMask, channelIndexMask);
+ format.stream_type = AUDIO_STREAM_MUSIC;
+ format.has_video = false;
+ format.is_streaming = false;
+ // offload duration unknown at this point:
+ // client side code cannot access "audio.offload.min.duration.secs" property to make a query
+ // agnostic of duration, so using acceptable estimate of 2mn
+ format.duration_us = 120 * 1000000;
+ return AudioSystem::isOffloadSupported(format);
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gMethods[] = {
@@ -1823,6 +1841,7 @@
(void *)android_media_AudioSystem_registerRecordingCallback},
{"systemReady", "()I", (void *)android_media_AudioSystem_systemReady},
{"getStreamVolumeDB", "(III)F", (void *)android_media_AudioSystem_getStreamVolumeDB},
+ {"native_is_offload_supported", "(IIII)Z", (void *)android_media_AudioSystem_isOffloadSupported},
};
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 556ac27..11011b1 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -73,6 +73,7 @@
jobject audioTrack_ref;
bool busy;
Condition cond;
+ bool isOffload;
};
// keep these values in sync with AudioTrack.java
@@ -90,6 +91,7 @@
AudioTrackJniStorage() {
mCallbackData.audioTrack_class = 0;
mCallbackData.audioTrack_ref = 0;
+ mCallbackData.isOffload = false;
}
~AudioTrackJniStorage() {
@@ -132,27 +134,34 @@
}
switch (event) {
- case AudioTrack::EVENT_MARKER: {
- JNIEnv *env = AndroidRuntime::getJNIEnv();
- if (user != NULL && env != NULL) {
- env->CallStaticVoidMethod(
- callbackInfo->audioTrack_class,
- javaAudioTrackFields.postNativeEventInJava,
- callbackInfo->audioTrack_ref, event, 0,0, NULL);
- if (env->ExceptionCheck()) {
- env->ExceptionDescribe();
- env->ExceptionClear();
+ // Offload only events
+ case AudioTrack::EVENT_STREAM_END:
+ case AudioTrack::EVENT_MORE_DATA:
+ // a.k.a. tear down
+ case AudioTrack::EVENT_NEW_IAUDIOTRACK:
+ if (callbackInfo->isOffload) {
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ if (user != NULL && env != NULL) {
+ env->CallStaticVoidMethod(
+ callbackInfo->audioTrack_class,
+ javaAudioTrackFields.postNativeEventInJava,
+ callbackInfo->audioTrack_ref, event, 0,0, NULL);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ }
}
- }
} break;
+ // PCM and offload events
+ case AudioTrack::EVENT_MARKER:
case AudioTrack::EVENT_NEW_POS: {
JNIEnv *env = AndroidRuntime::getJNIEnv();
if (user != NULL && env != NULL) {
env->CallStaticVoidMethod(
- callbackInfo->audioTrack_class,
- javaAudioTrackFields.postNativeEventInJava,
- callbackInfo->audioTrack_ref, event, 0,0, NULL);
+ callbackInfo->audioTrack_class,
+ javaAudioTrackFields.postNativeEventInJava,
+ callbackInfo->audioTrack_ref, event, 0,0, NULL);
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
@@ -198,30 +207,12 @@
return getAudioTrack(env, audioTrackObj);
}
-// This function converts Java channel masks to a native channel mask.
-// validity should be checked with audio_is_output_channel().
-static inline audio_channel_mask_t nativeChannelMaskFromJavaChannelMasks(
- jint channelPositionMask, jint channelIndexMask)
-{
- if (channelIndexMask != 0) { // channel index mask takes priority
- // To convert to a native channel mask, the Java channel index mask
- // requires adding the index representation.
- return audio_channel_mask_from_representation_and_bits(
- AUDIO_CHANNEL_REPRESENTATION_INDEX,
- channelIndexMask);
- }
- // To convert to a native channel mask, the Java channel position mask
- // requires a shift by 2 to skip the two deprecated channel
- // configurations "default" and "mono".
- return (audio_channel_mask_t)(channelPositionMask >> 2);
-}
-
// ----------------------------------------------------------------------------
static jint
android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, jobject jaa,
jintArray jSampleRate, jint channelPositionMask, jint channelIndexMask,
jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession,
- jlong nativeAudioTrack) {
+ jlong nativeAudioTrack, jboolean offload) {
ALOGV("sampleRates=%p, channel mask=%x, index mask=%x, audioFormat(Java)=%d, buffSize=%d"
"nativeAudioTrack=0x%" PRIX64,
@@ -322,8 +313,19 @@
lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);
// we use a weak reference so the AudioTrack object can be garbage collected.
lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);
+ lpJniStorage->mCallbackData.isOffload = offload;
lpJniStorage->mCallbackData.busy = false;
+ audio_offload_info_t offloadInfo;
+ if (offload) {
+ offloadInfo = AUDIO_INFO_INITIALIZER;
+ offloadInfo.format = format;
+ offloadInfo.sample_rate = sampleRateInHertz;
+ offloadInfo.channel_mask = nativeChannelMask;
+ offloadInfo.has_video = false;
+ offloadInfo.stream_type = AUDIO_STREAM_MUSIC; //required for offload
+ }
+
// initialize the native AudioTrack object
status_t status = NO_ERROR;
switch (memoryMode) {
@@ -342,7 +344,7 @@
true,// thread can call Java
sessionId,// audio session ID
AudioTrack::TRANSFER_SYNC,
- NULL, // default offloadInfo
+ offload ? &offloadInfo : NULL,
-1, -1, // default uid, pid values
paa);
break;
@@ -1234,7 +1236,7 @@
{"native_stop", "()V", (void *)android_media_AudioTrack_stop},
{"native_pause", "()V", (void *)android_media_AudioTrack_pause},
{"native_flush", "()V", (void *)android_media_AudioTrack_flush},
- {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJ)I",
+ {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJZ)I",
(void *)android_media_AudioTrack_setup},
{"native_finalize", "()V", (void *)android_media_AudioTrack_finalize},
{"native_release", "()V", (void *)android_media_AudioTrack_release},
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 2752a7e..e5efb90 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -21,7 +21,6 @@
import "frameworks/base/core/proto/android/os/batterytype.proto";
import "frameworks/base/core/proto/android/os/cpufreq.proto";
import "frameworks/base/core/proto/android/os/cpuinfo.proto";
-import "frameworks/base/core/proto/android/os/incidentheader.proto";
import "frameworks/base/core/proto/android/os/kernelwake.proto";
import "frameworks/base/core/proto/android/os/pagetypeinfo.proto";
import "frameworks/base/core/proto/android/os/procrank.proto";
@@ -46,6 +45,7 @@
import "frameworks/base/core/proto/android/service/procstats.proto";
import "frameworks/base/core/proto/android/util/event_log_tags.proto";
import "frameworks/base/core/proto/android/util/log.proto";
+import "frameworks/base/libs/incident/proto/android/os/header.proto";
import "frameworks/base/libs/incident/proto/android/privacy.proto";
import "frameworks/base/libs/incident/proto/android/section.proto";
diff --git a/core/proto/android/os/incidentheader.proto b/core/proto/android/os/incidentheader.proto
deleted file mode 100644
index f0c736a..0000000
--- a/core/proto/android/os/incidentheader.proto
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-syntax = "proto2";
-option java_multiple_files = true;
-
-package android.os;
-
-// IncidentHeaderProto contains extra information the caller of incidentd want to
-// attach in an incident report, the data should just be informative.
-message IncidentHeaderProto {
-
- // From statsd config, the name of the anomaly, usually human readable.
- optional string incident_name = 1;
-
- // Format a human readable reason why an incident report is requested.
- // It's optional and may directly come from a user clicking the bug-report button.
- optional string reason = 2;
-
- // From statsd, the metric which causes the anomaly triggered.
- optional string metric_name = 3;
-
- // From statsd, the metric value exceeds the threshold. This is useful for
- // ValueMetric and GaugeMetric.
- oneof metric_value {
- int64 int64_value = 4;
- double double_value = 5;
- }
-
- // Defines which stats config used to fire the request.
- message StatsdConfigKey {
- optional int32 uid = 1;
- optional string name = 2;
- }
- optional StatsdConfigKey config_key = 6;
-}
diff --git a/core/proto/android/service/netstats.proto b/core/proto/android/service/netstats.proto
index 23613fd..ad9191c 100644
--- a/core/proto/android/service/netstats.proto
+++ b/core/proto/android/service/netstats.proto
@@ -63,6 +63,8 @@
optional bool roaming = 4;
optional bool metered = 5;
+
+ optional bool default_network = 6;
}
// Corresponds to NetworkStatsRecorder.
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 547e83c..ba30981 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1747,6 +1747,12 @@
<permission android:name="android.permission.SEND_EMBMS_INTENTS"
android:protectionLevel="signature|privileged" />
+
+ <!-- Allows internal management of the sensor framework
+ @hide -->
+ <permission android:name="android.permission.MANAGE_SENSORS"
+ android:protectionLevel="signature" />
+
<!-- Must be required by an ImsService to ensure that only the
system can bind to it.
<p>Protection level: signature|privileged
@@ -3186,10 +3192,14 @@
<permission android:name="android.permission.BIND_APPWIDGET"
android:protectionLevel="signature|privileged" />
+ <!-- @hide Allows sysui to manage user grants of slice permissions. -->
+ <permission android:name="android.permission.MANAGE_SLICE_PERMISSIONS"
+ android:protectionLevel="signature" />
+
<!-- Allows an application to bind app's slices and get their
content. This content will be surfaced to the
user and not to leave the device.
- <p>Not for use by third-party applications. -->
+ <p>Not for use by third-party applications.-->
<permission android:name="android.permission.BIND_SLICE"
android:protectionLevel="signature|privileged|development" />
@@ -3692,11 +3702,20 @@
<permission android:name="android.permission.READ_RUNTIME_PROFILES"
android:protectionLevel="signature|privileged" />
+ <!-- @hide Allows audio policy management. -->
+ <permission android:name="android.permission.MANAGE_AUDIO_POLICY"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi Allows an application to turn on / off quiet mode.
@hide <p>Not for use by third-party applications. -->
<permission android:name="android.permission.MODIFY_QUIET_MODE"
android:protectionLevel="signature|privileged" />
+ <!-- Allows internal management of the camera framework
+ @hide -->
+ <permission android:name="android.permission.MANAGE_CAMERA"
+ android:protectionLevel="signature" />
+
<!-- Allows an application to control remote animations. See
{@link ActivityOptions#makeRemoteAnimation}
@hide <p>Not for use by third-party applications. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index f084159..64f291c 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -430,7 +430,7 @@
<integer translatable="false" name="config_mobile_hotspot_provision_check_period">24</integer>
<!-- Activity name to enable wifi tethering after provisioning app succeeds -->
- <string translatable="false" name="config_wifi_tether_enable">com.android.settings/.TetherService</string>
+ <string translatable="false" name="config_wifi_tether_enable">com.android.settings/.wifi.tether.TetherService</string>
<!-- Controls the WiFi wakeup feature.
0 = Not available.
@@ -640,6 +640,12 @@
<!-- Wifi driver supports batched scan -->
<bool translatable="false" name="config_wifi_batched_scan_supported">false</bool>
+ <!-- Wifi driver supports Automatic channel selection (ACS) for softap -->
+ <bool translatable="false" name="config_wifi_softap_acs_supported">false</bool>
+
+ <!-- Wifi driver supports IEEE80211AC for softap -->
+ <bool translatable="false" name="config_wifi_softap_ieee80211ac_supported">false</bool>
+
<!-- Idle Receive current for wifi radio. 0 by default-->
<integer translatable="false" name="config_wifi_idle_receive_cur_ma">0</integer>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 2cfe919..170ba42 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4817,4 +4817,11 @@
<string name="harmful_app_warning_launch_anyway">Launch anyway</string>
<!-- Title for the harmful app warning dialog. -->
<string name="harmful_app_warning_title">Uninstall harmful app?</string>
+
+ <!-- Text describing a permission request for one app to show another app's
+ slices [CHAR LIMIT=NONE] -->
+ <string name="slices_permission_request"><xliff:g id="app" example="Example App">%1$s</xliff:g> wants to show <xliff:g id="app_2" example="Other Example App">%2$s</xliff:g> slices</string>
+
+ <!-- Notification action for editing a screenshot (drawing on it, cropping it, etc) -->
+ <string name="screenshot_edit">Edit</string>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 1993ab4..9550657 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -309,6 +309,8 @@
<java-symbol type="bool" name="config_useFixedVolume" />
<java-symbol type="bool" name="config_forceDefaultOrientation" />
<java-symbol type="bool" name="config_wifi_batched_scan_supported" />
+ <java-symbol type="bool" name="config_wifi_softap_acs_supported" />
+ <java-symbol type="bool" name="config_wifi_softap_ieee80211ac_supported" />
<java-symbol type="bool" name="config_enableMultiUserUI"/>
<java-symbol type="bool" name="config_disableUsbPermissionDialogs"/>
<java-symbol type="bool" name="config_hasRecents" />
@@ -1508,6 +1510,7 @@
<java-symbol type="xml" name="password_kbd_symbols" />
<java-symbol type="xml" name="password_kbd_symbols_shift" />
<java-symbol type="xml" name="power_profile" />
+ <java-symbol type="xml" name="power_profile_test" />
<java-symbol type="xml" name="sms_short_codes" />
<java-symbol type="xml" name="audio_assets" />
<java-symbol type="xml" name="global_keys" />
@@ -3224,4 +3227,8 @@
<java-symbol type="string" name="config_defaultAssistantAccessPackage" />
<java-symbol type="bool" name="config_supportBluetoothPersistedState" />
+
+ <java-symbol type="string" name="slices_permission_request" />
+
+ <java-symbol type="string" name="screenshot_edit" />
</resources>
diff --git a/core/res/res/xml/power_profile_test.xml b/core/res/res/xml/power_profile_test.xml
new file mode 100644
index 0000000..cdb7134
--- /dev/null
+++ b/core/res/res/xml/power_profile_test.xml
@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+<device name="Android">
+ <!-- All values are in mAh except as noted.
+ This file is for PowerProfileTest.java. Changes must be synced between these two. Since
+ power_profile.xml may be overridden by actual device's power_profile.xml at compile time,
+ this test config ensures we have something constant to test against. Values below are
+ sample values, not meant to reflect any real device.
+ -->
+
+ <!-- Nothing -->
+ <item name="none">0</item>
+
+ <!-- This is the battery capacity in mAh -->
+ <item name="battery.capacity">3000</item>
+
+ <!-- Number of cores each CPU cluster contains -->
+ <array name="cpu.clusters.cores">
+ <value>4</value> <!-- Cluster 0 has 4 cores (cpu0, cpu1, cpu2, cpu3) -->
+ <value>4</value> <!-- Cluster 1 has 4 cores (cpu4, cpu5, cpu5, cpu7) -->
+ </array>
+
+ <!-- Power consumption when CPU is suspended -->
+ <item name="cpu.suspend">5</item>
+ <!-- Additional power consumption when CPU is in a kernel idle loop -->
+ <item name="cpu.idle">1.11</item>
+ <!-- Additional power consumption by CPU excluding cluster and core when running -->
+ <item name="cpu.active">2.55</item>
+
+ <!-- Additional power consumption by CPU cluster0 itself when running excluding cores in it -->
+ <item name="cpu.cluster_power.cluster0">2.11</item>
+ <!-- Additional power consumption by CPU cluster1 itself when running excluding cores in it -->
+ <item name="cpu.cluster_power.cluster1">2.22</item>
+
+ <!-- Different CPU speeds as reported in
+ /sys/devices/system/cpu/cpu0/cpufreq/stats/scaling_available_frequencies -->
+ <array name="cpu.core_speeds.cluster0">
+ <value>300000</value> <!-- 300 MHz CPU speed -->
+ <value>1000000</value> <!-- 1000 MHz CPU speed -->
+ <value>2000000</value> <!-- 2000 MHz CPU speed -->
+ </array>
+ <!-- Different CPU speeds as reported in
+ /sys/devices/system/cpu/cpu4/cpufreq/stats/scaling_available_frequencies -->
+ <array name="cpu.core_speeds.cluster1">
+ <value>300000</value> <!-- 300 MHz CPU speed -->
+ <value>1000000</value> <!-- 1000 MHz CPU speed -->
+ <value>2500000</value> <!-- 2500 MHz CPU speed -->
+ <value>3000000</value> <!-- 3000 MHz CPU speed -->
+ </array>
+
+ <!-- Additional power used by a CPU from cluster 0 when running at different
+ speeds. Currently this measurement also includes cluster cost. -->
+ <array name="cpu.core_power.cluster0">
+ <value>10</value> <!-- 300 MHz CPU speed -->
+ <value>20</value> <!-- 1000 MHz CPU speed -->
+ <value>30</value> <!-- 1900 MHz CPU speed -->
+ </array>
+ <!-- Additional power used by a CPU from cluster 1 when running at different
+ speeds. Currently this measurement also includes cluster cost. -->
+ <array name="cpu.core_power.cluster1">
+ <value>25</value> <!-- 300 MHz CPU speed -->
+ <value>35</value> <!-- 1000 MHz CPU speed -->
+ <value>50</value> <!-- 2500 MHz CPU speed -->
+ <value>60</value> <!-- 3000 MHz CPU speed -->
+ </array>
+
+ <!-- Additional power used when screen is turned on at minimum brightness -->
+ <item name="screen.on">100</item>
+ <!-- Additional power used when screen is at maximum brightness, compared to
+ screen at minimum brightness -->
+ <item name="screen.full">800</item>
+
+ <!-- Average power used by the camera flash module when on -->
+ <item name="camera.flashlight">500</item>
+ <!-- Average power use by the camera subsystem for a typical camera
+ application. Intended as a rough estimate for an application running a
+ preview and capturing approximately 10 full-resolution pictures per
+ minute. -->
+ <item name="camera.avg">600</item>
+
+ <!-- Additional power used when audio decoding/encoding via DSP -->
+ <item name="dsp.audio">100</item>
+
+ <!-- Additional power used when GPS is acquiring a signal -->
+ <item name="gps.on">10</item>
+
+ <!-- Additional power used when cellular radio is transmitting/receiving -->
+ <item name="radio.active">60</item>
+ <!-- Additional power used when cellular radio is paging the tower -->
+ <item name="radio.scanning">3</item>
+ <!-- Additional power used when the cellular radio is on. Multi-value entry,
+ one per signal strength (no signal, weak, moderate, strong) -->
+ <array name="radio.on"> <!-- Strength 0 to BINS-1 -->
+ <value>6</value> <!-- none -->
+ <value>5</value> <!-- poor -->
+ <value>4</value> <!-- moderate -->
+ <value>3</value> <!-- good -->
+ <value>3</value> <!-- great -->
+ </array>
+</device>
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 1482baf..fa0ea5c 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -535,7 +535,9 @@
Settings.Secure.VOICE_RECOGNITION_SERVICE,
Settings.Secure.INSTANT_APPS_ENABLED,
Settings.Secure.BACKUP_MANAGER_CONSTANTS,
- Settings.Secure.KEYGUARD_SLICE_URI);
+ Settings.Secure.KEYGUARD_SLICE_URI,
+ Settings.Secure.PARENTAL_CONTROL_ENABLED,
+ Settings.Secure.PARENTAL_CONTROL_REDIRECT_URL);
@Test
public void systemSettingsBackedUpOrBlacklisted() {
diff --git a/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java b/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java
index 00732b0..4c4aeaf 100644
--- a/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java
@@ -36,20 +36,28 @@
@Test
public void ensureAllBackedUpSystemSettingsHaveValidators() {
- String offenders = getOffenders(Settings.System.SETTINGS_TO_BACKUP,
- Settings.System.VALIDATORS);
+ String offenders = getOffenders(concat(Settings.System.SETTINGS_TO_BACKUP,
+ Settings.System.LEGACY_RESTORE_SETTINGS), Settings.System.VALIDATORS);
failIfOffendersPresent(offenders, "Settings.System");
}
@Test
public void ensureAllBackedUpGlobalSettingsHaveValidators() {
- String offenders = getOffenders(Settings.Global.SETTINGS_TO_BACKUP,
- Settings.Global.VALIDATORS);
+ String offenders = getOffenders(concat(Settings.Global.SETTINGS_TO_BACKUP,
+ Settings.Global.LEGACY_RESTORE_SETTINGS), Settings.Global.VALIDATORS);
failIfOffendersPresent(offenders, "Settings.Global");
}
+ @Test
+ public void ensureAllBackedUpSecureSettingsHaveValidators() {
+ String offenders = getOffenders(concat(Settings.Secure.SETTINGS_TO_BACKUP,
+ Settings.Secure.LEGACY_RESTORE_SETTINGS), Settings.Secure.VALIDATORS);
+
+ failIfOffendersPresent(offenders, "Settings.Secure");
+ }
+
private void failIfOffendersPresent(String offenders, String settingsType) {
if (offenders.length() > 0) {
fail("All " + settingsType + " settings that are backed up have to have a non-null"
@@ -66,4 +74,16 @@
}
return offenders.toString();
}
+
+ private String[] concat(String[] first, String[] second) {
+ if (second == null || second.length == 0) {
+ return first;
+ }
+ final int firstLen = first.length;
+ final int secondLen = second.length;
+ String[] both = new String[firstLen + secondLen];
+ System.arraycopy(first, 0, both, 0, firstLen);
+ System.arraycopy(second, 0, both, firstLen, secondLen);
+ return both;
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
index b5a7bec..32053e3 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
@@ -62,9 +62,9 @@
*
* Build: m FrameworksCoreTests
* Install: adb install -r \
- * ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk
+ * ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk
* Run: adb shell am instrument -e class com.android.internal.os.BatteryStatsCpuTimesTest -w \
- * com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
+ * com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
*
* or
*
@@ -73,10 +73,18 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public class BatteryStatsCpuTimesTest {
- @Mock KernelUidCpuTimeReader mKernelUidCpuTimeReader;
- @Mock KernelUidCpuFreqTimeReader mKernelUidCpuFreqTimeReader;
- @Mock BatteryStatsImpl.UserInfoProvider mUserInfoProvider;
- @Mock PowerProfile mPowerProfile;
+ @Mock
+ KernelUidCpuTimeReader mKernelUidCpuTimeReader;
+ @Mock
+ KernelUidCpuFreqTimeReader mKernelUidCpuFreqTimeReader;
+ @Mock
+ KernelUidCpuActiveTimeReader mKernelUidCpuActiveTimeReader;
+ @Mock
+ KernelUidCpuClusterTimeReader mKernelUidCpuClusterTimeReader;
+ @Mock
+ BatteryStatsImpl.UserInfoProvider mUserInfoProvider;
+ @Mock
+ PowerProfile mPowerProfile;
private MockClocks mClocks;
private MockBatteryStatsImpl mBatteryStatsImpl;
@@ -90,6 +98,8 @@
mBatteryStatsImpl = new MockBatteryStatsImpl(mClocks)
.setKernelUidCpuTimeReader(mKernelUidCpuTimeReader)
.setKernelUidCpuFreqTimeReader(mKernelUidCpuFreqTimeReader)
+ .setKernelUidCpuActiveTimeReader(mKernelUidCpuActiveTimeReader)
+ .setKernelUidCpuClusterTimeReader(mKernelUidCpuClusterTimeReader)
.setUserInfoProvider(mUserInfoProvider);
}
@@ -134,6 +144,10 @@
verify(mKernelUidCpuFreqTimeReader, times(2)).perClusterTimesAvailable();
verify(mKernelUidCpuFreqTimeReader).readDelta(
any(KernelUidCpuFreqTimeReader.Callback.class));
+ verify(mKernelUidCpuActiveTimeReader).readDelta(
+ any(KernelUidCpuActiveTimeReader.Callback.class));
+ verify(mKernelUidCpuClusterTimeReader).readDelta(
+ any(KernelUidCpuClusterTimeReader.Callback.class));
verifyNoMoreInteractions(mKernelUidCpuFreqTimeReader);
for (int i = 0; i < numClusters; ++i) {
verify(mKernelCpuSpeedReaders[i]).readDelta();
@@ -228,7 +242,7 @@
updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
final int testUserId = 11;
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
- final int[] testUids = getUids(testUserId, new int[] {
+ final int[] testUids = getUids(testUserId, new int[]{
FIRST_APPLICATION_UID + 22,
FIRST_APPLICATION_UID + 27,
FIRST_APPLICATION_UID + 33
@@ -301,7 +315,7 @@
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
final int isolatedAppId = FIRST_ISOLATED_UID + 27;
final int isolatedUid = UserHandle.getUid(testUserId, isolatedAppId);
- final int[] testUids = getUids(testUserId, new int[] {
+ final int[] testUids = getUids(testUserId, new int[]{
FIRST_APPLICATION_UID + 22,
isolatedAppId,
FIRST_APPLICATION_UID + 33
@@ -389,7 +403,7 @@
final int invalidUid = UserHandle.getUid(invalidUserId, FIRST_APPLICATION_UID + 99);
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
when(mUserInfoProvider.exists(invalidUserId)).thenReturn(false);
- final int[] testUids = getUids(testUserId, new int[] {
+ final int[] testUids = getUids(testUserId, new int[]{
FIRST_APPLICATION_UID + 22,
FIRST_APPLICATION_UID + 27,
FIRST_APPLICATION_UID + 33
@@ -431,7 +445,7 @@
updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
final int testUserId = 11;
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
- final int[] testUids = getUids(testUserId, new int[] {
+ final int[] testUids = getUids(testUserId, new int[]{
FIRST_APPLICATION_UID + 22,
FIRST_APPLICATION_UID + 27,
FIRST_APPLICATION_UID + 33
@@ -514,10 +528,10 @@
final int testUserId = 11;
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
- final int[] testUids = getUids(testUserId, new int[] {
- FIRST_APPLICATION_UID + 22,
- FIRST_APPLICATION_UID + 27,
- FIRST_APPLICATION_UID + 33
+ final int[] testUids = getUids(testUserId, new int[]{
+ FIRST_APPLICATION_UID + 22,
+ FIRST_APPLICATION_UID + 27,
+ FIRST_APPLICATION_UID + 33
});
final long[][] uidTimesMs = {
{4, 10, 5, 9, 4},
@@ -589,7 +603,7 @@
final int testUserId = 11;
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
- final int[] testUids = getUids(testUserId, new int[] {
+ final int[] testUids = getUids(testUserId, new int[]{
FIRST_APPLICATION_UID + 22,
FIRST_APPLICATION_UID + 27,
FIRST_APPLICATION_UID + 33
@@ -693,7 +707,7 @@
final int testUserId = 11;
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
- final int[] testUids = getUids(testUserId, new int[] {
+ final int[] testUids = getUids(testUserId, new int[]{
FIRST_APPLICATION_UID + 22,
FIRST_APPLICATION_UID + 27,
FIRST_APPLICATION_UID + 33
@@ -782,7 +796,7 @@
}
}
for (int cluster = 0; cluster < clusterFreqs.length; ++cluster) {
- for (int speed = 0 ; speed < clusterFreqs[cluster]; ++speed) {
+ for (int speed = 0; speed < clusterFreqs[cluster]; ++speed) {
assertEquals("There shouldn't be any left-overs: "
+ Arrays.deepToString(expectedWakeLockUidTimesUs),
0, expectedWakeLockUidTimesUs[cluster][speed]);
@@ -797,7 +811,7 @@
final int testUserId = 11;
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
- final int[] testUids = getUids(testUserId, new int[] {
+ final int[] testUids = getUids(testUserId, new int[]{
FIRST_APPLICATION_UID + 22,
FIRST_APPLICATION_UID + 27,
FIRST_APPLICATION_UID + 33
@@ -874,7 +888,7 @@
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
final int isolatedAppId = FIRST_ISOLATED_UID + 27;
final int isolatedUid = UserHandle.getUid(testUserId, isolatedAppId);
- final int[] testUids = getUids(testUserId, new int[] {
+ final int[] testUids = getUids(testUserId, new int[]{
FIRST_APPLICATION_UID + 22,
isolatedAppId,
FIRST_APPLICATION_UID + 33
@@ -969,7 +983,7 @@
final int invalidUid = UserHandle.getUid(invalidUserId, FIRST_APPLICATION_UID + 99);
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
when(mUserInfoProvider.exists(invalidUserId)).thenReturn(false);
- final int[] testUids = getUids(testUserId, new int[] {
+ final int[] testUids = getUids(testUserId, new int[]{
FIRST_APPLICATION_UID + 22,
FIRST_APPLICATION_UID + 27,
FIRST_APPLICATION_UID + 33
@@ -986,7 +1000,7 @@
callback.onUidCpuFreqTime(testUids[i], uidTimesMs[i]);
}
// And one for the invalid uid
- callback.onUidCpuFreqTime(invalidUid, new long[] {12, 839, 32, 34, 21});
+ callback.onUidCpuFreqTime(invalidUid, new long[]{12, 839, 32, 34, 21});
return null;
}).when(mKernelUidCpuFreqTimeReader).readDelta(
any(KernelUidCpuFreqTimeReader.Callback.class));
@@ -1009,6 +1023,136 @@
verify(mKernelUidCpuFreqTimeReader).removeUid(invalidUid);
}
+ @Test
+ public void testReadKernelUidCpuActiveTimesLocked() {
+ // PRECONDITIONS
+ updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
+
+ final int testUserId = 11;
+ when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
+ final int[] testUids = getUids(testUserId, new int[]{
+ FIRST_APPLICATION_UID + 22,
+ FIRST_APPLICATION_UID + 27,
+ FIRST_APPLICATION_UID + 33
+ });
+ final long[] uidTimesMs = {8000, 25000, 3000, 0, 42000};
+ doAnswer(invocation -> {
+ final KernelUidCpuActiveTimeReader.Callback callback =
+ (KernelUidCpuActiveTimeReader.Callback) invocation.getArguments()[0];
+ for (int i = 0; i < testUids.length; ++i) {
+ callback.onUidCpuActiveTime(testUids[i], uidTimesMs[i]);
+ }
+ return null;
+ }).when(mKernelUidCpuActiveTimeReader).readDelta(
+ any(KernelUidCpuActiveTimeReader.Callback.class));
+
+ // RUN
+ mBatteryStatsImpl.readKernelUidCpuActiveTimesLocked();
+
+ // VERIFY
+ for (int i = 0; i < testUids.length; ++i) {
+ final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
+ assertNotNull("No entry for uid=" + testUids[i], u);
+ assertEquals("Unexpected cpu active time for uid=" + testUids[i], uidTimesMs[i],
+ u.getCpuActiveTime());
+ }
+
+ // Repeat the test when the screen is off.
+
+ // PRECONDITIONS
+ updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
+ final long[] deltasMs = {43000, 3345000, 2143000, 123000, 4554000};
+ doAnswer(invocation -> {
+ final KernelUidCpuActiveTimeReader.Callback callback =
+ (KernelUidCpuActiveTimeReader.Callback) invocation.getArguments()[0];
+ for (int i = 0; i < testUids.length; ++i) {
+ callback.onUidCpuActiveTime(testUids[i], deltasMs[i]);
+ }
+ return null;
+ }).when(mKernelUidCpuActiveTimeReader).readDelta(
+ any(KernelUidCpuActiveTimeReader.Callback.class));
+
+ // RUN
+ mBatteryStatsImpl.readKernelUidCpuActiveTimesLocked();
+
+ // VERIFY
+ for (int i = 0; i < testUids.length; ++i) {
+ final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
+ assertNotNull("No entry for uid=" + testUids[i], u);
+ assertEquals("Unexpected cpu active time for uid=" + testUids[i],
+ uidTimesMs[i] + deltasMs[i], u.getCpuActiveTime());
+ }
+ }
+
+ @Test
+ public void testReadKernelUidCpuClusterTimesLocked() {
+ // PRECONDITIONS
+ updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
+
+ final int testUserId = 11;
+ when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
+ final int[] testUids = getUids(testUserId, new int[]{
+ FIRST_APPLICATION_UID + 22,
+ FIRST_APPLICATION_UID + 27,
+ FIRST_APPLICATION_UID + 33
+ });
+ final long[][] uidTimesMs = {
+ {4000, 10000},
+ {5000, 1000},
+ {8000, 0}
+ };
+ doAnswer(invocation -> {
+ final KernelUidCpuClusterTimeReader.Callback callback =
+ (KernelUidCpuClusterTimeReader.Callback) invocation.getArguments()[0];
+ for (int i = 0; i < testUids.length; ++i) {
+ callback.onUidCpuPolicyTime(testUids[i], uidTimesMs[i]);
+ }
+ return null;
+ }).when(mKernelUidCpuClusterTimeReader).readDelta(
+ any(KernelUidCpuClusterTimeReader.Callback.class));
+
+ // RUN
+ mBatteryStatsImpl.readKernelUidCpuClusterTimesLocked();
+
+ // VERIFY
+ for (int i = 0; i < testUids.length; ++i) {
+ final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
+ assertNotNull("No entry for uid=" + testUids[i], u);
+ assertArrayEquals("Unexpected cpu cluster time for uid=" + testUids[i], uidTimesMs[i],
+ u.getCpuClusterTimes());
+ }
+
+ // Repeat the test when the screen is off.
+
+ // PRECONDITIONS
+ updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
+ final long[][] deltasMs = {
+ {3000, 12000},
+ {3248327490475L, 0},
+ {43000, 3345000}
+ };
+ doAnswer(invocation -> {
+ final KernelUidCpuClusterTimeReader.Callback callback =
+ (KernelUidCpuClusterTimeReader.Callback) invocation.getArguments()[0];
+ for (int i = 0; i < testUids.length; ++i) {
+ callback.onUidCpuPolicyTime(testUids[i], deltasMs[i]);
+ }
+ return null;
+ }).when(mKernelUidCpuClusterTimeReader).readDelta(
+ any(KernelUidCpuClusterTimeReader.Callback.class));
+
+ // RUN
+ mBatteryStatsImpl.readKernelUidCpuClusterTimesLocked();
+
+ // VERIFY
+ for (int i = 0; i < testUids.length; ++i) {
+ final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
+ assertNotNull("No entry for uid=" + testUids[i], u);
+ assertArrayEquals("Unexpected cpu cluster time for uid=" + testUids[i], sum(uidTimesMs[i], deltasMs[i]),
+ u.getCpuClusterTimes());
+ }
+ }
+
private void updateTimeBasesLocked(boolean unplugged, int screenState,
long upTime, long realTime) {
// Set PowerProfile=null before calling updateTimeBasesLocked to avoid execution of
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index e8f2456..702f4b8 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -39,8 +39,11 @@
KernelMemoryBandwidthStatsTest.class,
KernelSingleUidTimeReaderTest.class,
KernelUidCpuFreqTimeReaderTest.class,
+ KernelUidCpuActiveTimeReaderTest.class,
+ KernelUidCpuClusterTimeReaderTest.class,
KernelWakelockReaderTest.class,
- LongSamplingCounterArrayTest.class
+ LongSamplingCounterArrayTest.class,
+ PowerProfileTest.class
})
public class BatteryStatsTests {
}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java
new file mode 100644
index 0000000..1ac82bd
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.io.BufferedReader;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * Test class for {@link KernelUidCpuActiveTimeReader}.
+ *
+ * To run it:
+ * bit FrameworksCoreTests:com.android.internal.os.KernelUidCpuActiveTimeReaderTest
+ *
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KernelUidCpuActiveTimeReaderTest {
+ @Mock private BufferedReader mBufferedReader;
+ @Mock private KernelUidCpuActiveTimeReader.Callback mCallback;
+
+ private KernelUidCpuActiveTimeReader mReader;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mReader = new KernelUidCpuActiveTimeReader();
+ }
+
+ public class Temp {
+
+ public void method() {
+ method1(new long[][]{{1,2,3}, {2,3,4}});
+ method1(new long[][]{{2,2,3}, {2,3,4}});
+ }
+ public int method1(long[][] array) {
+ return array.length * array[0].length;
+ }
+ }
+
+ @Test
+ public void testReadDelta() throws Exception {
+ final int cores = 8;
+ final String info = "active: 8";
+ final int[] uids = {1, 22, 333, 4444, 5555};
+
+ final long[][] times = increaseTime(new long[uids.length][cores]);
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ for(int i=0;i<uids.length;i++){
+ verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(times[i]));
+ }
+ verifyNoMoreInteractions(mCallback);
+
+ // Verify that a second call will only return deltas.
+ Mockito.reset(mCallback, mBufferedReader);
+ final long[][] times1 = increaseTime(times);
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times1));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ for(int i=0;i<uids.length;i++){
+ verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(subtract(times1[i], times[i])));
+ }
+ verifyNoMoreInteractions(mCallback);
+
+ // Verify that there won't be a callback if the proc file values didn't change.
+ Mockito.reset(mCallback, mBufferedReader);
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times1));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ verifyNoMoreInteractions(mCallback);
+
+ // Verify that calling with a null callback doesn't result in any crashes
+ Mockito.reset(mCallback, mBufferedReader);
+ final long[][] times2 = increaseTime(times1);
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times2));
+ mReader.readDeltaInternal(mBufferedReader, null);
+
+ // Verify that the readDelta call will only return deltas when
+ // the previous call had null callback.
+ Mockito.reset(mCallback, mBufferedReader);
+ final long[][] times3 = increaseTime(times2);
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times3));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ for (int i = 0; i < uids.length; ++i) {
+ verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(subtract(times3[i], times2[i])));
+ }
+ verifyNoMoreInteractions(mCallback);
+ }
+
+ @Test
+ public void testReadDelta_malformedData() throws Exception {
+ final int cores = 8;
+ final String info = "active: 8";
+ final int[] uids = {1, 22, 333, 4444, 5555};
+ final long[][] times = increaseTime(new long[uids.length][cores]);
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ for(int i=0;i<uids.length;i++){
+ verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(times[i]));
+ }
+ verifyNoMoreInteractions(mCallback);
+
+ // Verify that there is no callback if subsequent call provides wrong # of entries.
+ Mockito.reset(mCallback, mBufferedReader);
+ final long[][] temp = increaseTime(times);
+ final long[][] times1 = new long[uids.length][];
+ for(int i=0;i<temp.length;i++){
+ times1[i] = Arrays.copyOfRange(temp[i], 0, 6);
+ }
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times1));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ verifyNoMoreInteractions(mCallback);
+
+ // Verify that the internal state was not modified if the given core count does not match
+ // the following # of entries.
+ Mockito.reset(mCallback, mBufferedReader);
+ final long[][] times2 = increaseTime(times);
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times2));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ for(int i=0;i<uids.length;i++){
+ verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(subtract(times2[i], times[i])));
+ }
+ verifyNoMoreInteractions(mCallback);
+
+ // Verify that there is no callback if any value in the proc file is -ve.
+ Mockito.reset(mCallback, mBufferedReader);
+ final long[][] times3 = increaseTime(times2);
+ times3[uids.length - 1][cores - 1] *= -1;
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times3));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ for (int i = 0; i < uids.length - 1; ++i) {
+ verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(subtract(times3[i], times2[i])));
+ }
+ verifyNoMoreInteractions(mCallback);
+
+ // Verify that the internal state was not modified when the proc file had -ve value.
+ Mockito.reset(mCallback, mBufferedReader);
+ for (int i = 0; i < cores; i++) {
+ times3[uids.length - 1][i] = times2[uids.length - 1][i] + uids[uids.length - 1] * 1000;
+ }
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times3));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ verify(mCallback).onUidCpuActiveTime(uids[uids.length - 1], getTotal(subtract(times3[uids.length - 1], times2[uids.length - 1])));
+ verifyNoMoreInteractions(mCallback);
+
+ // Verify that there is no callback if the values in the proc file are decreased.
+ Mockito.reset(mCallback, mBufferedReader);
+ final long[][] times4 = increaseTime(times3);
+ times4[uids.length - 1][cores - 1] = times3[uids.length - 1][cores - 1] - 1;
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times4));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ for (int i = 0; i < uids.length - 1; ++i) {
+ verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(subtract(times4[i], times3[i])));
+ }
+ verifyNoMoreInteractions(mCallback);
+
+ // Verify that the internal state was not modified when the proc file had decreased values.
+ Mockito.reset(mCallback, mBufferedReader);
+ for (int i = 0; i < cores; i++) {
+ times4[uids.length - 1][i] = times3[uids.length - 1][i] + uids[uids.length - 1] * 1000;
+ }
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times4));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ verify(mCallback).onUidCpuActiveTime(uids[uids.length - 1], getTotal(subtract(times4[uids.length - 1], times3[uids.length - 1])));
+ verifyNoMoreInteractions(mCallback);
+ }
+
+ private long[] subtract(long[] a1, long[] a2) {
+ long[] val = new long[a1.length];
+ for (int i = 0; i < val.length; ++i) {
+ val[i] = a1[i] - a2[i];
+ }
+ return val;
+ }
+
+ private String[] formatTime(int[] uids, long[][] times) {
+ String[] lines = new String[uids.length + 1];
+ for (int i=0;i<uids.length;i++){
+ StringBuilder sb = new StringBuilder();
+ sb.append(uids[i]).append(':');
+ for(int j=0;j<times[i].length;j++){
+ sb.append(' ').append(times[i][j]);
+ }
+ lines[i] = sb.toString();
+ }
+ lines[uids.length] = null;
+ return lines;
+ }
+
+ private long[][] increaseTime(long[][] original) {
+ long[][] newTime = new long[original.length][original[0].length];
+ Random rand = new Random();
+ for(int i = 0;i<original.length;i++){
+ for(int j=0;j<original[0].length;j++){
+ newTime[i][j] = original[i][j] + rand.nextInt(1000_000) + 10000;
+ }
+ }
+ return newTime;
+ }
+
+ private long getTotal(long[] times) {
+ long sum = 0;
+ for(int i=0;i<times.length;i++){
+ sum+=times[i] * 10 / (i+1);
+ }
+ return sum;
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuClusterTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuClusterTimeReaderTest.java
new file mode 100644
index 0000000..0d1f852
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuClusterTimeReaderTest.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import static org.junit.Assert.*;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.io.BufferedReader;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * Test class for {@link KernelUidCpuClusterTimeReader}.
+ *
+ * To run it:
+ * bit FrameworksCoreTests:com.android.internal.os.KernelUidCpuClusterTimeReaderTest
+ *
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KernelUidCpuClusterTimeReaderTest {
+ @Mock private BufferedReader mBufferedReader;
+ @Mock private KernelUidCpuClusterTimeReader.Callback mCallback;
+
+ private KernelUidCpuClusterTimeReader mReader;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mReader = new KernelUidCpuClusterTimeReader();
+ }
+
+ @Test
+ public void testReadDelta() throws Exception {
+ final String info = "policy0: 2 policy4: 4";
+ final int cores = 6;
+ final int[] cluster = {2, 4};
+ final int[] uids = {1, 22, 333, 4444, 5555};
+
+ // Verify initial call
+ final long[][] times = increaseTime(new long[uids.length][cores]);
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ for (int i=0;i<uids.length;i++){
+ verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, times[i]));
+ }
+
+ // Verify that a second call will only return deltas.
+ Mockito.reset(mCallback, mBufferedReader);
+ final long[][] times1 = increaseTime(times);
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times1));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ for (int i=0;i<uids.length;i++){
+ verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, subtract(times1[i], times[i])));
+ }
+
+ // Verify that there won't be a callback if the proc file values didn't change.
+ Mockito.reset(mCallback, mBufferedReader);
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times1));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ verifyNoMoreInteractions(mCallback);
+
+ // Verify that calling with a null callback doesn't result in any crashes
+ Mockito.reset(mCallback, mBufferedReader);
+ final long[][] times2 = increaseTime(times1);
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times2));
+ mReader.readDeltaInternal(mBufferedReader, null);
+
+ // Verify that the readDelta call will only return deltas when
+ // the previous call had null callback.
+ Mockito.reset(mCallback, mBufferedReader);
+ final long[][] times3 = increaseTime(times2);
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times3));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ for (int i=0;i<uids.length;i++){
+ verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, subtract(times3[i], times2[i])));
+ }
+
+ }
+
+ @Test
+ public void testReadDelta_malformedData() throws Exception {
+ final String info = "policy0: 2 policy4: 4";
+ final int cores = 6;
+ final int[] cluster = {2, 4};
+ final int[] uids = {1, 22, 333, 4444, 5555};
+
+ // Verify initial call
+ final long[][] times = increaseTime(new long[uids.length][cores]);
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ for (int i=0;i<uids.length;i++){
+ verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, times[i]));
+ }
+
+ // Verify that there is no callback if subsequent call provides wrong # of entries.
+ Mockito.reset(mCallback, mBufferedReader);
+ final long[][] temp = increaseTime(times);
+ final long[][] times1 = new long[uids.length][];
+ for(int i=0;i<temp.length;i++){
+ times1[i] = Arrays.copyOfRange(temp[i], 0, 4);
+ }
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times1));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ verifyNoMoreInteractions(mCallback);
+
+ // Verify that the internal state was not modified if the given core count does not match
+ // the following # of entries.
+ Mockito.reset(mCallback, mBufferedReader);
+ final long[][] times2 = increaseTime(times);
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times2));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ for (int i=0;i<uids.length;i++){
+ verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, subtract(times2[i], times[i])));
+ }
+
+ // Verify that there is no callback if any value in the proc file is -ve.
+ Mockito.reset(mCallback, mBufferedReader);
+ final long[][] times3 = increaseTime(times2);
+ times3[uids.length - 1][cores - 1] *= -1;
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times3));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ for (int i=0;i<uids.length-1;i++){
+ verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, subtract(times3[i], times2[i])));
+ }
+ verifyNoMoreInteractions(mCallback);
+
+ // Verify that the internal state was not modified when the proc file had -ve value.
+ Mockito.reset(mCallback, mBufferedReader);
+ for(int i=0;i<cores;i++){
+ times3[uids.length -1][i] = times2[uids.length -1][i] + uids[uids.length -1]*1000;
+ }
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times3));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ verify(mCallback, times(1)).onUidCpuPolicyTime(uids[uids.length-1], getTotal(cluster, subtract(times3[uids.length -1], times2[uids.length -1])));
+
+ // // Verify that there is no callback if the values in the proc file are decreased.
+ Mockito.reset(mCallback, mBufferedReader);
+ final long[][] times4 = increaseTime(times3);
+ times4[uids.length - 1][cores - 1] = times3[uids.length - 1][cores - 1] - 1;
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times4));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ for (int i=0;i<uids.length-1;i++){
+ verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, subtract(times4[i], times3[i])));
+ }
+ verifyNoMoreInteractions(mCallback);
+
+ // Verify that the internal state was not modified when the proc file had decreased values.
+ Mockito.reset(mCallback, mBufferedReader);
+ for(int i=0;i<cores;i++){
+ times4[uids.length -1][i] = times3[uids.length -1][i] + uids[uids.length -1]*1000;
+ }
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times4));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ verify(mCallback, times(1))
+ .onUidCpuPolicyTime(uids[uids.length-1], getTotal(cluster, subtract(times3[uids.length -1], times2[uids.length -1])));
+
+ }
+
+
+ private long[] subtract(long[] a1, long[] a2) {
+ long[] val = new long[a1.length];
+ for (int i = 0; i < val.length; ++i) {
+ val[i] = a1[i] - a2[i];
+ }
+ return val;
+ }
+
+ private String[] formatTime(int[] uids, long[][] times) {
+ String[] lines = new String[uids.length + 1];
+ for (int i=0;i<uids.length;i++){
+ StringBuilder sb = new StringBuilder();
+ sb.append(uids[i]).append(':');
+ for(int j=0;j<times[i].length;j++){
+ sb.append(' ').append(times[i][j]);
+ }
+ lines[i] = sb.toString();
+ }
+ lines[uids.length] = null;
+ return lines;
+ }
+
+ private long[][] increaseTime(long[][] original) {
+ long[][] newTime = new long[original.length][original[0].length];
+ Random rand = new Random();
+ for(int i = 0;i<original.length;i++){
+ for(int j=0;j<original[0].length;j++){
+ newTime[i][j] = original[i][j] + rand.nextInt(1000_000) + 10000;
+ }
+ }
+ return newTime;
+ }
+
+ // Format an array of cluster times according to the algorithm in KernelUidCpuClusterTimeReader
+ private long[] getTotal(int[] cluster, long[] times) {
+ int core = 0;
+ long[] sum = new long[cluster.length];
+ for(int i=0;i<cluster.length;i++){
+ for(int j=0;j<cluster[i];j++){
+ sum[i] += times[core++] * 10 / (j+1);
+ }
+ }
+ return sum;
+ }
+
+ // Compare array1 against flattened 2d array array2 element by element
+ private boolean testEqual(long[] array1, long[][] array2) {
+ int k=0;
+ for(int i=0;i<array2.length;i++){
+ for(int j=0;j<array2[i].length;j++){
+ if (k >= array1.length || array1[k++]!=array2[i][j])return false;
+ }
+ }
+ return k == array1.length;
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index 6c5a2aa..660c744 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -88,6 +88,16 @@
return this;
}
+ public MockBatteryStatsImpl setKernelUidCpuActiveTimeReader(KernelUidCpuActiveTimeReader reader) {
+ mKernelUidCpuActiveTimeReader = reader;
+ return this;
+ }
+
+ public MockBatteryStatsImpl setKernelUidCpuClusterTimeReader(KernelUidCpuClusterTimeReader reader) {
+ mKernelUidCpuClusterTimeReader = reader;
+ return this;
+ }
+
public MockBatteryStatsImpl setKernelUidCpuTimeReader(KernelUidCpuTimeReader reader) {
mKernelUidCpuTimeReader = reader;
return this;
diff --git a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
new file mode 100644
index 0000000..eb7da9c
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ */
+
+package com.android.internal.os;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+
+import junit.framework.TestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/*
+ * Keep this file in sync with frameworks/base/core/res/res/xml/power_profile_test.xml
+ */
+@SmallTest
+public class PowerProfileTest extends TestCase {
+
+ private PowerProfile mProfile;
+
+ @Before
+ public void setUp() {
+ mProfile = new PowerProfile(InstrumentationRegistry.getContext(), true);
+ }
+
+ @Test
+ public void testPowerProfile() {
+ assertEquals(2, mProfile.getNumCpuClusters());
+ assertEquals(4, mProfile.getNumCoresInCpuCluster(0));
+ assertEquals(4, mProfile.getNumCoresInCpuCluster(1));
+ assertEquals(5.0, mProfile.getAveragePower(PowerProfile.POWER_CPU_SUSPEND));
+ assertEquals(1.11, mProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE));
+ assertEquals(2.55, mProfile.getAveragePower(PowerProfile.POWER_CPU_ACTIVE));
+ assertEquals(2.11, mProfile.getAveragePowerForCpuCluster(0));
+ assertEquals(2.22, mProfile.getAveragePowerForCpuCluster(1));
+ assertEquals(3, mProfile.getNumSpeedStepsInCpuCluster(0));
+ assertEquals(30.0, mProfile.getAveragePowerForCpuCore(0, 2));
+ assertEquals(4, mProfile.getNumSpeedStepsInCpuCluster(1));
+ assertEquals(60.0, mProfile.getAveragePowerForCpuCore(1, 3));
+ assertEquals(3000.0, mProfile.getBatteryCapacity());
+ }
+
+}
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 993bae1..4be6408 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -162,9 +162,13 @@
<assign-permission name="android.permission.UPDATE_DEVICE_STATS" uid="cameraserver" />
<assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="cameraserver" />
<assign-permission name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE" uid="cameraserver" />
+ <assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="cameraserver" />
<assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="graphics" />
+ <assign-permission name="android.permission.DUMP" uid="incidentd" />
+ <assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="incidentd" />
+
<assign-permission name="android.permission.ACCESS_LOWPAN_STATE" uid="lowpan" />
<assign-permission name="android.permission.MANAGE_LOWPAN_INTERFACES" uid="lowpan" />
diff --git a/docs/html/reference/images/text/style/backgroundcolorspan.png b/docs/html/reference/images/text/style/backgroundcolorspan.png
new file mode 100644
index 0000000..e7e7271
--- /dev/null
+++ b/docs/html/reference/images/text/style/backgroundcolorspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/foregroundcolorspan.png b/docs/html/reference/images/text/style/foregroundcolorspan.png
new file mode 100644
index 0000000..f60db6c
--- /dev/null
+++ b/docs/html/reference/images/text/style/foregroundcolorspan.png
Binary files differ
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 05dadc9..4d715d1 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -33,6 +33,8 @@
import android.net.Uri;
import android.system.ErrnoException;
import android.system.Os;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
import libcore.io.IoUtils;
import dalvik.system.CloseGuard;
@@ -64,6 +66,19 @@
Resources getResources() { return null; }
/* @hide */
+ int getDensity() { return Bitmap.DENSITY_NONE; }
+
+ /* @hide */
+ int computeDstDensity() {
+ Resources res = getResources();
+ if (res == null) {
+ return Bitmap.getDefaultDensity();
+ }
+
+ return res.getDisplayMetrics().densityDpi;
+ }
+
+ /* @hide */
abstract ImageDecoder createImageDecoder() throws IOException;
};
@@ -170,26 +185,73 @@
return decoder;
}
+ private static class InputStreamSource extends Source {
+ InputStreamSource(Resources res, InputStream is, int inputDensity) {
+ if (is == null) {
+ throw new IllegalArgumentException("The InputStream cannot be null");
+ }
+ mResources = res;
+ mInputStream = is;
+ mInputDensity = res != null ? inputDensity : Bitmap.DENSITY_NONE;
+ }
+
+ final Resources mResources;
+ InputStream mInputStream;
+ final int mInputDensity;
+
+ @Override
+ public Resources getResources() { return mResources; }
+
+ @Override
+ public int getDensity() { return mInputDensity; }
+
+ @Override
+ public ImageDecoder createImageDecoder() throws IOException {
+
+ synchronized (this) {
+ if (mInputStream == null) {
+ throw new IOException("Cannot reuse InputStreamSource");
+ }
+ InputStream is = mInputStream;
+ mInputStream = null;
+ return createFromStream(is);
+ }
+ }
+ }
+
private static class ResourceSource extends Source {
ResourceSource(Resources res, int resId) {
mResources = res;
mResId = resId;
+ mResDensity = Bitmap.DENSITY_NONE;
}
final Resources mResources;
final int mResId;
+ int mResDensity;
@Override
public Resources getResources() { return mResources; }
@Override
+ public int getDensity() { return mResDensity; }
+
+ @Override
public ImageDecoder createImageDecoder() throws IOException {
// This is just used in order to access the underlying Asset and
// keep it alive. FIXME: Can we skip creating this object?
InputStream is = null;
ImageDecoder decoder = null;
+ TypedValue value = new TypedValue();
try {
- is = mResources.openRawResource(mResId);
+ is = mResources.openRawResource(mResId, value);
+
+ if (value.density == TypedValue.DENSITY_DEFAULT) {
+ mResDensity = DisplayMetrics.DENSITY_DEFAULT;
+ } else if (value.density != TypedValue.DENSITY_NONE) {
+ mResDensity = value.density;
+ }
+
if (!(is instanceof AssetManager.AssetInputStream)) {
// This should never happen.
throw new RuntimeException("Resource is not an asset?");
@@ -421,6 +483,22 @@
}
/**
+ * Internal API used to generate bitmaps for use by Drawables (i.e. BitmapDrawable)
+ * @hide
+ */
+ public static Source createSource(Resources res, InputStream is) {
+ return new InputStreamSource(res, is, Bitmap.getDefaultDensity());
+ }
+
+ /**
+ * Internal API used to generate bitmaps for use by Drawables (i.e. BitmapDrawable)
+ * @hide
+ */
+ public static Source createSource(Resources res, InputStream is, int density) {
+ return new InputStreamSource(res, is, density);
+ }
+
+ /**
* Return the width and height of a given sample size.
*
* This takes an input that functions like
@@ -476,6 +554,10 @@
this.resize(dimensions.x, dimensions.y);
}
+ private boolean requestedResize() {
+ return mWidth != mDesiredWidth || mHeight != mDesiredHeight;
+ }
+
// These need to stay in sync with ImageDecoder.cpp's Allocator enum.
/**
* Use the default allocation for the pixel memory.
@@ -730,6 +812,9 @@
"Drawable!");
}
+ // this call potentially manipulates the decoder so it must be performed prior to
+ // decoding the bitmap and after decode set the density on the resulting bitmap
+ final int srcDensity = computeDensity(src, decoder);
if (decoder.mAnimated) {
// AnimatedImageDrawable calls postProcessAndRelease only if
// mPostProcess exists.
@@ -737,7 +822,8 @@
null : decoder;
Drawable d = new AnimatedImageDrawable(decoder.mNativePtr,
postProcessPtr, decoder.mDesiredWidth,
- decoder.mDesiredHeight, decoder.mCropRect,
+ decoder.mDesiredHeight, srcDensity,
+ src.computeDstDensity(), decoder.mCropRect,
decoder.mInputStream, decoder.mAssetFd);
// d has taken ownership of these objects.
decoder.mInputStream = null;
@@ -746,13 +832,15 @@
}
Bitmap bm = decoder.decodeBitmap();
- Resources res = src.getResources();
- if (res == null) {
- bm.setDensity(Bitmap.DENSITY_NONE);
- }
+ bm.setDensity(srcDensity);
+ Resources res = src.getResources();
byte[] np = bm.getNinePatchChunk();
if (np != null && NinePatch.isNinePatchChunk(np)) {
+ if (res != null) {
+ bm.setDensity(res.getDisplayMetrics().densityDpi);
+ }
+
Rect opticalInsets = new Rect();
bm.getOpticalInsets(opticalInsets);
Rect padding = new Rect();
@@ -799,10 +887,48 @@
}
}
- return decoder.decodeBitmap();
+ // this call potentially manipulates the decoder so it must be performed prior to
+ // decoding the bitmap
+ final int srcDensity = computeDensity(src, decoder);
+ Bitmap bm = decoder.decodeBitmap();
+ bm.setDensity(srcDensity);
+ return bm;
}
}
+ // This method may modify the decoder so it must be called prior to performing the decode
+ private static int computeDensity(@NonNull Source src, @NonNull ImageDecoder decoder) {
+ // if the caller changed the size then we treat the density as unknown
+ if (decoder.requestedResize()) {
+ return Bitmap.DENSITY_NONE;
+ }
+
+ // Special stuff for compatibility mode: if the target density is not
+ // the same as the display density, but the resource -is- the same as
+ // the display density, then don't scale it down to the target density.
+ // This allows us to load the system's density-correct resources into
+ // an application in compatibility mode, without scaling those down
+ // to the compatibility density only to have them scaled back up when
+ // drawn to the screen.
+ Resources res = src.getResources();
+ final int srcDensity = src.getDensity();
+ if (res != null && res.getDisplayMetrics().noncompatDensityDpi == srcDensity) {
+ return srcDensity;
+ }
+
+ // downscale the bitmap if the asset has a higher density than the default
+ final int dstDensity = src.computeDstDensity();
+ if (srcDensity != Bitmap.DENSITY_NONE && srcDensity > dstDensity) {
+ float scale = (float) dstDensity / srcDensity;
+ int scaledWidth = (int) (decoder.mWidth * scale + 0.5f);
+ int scaledHeight = (int) (decoder.mHeight * scale + 0.5f);
+ decoder.resize(scaledWidth, scaledHeight);
+ return dstDensity;
+ }
+
+ return srcDensity;
+ }
+
private String getMimeType() {
return nGetMimeType(mNativePtr);
}
diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
index ce3bd9a..da170c0 100644
--- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
@@ -20,12 +20,14 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.res.AssetFileDescriptor;
+import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.ImageDecoder;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.SystemClock;
+import android.util.DisplayMetrics;
import libcore.io.IoUtils;
import libcore.util.NativeAllocationRegistry;
@@ -59,22 +61,31 @@
* decoder is only non-null if it has a PostProcess
*/
public AnimatedImageDrawable(long nativeImageDecoder,
- @Nullable ImageDecoder decoder, int width, int height, Rect cropRect,
+ @Nullable ImageDecoder decoder, int width, int height,
+ int srcDensity, int dstDensity, Rect cropRect,
InputStream inputStream, AssetFileDescriptor afd)
throws IOException {
- mNativePtr = nCreate(nativeImageDecoder, decoder, width, height, cropRect);
- mInputStream = inputStream;
- mAssetFd = afd;
+ width = Bitmap.scaleFromDensity(width, srcDensity, dstDensity);
+ height = Bitmap.scaleFromDensity(height, srcDensity, dstDensity);
if (cropRect == null) {
mIntrinsicWidth = width;
mIntrinsicHeight = height;
} else {
+ cropRect.set(Bitmap.scaleFromDensity(cropRect.left, srcDensity, dstDensity),
+ Bitmap.scaleFromDensity(cropRect.top, srcDensity, dstDensity),
+ Bitmap.scaleFromDensity(cropRect.right, srcDensity, dstDensity),
+ Bitmap.scaleFromDensity(cropRect.bottom, srcDensity, dstDensity));
mIntrinsicWidth = cropRect.width();
mIntrinsicHeight = cropRect.height();
}
- long nativeSize = nNativeByteSize(mNativePtr);
+ mNativePtr = nCreate(nativeImageDecoder, decoder, width, height, cropRect);
+ mInputStream = inputStream;
+ mAssetFd = afd;
+
+ // FIXME: Use the right size for the native allocation.
+ long nativeSize = 200;
NativeAllocationRegistry registry = new NativeAllocationRegistry(
AnimatedImageDrawable.class.getClassLoader(), nGetNativeFinalizer(), nativeSize);
registry.registerNativeAllocation(this, mNativePtr);
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index e3740e3..f74c39d 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -27,6 +27,7 @@
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
+import android.graphics.ImageDecoder;
import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.Outline;
@@ -49,6 +50,7 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -111,7 +113,7 @@
*/
@Deprecated
public BitmapDrawable() {
- mBitmapState = new BitmapState((Bitmap) null);
+ init(new BitmapState((Bitmap) null), null);
}
/**
@@ -124,8 +126,7 @@
@SuppressWarnings("unused")
@Deprecated
public BitmapDrawable(Resources res) {
- mBitmapState = new BitmapState((Bitmap) null);
- mBitmapState.mTargetDensity = mTargetDensity;
+ init(new BitmapState((Bitmap) null), res);
}
/**
@@ -135,7 +136,7 @@
*/
@Deprecated
public BitmapDrawable(Bitmap bitmap) {
- this(new BitmapState(bitmap), null);
+ init(new BitmapState(bitmap), null);
}
/**
@@ -143,8 +144,7 @@
* the display metrics of the resources.
*/
public BitmapDrawable(Resources res, Bitmap bitmap) {
- this(new BitmapState(bitmap), res);
- mBitmapState.mTargetDensity = mTargetDensity;
+ init(new BitmapState(bitmap), res);
}
/**
@@ -154,10 +154,7 @@
*/
@Deprecated
public BitmapDrawable(String filepath) {
- this(new BitmapState(BitmapFactory.decodeFile(filepath)), null);
- if (mBitmapState.mBitmap == null) {
- android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath);
- }
+ this(null, filepath);
}
/**
@@ -165,10 +162,21 @@
*/
@SuppressWarnings("unused")
public BitmapDrawable(Resources res, String filepath) {
- this(new BitmapState(BitmapFactory.decodeFile(filepath)), null);
- mBitmapState.mTargetDensity = mTargetDensity;
- if (mBitmapState.mBitmap == null) {
- android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath);
+ Bitmap bitmap = null;
+ try (FileInputStream stream = new FileInputStream(filepath)) {
+ bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(res, stream),
+ (info, decoder) -> {
+ decoder.setAllocator(ImageDecoder.SOFTWARE_ALLOCATOR);
+ });
+ } catch (Exception e) {
+ /* do nothing. This matches the behavior of BitmapFactory.decodeFile()
+ If the exception happened on decode, mBitmapState.mBitmap will be null.
+ */
+ } finally {
+ init(new BitmapState(bitmap), res);
+ if (mBitmapState.mBitmap == null) {
+ android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath);
+ }
}
}
@@ -179,10 +187,7 @@
*/
@Deprecated
public BitmapDrawable(java.io.InputStream is) {
- this(new BitmapState(BitmapFactory.decodeStream(is)), null);
- if (mBitmapState.mBitmap == null) {
- android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is);
- }
+ this(null, is);
}
/**
@@ -190,10 +195,21 @@
*/
@SuppressWarnings("unused")
public BitmapDrawable(Resources res, java.io.InputStream is) {
- this(new BitmapState(BitmapFactory.decodeStream(is)), null);
- mBitmapState.mTargetDensity = mTargetDensity;
- if (mBitmapState.mBitmap == null) {
- android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is);
+ Bitmap bitmap = null;
+ try {
+ bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(res, is),
+ (info, decoder) -> {
+ decoder.setAllocator(ImageDecoder.SOFTWARE_ALLOCATOR);
+ });
+ } catch (Exception e) {
+ /* do nothing. This matches the behavior of BitmapFactory.decodeStream()
+ If the exception happened on decode, mBitmapState.mBitmap will be null.
+ */
+ } finally {
+ init(new BitmapState(bitmap), res);
+ if (mBitmapState.mBitmap == null) {
+ android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is);
+ }
}
}
@@ -812,9 +828,19 @@
}
}
+ int density = Bitmap.DENSITY_NONE;
+ if (value.density == TypedValue.DENSITY_DEFAULT) {
+ density = DisplayMetrics.DENSITY_DEFAULT;
+ } else if (value.density != TypedValue.DENSITY_NONE) {
+ density = value.density;
+ }
+
Bitmap bitmap = null;
try (InputStream is = r.openRawResource(srcResId, value)) {
- bitmap = BitmapFactory.decodeResourceStream(r, value, is, null, null);
+ ImageDecoder.Source source = ImageDecoder.createSource(r, is, density);
+ bitmap = ImageDecoder.decodeBitmap(source, (info, decoder) -> {
+ decoder.setAllocator(ImageDecoder.SOFTWARE_ALLOCATOR);
+ });
} catch (Exception e) {
// Do nothing and pick up the error below.
}
@@ -1013,14 +1039,21 @@
}
}
+ private BitmapDrawable(BitmapState state, Resources res) {
+ init(state, res);
+ }
+
/**
- * The one constructor to rule them all. This is called by all public
+ * The one helper to rule them all. This is called by all public & private
* constructors to set the state and initialize local properties.
*/
- private BitmapDrawable(BitmapState state, Resources res) {
+ private void init(BitmapState state, Resources res) {
mBitmapState = state;
-
updateLocalState(res);
+
+ if (mBitmapState != null && res != null) {
+ mBitmapState.mTargetDensity = mTargetDensity;
+ }
}
/**
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index f17cd76..291b0a0 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -37,6 +37,7 @@
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
+import android.graphics.ImageDecoder;
import android.graphics.Insets;
import android.graphics.NinePatch;
import android.graphics.Outline;
@@ -50,11 +51,13 @@
import android.os.Trace;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
+import android.util.Log;
import android.util.StateSet;
import android.util.TypedValue;
import android.util.Xml;
import android.view.View;
+import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
@@ -1175,6 +1178,10 @@
return null;
}
+ if (opts == null) {
+ return getBitmapDrawable(res, value, is);
+ }
+
/* ugh. The decodeStream contract is that we have already allocated
the pad rect, but if the bitmap does not had a ninepatch chunk,
then the pad will be ignored. If we could change this to lazily
@@ -1207,6 +1214,33 @@
return null;
}
+ private static Drawable getBitmapDrawable(Resources res, TypedValue value, InputStream is) {
+ try {
+ ImageDecoder.Source source = null;
+ if (value != null) {
+ int density = Bitmap.DENSITY_NONE;
+ if (value.density == TypedValue.DENSITY_DEFAULT) {
+ density = DisplayMetrics.DENSITY_DEFAULT;
+ } else if (value.density != TypedValue.DENSITY_NONE) {
+ density = value.density;
+ }
+ source = ImageDecoder.createSource(res, is, density);
+ } else {
+ source = ImageDecoder.createSource(res, is);
+ }
+
+ return ImageDecoder.decodeDrawable(source, (info, decoder) -> {
+ decoder.setAllocator(ImageDecoder.SOFTWARE_ALLOCATOR);
+ });
+ } catch (IOException e) {
+ /* do nothing.
+ If the exception happened on decode, the drawable will be null.
+ */
+ Log.e("Drawable", "Unable to decode stream: " + e);
+ }
+ return null;
+ }
+
/**
* Create a drawable from an XML document. For more information on how to
* create resources in XML, see
@@ -1306,11 +1340,10 @@
}
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, pathName);
- try {
- Bitmap bm = BitmapFactory.decodeFile(pathName);
- if (bm != null) {
- return drawableFromBitmap(null, bm, null, null, null, pathName);
- }
+ try (FileInputStream stream = new FileInputStream(pathName)) {
+ return getBitmapDrawable(null, null, stream);
+ } catch(IOException e) {
+ // Do nothing; we will just return null if the FileInputStream had an error
} finally {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
diff --git a/libs/incident/Android.mk b/libs/incident/Android.mk
index 5f3e407..b63400f 100644
--- a/libs/incident/Android.mk
+++ b/libs/incident/Android.mk
@@ -32,6 +32,7 @@
LOCAL_SRC_FILES := \
../../core/java/android/os/IIncidentManager.aidl \
../../core/java/android/os/IIncidentReportStatusListener.aidl \
+ proto/android/os/header.proto \
src/IncidentReportArgs.cpp
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
diff --git a/libs/incident/include/android/os/IncidentReportArgs.h b/libs/incident/include/android/os/IncidentReportArgs.h
index 2849d58..c56f689 100644
--- a/libs/incident/include/android/os/IncidentReportArgs.h
+++ b/libs/incident/include/android/os/IncidentReportArgs.h
@@ -24,6 +24,8 @@
#include <set>
#include <vector>
+#include "frameworks/base/libs/incident/proto/android/os/header.pb.h"
+
namespace android {
namespace os {
@@ -47,7 +49,7 @@
void setAll(bool all);
void setDest(int dest);
void addSection(int section);
- void addHeader(const vector<uint8_t>& header);
+ void addHeader(const IncidentHeaderProto& headerProto);
inline bool all() const { return mAll; }
bool containsSection(int section) const;
diff --git a/libs/incident/proto/android/os/header.proto b/libs/incident/proto/android/os/header.proto
new file mode 100644
index 0000000..a84dc48
--- /dev/null
+++ b/libs/incident/proto/android/os/header.proto
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+option java_multiple_files = true;
+
+package android.os;
+
+// IncidentHeaderProto contains extra information the caller of incidentd wants
+// to attach in an incident report, the data should just be informative.
+message IncidentHeaderProto {
+ // From statsd config, the id of the anomaly alert, unique among alerts.
+ optional int64 alert_id = 1;
+
+ // Format a human readable reason why an incident report is requested.
+ // It's optional and may directly come from a user input clicking the
+ // bug-report button.
+ optional string reason = 2;
+
+ // Defines which stats config used to fire the request, incident report will
+ // only be uploaded if this value is given.
+ message StatsdConfigKey {
+ optional int32 uid = 1; // The uid pushes the config to statsd.
+ optional int64 id = 2; // The unique id of the statsd config.
+ }
+ optional StatsdConfigKey config_key = 3;
+}
diff --git a/libs/incident/src/IncidentReportArgs.cpp b/libs/incident/src/IncidentReportArgs.cpp
index bd9c8ee..fbc21e5 100644
--- a/libs/incident/src/IncidentReportArgs.cpp
+++ b/libs/incident/src/IncidentReportArgs.cpp
@@ -161,8 +161,14 @@
}
void
-IncidentReportArgs::addHeader(const vector<uint8_t>& header)
+IncidentReportArgs::addHeader(const IncidentHeaderProto& headerProto)
{
+ vector<uint8_t> header;
+ auto serialized = headerProto.SerializeAsString();
+ if (serialized.empty()) return;
+ for (auto it = serialized.begin(); it != serialized.end(); it++) {
+ header.push_back((uint8_t)*it);
+ }
mHeaders.push_back(header);
}
diff --git a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
index 833376c..603926f 100644
--- a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
+++ b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
@@ -19,10 +19,12 @@
import android.os.SystemClock;
import android.util.Base64;
+import android.util.Log;
import android.util.TimeUtils;
import java.util.Arrays;
+import com.android.internal.app.IBatteryStats;
import com.android.internal.location.nano.GnssLogsProto.GnssLog;
/**
@@ -31,14 +33,29 @@
*/
public class GnssMetrics {
+ private static final String TAG = GnssMetrics.class.getSimpleName();
+
+ /* Constant which indicates GPS signal quality is poor */
+ public static final int GPS_SIGNAL_QUALITY_POOR = 0;
+
+ /* Constant which indicates GPS signal quality is good */
+ public static final int GPS_SIGNAL_QUALITY_GOOD = 1;
+
+ /* Number of GPS signal quality levels */
+ public static final int NUM_GPS_SIGNAL_QUALITY_LEVELS = GPS_SIGNAL_QUALITY_GOOD + 1;
+
/** Default time between location fixes (in millisecs) */
private static final int DEFAULT_TIME_BETWEEN_FIXES_MILLISECS = 1000;
/* The time since boot when logging started */
private String logStartInElapsedRealTime;
+ /* GNSS power metrics */
+ private GnssPowerMetrics mGnssPowerMetrics;
+
/** Constructor */
- public GnssMetrics() {
+ public GnssMetrics(IBatteryStats stats) {
+ mGnssPowerMetrics = new GnssPowerMetrics(stats);
locationFailureStatistics = new Statistics();
timeToFirstFixSecStatistics = new Statistics();
positionAccuracyMeterStatistics = new Statistics();
@@ -103,11 +120,18 @@
*
*/
public void logCn0(float[] cn0s, int numSv) {
- if (numSv < 4) {
+ if (numSv == 0 || cn0s == null || cn0s.length == 0 || cn0s.length < numSv) {
+ if (numSv == 0) {
+ mGnssPowerMetrics.reportSignalQuality(null, 0);
+ }
return;
}
float[] cn0Array = Arrays.copyOf(cn0s, numSv);
Arrays.sort(cn0Array);
+ mGnssPowerMetrics.reportSignalQuality(cn0Array, numSv);
+ if (numSv < 4) {
+ return;
+ }
if (cn0Array[numSv - 4] > 0.0) {
double top4AvgCn0 = 0.0;
for (int i = numSv - 4; i < numSv; i++) {
@@ -265,4 +289,62 @@
topFourAverageCn0Statistics.reset();
return;
}
+
+ /* Class for handling GNSS power related metrics */
+ private class GnssPowerMetrics {
+
+ /* Threshold for Top Four Average CN0 below which GNSS signal quality is declared poor */
+ private static final double POOR_TOP_FOUR_AVG_CN0_THRESHOLD_DB_HZ = 20.0;
+
+ /* Minimum change in Top Four Average CN0 needed to trigger a report */
+ private static final double REPORTING_THRESHOLD_DB_HZ = 1.0;
+
+ /* BatteryStats API */
+ private final IBatteryStats mBatteryStats;
+
+ /* Last reported Top Four Average CN0 */
+ private double mLastAverageCn0;
+
+ public GnssPowerMetrics(IBatteryStats stats) {
+ mBatteryStats = stats;
+ // Used to initialize the variable to a very small value (unachievable in practice) so that
+ // the first CNO report will trigger an update to BatteryStats
+ mLastAverageCn0 = -100.0;
+ }
+
+ /**
+ * Reports signal quality to BatteryStats. Signal quality is based on Top four average CN0. If
+ * the number of SVs seen is less than 4, then signal quality is the average CN0.
+ * Changes are reported only if the average CN0 changes by more than REPORTING_THRESHOLD_DB_HZ.
+ */
+ public void reportSignalQuality(float[] ascendingCN0Array, int numSv) {
+ double avgCn0 = 0.0;
+ if (numSv > 0) {
+ for (int i = Math.max(0, numSv - 4); i < numSv; i++) {
+ avgCn0 += (double) ascendingCN0Array[i];
+ }
+ avgCn0 /= Math.min(numSv, 4);
+ }
+ if (Math.abs(avgCn0 - mLastAverageCn0) < REPORTING_THRESHOLD_DB_HZ) {
+ return;
+ }
+ try {
+ mBatteryStats.noteGpsSignalQuality(getSignalLevel(avgCn0));
+ mLastAverageCn0 = avgCn0;
+ } catch (Exception e) {
+ Log.w(TAG, "Exception", e);
+ }
+ return;
+ }
+
+ /**
+ * Obtains signal level based on CN0
+ */
+ private int getSignalLevel(double cn0) {
+ if (cn0 > POOR_TOP_FOUR_AVG_CN0_THRESHOLD_DB_HZ) {
+ return GnssMetrics.GPS_SIGNAL_QUALITY_GOOD;
+ }
+ return GnssMetrics.GPS_SIGNAL_QUALITY_POOR;
+ }
+ }
}
\ No newline at end of file
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 93fc3da..b07d042 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -238,22 +238,15 @@
public static final int ENCODING_DTS = 7;
/** Audio data format: DTS HD compressed */
public static final int ENCODING_DTS_HD = 8;
- /** Audio data format: MP3 compressed
- * @hide
- * */
+ /** Audio data format: MP3 compressed */
public static final int ENCODING_MP3 = 9;
- /** Audio data format: AAC LC compressed
- * @hide
- * */
+ /** Audio data format: AAC LC compressed */
public static final int ENCODING_AAC_LC = 10;
- /** Audio data format: AAC HE V1 compressed
- * @hide
- * */
+ /** Audio data format: AAC HE V1 compressed */
public static final int ENCODING_AAC_HE_V1 = 11;
- /** Audio data format: AAC HE V2 compressed
- * @hide
- * */
+ /** Audio data format: AAC HE V2 compressed */
public static final int ENCODING_AAC_HE_V2 = 12;
+
/** Audio data format: compressed audio wrapped in PCM for HDMI
* or S/PDIF passthrough.
* IEC61937 uses a stereo stream of 16-bit samples as the wrapper.
@@ -266,6 +259,12 @@
/** Audio data format: DOLBY TRUEHD compressed
**/
public static final int ENCODING_DOLBY_TRUEHD = 14;
+ /** Audio data format: AAC ELD compressed */
+ public static final int ENCODING_AAC_ELD = 15;
+ /** Audio data format: AAC xHE compressed */
+ public static final int ENCODING_AAC_XHE = 16;
+ /** Audio data format: AC-4 sync frame transport format */
+ public static final int ENCODING_AC4 = 17;
/** @hide */
public static String toLogFriendlyEncoding(int enc) {
@@ -298,6 +297,12 @@
return "ENCODING_IEC61937";
case ENCODING_DOLBY_TRUEHD:
return "ENCODING_DOLBY_TRUEHD";
+ case ENCODING_AAC_ELD:
+ return "ENCODING_AAC_ELD";
+ case ENCODING_AAC_XHE:
+ return "ENCODING_AAC_XHE";
+ case ENCODING_AC4:
+ return "ENCODING_AC4";
default :
return "invalid encoding " + enc;
}
@@ -514,6 +519,9 @@
case ENCODING_AAC_HE_V1:
case ENCODING_AAC_HE_V2:
case ENCODING_IEC61937:
+ case ENCODING_AAC_ELD:
+ case ENCODING_AAC_XHE:
+ case ENCODING_AC4:
return true;
default:
return false;
@@ -532,6 +540,13 @@
case ENCODING_DTS:
case ENCODING_DTS_HD:
case ENCODING_IEC61937:
+ case ENCODING_MP3:
+ case ENCODING_AAC_LC:
+ case ENCODING_AAC_HE_V1:
+ case ENCODING_AAC_HE_V2:
+ case ENCODING_AAC_ELD:
+ case ENCODING_AAC_XHE:
+ case ENCODING_AC4:
return true;
default:
return false;
@@ -556,6 +571,9 @@
case ENCODING_AAC_HE_V1:
case ENCODING_AAC_HE_V2:
case ENCODING_IEC61937: // wrapped in PCM but compressed
+ case ENCODING_AAC_ELD:
+ case ENCODING_AAC_XHE:
+ case ENCODING_AC4:
return false;
case ENCODING_INVALID:
default:
@@ -581,6 +599,9 @@
case ENCODING_AAC_LC:
case ENCODING_AAC_HE_V1:
case ENCODING_AAC_HE_V2:
+ case ENCODING_AAC_ELD:
+ case ENCODING_AAC_XHE:
+ case ENCODING_AC4:
return false;
case ENCODING_INVALID:
default:
@@ -794,14 +815,7 @@
/**
* Sets the data encoding format.
- * @param encoding one of {@link AudioFormat#ENCODING_DEFAULT},
- * {@link AudioFormat#ENCODING_PCM_8BIT},
- * {@link AudioFormat#ENCODING_PCM_16BIT},
- * {@link AudioFormat#ENCODING_PCM_FLOAT},
- * {@link AudioFormat#ENCODING_AC3},
- * {@link AudioFormat#ENCODING_E_AC3}.
- * {@link AudioFormat#ENCODING_DTS},
- * {@link AudioFormat#ENCODING_DTS_HD}.
+ * @param encoding the specified encoding or default.
* @return the same Builder instance.
* @throws java.lang.IllegalArgumentException
*/
@@ -818,6 +832,13 @@
case ENCODING_DTS:
case ENCODING_DTS_HD:
case ENCODING_IEC61937:
+ case ENCODING_MP3:
+ case ENCODING_AAC_LC:
+ case ENCODING_AAC_HE_V1:
+ case ENCODING_AAC_HE_V2:
+ case ENCODING_AAC_ELD:
+ case ENCODING_AAC_XHE:
+ case ENCODING_AC4:
mEncoding = encoding;
break;
case ENCODING_INVALID:
@@ -1016,7 +1037,7 @@
}
/** @hide */
- @IntDef({
+ @IntDef(flag = false, prefix = "ENCODING", value = {
ENCODING_DEFAULT,
ENCODING_PCM_8BIT,
ENCODING_PCM_16BIT,
@@ -1025,8 +1046,14 @@
ENCODING_E_AC3,
ENCODING_DTS,
ENCODING_DTS_HD,
- ENCODING_IEC61937
- })
+ ENCODING_IEC61937,
+ ENCODING_AAC_HE_V1,
+ ENCODING_AAC_HE_V2,
+ ENCODING_AAC_LC,
+ ENCODING_AAC_ELD,
+ ENCODING_AAC_XHE,
+ ENCODING_AC4 }
+ )
@Retention(RetentionPolicy.SOURCE)
public @interface Encoding {}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 913b5e8..2ac4063 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1329,6 +1329,19 @@
}
//====================================================================
+ // Offload query
+ /**
+ * Returns whether offloaded playback of an audio format is supported on the device.
+ * Offloaded playback is where the decoding of an audio stream is not competing with other
+ * software resources. In general, it is supported by dedicated hardware, such as audio DSPs.
+ * @param format the audio format (codec, sample rate, channels) being checked.
+ * @return true if the given audio format can be offloaded.
+ */
+ public boolean isOffloadedPlaybackSupported(@NonNull AudioFormat format) {
+ return AudioSystem.isOffloadSupported(format);
+ }
+
+ //====================================================================
// Bluetooth SCO control
/**
* Sticky broadcast intent action indicating that the Bluetooth SCO audio
@@ -3746,6 +3759,33 @@
}
/**
+ * Indicate A2DP source or sink connection state change and eventually suppress
+ * the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent.
+ * @param device Bluetooth device connected/disconnected
+ * @param state new connection state (BluetoothProfile.STATE_xxx)
+ * @param profile profile for the A2DP device
+ * (either {@link android.bluetooth.BluetoothProfile.A2DP} or
+ * {@link android.bluetooth.BluetoothProfile.A2DP_SINK})
+ * @param suppressNoisyIntent if true the
+ * {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be sent.
+ * @return a delay in ms that the caller should wait before broadcasting
+ * BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED intent.
+ * {@hide}
+ */
+ public int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
+ BluetoothDevice device, int state, int profile, boolean suppressNoisyIntent) {
+ final IAudioService service = getService();
+ int delay = 0;
+ try {
+ delay = service.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(device,
+ state, profile, suppressNoisyIntent);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ return delay;
+ }
+
+ /**
* Indicate A2DP device configuration has changed.
* @param device Bluetooth device whose configuration has changed.
* {@hide}
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index e56944d..b4316ba 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -16,6 +16,7 @@
package android.media;
+import android.annotation.NonNull;
import android.content.Context;
import android.content.pm.PackageManager;
import android.media.audiopolicy.AudioMix;
@@ -818,6 +819,14 @@
public static native float getStreamVolumeDB(int stream, int index, int device);
+ static boolean isOffloadSupported(@NonNull AudioFormat format) {
+ return native_is_offload_supported(format.getEncoding(), format.getSampleRate(),
+ format.getChannelMask(), format.getChannelIndexMask());
+ }
+
+ private static native boolean native_is_offload_supported(int encoding, int sampleRate,
+ int channelMask, int channelIndexMask);
+
// Items shared with audio service
/**
@@ -914,7 +923,8 @@
(1 << STREAM_MUSIC) |
(1 << STREAM_RING) |
(1 << STREAM_NOTIFICATION) |
- (1 << STREAM_SYSTEM);
+ (1 << STREAM_SYSTEM) |
+ (1 << STREAM_VOICE_CALL);
/**
* Event posted by AudioTrack and AudioRecord JNI (JNIDeviceCallback) when routing changes.
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index e535fdf..5928d03 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -24,7 +24,9 @@
import java.nio.ByteOrder;
import java.nio.NioUtils;
import java.util.Collection;
+import java.util.concurrent.Executor;
+import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -185,6 +187,22 @@
* Event id denotes when previously set update period has elapsed during playback.
*/
private static final int NATIVE_EVENT_NEW_POS = 4;
+ /**
+ * Callback for more data
+ * TODO only for offload
+ */
+ private static final int NATIVE_EVENT_MORE_DATA = 0;
+ /**
+ * IAudioTrack tear down for offloaded tracks
+ * TODO: when received, java AudioTrack must be released
+ */
+ private static final int NATIVE_EVENT_NEW_IAUDIOTRACK = 6;
+ /**
+ * Event id denotes when all the buffers queued in AF and HW are played
+ * back (after stop is called) for an offloaded track.
+ * TODO: not just for offload
+ */
+ private static final int NATIVE_EVENT_STREAM_END = 7;
private final static String TAG = "android.media.AudioTrack";
@@ -540,6 +558,12 @@
public AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
int mode, int sessionId)
throws IllegalArgumentException {
+ this(attributes, format, bufferSizeInBytes, mode, sessionId, false /*offload*/);
+ }
+
+ private AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
+ int mode, int sessionId, boolean offload)
+ throws IllegalArgumentException {
super(attributes, AudioPlaybackConfiguration.PLAYER_TYPE_JAM_AUDIOTRACK);
// mState already == STATE_UNINITIALIZED
@@ -601,7 +625,8 @@
// native initialization
int initResult = native_setup(new WeakReference<AudioTrack>(this), mAttributes,
sampleRate, mChannelMask, mChannelIndexMask, mAudioFormat,
- mNativeBufferSizeInBytes, mDataLoadMode, session, 0 /*nativeTrackInJavaObj*/);
+ mNativeBufferSizeInBytes, mDataLoadMode, session, 0 /*nativeTrackInJavaObj*/,
+ offload);
if (initResult != SUCCESS) {
loge("Error code "+initResult+" when initializing AudioTrack.");
return; // with mState == STATE_UNINITIALIZED
@@ -681,7 +706,8 @@
0 /*mNativeBufferSizeInBytes - NA*/,
0 /*mDataLoadMode - NA*/,
session,
- nativeTrackInJavaObj);
+ nativeTrackInJavaObj,
+ false /*offload*/);
if (initResult != SUCCESS) {
loge("Error code "+initResult+" when initializing AudioTrack.");
return; // with mState == STATE_UNINITIALIZED
@@ -729,6 +755,7 @@
* <code>MODE_STREAM</code> will be used.
* <br>If the session ID is not specified with {@link #setSessionId(int)}, a new one will
* be generated.
+ * <br>Offload is false by default.
*/
public static class Builder {
private AudioAttributes mAttributes;
@@ -737,6 +764,7 @@
private int mSessionId = AudioManager.AUDIO_SESSION_ID_GENERATE;
private int mMode = MODE_STREAM;
private int mPerformanceMode = PERFORMANCE_MODE_NONE;
+ private boolean mOffload = false;
/**
* Constructs a new Builder with the default values as described above.
@@ -867,6 +895,21 @@
}
/**
+ * Sets whether this track will play through the offloaded audio path.
+ * When set to true, at build time, the audio format will be checked against
+ * {@link AudioManager#isOffloadedPlaybackSupported(AudioFormat)} to verify the audio format
+ * used by this track is supported on the device's offload path (if any).
+ * <br>Offload is only supported for media audio streams, and therefore requires that
+ * the usage be {@link AudioAttributes#USAGE_MEDIA}.
+ * @param offload true to require the offload path for playback.
+ * @return the same Builder instance.
+ */
+ public @NonNull Builder setOffloadedPlayback(boolean offload) {
+ mOffload = offload;
+ return this;
+ }
+
+ /**
* Builds an {@link AudioTrack} instance initialized with all the parameters set
* on this <code>Builder</code>.
* @return a new successfully initialized {@link AudioTrack} instance.
@@ -909,6 +952,19 @@
.setEncoding(AudioFormat.ENCODING_DEFAULT)
.build();
}
+
+ //TODO tie offload to PERFORMANCE_MODE_POWER_SAVING?
+ if (mOffload) {
+ if (mAttributes.getUsage() != AudioAttributes.USAGE_MEDIA) {
+ throw new UnsupportedOperationException(
+ "Cannot create AudioTrack, offload requires USAGE_MEDIA");
+ }
+ if (!AudioSystem.isOffloadSupported(mFormat)) {
+ throw new UnsupportedOperationException(
+ "Cannot create AudioTrack, offload format not supported");
+ }
+ }
+
try {
// If the buffer size is not specified in streaming mode,
// use a single frame for the buffer size and let the
@@ -918,7 +974,7 @@
* mFormat.getBytesPerSample(mFormat.getEncoding());
}
final AudioTrack track = new AudioTrack(
- mAttributes, mFormat, mBufferSizeInBytes, mMode, mSessionId);
+ mAttributes, mFormat, mBufferSizeInBytes, mMode, mSessionId, mOffload);
if (track.getState() == STATE_UNINITIALIZED) {
// release is not necessary
throw new UnsupportedOperationException("Cannot create AudioTrack");
@@ -2882,6 +2938,69 @@
void onPeriodicNotification(AudioTrack track);
}
+ /**
+ * Abstract class to receive event notification about the stream playback.
+ * See {@link AudioTrack#setStreamEventCallback(Executor, StreamEventCallback)} to register
+ * the callback on the given {@link AudioTrack} instance.
+ */
+ public abstract static class StreamEventCallback {
+ /** @hide */ // add hidden empty constructor so it doesn't show in SDK
+ public StreamEventCallback() { }
+ /**
+ * Called when an offloaded track is no longer valid and has been discarded by the system.
+ * An example of this happening is when an offloaded track has been paused too long, and
+ * gets invalidated by the system to prevent any other offload.
+ * @param track the {@link AudioTrack} on which the event happened
+ */
+ public void onTearDown(AudioTrack track) { }
+ /**
+ * Called when all the buffers of an offloaded track that were queued in the audio system
+ * (e.g. the combination of the Android audio framework and the device's audio hardware)
+ * have been played after {@link AudioTrack#stop()} has been called.
+ * @param track the {@link AudioTrack} on which the event happened
+ */
+ public void onStreamPresentationEnd(AudioTrack track) { }
+ /**
+ * Called when more audio data can be written without blocking on an offloaded track.
+ * @param track the {@link AudioTrack} on which the event happened
+ */
+ public void onStreamDataRequest(AudioTrack track) { }
+ }
+
+ private Executor mStreamEventExec;
+ private StreamEventCallback mStreamEventCb;
+ private final Object mStreamEventCbLock = new Object();
+
+ /**
+ * Sets the callback for the notification of stream events.
+ * @param executor {@link Executor} to handle the callbacks
+ * @param eventCallback the callback to receive the stream event notifications
+ */
+ public void setStreamEventCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull StreamEventCallback eventCallback) {
+ if (eventCallback == null) {
+ throw new IllegalArgumentException("Illegal null StreamEventCallback");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Illegal null Executor for the StreamEventCallback");
+ }
+ synchronized (mStreamEventCbLock) {
+ mStreamEventExec = executor;
+ mStreamEventCb = eventCallback;
+ }
+ }
+
+ /**
+ * Unregisters the callback for notification of stream events, previously set
+ * by {@link #setStreamEventCallback(Executor, StreamEventCallback)}.
+ */
+ public void removeStreamEventCallback() {
+ synchronized (mStreamEventCbLock) {
+ mStreamEventExec = null;
+ mStreamEventCb = null;
+ }
+ }
+
//---------------------------------------------------------
// Inner classes
//--------------------
@@ -2965,7 +3084,7 @@
private static void postEventFromNative(Object audiotrack_ref,
int what, int arg1, int arg2, Object obj) {
//logd("Event posted from the native side: event="+ what + " args="+ arg1+" "+arg2);
- AudioTrack track = (AudioTrack)((WeakReference)audiotrack_ref).get();
+ final AudioTrack track = (AudioTrack)((WeakReference)audiotrack_ref).get();
if (track == null) {
return;
}
@@ -2974,6 +3093,32 @@
track.broadcastRoutingChange();
return;
}
+
+ if (what == NATIVE_EVENT_MORE_DATA || what == NATIVE_EVENT_NEW_IAUDIOTRACK
+ || what == NATIVE_EVENT_STREAM_END) {
+ final Executor exec;
+ final StreamEventCallback cb;
+ synchronized (track.mStreamEventCbLock) {
+ exec = track.mStreamEventExec;
+ cb = track.mStreamEventCb;
+ }
+ if ((exec == null) || (cb == null)) {
+ return;
+ }
+ switch (what) {
+ case NATIVE_EVENT_MORE_DATA:
+ exec.execute(() -> cb.onStreamDataRequest(track));
+ return;
+ case NATIVE_EVENT_NEW_IAUDIOTRACK:
+ // TODO also release track as it's not longer usable
+ exec.execute(() -> cb.onTearDown(track));
+ return;
+ case NATIVE_EVENT_STREAM_END:
+ exec.execute(() -> cb.onStreamPresentationEnd(track));
+ return;
+ }
+ }
+
NativePositionEventHandlerDelegate delegate = track.mEventHandlerDelegate;
if (delegate != null) {
Handler handler = delegate.getHandler();
@@ -2995,7 +3140,8 @@
private native final int native_setup(Object /*WeakReference<AudioTrack>*/ audiotrack_this,
Object /*AudioAttributes*/ attributes,
int[] sampleRate, int channelMask, int channelIndexMask, int audioFormat,
- int buffSizeInBytes, int mode, int[] sessionId, long nativeAudioTrack);
+ int buffSizeInBytes, int mode, int[] sessionId, long nativeAudioTrack,
+ boolean offload);
private native final void native_finalize();
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index bb6ae98..6c65223 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -203,5 +203,8 @@
oneway void playerHasOpPlayAudio(in int piid, in boolean hasOpPlayAudio);
+ int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(in BluetoothDevice device,
+ int state, int profile, boolean suppressNoisyIntent);
+
// WARNING: read warning at top of file, it is recommended to add new methods at the end
}
diff --git a/packages/ExtServices/AndroidManifest.xml b/packages/ExtServices/AndroidManifest.xml
index 63d3623..45e557c 100644
--- a/packages/ExtServices/AndroidManifest.xml
+++ b/packages/ExtServices/AndroidManifest.xml
@@ -56,6 +56,12 @@
<intent-filter>
<action android:name="android.service.autofill.AutofillFieldClassificationService" />
</intent-filter>
+ <meta-data
+ android:name="android.autofill.field_classification.default_algorithm"
+ android:resource="@string/autofill_field_classification_default_algorithm" />
+ <meta-data
+ android:name="android.autofill.field_classification.available_algorithms"
+ android:resource="@array/autofill_field_classification_available_algorithms" />
</service>
<library android:name="android.ext.services"/>
diff --git a/packages/ExtServices/res/values/strings.xml b/packages/ExtServices/res/values/strings.xml
index a2e65bc..72647ab 100644
--- a/packages/ExtServices/res/values/strings.xml
+++ b/packages/ExtServices/res/values/strings.xml
@@ -19,4 +19,9 @@
<string name="notification_assistant">Notification Assistant</string>
<string name="prompt_block_reason">Too many dismissals:views</string>
+
+ <string name="autofill_field_classification_default_algorithm">EDIT_DISTANCE</string>
+ <string-array name="autofill_field_classification_available_algorithms">
+ <item>EDIT_DISTANCE</item>
+ </string-array>
</resources>
diff --git a/packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java b/packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java
index ea516a1..4709d35 100644
--- a/packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java
+++ b/packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java
@@ -24,28 +24,17 @@
import com.android.internal.util.ArrayUtils;
-import java.util.Arrays;
import java.util.List;
public class AutofillFieldClassificationServiceImpl extends AutofillFieldClassificationService {
private static final String TAG = "AutofillFieldClassificationServiceImpl";
- private static final boolean DEBUG = false;
- private static final List<String> sAvailableAlgorithms = Arrays.asList(EditDistanceScorer.NAME);
-
- @Override
- public List<String> onGetAvailableAlgorithms() {
- return sAvailableAlgorithms;
- }
-
- @Override
- public String onGetDefaultAlgorithm() {
- return EditDistanceScorer.NAME;
- }
+ // TODO(b/70291841): set to false before launching
+ private static final boolean DEBUG = true;
@Nullable
@Override
- public Scores onGetScores(@Nullable String algorithmName,
+ public float[][] onGetScores(@Nullable String algorithmName,
@Nullable Bundle algorithmArgs, @NonNull List<AutofillValue> actualValues,
@NonNull List<String> userDataValues) {
if (ArrayUtils.isEmpty(actualValues) || ArrayUtils.isEmpty(userDataValues)) {
@@ -66,14 +55,13 @@
Log.d(TAG, "getScores() will return a " + actualValuesSize + "x"
+ userDataValuesSize + " matrix for " + actualAlgorithmName);
}
- final Scores scores = new Scores(actualAlgorithmName, actualValuesSize, userDataValuesSize);
- final float[][] scoresMatrix = scores.getScores();
+ final float[][] scores = new float[actualValuesSize][userDataValuesSize];
final EditDistanceScorer algorithm = EditDistanceScorer.getInstance();
for (int i = 0; i < actualValuesSize; i++) {
for (int j = 0; j < userDataValuesSize; j++) {
final float score = algorithm.getScore(actualValues.get(i), userDataValues.get(j));
- scoresMatrix[i][j] = score;
+ scores[i][j] = score;
}
}
return scores;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 9ee205f..2a697b8 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -302,6 +302,7 @@
// fail to boot if there're any backed up settings that don't have a non-null validator
ensureAllBackedUpSystemSettingsHaveValidators();
ensureAllBackedUpGlobalSettingsHaveValidators();
+ ensureAllBackedUpSecureSettingsHaveValidators();
synchronized (mLock) {
mUserManager = UserManager.get(getContext());
@@ -321,19 +322,26 @@
}
private void ensureAllBackedUpSystemSettingsHaveValidators() {
- String offenders = getOffenders(Settings.System.SETTINGS_TO_BACKUP,
- Settings.System.VALIDATORS);
+ String offenders = getOffenders(concat(Settings.System.SETTINGS_TO_BACKUP,
+ Settings.System.LEGACY_RESTORE_SETTINGS), Settings.System.VALIDATORS);
failToBootIfOffendersPresent(offenders, "Settings.System");
}
private void ensureAllBackedUpGlobalSettingsHaveValidators() {
- String offenders = getOffenders(Settings.Global.SETTINGS_TO_BACKUP,
- Settings.Global.VALIDATORS);
+ String offenders = getOffenders(concat(Settings.Global.SETTINGS_TO_BACKUP,
+ Settings.Global.LEGACY_RESTORE_SETTINGS), Settings.Global.VALIDATORS);
failToBootIfOffendersPresent(offenders, "Settings.Global");
}
+ private void ensureAllBackedUpSecureSettingsHaveValidators() {
+ String offenders = getOffenders(concat(Settings.Secure.SETTINGS_TO_BACKUP,
+ Settings.Secure.LEGACY_RESTORE_SETTINGS), Settings.Secure.VALIDATORS);
+
+ failToBootIfOffendersPresent(offenders, "Settings.Secure");
+ }
+
private void failToBootIfOffendersPresent(String offenders, String settingsType) {
if (offenders.length() > 0) {
throw new RuntimeException("All " + settingsType + " settings that are backed up"
@@ -352,6 +360,18 @@
return offenders.toString();
}
+ private final String[] concat(String[] first, String[] second) {
+ if (second == null || second.length == 0) {
+ return first;
+ }
+ final int firstLen = first.length;
+ final int secondLen = second.length;
+ String[] both = new String[firstLen + secondLen];
+ System.arraycopy(first, 0, both, 0, firstLen);
+ System.arraycopy(second, 0, both, firstLen, secondLen);
+ return both;
+ }
+
@Override
public Bundle call(String method, String name, Bundle args) {
final int requestingUserId = getRequestingUserId(args);
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 0f43db0..64b2ae6 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -135,6 +135,9 @@
<uses-permission android:name="android.permission.RESTRICTED_VR_ACCESS" />
<uses-permission android:name="android.permission.MANAGE_BIND_INSTANT_SERVICE" />
<uses-permission android:name="android.permission.SET_HARMFUL_APP_WARNINGS" />
+ <uses-permission android:name="android.permission.MANAGE_SENSORS" />
+ <uses-permission android:name="android.permission.MANAGE_AUDIO_POLICY" />
+ <uses-permission android:name="android.permission.MANAGE_CAMERA" />
<application android:label="@string/app_label"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index aa2cdbb..80ac825 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -121,7 +121,7 @@
<uses-permission android:name="android.permission.TRUST_LISTENER" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.RESET_FINGERPRINT_LOCKOUT" />
- <uses-permission android:name="android.permission.BIND_SLICE" />
+ <uses-permission android:name="android.permission.MANAGE_SLICE_PERMISSIONS" />
<!-- Needed for WallpaperManager.clear in ImageWallpaper.updateWallpaperLocked -->
<uses-permission android:name="android.permission.SET_WALLPAPER"/>
@@ -435,6 +435,16 @@
android:launchMode="singleTop"
androidprv:alwaysFocusable="true" />
+ <!-- started from SliceProvider -->
+ <activity android:name=".SlicePermissionActivity"
+ android:theme="@style/Theme.SystemUI.Dialog.Alert"
+ android:finishOnCloseSystemDialogs="true"
+ android:excludeFromRecents="true">
+ <intent-filter>
+ <action android:name="android.intent.action.REQUEST_SLICE_PERMISSION" />
+ </intent-filter>
+ </activity>
+
<!-- platform logo easter egg activity -->
<activity
android:name=".DessertCase"
@@ -572,6 +582,7 @@
<provider android:name=".keyguard.KeyguardSliceProvider"
android:authorities="com.android.systemui.keyguard"
+ android:grantUriPermissions="true"
android:exported="true">
</provider>
diff --git a/packages/SystemUI/res/color/qs_background_dark.xml b/packages/SystemUI/res/color/qs_background_dark.xml
index c19fa08..24afebd 100644
--- a/packages/SystemUI/res/color/qs_background_dark.xml
+++ b/packages/SystemUI/res/color/qs_background_dark.xml
@@ -15,6 +15,6 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:alpha="0.87"
+ <item android:alpha="1"
android:color="?android:attr/colorBackgroundFloating"/>
</selector>
diff --git a/packages/SystemUI/res/drawable/ic_screenshot_edit.xml b/packages/SystemUI/res/drawable/ic_screenshot_edit.xml
new file mode 100644
index 0000000..d901292
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_screenshot_edit.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3.0,17.25L3.0,21.0l3.75,0.0L17.81,9.94l-3.75,-3.75L3.0,17.25zM20.71,7.04c0.39,-0.3 0.39,-1.02 0.0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0.0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
+ <path
+ android:pathData="M0 0h24v24H0z"
+ android:fillColor="#00000000"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/qs_bg_gradient.xml b/packages/SystemUI/res/drawable/qs_bg_gradient.xml
new file mode 100644
index 0000000..a1ad528
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_bg_gradient.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <gradient
+ android:angle="270"
+ android:startColor="#ff000000"
+ android:endColor="#00000000"
+ android:type="linear" />
+</shape>
diff --git a/packages/SystemUI/res/layout/battery_percentage_view.xml b/packages/SystemUI/res/layout/battery_percentage_view.xml
index 59c0957..e52aa14 100644
--- a/packages/SystemUI/res/layout/battery_percentage_view.xml
+++ b/packages/SystemUI/res/layout/battery_percentage_view.xml
@@ -25,6 +25,6 @@
android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
android:textColor="?android:attr/textColorPrimary"
android:gravity="center_vertical|start"
- android:paddingEnd="@dimen/battery_level_padding_start"
+ android:paddingStart="@dimen/battery_level_padding_start"
android:importantForAccessibility="no"
/>
diff --git a/packages/SystemUI/res/layout/output_chooser.xml b/packages/SystemUI/res/layout/output_chooser.xml
index 3d0ab35..b96f447 100644
--- a/packages/SystemUI/res/layout/output_chooser.xml
+++ b/packages/SystemUI/res/layout/output_chooser.xml
@@ -14,7 +14,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<!-- extends FrameLayout -->
+<!-- extends LinearLayout -->
<com.android.systemui.volume.OutputChooserLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:sysui="http://schemas.android.com/apk/res-auto"
@@ -23,8 +23,17 @@
android:minHeight="320dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:orientation="vertical"
android:padding="20dp" >
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textDirection="locale"
+ android:textAppearance="@style/TextAppearance.QS.DetailHeader"
+ android:layout_marginBottom="20dp" />
+
<com.android.systemui.qs.AutoSizingList
android:id="@android:id/list"
android:layout_width="match_parent"
@@ -42,9 +51,10 @@
android:orientation="vertical">
<TextView
- android:id="@android:id/title"
+ android:id="@+id/empty_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:textDirection="locale"
android:layout_marginTop="20dp"
android:textAppearance="@style/TextAppearance.QS.DetailEmpty"/>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/output_chooser_item.xml b/packages/SystemUI/res/layout/output_chooser_item.xml
index ed7df4b..c3ddbbe 100644
--- a/packages/SystemUI/res/layout/output_chooser_item.xml
+++ b/packages/SystemUI/res/layout/output_chooser_item.xml
@@ -30,6 +30,7 @@
android:layout_height="@dimen/qs_detail_item_icon_size"
android:layout_marginStart="@dimen/qs_detail_item_icon_marginStart"
android:layout_marginEnd="@dimen/qs_detail_item_icon_marginEnd"
+ android:background="?android:selectableItemBackgroundBorderless"
android:tint="?android:attr/textColorPrimary"/>
<LinearLayout
diff --git a/packages/SystemUI/res/layout/qs_customize_panel.xml b/packages/SystemUI/res/layout/qs_customize_panel.xml
index 9ab8ac6..b3b6a0c 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel.xml
@@ -20,6 +20,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="0dp"
+ android:elevation="4dp"
android:orientation="vertical"
android:background="@drawable/qs_customizer_background"
android:gravity="center_horizontal">
diff --git a/packages/SystemUI/res/layout/qs_detail.xml b/packages/SystemUI/res/layout/qs_detail.xml
index 1fd239b..0b9a7b2 100644
--- a/packages/SystemUI/res/layout/qs_detail.xml
+++ b/packages/SystemUI/res/layout/qs_detail.xml
@@ -23,7 +23,8 @@
android:clickable="true"
android:orientation="vertical"
android:paddingBottom="8dp"
- android:visibility="invisible">
+ android:visibility="invisible"
+ android:elevation="4dp" >
<include
android:id="@+id/qs_detail_header"
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index 3d09b74..9f6a946 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -21,6 +21,7 @@
android:id="@+id/qs_footer"
android:layout_width="match_parent"
android:layout_height="48dp"
+ android:elevation="4dp"
android:baselineAligned="false"
android:clickable="false"
android:clipChildren="false"
@@ -105,17 +106,6 @@
android:visibility="invisible"/>
</com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
-
- <com.android.systemui.statusbar.phone.ExpandableIndicator
- android:id="@+id/expand_indicator"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:clipToPadding="false"
- android:clickable="true"
- android:focusable="true"
- android:background="?android:attr/selectableItemBackgroundBorderless"
- android:contentDescription="@string/accessibility_quick_settings_expand"
- android:padding="14dp" />
</LinearLayout>
</com.android.systemui.qs.QSFooterImpl>
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 5541f3d..5bcb7fd 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -18,17 +18,46 @@
android:id="@+id/quick_settings_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@drawable/qs_background_primary"
- android:elevation="4dp"
android:clipToPadding="false"
- android:clipChildren="false">
+ android:clipChildren="false" >
+
+ <!-- Main QS background -->
+ <View
+ android:id="@+id/quick_settings_background"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:elevation="4dp"
+ android:background="@drawable/qs_background_primary" />
+
+ <!-- Black part behind the status bar -->
+ <View
+ android:id="@+id/quick_settings_status_bar_background"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/qs_header_system_icons_area_height"
+ android:clipToPadding="false"
+ android:clipChildren="false"
+ android:background="#ff000000" />
+
+ <!-- Gradient view behind QS -->
+ <View
+ android:id="@+id/quick_settings_gradient_view"
+ android:layout_width="match_parent"
+ android:layout_height="126dp"
+ android:layout_marginTop="@dimen/qs_header_system_icons_area_height"
+ android:clipToPadding="false"
+ android:clipChildren="false"
+ android:background="@drawable/qs_bg_gradient" />
+
<com.android.systemui.qs.QSPanel
android:id="@+id/quick_settings_panel"
- android:layout_marginTop="28dp"
+ android:layout_marginTop="@dimen/qs_header_system_icons_area_height"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="48dp" />
+ android:layout_marginBottom="48dp"
+ android:elevation="4dp"
+ />
+
<include layout="@layout/quick_status_bar_expanded_header" />
diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
new file mode 100644
index 0000000..e0f0ed9
--- /dev/null
+++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<View
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/quick_qs_status_icons"
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:layout_marginBottom="22dp"
+ android:layout_below="@id/quick_status_bar_system_icons"
+ >
+
+</View>
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index e8b418c..dacc3f9 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -28,23 +28,21 @@
android:clipToPadding="false"
android:paddingTop="0dp"
android:paddingEnd="0dp"
- android:paddingStart="0dp">
+ android:paddingStart="0dp"
+ android:elevation="4dp" >
<include layout="@layout/quick_status_bar_header_system_icons" />
+ <include layout="@layout/quick_qs_status_icons" />
<com.android.systemui.qs.QuickQSPanel
android:id="@+id/quick_qs_panel"
android:layout_width="match_parent"
android:layout_height="48dp"
- android:layout_alignParentEnd="true"
- android:layout_marginTop="31dp"
- android:layout_alignParentTop="true"
+ android:layout_below="@id/quick_qs_status_icons"
android:accessibilityTraversalAfter="@+id/date_time_group"
android:accessibilityTraversalBefore="@id/expand_indicator"
android:clipChildren="false"
android:clipToPadding="false"
- android:layout_marginStart="8dp"
- android:layout_marginEnd="8dp"
android:focusable="true"
android:importantForAccessibility="yes" />
diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
index 739a255..bfe1b62 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
@@ -17,6 +17,7 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/quick_status_bar_system_icons"
android:layout_width="match_parent"
android:layout_height="@dimen/qs_header_system_icons_area_height"
android:layout_alignParentEnd="true"
diff --git a/packages/SystemUI/res/layout/slice_permission_request.xml b/packages/SystemUI/res/layout/slice_permission_request.xml
new file mode 100644
index 0000000..cdb2a91
--- /dev/null
+++ b/packages/SystemUI/res/layout/slice_permission_request.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- Extends LinearLayout -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="16dp"
+ android:paddingEnd="16dp"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/text2"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="8dp"
+ android:paddingStart="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="@string/slice_permission_text_1" />
+
+ <TextView
+ android:id="@+id/text1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:paddingBottom="16dp"
+ android:text="@string/slice_permission_text_2" />
+
+ <CheckBox
+ android:id="@+id/slice_permission_checkbox"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/slice_permission_checkbox" />
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index 17b38cb..8c0b9ba 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -54,30 +54,47 @@
android:layout_height="match_parent"
android:layout="@layout/operator_name" />
- <com.android.systemui.statusbar.policy.Clock
- android:id="@+id/clock"
- android:textAppearance="@style/TextAppearance.StatusBar.Clock"
- android:layout_width="wrap_content"
+ <LinearLayout
android:layout_height="match_parent"
- android:singleLine="true"
- android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
- android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
- android:gravity="center_vertical|start"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ >
+ <com.android.systemui.statusbar.policy.Clock
+ android:id="@+id/clock"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:textAppearance="@style/TextAppearance.StatusBar.Clock"
+ android:singleLine="true"
+ android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
+ android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
+ android:gravity="center_vertical|start"
+ />
+
+ <!-- The alpha of this area is controlled from both PhoneStatusBarTransitions and
+ PhoneStatusBar (DISABLE_NOTIFICATION_ICONS). -->
+ <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
+ android:id="@+id/notification_icon_area"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:orientation="horizontal" />
+
+ </LinearLayout>
+
+ <!-- Space should cover the notch (if it exists) and let other views lay out around it -->
+ <android.widget.Space
+ android:id="@+id/cutout_space_view"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:gravity="center_horizontal|center_vertical"
/>
- <!-- The alpha of this area is controlled from both PhoneStatusBarTransitions and
- PhoneStatusBar (DISABLE_NOTIFICATION_ICONS). -->
- <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
- android:id="@+id/notification_icon_area"
- android:layout_width="0dip"
+ <com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area"
+ android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
- android:orientation="horizontal" />
-
- <com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
android:orientation="horizontal"
+ android:gravity="center_vertical|end"
>
<include layout="@layout/system_icons" />
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index c5e5ee1..bef0830 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -43,8 +43,6 @@
android:layout_width="@dimen/qs_panel_width"
android:layout_height="match_parent"
android:layout_gravity="@integer/notification_panel_layout_gravity"
- android:layout_marginStart="@dimen/notification_side_paddings"
- android:layout_marginEnd="@dimen/notification_side_paddings"
android:clipToPadding="false"
android:clipChildren="false"
systemui:viewType="com.android.systemui.plugins.qs.QS" />
diff --git a/packages/SystemUI/res/layout/system_icons.xml b/packages/SystemUI/res/layout/system_icons.xml
index bfa92ad..1fafb2f 100644
--- a/packages/SystemUI/res/layout/system_icons.xml
+++ b/packages/SystemUI/res/layout/system_icons.xml
@@ -16,12 +16,13 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/system_icons"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical">
- <com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/statusIcons"
- android:layout_width="wrap_content"
+ <com.android.systemui.statusbar.phone.StatusIconContainer android:id="@+id/statusIcons"
+ android:layout_width="0dp"
+ android:layout_weight="1"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:orientation="horizontal"/>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 0134086..4e17c71 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -215,7 +215,7 @@
<dimen name="close_handle_underlap">32dp</dimen>
<!-- Height of the status bar header bar -->
- <dimen name="status_bar_header_height">124dp</dimen>
+ <dimen name="status_bar_header_height">178dp</dimen>
<!-- Height of the status bar header bar in the car setting. -->
<dimen name="car_status_bar_header_height">128dp</dimen>
@@ -223,8 +223,9 @@
<!-- The bottom padding of the status bar header. -->
<dimen name="status_bar_header_padding_bottom">48dp</dimen>
- <!-- The height of the container that holds the system icons in the quick settings header. -->
- <dimen name="qs_header_system_icons_area_height">40dp</dimen>
+ <!-- The height of the container that holds the battery and time in the quick settings header.
+ -->
+ <dimen name="qs_header_system_icons_area_height">48dp</dimen>
<!-- The height of the container that holds the system icons in the quick settings header in the
car setting. -->
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index fed97c5..edda613 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -93,5 +93,8 @@
<item type="id" name="action_snooze_long"/>
<item type="id" name="action_snooze_longer"/>
<item type="id" name="action_snooze_assistant_suggestion_1"/>
+
+ <!-- For StatusBarIconContainer to tag its icon views -->
+ <item type="id" name="status_bar_view_state_tag" />
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 6437903..199ccfc 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1702,12 +1702,14 @@
<item>Clipboard</item>
<item>Keycode</item>
<item>Keyboard switcher</item>
+ <item>Rotation suggestion</item>
<item>None</item>
</string-array>
<string-array name="nav_bar_button_values" translatable="false">
<item>clipboard</item>
<item>key</item>
<item>menu_ime</item>
+ <item>rotate</item>
<item>space</item>
</string-array>
@@ -2065,5 +2067,21 @@
<string name="touch_filtered_warning">Because an app is obscuring a permission request, Settings
can’t verify your response.</string>
+ <!-- Title of prompt requesting access to display slices [CHAR LIMIT=NONE] -->
+ <string name="slice_permission_title">Allow <xliff:g id="app" example="Example App">%1$s</xliff:g> to show <xliff:g id="app_2" example="Other Example App">%2$s</xliff:g> slices?</string>
+
+ <!-- Description of what kind of access is given to a slice host [CHAR LIMIT=NONE] -->
+ <string name="slice_permission_text_1"> - It can read information from <xliff:g id="app" example="Example App">%1$s</xliff:g></string>
+ <!-- Description of what kind of access is given to a slice host [CHAR LIMIT=NONE] -->
+ <string name="slice_permission_text_2"> - It can take actions inside <xliff:g id="app" example="Example App">%1$s</xliff:g></string>
+
+ <!-- Text on checkbox allowing the app to show slices from all apps [CHAR LIMIT=NONE] -->
+ <string name="slice_permission_checkbox">Allow <xliff:g id="app" example="Example App">%1$s</xliff:g> to show slices from any app</string>
+
+ <!-- Option to grant the slice permission request on the screen [CHAR LIMIT=15] -->
+ <string name="slice_permission_allow">Allow</string>
+
+ <!-- Option to grant the slice permission request on the screen [CHAR LIMIT=15] -->
+ <string name="slice_permission_deny">Deny</string>
</resources>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index e58ad05..19afcf5 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -991,6 +991,12 @@
maxChargingWattage > fastThreshold ? CHARGING_FAST :
CHARGING_REGULAR;
}
+
+ @Override
+ public String toString() {
+ return "BatteryStatus{status=" + status + ",level=" + level + ",plugged=" + plugged
+ + ",health=" + health + ",maxChargingWattage=" + maxChargingWattage + "}";
+ }
}
public class StrongAuthTracker extends LockPatternUtils.StrongAuthTracker {
@@ -1624,18 +1630,8 @@
return true;
}
- // change in battery level while plugged in
- if (nowPluggedIn && old.level != current.level) {
- return true;
- }
-
- // change in battery level while keyguard visible
- if (mKeyguardIsVisible && old.level != current.level) {
- return true;
- }
-
- // change where battery needs charging
- if (!nowPluggedIn && current.isBatteryLow() && current.level != old.level) {
+ // change in battery level
+ if (old.level != current.level) {
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 2fe66a1..8666b0c 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -224,7 +224,6 @@
if (mTextColor != 0) mBatteryPercentView.setTextColor(mTextColor);
updatePercentText();
addView(mBatteryPercentView,
- 0,
new ViewGroup.LayoutParams(
LayoutParams.WRAP_CONTENT,
LayoutParams.MATCH_PARENT));
diff --git a/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java b/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java
new file mode 100644
index 0000000..302face
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java
@@ -0,0 +1,88 @@
+/*
+ * 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 com.android.systemui;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.slice.SliceManager;
+import android.app.slice.SliceProvider;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.DialogInterface.OnDismissListener;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.CheckBox;
+import android.widget.TextView;
+
+public class SlicePermissionActivity extends Activity implements OnClickListener,
+ OnDismissListener {
+
+ private static final String TAG = "SlicePermissionActivity";
+
+ private CheckBox mAllCheckbox;
+
+ private Uri mUri;
+ private String mCallingPkg;
+ private String mProviderPkg;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mUri = getIntent().getParcelableExtra(SliceProvider.EXTRA_BIND_URI);
+ mCallingPkg = getIntent().getStringExtra(SliceProvider.EXTRA_PKG);
+ mProviderPkg = getIntent().getStringExtra(SliceProvider.EXTRA_PROVIDER_PKG);
+
+ try {
+ PackageManager pm = getPackageManager();
+ CharSequence app1 = pm.getApplicationInfo(mCallingPkg, 0).loadLabel(pm);
+ CharSequence app2 = pm.getApplicationInfo(mProviderPkg, 0).loadLabel(pm);
+ AlertDialog dialog = new AlertDialog.Builder(this)
+ .setTitle(getString(R.string.slice_permission_title, app1, app2))
+ .setView(R.layout.slice_permission_request)
+ .setNegativeButton(R.string.slice_permission_deny, this)
+ .setPositiveButton(R.string.slice_permission_allow, this)
+ .setOnDismissListener(this)
+ .show();
+ TextView t1 = dialog.getWindow().getDecorView().findViewById(R.id.text1);
+ t1.setText(getString(R.string.slice_permission_text_1, app2));
+ TextView t2 = dialog.getWindow().getDecorView().findViewById(R.id.text2);
+ t2.setText(getString(R.string.slice_permission_text_2, app2));
+ mAllCheckbox = dialog.getWindow().getDecorView().findViewById(
+ R.id.slice_permission_checkbox);
+ mAllCheckbox.setText(getString(R.string.slice_permission_checkbox, app1));
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "Couldn't find package", e);
+ finish();
+ }
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ getSystemService(SliceManager.class).grantPermissionFromUser(mUri, mCallingPkg,
+ mAllCheckbox.isChecked());
+ }
+ finish();
+ }
+
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ finish();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java b/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java
index 931a994..69e347c9 100644
--- a/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java
+++ b/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java
@@ -463,4 +463,8 @@
public boolean isReportingEnabled() {
return mAllowReportRejectedTouch;
}
+
+ public void onFalsingSessionStarted() {
+ sessionEntrypoint();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
index e4b405f..ed659e2 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
@@ -167,6 +167,9 @@
if (mDataCollector.isEnabledFull()) {
registerSensors(COLLECTOR_SENSORS);
}
+ if (mDataCollector.isEnabled()) {
+ mDataCollector.onFalsingSessionStarted();
+ }
}
private void registerSensors(int [] sensors) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 7f0acc2..7320b86 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -45,8 +45,9 @@
protected float mQsExpansion;
private QSCustomizer mQSCustomizer;
private View mQSFooter;
- private float mFullElevation;
+ private View mBackground;
private float mRadius;
+ private int mSideMargins;
public QSContainerImpl(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -60,12 +61,14 @@
mHeader = findViewById(R.id.header);
mQSCustomizer = findViewById(R.id.qs_customize);
mQSFooter = findViewById(R.id.qs_footer);
- mFullElevation = mQSPanel.getElevation();
+ mBackground = findViewById(R.id.quick_settings_background);
mRadius = getResources().getDimensionPixelSize(
Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
+ mSideMargins = getResources().getDimensionPixelSize(R.dimen.notification_side_paddings);
setClickable(true);
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
+ setMargins();
}
@Override
@@ -131,6 +134,8 @@
mQSDetail.setBottom(getTop() + height);
// Pin QS Footer to the bottom of the panel.
mQSFooter.setTranslationY(height - mQSFooter.getHeight());
+ mBackground.setTop(mQSPanel.getTop());
+ mBackground.setBottom(height);
ExpandableOutlineView.getRoundedRectPath(0, 0, getWidth(), height, mRadius,
mRadius,
@@ -148,4 +153,19 @@
mQsExpansion = expansion;
updateExpansion();
}
+
+ private void setMargins() {
+ setMargins(mQSDetail);
+ setMargins(mBackground);
+ setMargins(mQSFooter);
+ setMargins(mQSPanel);
+ setMargins(mHeader);
+ setMargins(mQSCustomizer);
+ }
+
+ private void setMargins(View view) {
+ FrameLayout.LayoutParams lp = (LayoutParams) view.getLayoutParams();
+ lp.rightMargin = mSideMargins;
+ lp.leftMargin = mSideMargins;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index 927a49c..92475da 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -61,18 +61,16 @@
public class QSFooterImpl extends FrameLayout implements QSFooter,
OnClickListener, OnUserInfoChangedListener, EmergencyListener,
SignalCallback, CommandQueue.Callbacks {
- private static final float EXPAND_INDICATOR_THRESHOLD = .93f;
-
private ActivityStarter mActivityStarter;
private UserInfoController mUserInfoController;
private SettingsButton mSettingsButton;
protected View mSettingsContainer;
+ private View mCarrierText;
private boolean mQsDisabled;
private QSPanel mQsPanel;
private boolean mExpanded;
- protected ExpandableIndicator mExpandIndicator;
private boolean mListening;
@@ -100,18 +98,18 @@
Dependency.get(ActivityStarter.class).postQSRunnableDismissingKeyguard(() ->
mQsPanel.showEdit(view)));
- mExpandIndicator = findViewById(R.id.expand_indicator);
mSettingsButton = findViewById(R.id.settings_button);
mSettingsContainer = findViewById(R.id.settings_button_container);
mSettingsButton.setOnClickListener(this);
+ mCarrierText = findViewById(R.id.qs_carrier_text);
+
mMultiUserSwitch = findViewById(R.id.multi_user_switch);
mMultiUserAvatar = mMultiUserSwitch.findViewById(R.id.multi_user_avatar);
// RenderThread is doing more harm than good when touching the header (to expand quick
// settings), so disable it for this view
((RippleDrawable) mSettingsButton.getBackground()).setForceSoftware(true);
- ((RippleDrawable) mExpandIndicator.getBackground()).setForceSoftware(true);
updateResources();
@@ -162,6 +160,8 @@
return new TouchAnimator.Builder()
.addFloat(mEdit, "alpha", 0, 1)
.addFloat(mMultiUserSwitch, "alpha", 0, 1)
+ .addFloat(mCarrierText, "alpha", 0, 1)
+ .addFloat(mSettingsButton, "alpha", 0, 1)
.build();
}
@@ -185,8 +185,6 @@
if (mSettingsAlpha != null) {
mSettingsAlpha.setPosition(headerExpansionFraction);
}
-
- mExpandIndicator.setExpanded(headerExpansionFraction > EXPAND_INDICATOR_THRESHOLD);
}
@Override
@@ -237,8 +235,6 @@
mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility(
TunerService.isTunerEnabled(mContext) ? View.VISIBLE : View.INVISIBLE);
- mExpandIndicator.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
-
final boolean isDemo = UserManager.isDeviceInDemoMode(mContext);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 398592a..e6fd2f4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -38,6 +38,7 @@
import com.android.systemui.qs.QSDetail.Callback;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.SignalClusterView;
+import com.android.systemui.statusbar.policy.DarkIconDispatcher;
import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
public class QuickStatusBarHeader extends RelativeLayout implements CommandQueue.Callbacks {
@@ -69,15 +70,13 @@
updateResources();
- // Set the light/dark theming on the header status UI to match the current theme.
+ // Set light text on the header icons because they will always be on a black background
int colorForeground = Utils.getColorAttr(getContext(), android.R.attr.colorForeground);
- float intensity = colorForeground == Color.WHITE ? 0 : 1;
Rect tintArea = new Rect(0, 0, 0, 0);
-
- applyDarkness(R.id.battery, tintArea, intensity, colorForeground);
- applyDarkness(R.id.clock, tintArea, intensity, colorForeground);
+ applyDarkness(R.id.clock, tintArea, 0, DarkIconDispatcher.DEFAULT_ICON_TINT);
BatteryMeterView battery = findViewById(R.id.battery);
+ battery.setFillColor(Color.WHITE);
battery.setForceShowPercent(true);
mActivityStarter = Dependency.get(ActivityStarter.class);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 6db46b5..675aa8f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -293,12 +293,13 @@
sharingIntent.putExtra(Intent.EXTRA_STREAM, uri);
sharingIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
- // Create a share action for the notification. Note, we proxy the call to ShareReceiver
- // because RemoteViews currently forces an activity options on the PendingIntent being
- // launched, and since we don't want to trigger the share sheet in this case, we will
- // start the chooser activitiy directly in ShareReceiver.
+ // Create a share action for the notification. Note, we proxy the call to
+ // ScreenshotActionReceiver because RemoteViews currently forces an activity options
+ // on the PendingIntent being launched, and since we don't want to trigger the share
+ // sheet in this case, we start the chooser activity directly in
+ // ScreenshotActionReceiver.
PendingIntent shareAction = PendingIntent.getBroadcast(context, 0,
- new Intent(context, GlobalScreenshot.ShareReceiver.class)
+ new Intent(context, GlobalScreenshot.ScreenshotActionReceiver.class)
.putExtra(SHARING_INTENT, sharingIntent),
PendingIntent.FLAG_CANCEL_CURRENT);
Notification.Action.Builder shareActionBuilder = new Notification.Action.Builder(
@@ -306,15 +307,19 @@
r.getString(com.android.internal.R.string.share), shareAction);
mNotificationBuilder.addAction(shareActionBuilder.build());
- // Create a delete action for the notification
- PendingIntent deleteAction = PendingIntent.getBroadcast(context, 0,
- new Intent(context, GlobalScreenshot.DeleteScreenshotReceiver.class)
- .putExtra(GlobalScreenshot.SCREENSHOT_URI_ID, uri.toString()),
- PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
- Notification.Action.Builder deleteActionBuilder = new Notification.Action.Builder(
- R.drawable.ic_screenshot_delete,
- r.getString(com.android.internal.R.string.delete), deleteAction);
- mNotificationBuilder.addAction(deleteActionBuilder.build());
+ Intent editIntent = new Intent(Intent.ACTION_EDIT);
+ editIntent.setType("image/png");
+ editIntent.putExtra(Intent.EXTRA_STREAM, uri);
+
+ // Create a edit action for the notification the same way.
+ PendingIntent editAction = PendingIntent.getBroadcast(context, 1,
+ new Intent(context, GlobalScreenshot.ScreenshotActionReceiver.class)
+ .putExtra(SHARING_INTENT, editIntent),
+ PendingIntent.FLAG_CANCEL_CURRENT);
+ Notification.Action.Builder editActionBuilder = new Notification.Action.Builder(
+ R.drawable.ic_screenshot_edit,
+ r.getString(com.android.internal.R.string.screenshot_edit), editAction);
+ mNotificationBuilder.addAction(editActionBuilder.build());
mParams.imageUri = uri;
mParams.image = null;
@@ -879,9 +884,9 @@
}
/**
- * Receiver to proxy the share intent.
+ * Receiver to proxy the share or edit intent.
*/
- public static class ShareReceiver extends BroadcastReceiver {
+ public static class ScreenshotActionReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
try {
@@ -903,7 +908,7 @@
}
/**
- * Removes the notification for a screenshot after a share target is chosen.
+ * Removes the notification for a screenshot after a share or edit target is chosen.
*/
public static class TargetChosenReceiver extends BroadcastReceiver {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 657b953..c6abcf2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -144,7 +144,7 @@
default void handleShowGlobalActionsMenu() { }
default void handleShowShutdownUi(boolean isReboot, String reason) { }
- default void onRotationProposal(int rotation) { }
+ default void onRotationProposal(int rotation, boolean isValid) { }
}
@VisibleForTesting
@@ -462,10 +462,10 @@
}
@Override
- public void onProposedRotationChanged(int rotation) {
+ public void onProposedRotationChanged(int rotation, boolean isValid) {
synchronized (mLock) {
mHandler.removeMessages(MSG_ROTATION_PROPOSAL);
- mHandler.obtainMessage(MSG_ROTATION_PROPOSAL, rotation, 0,
+ mHandler.obtainMessage(MSG_ROTATION_PROPOSAL, rotation, isValid ? 1 : 0,
null).sendToTarget();
}
}
@@ -668,7 +668,7 @@
break;
case MSG_ROTATION_PROPOSAL:
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).onRotationProposal(msg.arg1);
+ mCallbacks.get(i).onRotationProposal(msg.arg1, msg.arg2 != 0);
}
break;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 43047ed6..0a12be4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -52,6 +52,8 @@
import com.android.systemui.util.wakelock.SettableWakeLock;
import com.android.systemui.util.wakelock.WakeLock;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.text.NumberFormat;
/**
@@ -116,11 +118,9 @@
WakeLock wakeLock) {
mContext = context;
mIndicationArea = indicationArea;
- mTextView = (KeyguardIndicationTextView) indicationArea.findViewById(
- R.id.keyguard_indication_text);
+ mTextView = indicationArea.findViewById(R.id.keyguard_indication_text);
mInitialTextColor = mTextView != null ? mTextView.getCurrentTextColor() : Color.WHITE;
- mDisclosure = (KeyguardIndicationTextView) indicationArea.findViewById(
- R.id.keyguard_indication_enterprise_disclosure);
+ mDisclosure = indicationArea.findViewById(R.id.keyguard_indication_enterprise_disclosure);
mLockIcon = lockIcon;
mWakeLock = new SettableWakeLock(wakeLock);
@@ -416,6 +416,21 @@
updateDisclosure();
}
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("KeyguardIndicationController:");
+ pw.println(" mTransientTextColor: " + Integer.toHexString(mTransientTextColor));
+ pw.println(" mInitialTextColor: " + Integer.toHexString(mInitialTextColor));
+ pw.println(" mPowerPluggedIn: " + mPowerPluggedIn);
+ pw.println(" mPowerCharged: " + mPowerCharged);
+ pw.println(" mChargingSpeed: " + mChargingSpeed);
+ pw.println(" mChargingWattage: " + mChargingWattage);
+ pw.println(" mMessageToShowOnScreenOn: " + mMessageToShowOnScreenOn);
+ pw.println(" mDozing: " + mDozing);
+ pw.println(" mBatteryLevel: " + mBatteryLevel);
+ pw.println(" mTextView.getText(): " + (mTextView == null ? null : mTextView.getText()));
+ pw.println(" computePowerIndication(): " + computePowerIndication());
+ }
+
protected class BaseKeyguardCallback extends KeyguardUpdateMonitorCallback {
public static final int HIDE_DELAY_MS = 5000;
private int mLastSuccessiveErrorMessage = -1;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
index 5b06874..7360486 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
@@ -616,8 +616,7 @@
&& entry.row.getGuts() == mGutsManager.getExposedGuts();
entry.row.onDensityOrFontScaleChanged();
if (exposedGuts) {
- mGutsManager.setExposedGuts(entry.row.getGuts());
- mGutsManager.bindGuts(entry.row);
+ mGutsManager.onDensityOrFontScaleChanged(entry.row);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
index c4024a5..52776d7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
@@ -23,6 +23,7 @@
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.os.Handler;
+import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewAnimationUtils;
@@ -187,6 +188,12 @@
}
}
+ public void openControls(
+ int x, int y, boolean needsFalsingProtection, @Nullable Runnable onAnimationEnd) {
+ animateOpen(x, y, onAnimationEnd);
+ setExposed(true /* exposed */, needsFalsingProtection);
+ }
+
public void closeControls(boolean leavebehinds, boolean controls, int x, int y, boolean force) {
if (mGutsContent != null) {
if (mGutsContent.isLeavebehind() && leavebehinds) {
@@ -214,6 +221,27 @@
}
}
+ private void animateOpen(int x, int y, @Nullable Runnable onAnimationEnd) {
+ final double horz = Math.max(getWidth() - x, x);
+ final double vert = Math.max(getHeight() - y, y);
+ final float r = (float) Math.hypot(horz, vert);
+
+ final Animator a
+ = ViewAnimationUtils.createCircularReveal(this, x, y, 0, r);
+ a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ a.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+ a.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ if (onAnimationEnd != null) {
+ onAnimationEnd.run();
+ }
+ }
+ });
+ a.start();
+ }
+
private void animateClose(int x, int y) {
if (x == -1 || y == -1) {
x = (getLeft() + getRight()) / 2;
@@ -279,7 +307,7 @@
}
}
- public void setExposed(boolean exposed, boolean needsFalsingProtection) {
+ private void setExposed(boolean exposed, boolean needsFalsingProtection) {
final boolean wasExposed = mExposed;
mExposed = exposed;
mNeedsFalsingProtection = needsFalsingProtection;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
index 441c184..9d8892d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
@@ -15,8 +15,6 @@
*/
package com.android.systemui.statusbar;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.app.INotificationManager;
import android.app.NotificationChannel;
import android.content.Context;
@@ -32,17 +30,14 @@
import android.util.Log;
import android.view.HapticFeedbackConstants;
import android.view.View;
-import android.view.ViewAnimationUtils;
import android.view.accessibility.AccessibilityManager;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
-import com.android.systemui.Interpolators;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.stack.StackStateAnimator;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -112,6 +107,11 @@
mKeyToRemoveOnGutsClosed = keyToRemoveOnGutsClosed;
}
+ public void onDensityOrFontScaleChanged(ExpandableNotificationRow row) {
+ setExposedGuts(row.getGuts());
+ bindGuts(row);
+ }
+
private void saveAndCloseNotificationMenu(
ExpandableNotificationRow row, NotificationGuts guts, View done) {
guts.resetFalsingCheck();
@@ -270,7 +270,7 @@
}
/**
- * Opens guts on the given ExpandableNotificationRow |v|.
+ * Opens guts on the given ExpandableNotificationRow |v|.
*
* @param v ExpandableNotificationRow to open guts on
* @param x x coordinate of origin of circular reveal
@@ -326,26 +326,15 @@
true /* removeControls */, -1 /* x */, -1 /* y */,
false /* resetMenu */);
guts.setVisibility(View.VISIBLE);
- final double horz = Math.max(guts.getWidth() - x, x);
- final double vert = Math.max(guts.getHeight() - y, y);
- final float r = (float) Math.hypot(horz, vert);
- final Animator a
- = ViewAnimationUtils.createCircularReveal(guts, x, y, 0, r);
- a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
- a.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
- a.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
- // Move the notification view back over the menu
- row.resetTranslation();
- }
- });
- a.start();
+
final boolean needsFalsingProtection =
(mPresenter.isPresenterLocked() &&
!mAccessibilityManager.isTouchExplorationEnabled());
- guts.setExposed(true /* exposed */, needsFalsingProtection);
+ guts.openControls(x, y, needsFalsingProtection, () -> {
+ // Move the notification view back over the menu
+ row.resetTranslation();
+ });
+
row.closeRemoteInput();
mListContainer.onHeightChanged(row, true /* needsAnimation */);
mNotificationGutsExposed = guts;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 70ec45e..4225843 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -335,9 +335,16 @@
}
@Override
- public void onRotationProposal(final int rotation) {
- // This method will only be called if rotation is valid but will include proposals for the
- // current system rotation
+ public void onRotationProposal(final int rotation, boolean isValid) {
+ // This method will be called on rotation suggestion changes even if the proposed rotation
+ // is not valid for the top app. Use invalid rotation choices as a signal to remove the
+ // rotate button if shown.
+
+ if (!isValid) {
+ setRotateSuggestionButtonState(false);
+ return;
+ }
+
Handler h = getView().getHandler();
if (rotation == mWindowManager.getDefaultDisplay().getRotation()) {
// Use this as a signal to remove any current suggestions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 970d1de..e8b28f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -16,26 +16,34 @@
package com.android.systemui.statusbar.phone;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+
+import android.annotation.Nullable;
import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.EventLog;
+import android.view.DisplayCutout;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
-import com.android.systemui.BatteryMeterView;
-import com.android.systemui.DejankUtils;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
import com.android.systemui.Dependency;
import com.android.systemui.EventLogTags;
import com.android.systemui.R;
import com.android.systemui.statusbar.policy.DarkIconDispatcher;
import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
+import com.android.systemui.util.leak.RotationUtils;
public class PhoneStatusBarView extends PanelBar {
private static final String TAG = "PhoneStatusBarView";
private static final boolean DEBUG = StatusBar.DEBUG;
private static final boolean DEBUG_GESTURES = false;
+ private static final int NO_VALUE = Integer.MIN_VALUE;
StatusBar mBar;
@@ -53,6 +61,10 @@
}
};
private DarkReceiver mBattery;
+ private int mLastOrientation;
+ private View mCutoutSpace;
+ @Nullable
+ private DisplayCutout mDisplayCutout;
public PhoneStatusBarView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -76,6 +88,7 @@
public void onFinishInflate() {
mBarTransitions.init();
mBattery = findViewById(R.id.battery);
+ mCutoutSpace = findViewById(R.id.cutout_space_view);
}
@Override
@@ -83,12 +96,51 @@
super.onAttachedToWindow();
// Always have Battery meters in the status bar observe the dark/light modes.
Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mBattery);
+ if (updateOrientationAndCutout(getResources().getConfiguration().orientation)) {
+ postUpdateLayoutForCutout();
+ }
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(mBattery);
+ mDisplayCutout = null;
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+
+ // May trigger cutout space layout-ing
+ if (updateOrientationAndCutout(newConfig.orientation)) {
+ postUpdateLayoutForCutout();
+ }
+ }
+
+ /**
+ *
+ * @param newOrientation may pass NO_VALUE for no change
+ * @return boolean indicating if we need to update the cutout location / margins
+ */
+ private boolean updateOrientationAndCutout(int newOrientation) {
+ boolean changed = false;
+ if (newOrientation != NO_VALUE) {
+ if (mLastOrientation != newOrientation) {
+ changed = true;
+ mLastOrientation = newOrientation;
+ }
+ }
+
+ if (mDisplayCutout == null) {
+ DisplayCutout cutout = getRootWindowInsets().getDisplayCutout();
+ if (cutout != null) {
+ changed = true;
+ mDisplayCutout = cutout;
+ }
+ }
+
+ return changed;
}
@Override
@@ -214,4 +266,75 @@
R.dimen.status_bar_height);
setLayoutParams(layoutParams);
}
+
+ private void updateLayoutForCutout() {
+ updateCutoutLocation();
+ updateSafeInsets();
+ }
+
+ private void postUpdateLayoutForCutout() {
+ Runnable r = new Runnable() {
+ @Override
+ public void run() {
+ updateLayoutForCutout();
+ }
+ };
+ // Let the cutout emulation draw first
+ postDelayed(r, 0);
+ }
+
+ private void updateCutoutLocation() {
+ if (mDisplayCutout == null || mDisplayCutout.isEmpty()
+ || mLastOrientation != ORIENTATION_PORTRAIT) {
+ mCutoutSpace.setVisibility(View.GONE);
+ return;
+ }
+
+ mCutoutSpace.setVisibility(View.VISIBLE);
+ LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mCutoutSpace.getLayoutParams();
+ lp.width = mDisplayCutout.getBoundingRect().width();
+ lp.height = mDisplayCutout.getBoundingRect().height();
+ }
+
+ private void updateSafeInsets() {
+ // Depending on our rotation, we may have to work around a cutout in the middle of the view,
+ // or letterboxing from the right or left sides.
+
+ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
+ if (mDisplayCutout == null || mDisplayCutout.isEmpty()) {
+ lp.leftMargin = 0;
+ lp.rightMargin = 0;
+ return;
+ }
+
+ int leftMargin = 0;
+ int rightMargin = 0;
+ switch (RotationUtils.getRotation(getContext())) {
+ /*
+ * Landscape: <-|
+ * Seascape: |->
+ */
+ case RotationUtils.ROTATION_LANDSCAPE:
+ leftMargin = getDisplayCutoutHeight();
+ break;
+ case RotationUtils.ROTATION_SEASCAPE:
+ rightMargin = getDisplayCutoutHeight();
+ break;
+ default:
+ break;
+ }
+
+ lp.leftMargin = leftMargin;
+ lp.rightMargin = rightMargin;
+ }
+
+ //TODO: Find a better way
+ private int getDisplayCutoutHeight() {
+ if (mDisplayCutout == null || mDisplayCutout.isEmpty()) {
+ return 0;
+ }
+
+ Rect r = mDisplayCutout.getBoundingRect();
+ return r.bottom - r.top;
+ }
}
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 14329b5..3b394dd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -866,13 +866,13 @@
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println(" ScrimController:");
- pw.print(" state:"); pw.println(mState);
- pw.print(" frontScrim:"); pw.print(" viewAlpha="); pw.print(mScrimInFront.getViewAlpha());
+ pw.println(" ScrimController: ");
+ pw.print(" state: "); pw.println(mState);
+ pw.print(" frontScrim:"); pw.print(" viewAlpha="); pw.print(mScrimInFront.getViewAlpha());
pw.print(" alpha="); pw.print(mCurrentInFrontAlpha);
pw.print(" tint=0x"); pw.println(Integer.toHexString(mScrimInFront.getTint()));
- pw.print(" backScrim:"); pw.print(" viewAlpha="); pw.print(mScrimBehind.getViewAlpha());
+ pw.print(" backScrim:"); pw.print(" viewAlpha="); pw.print(mScrimBehind.getViewAlpha());
pw.print(" alpha="); pw.print(mCurrentBehindAlpha);
pw.print(" tint=0x"); pw.println(Integer.toHexString(mScrimBehind.getTint()));
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 5e08ec3..c30fb22 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1704,7 +1704,7 @@
if (mReportRejectedTouch == null) {
return;
}
- mReportRejectedTouch.setVisibility(mState == StatusBarState.KEYGUARD
+ mReportRejectedTouch.setVisibility(mState == StatusBarState.KEYGUARD && !mDozing
&& mFalsingManager.isReportingEnabled() ? View.VISIBLE : View.INVISIBLE);
}
@@ -2663,6 +2663,10 @@
mFingerprintUnlockController.dump(pw);
}
+ if (mKeyguardIndicationController != null) {
+ mKeyguardIndicationController.dump(fd, pw, args);
+ }
+
if (mScrimController != null) {
mScrimController.dump(fd, pw, args);
}
@@ -4506,6 +4510,7 @@
((DozeReceiver) mAmbientIndicationContainer).setDozing(mDozing);
}
updateDozingState();
+ updateReportRejectedTouchVisibility();
Trace.endSection();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index bcda60e..07610ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -62,7 +62,7 @@
}
/**
- * Version of ViewGroup that observers state from the DarkIconDispatcher.
+ * Version of ViewGroup that observes state from the DarkIconDispatcher.
*/
public static class DarkIconManager extends IconManager {
private final DarkIconDispatcher mDarkIconDispatcher;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
new file mode 100644
index 0000000..1897171
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
@@ -0,0 +1,169 @@
+/*
+ * 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.
+ */
+
+/**
+ * A container for Status bar system icons. Limits the number of system icons and handles overflow
+ * similar to NotificationIconController. Can be used to layout nested StatusIconContainers
+ *
+ * Children are expected to be of type StatusBarIconView.
+ */
+package com.android.systemui.statusbar.phone;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.util.ArrayMap;
+import android.util.AttributeSet;
+
+import android.view.View;
+import com.android.keyguard.AlphaOptimizedLinearLayout;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.StatusBarIconView;
+import com.android.systemui.statusbar.stack.ViewState;
+
+public class StatusIconContainer extends AlphaOptimizedLinearLayout {
+
+ private static final String TAG = "StatusIconContainer";
+ private static final int MAX_ICONS = 5;
+ private static final int MAX_DOTS = 3;
+
+ public StatusIconContainer(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ float midY = getHeight() / 2.0f;
+
+ // Layout all child views so that we can move them around later
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ int width = child.getMeasuredWidth();
+ int height = child.getMeasuredHeight();
+ int top = (int) (midY - height / 2.0f);
+ child.layout(0, top, width, top + height);
+ }
+
+ resetViewStates();
+ calculateIconTranslations();
+ applyIconStates();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ final int count = getChildCount();
+ // Measure all children so that they report the correct width
+ for (int i = 0; i < count; i++) {
+ measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec);
+ }
+ }
+
+ @Override
+ public void onViewAdded(View child) {
+ super.onViewAdded(child);
+ ViewState vs = new ViewState();
+ child.setTag(R.id.status_bar_view_state_tag, vs);
+ }
+
+ @Override
+ public void onViewRemoved(View child) {
+ super.onViewRemoved(child);
+ child.setTag(R.id.status_bar_view_state_tag, null);
+ }
+
+ /**
+ * Layout is happening from end -> start
+ */
+ private void calculateIconTranslations() {
+ float translationX = getWidth();
+ float contentStart = getPaddingStart();
+ int childCount = getChildCount();
+ // Underflow === don't show content until that index
+ int firstUnderflowIndex = -1;
+ android.util.Log.d(TAG, "calculateIconTransitions: start=" + translationX);
+
+ //TODO: Dots
+ for (int i = childCount - 1; i >= 0; i--) {
+ View child = getChildAt(i);
+ if (!(child instanceof StatusBarIconView)) {
+ continue;
+ }
+
+ ViewState childState = getViewStateFromChild(child);
+ if (childState == null ) {
+ continue;
+ }
+
+ // Rely on StatusBarIcon for truth about visibility
+ if (!((StatusBarIconView) child).getStatusBarIcon().visible) {
+ childState.hidden = true;
+ continue;
+ }
+
+ childState.xTranslation = translationX - child.getWidth();
+
+ if (childState.xTranslation < contentStart) {
+ if (firstUnderflowIndex == -1) {
+ firstUnderflowIndex = i;
+ }
+ }
+
+ translationX -= child.getWidth();
+ }
+
+ if (firstUnderflowIndex != -1) {
+ for (int i = 0; i <= firstUnderflowIndex; i++) {
+ View child = getChildAt(i);
+ ViewState vs = getViewStateFromChild(child);
+ if (vs != null) {
+ vs.hidden = true;
+ }
+ }
+ }
+ }
+
+ private void applyIconStates() {
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ ViewState vs = getViewStateFromChild(child);
+ if (vs != null) {
+ vs.applyToView(child);
+ }
+ }
+ }
+
+ private void resetViewStates() {
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ ViewState vs = getViewStateFromChild(child);
+ if (vs == null) {
+ continue;
+ }
+
+ vs.initFrom(child);
+ vs.alpha = 1.0f;
+ if (child instanceof StatusBarIconView) {
+ vs.hidden = !((StatusBarIconView)child).getStatusBarIcon().visible;
+ } else {
+ vs.hidden = false;
+ }
+ }
+ }
+
+ private static @Nullable ViewState getViewStateFromChild(View child) {
+ return (ViewState) child.getTag(R.id.status_bar_view_state_tag);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
index 5505099..4ca33cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -394,7 +394,7 @@
}
} else if (mOverflowNumber != null) {
removeView(mOverflowNumber);
- if (isShown()) {
+ if (isShown() && isAttachedToWindow()) {
final View removedOverflowNumber = mOverflowNumber;
addTransientView(removedOverflowNumber, getTransientViewCount());
CrossFadeHelper.fadeOut(removedOverflowNumber, new Runnable() {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/MediaRouterWrapper.java b/packages/SystemUI/src/com/android/systemui/volume/MediaRouterWrapper.java
new file mode 100644
index 0000000..3423452
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/MediaRouterWrapper.java
@@ -0,0 +1,51 @@
+/*
+ * 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 com.android.systemui.volume;
+
+import android.support.v7.media.MediaRouteSelector;
+import android.support.v7.media.MediaRouter;
+
+import java.util.List;
+
+/**
+ * Wrapper for final class MediaRouter, for testing.
+ */
+public class MediaRouterWrapper {
+
+ private final MediaRouter mRouter;
+
+ public MediaRouterWrapper(MediaRouter router)
+ {
+ mRouter = router;
+ }
+
+ public void addCallback(MediaRouteSelector selector, MediaRouter.Callback callback, int flags) {
+ mRouter.addCallback(selector, callback, flags);
+ }
+
+ public void removeCallback(MediaRouter.Callback callback) {
+ mRouter.removeCallback(callback);
+ }
+
+ public void unselect(int reason) {
+ mRouter.unselect(reason);
+ }
+
+ public List<MediaRouter.RouteInfo> getRoutes() {
+ return mRouter.getRoutes();
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java b/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java
index f8843a9..e0af9ba 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -40,6 +40,7 @@
import android.support.v7.media.MediaControlIntent;
import android.support.v7.media.MediaRouteSelector;
import android.support.v7.media.MediaRouter;
+import android.telecom.TelecomManager;
import android.util.Log;
import android.util.Pair;
@@ -54,7 +55,6 @@
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.Comparator;
import java.util.List;
@@ -65,15 +65,17 @@
private static final int MAX_DEVICES = 10;
private static final long UPDATE_DELAY_MS = 300L;
- static final int MSG_UPDATE_ITEMS = 1;
+ private static final int MSG_UPDATE_ITEMS = 1;
private final Context mContext;
- private final BluetoothController mController;
- private final WifiManager mWifiManager;
+ private final BluetoothController mBluetoothController;
+ private WifiManager mWifiManager;
private OutputChooserLayout mView;
- private final MediaRouter mRouter;
+ private final MediaRouterWrapper mRouter;
private final MediaRouterCallback mRouterCallback;
private long mLastUpdateTime;
+ private boolean mIsInCall;
+ protected boolean isAttached;
private final MediaRouteSelector mRouteSelector;
private Drawable mDefaultIcon;
@@ -81,12 +83,14 @@
private Drawable mSpeakerIcon;
private Drawable mSpeakerGroupIcon;
- public OutputChooserDialog(Context context) {
+ public OutputChooserDialog(Context context, MediaRouterWrapper router) {
super(context);
mContext = context;
- mController = Dependency.get(BluetoothController.class);
+ mBluetoothController = Dependency.get(BluetoothController.class);
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
- mRouter = MediaRouter.getInstance(context);
+ TelecomManager tm = (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
+ mIsInCall = tm.isInCall();
+ mRouter = router;
mRouterCallback = new MediaRouterCallback();
mRouteSelector = new MediaRouteSelector.Builder()
.addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
@@ -96,27 +100,38 @@
context.registerReceiver(mReceiver, filter);
}
+ protected void setIsInCall(boolean inCall) {
+ mIsInCall = inCall;
+ }
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.output_chooser);
setCanceledOnTouchOutside(true);
setOnDismissListener(this::onDismiss);
- setTitle(R.string.output_title);
mView = findViewById(R.id.output_chooser);
mView.setCallback(this);
+ if (mIsInCall) {
+ mView.setTitle(R.string.output_calls_title);
+ } else {
+ mView.setTitle(R.string.output_title);
+ }
+
mDefaultIcon = mContext.getDrawable(R.drawable.ic_cast);
mTvIcon = mContext.getDrawable(R.drawable.ic_tv);
mSpeakerIcon = mContext.getDrawable(R.drawable.ic_speaker);
mSpeakerGroupIcon = mContext.getDrawable(R.drawable.ic_speaker_group);
final boolean wifiOff = !mWifiManager.isWifiEnabled();
- final boolean btOff = !mController.isBluetoothEnabled();
- if (wifiOff || btOff) {
+ final boolean btOff = !mBluetoothController.isBluetoothEnabled();
+ if (wifiOff && btOff) {
mView.setEmptyState(getDisabledServicesMessage(wifiOff, btOff));
}
+ // time out after 5 seconds
+ mView.postDelayed(() -> updateItems(true), 5000);
}
protected void cleanUp() {}
@@ -131,15 +146,19 @@
public void onAttachedToWindow() {
super.onAttachedToWindow();
- mRouter.addCallback(mRouteSelector, mRouterCallback,
- MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
- mController.addCallback(mCallback);
+ if (!mIsInCall) {
+ mRouter.addCallback(mRouteSelector, mRouterCallback,
+ MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
+ }
+ mBluetoothController.addCallback(mCallback);
+ isAttached = true;
}
@Override
public void onDetachedFromWindow() {
+ isAttached = false;
mRouter.removeCallback(mRouterCallback);
- mController.removeCallback(mCallback);
+ mBluetoothController.removeCallback(mCallback);
super.onDetachedFromWindow();
}
@@ -154,9 +173,8 @@
if (item == null || item.tag == null) return;
if (item.deviceType == OutputChooserLayout.Item.DEVICE_TYPE_BT) {
final CachedBluetoothDevice device = (CachedBluetoothDevice) item.tag;
- if (device != null && device.getMaxConnectionState()
- == BluetoothProfile.STATE_DISCONNECTED) {
- mController.connect(device);
+ if (device.getMaxConnectionState() == BluetoothProfile.STATE_DISCONNECTED) {
+ mBluetoothController.connect(device);
}
} else if (item.deviceType == OutputChooserLayout.Item.DEVICE_TYPE_MEDIA_ROUTER) {
final MediaRouter.RouteInfo route = (MediaRouter.RouteInfo) item.tag;
@@ -171,18 +189,16 @@
if (item == null || item.tag == null) return;
if (item.deviceType == OutputChooserLayout.Item.DEVICE_TYPE_BT) {
final CachedBluetoothDevice device = (CachedBluetoothDevice) item.tag;
- if (device != null) {
- mController.disconnect(device);
- }
+ mBluetoothController.disconnect(device);
} else if (item.deviceType == OutputChooserLayout.Item.DEVICE_TYPE_MEDIA_ROUTER) {
mRouter.unselect(UNSELECT_REASON_DISCONNECTED);
}
}
- private void updateItems() {
+ private void updateItems(boolean timeout) {
if (SystemClock.uptimeMillis() - mLastUpdateTime < UPDATE_DELAY_MS) {
mHandler.removeMessages(MSG_UPDATE_ITEMS);
- mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_UPDATE_ITEMS),
+ mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_UPDATE_ITEMS, timeout),
mLastUpdateTime + UPDATE_DELAY_MS);
return;
}
@@ -194,14 +210,16 @@
addBluetoothDevices(items);
// Add remote displays
- addRemoteDisplayRoutes(items);
+ if (!mIsInCall) {
+ addRemoteDisplayRoutes(items);
+ }
- Collections.sort(items, ItemComparator.sInstance);
+ items.sort(ItemComparator.sInstance);
- if (items.size() == 0) {
+ if (items.size() == 0 && timeout) {
String emptyMessage = mContext.getString(R.string.output_none_found);
final boolean wifiOff = !mWifiManager.isWifiEnabled();
- final boolean btOff = !mController.isBluetoothEnabled();
+ final boolean btOff = !mBluetoothController.isBluetoothEnabled();
if (wifiOff || btOff) {
emptyMessage = getDisabledServicesMessage(wifiOff, btOff);
}
@@ -219,12 +237,12 @@
}
private void addBluetoothDevices(List<OutputChooserLayout.Item> items) {
- final Collection<CachedBluetoothDevice> devices = mController.getDevices();
+ final Collection<CachedBluetoothDevice> devices = mBluetoothController.getDevices();
if (devices != null) {
int connectedDevices = 0;
int count = 0;
for (CachedBluetoothDevice device : devices) {
- if (mController.getBondState(device) == BluetoothDevice.BOND_NONE) continue;
+ if (mBluetoothController.getBondState(device) == BluetoothDevice.BOND_NONE) continue;
final int majorClass = device.getBtClass().getMajorDeviceClass();
if (majorClass != BluetoothClass.Device.Major.AUDIO_VIDEO
&& majorClass != BluetoothClass.Device.Major.UNCATEGORIZED) {
@@ -328,22 +346,22 @@
private final class MediaRouterCallback extends MediaRouter.Callback {
@Override
public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo info) {
- updateItems();
+ updateItems(false);
}
@Override
public void onRouteRemoved(MediaRouter router, MediaRouter.RouteInfo info) {
- updateItems();
+ updateItems(false);
}
@Override
public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo info) {
- updateItems();
+ updateItems(false);
}
@Override
public void onRouteSelected(MediaRouter router, MediaRouter.RouteInfo route) {
- dismiss();
+ updateItems(false);
}
}
@@ -361,12 +379,12 @@
private final BluetoothController.Callback mCallback = new BluetoothController.Callback() {
@Override
public void onBluetoothStateChange(boolean enabled) {
- updateItems();
+ updateItems(false);
}
@Override
public void onBluetoothDevicesChanged() {
- updateItems();
+ updateItems(false);
}
};
@@ -393,7 +411,7 @@
public void handleMessage(Message message) {
switch (message.what) {
case MSG_UPDATE_ITEMS:
- updateItems();
+ updateItems((Boolean) message.obj);
break;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/OutputChooserLayout.java b/packages/SystemUI/src/com/android/systemui/volume/OutputChooserLayout.java
index 22ced60..d4c6f89 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/OutputChooserLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/OutputChooserLayout.java
@@ -29,8 +29,8 @@
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
-import android.widget.FrameLayout;
import android.widget.ImageView;
+import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.systemui.FontSizeUtils;
@@ -40,11 +40,10 @@
/**
* Limited height list of devices.
*/
-public class OutputChooserLayout extends FrameLayout {
+public class OutputChooserLayout extends LinearLayout {
private static final String TAG = "OutputChooserLayout";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private final int mQsDetailIconOverlaySize;
private final Context mContext;
private final H mHandler = new H();
private final Adapter mAdapter = new Adapter();
@@ -55,6 +54,7 @@
private AutoSizingList mItemList;
private View mEmpty;
private TextView mEmptyText;
+ private TextView mTitle;
private Item[] mItems;
@@ -62,8 +62,6 @@
super(context, attrs);
mContext = context;
mTag = TAG;
- mQsDetailIconOverlaySize = (int) getResources().getDimension(
- R.dimen.qs_detail_icon_overlay_size);
}
@Override
@@ -74,7 +72,8 @@
mItemList.setAdapter(mAdapter);
mEmpty = findViewById(android.R.id.empty);
mEmpty.setVisibility(GONE);
- mEmptyText = mEmpty.findViewById(android.R.id.title);
+ mEmptyText = mEmpty.findViewById(R.id.empty_text);
+ mTitle = findViewById(R.id.title);
}
@Override
@@ -84,17 +83,21 @@
int count = mItemList.getChildCount();
for (int i = 0; i < count; i++) {
View item = mItemList.getChildAt(i);
- FontSizeUtils.updateFontSize(item, android.R.id.title,
+ FontSizeUtils.updateFontSize(item, R.id.empty_text,
R.dimen.qs_detail_item_primary_text_size);
FontSizeUtils.updateFontSize(item, android.R.id.summary,
R.dimen.qs_detail_item_secondary_text_size);
+ FontSizeUtils.updateFontSize(item, android.R.id.title,
+ R.dimen.qs_detail_header_text_size);
}
}
+ public void setTitle(int title) {
+ mTitle.setText(title);
+ }
+
public void setEmptyState(String text) {
- mEmpty.post(() -> {
- mEmptyText.setText(text);
- });
+ mEmptyText.setText(text);
}
@Override
@@ -176,11 +179,6 @@
} else {
iv.setImageResource(item.iconResId);
}
- iv.getOverlay().clear();
- if (item.overlay != null) {
- item.overlay.setBounds(0, 0, mQsDetailIconOverlaySize, mQsDetailIconOverlaySize);
- iv.getOverlay().add(item.overlay);
- }
final TextView title = view.findViewById(android.R.id.title);
title.setText(item.line1);
final TextView summary = view.findViewById(android.R.id.summary);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 9f7c5a7..e76bf57 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -47,6 +47,7 @@
import android.os.SystemClock;
import android.provider.Settings;
import android.provider.Settings.Global;
+import android.support.v7.media.MediaRouter;
import android.util.Log;
import android.util.Slog;
import android.util.SparseBooleanArray;
@@ -858,7 +859,8 @@
if (mOutputChooserDialog != null) {
return;
}
- mOutputChooserDialog = new OutputChooserDialog(mContext) {
+ mOutputChooserDialog = new OutputChooserDialog(mContext,
+ new MediaRouterWrapper(MediaRouter.getInstance(mContext))) {
@Override
protected void cleanUp() {
synchronized (mOutputChooserLock) {
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index e74736a..859dc2f 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -46,6 +46,7 @@
<uses-permission android:name="android.permission.STATUS_BAR" />
<uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
<uses-permission android:name="android.permission.REAL_GET_TASKS" />
+ <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW" />
<application>
<uses-library android:name="android.test.runner" />
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index fbcbd20..4c7c1d1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -83,7 +83,7 @@
return null;
}
- public Context getContext() {
+ public SysuiTestableContext getContext() {
return mContext;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsManagerTest.java
new file mode 100644
index 0000000..8f38c2c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsManagerTest.java
@@ -0,0 +1,196 @@
+/*
+ * 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 com.android.systemui.statusbar;
+
+import static junit.framework.Assert.assertNotNull;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.times;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.NotificationGuts;
+import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.NotificationInflater;
+import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoRule;
+import org.mockito.junit.MockitoJUnit;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class NotificationGutsManagerTest extends SysuiTestCase {
+ private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId";
+
+ private final String mPackageName = mContext.getPackageName();
+ private final int mUid = Binder.getCallingUid();
+
+ private NotificationChannel mTestNotificationChannel = new NotificationChannel(
+ TEST_CHANNEL_ID, TEST_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT);
+ private TestableLooper mTestableLooper;
+ private Handler mHandler;
+ private NotificationTestHelper mHelper;
+ private NotificationGutsManager mGutsManager;
+
+ @Rule public MockitoRule mockito = MockitoJUnit.rule();
+ @Mock private NotificationLockscreenUserManager mLockscreenUserManager;
+ @Mock private NotificationPresenter mPresenter;
+ @Mock private NotificationEntryManager mEntryManager;
+ @Mock private NotificationStackScrollLayout mStackScroller;
+ @Mock private NotificationInfo.CheckSaveListener mCheckSaveListener;
+ @Mock private NotificationGutsManager.OnSettingsClickListener mOnSettingsClickListener;
+
+ @Before
+ public void setUp() {
+ mTestableLooper = TestableLooper.get(this);
+ mHandler = new Handler(mTestableLooper.getLooper());
+
+ mHelper = new NotificationTestHelper(mContext);
+
+ mGutsManager = new NotificationGutsManager(mContext);
+ mGutsManager.setUpWithPresenter(mPresenter, mEntryManager, mStackScroller,
+ mCheckSaveListener, mOnSettingsClickListener);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ // Test methods:
+
+ @Test
+ public void testOpenAndCloseGuts() {
+ NotificationGuts guts = spy(new NotificationGuts(mContext));
+ when(guts.post(any())).thenAnswer(invocation -> {
+ mHandler.post(((Runnable) invocation.getArguments()[0]));
+ return null;
+ });
+
+ // Test doesn't support animation since the guts view is not attached.
+ doNothing().when(guts).openControls(anyInt(), anyInt(), anyBoolean(), any(Runnable.class));
+
+ ExpandableNotificationRow realRow = createTestNotificationRow();
+ NotificationMenuRowPlugin.MenuItem menuItem = createTestMenuItem(realRow);
+
+ ExpandableNotificationRow row = spy(realRow);
+ when(row.getWindowToken()).thenReturn(new Binder());
+ when(row.getGuts()).thenReturn(guts);
+
+ mGutsManager.openGuts(row, 0, 0, menuItem);
+ assertEquals(View.INVISIBLE, guts.getVisibility());
+ mTestableLooper.processAllMessages();
+ verify(guts).openControls(anyInt(), anyInt(), anyBoolean(), any(Runnable.class));
+
+ assertEquals(View.VISIBLE, guts.getVisibility());
+ mGutsManager.closeAndSaveGuts(false, false, false, 0, 0, false);
+
+ verify(guts).closeControls(anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean());
+ verify(row, times(1)).setGutsView(any());
+ }
+
+ @Test
+ public void testChangeDensityOrFontScale() {
+ NotificationGuts guts = spy(new NotificationGuts(mContext));
+ when(guts.post(any())).thenAnswer(invocation -> {
+ mHandler.post(((Runnable) invocation.getArguments()[0]));
+ return null;
+ });
+
+ // Test doesn't support animation since the guts view is not attached.
+ doNothing().when(guts).openControls(anyInt(), anyInt(), anyBoolean(), any(Runnable.class));
+
+ ExpandableNotificationRow realRow = createTestNotificationRow();
+ NotificationMenuRowPlugin.MenuItem menuItem = createTestMenuItem(realRow);
+
+ ExpandableNotificationRow row = spy(realRow);
+ when(row.getWindowToken()).thenReturn(new Binder());
+ when(row.getGuts()).thenReturn(guts);
+ doNothing().when(row).inflateGuts();
+
+ mGutsManager.openGuts(row, 0, 0, menuItem);
+ mTestableLooper.processAllMessages();
+ verify(guts).openControls(anyInt(), anyInt(), anyBoolean(), any(Runnable.class));
+
+ row.onDensityOrFontScaleChanged();
+ mGutsManager.onDensityOrFontScaleChanged(row);
+ mTestableLooper.processAllMessages();
+
+ mGutsManager.closeAndSaveGuts(false, false, false, 0, 0, false);
+
+ verify(guts).closeControls(anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean());
+ verify(row, times(2)).setGutsView(any());
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ // Utility methods:
+
+ private ExpandableNotificationRow createTestNotificationRow() {
+ Notification.Builder nb = new Notification.Builder(mContext,
+ mTestNotificationChannel.getId())
+ .setContentTitle("foo")
+ .setColorized(true)
+ .setFlag(Notification.FLAG_CAN_COLORIZE, true)
+ .setSmallIcon(android.R.drawable.sym_def_app_icon);
+
+ try {
+ ExpandableNotificationRow row = mHelper.createRow(nb.build());
+ row.getEntry().channel = mTestNotificationChannel;
+ return row;
+ } catch (Exception e) {
+ fail();
+ return null;
+ }
+ }
+
+ private NotificationMenuRowPlugin.MenuItem createTestMenuItem(ExpandableNotificationRow row) {
+ NotificationMenuRowPlugin menuRow = new NotificationMenuRow(mContext);
+ menuRow.createMenu(row, row.getStatusBarNotification());
+
+ NotificationMenuRowPlugin.MenuItem menuItem = menuRow.getLongpressMenuItem(mContext);
+ assertNotNull(menuItem);
+ return menuItem;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
index 66524cc..2edcd01 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
@@ -74,6 +74,8 @@
assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.system_icon_area)
.getVisibility());
+ assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.clock)
+ .getVisibility());
}
@Test
@@ -87,11 +89,15 @@
assertEquals(View.INVISIBLE, mFragment.getView().findViewById(R.id.system_icon_area)
.getVisibility());
+ assertEquals(View.INVISIBLE, mFragment.getView().findViewById(R.id.clock)
+ .getVisibility());
fragment.disable(0, 0, false);
assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.system_icon_area)
.getVisibility());
+ assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.clock)
+ .getVisibility());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java
new file mode 100644
index 0000000..537d606
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java
@@ -0,0 +1,158 @@
+/*
+ * 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 com.android.systemui.volume;
+
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothProfile;
+import android.net.wifi.WifiManager;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v7.media.MediaRouter;
+import android.telecom.TelecomManager;
+import android.widget.TextView;
+
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.policy.BluetoothController;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class OutputChooserDialogTest extends SysuiTestCase {
+
+ OutputChooserDialog mDialog;
+
+ @Mock
+ private BluetoothController mController;
+ @Mock
+ private WifiManager mWifiManager;
+ @Mock
+ private TelecomManager mTelecomManager;
+
+ @Mock
+ private MediaRouterWrapper mRouter;
+
+
+ @Before
+ @UiThreadTest
+ public void setup() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mController = mDependency.injectMockDependency(BluetoothController.class);
+ when(mWifiManager.isWifiEnabled()).thenReturn(true);
+
+ getContext().addMockSystemService(WifiManager.class, mWifiManager);
+ getContext().addMockSystemService(TelecomManager.class, mTelecomManager);
+
+ mDialog = new OutputChooserDialog(getContext(), mRouter);
+ }
+
+ @After
+ @UiThreadTest
+ public void tearDown() throws Exception {
+ mDialog.dismiss();
+ }
+
+ private void showDialog() {
+ mDialog.show();
+ }
+
+ @Test
+ @UiThreadTest
+ public void testClickMediaRouterItemConnectsMedia() {
+ showDialog();
+
+ OutputChooserLayout.Item item = new OutputChooserLayout.Item();
+ item.deviceType = OutputChooserLayout.Item.DEVICE_TYPE_MEDIA_ROUTER;
+ MediaRouter.RouteInfo info = mock(MediaRouter.RouteInfo.class);
+ when(info.isEnabled()).thenReturn(true);
+ item.tag = info;
+
+ mDialog.onDetailItemClick(item);
+ verify(info, times(1)).select();
+ verify(mController, never()).connect(any());
+ }
+
+ @Test
+ @UiThreadTest
+ public void testClickBtItemConnectsBt() {
+ showDialog();
+
+ OutputChooserLayout.Item item = new OutputChooserLayout.Item();
+ item.deviceType = OutputChooserLayout.Item.DEVICE_TYPE_BT;
+ CachedBluetoothDevice btDevice = mock(CachedBluetoothDevice.class);
+ when(btDevice.getMaxConnectionState()).thenReturn(BluetoothProfile.STATE_DISCONNECTED);
+ item.tag = btDevice;
+
+ mDialog.onDetailItemClick(item);
+ verify(mController, times(1)).connect(any());
+ }
+
+ @Test
+ @UiThreadTest
+ public void testTitleNotInCall() {
+ showDialog();
+
+ assertTrue(((TextView) mDialog.findViewById(R.id.title))
+ .getText().toString().contains("Media"));
+ }
+
+ @Test
+ @UiThreadTest
+ public void testTitleInCall() {
+ mDialog.setIsInCall(true);
+ showDialog();
+
+ assertTrue(((TextView) mDialog.findViewById(R.id.title))
+ .getText().toString().contains("Phone"));
+ }
+
+ @Test
+ @UiThreadTest
+ public void testNoMediaScanIfInCall() {
+ mDialog.setIsInCall(true);
+ mDialog.onAttachedToWindow();
+
+ verify(mRouter, never()).addCallback(any(), any(), anyInt());
+ }
+
+ @Test
+ @UiThreadTest
+ public void testMediaScanIfNotInCall() {
+ mDialog.setIsInCall(false);
+ mDialog.onAttachedToWindow();
+
+ verify(mRouter, times(1)).addCallback(any(), any(), anyInt());
+ }
+}
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index f99ac5c..9b67f8f 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -5144,6 +5144,11 @@
// OS: P
APPLICATIONS_DIRECTORY_ACCESS_DETAIL = 1284;
+ // OPEN: Settings > Battery > Smart Battery > Restricted apps
+ // CATEGORY: SETTINGS
+ // OS: P
+ FUELGAUGE_RESTRICTED_APP_DETAILS = 1285;
+
// ---- End P Constants, all P constants go above this line ----
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
diff --git a/services/accessibility/java/com/android/server/accessibility/GlobalActionPerformer.java b/services/accessibility/java/com/android/server/accessibility/GlobalActionPerformer.java
index 3b8d4bc..672518c 100644
--- a/services/accessibility/java/com/android/server/accessibility/GlobalActionPerformer.java
+++ b/services/accessibility/java/com/android/server/accessibility/GlobalActionPerformer.java
@@ -21,6 +21,8 @@
import android.content.Context;
import android.hardware.input.InputManager;
import android.os.Binder;
+import android.os.Handler;
+import android.os.Looper;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -30,20 +32,34 @@
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ScreenshotHelper;
import com.android.server.LocalServices;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.wm.WindowManagerInternal;
+import java.util.function.Supplier;
+
/**
* Handle the back-end of AccessibilityService#performGlobalAction
*/
public class GlobalActionPerformer {
private final WindowManagerInternal mWindowManagerService;
private final Context mContext;
+ private Supplier<ScreenshotHelper> mScreenshotHelperSupplier;
public GlobalActionPerformer(Context context, WindowManagerInternal windowManagerInternal) {
mContext = context;
mWindowManagerService = windowManagerInternal;
+ mScreenshotHelperSupplier = null;
+ }
+
+ // Used to mock ScreenshotHelper
+ @VisibleForTesting
+ public GlobalActionPerformer(Context context, WindowManagerInternal windowManagerInternal,
+ Supplier<ScreenshotHelper> screenshotHelperSupplier) {
+ this(context, windowManagerInternal);
+ mScreenshotHelperSupplier = screenshotHelperSupplier;
}
public boolean performGlobalAction(int action) {
@@ -79,6 +95,9 @@
case AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN: {
return lockScreen();
}
+ case AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT: {
+ return takeScreenshot();
+ }
}
return false;
} finally {
@@ -167,4 +186,12 @@
mWindowManagerService.lockNow();
return true;
}
+
+ private boolean takeScreenshot() {
+ ScreenshotHelper screenshotHelper = (mScreenshotHelperSupplier != null)
+ ? mScreenshotHelperSupplier.get() : new ScreenshotHelper(mContext);
+ screenshotHelper.takeScreenshot(android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN,
+ true, true, new Handler(Looper.getMainLooper()));
+ return true;
+ }
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 6d845f9..978ed25 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -52,7 +52,6 @@
import android.os.UserManager;
import android.os.UserManagerInternal;
import android.provider.Settings;
-import android.service.autofill.AutofillFieldClassificationService.Scores;
import android.service.autofill.FillEventHistory;
import android.service.autofill.UserData;
import android.util.LocalLog;
@@ -646,37 +645,35 @@
}
@Override
- public void getDefaultFieldClassificationAlgorithm(RemoteCallback callback)
- throws RemoteException {
+ public String getDefaultFieldClassificationAlgorithm() throws RemoteException {
final int userId = UserHandle.getCallingUserId();
synchronized (mLock) {
final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
if (service != null) {
- service.getDefaultFieldClassificationAlgorithm(getCallingUid(), callback);
+ return service.getDefaultFieldClassificationAlgorithm(getCallingUid());
} else {
if (sVerbose) {
Slog.v(TAG, "getDefaultFcAlgorithm(): no service for " + userId);
}
- callback.sendResult(null);
- }
+ return null;
+ }
}
}
@Override
- public void getAvailableFieldClassificationAlgorithms(RemoteCallback callback)
- throws RemoteException {
+ public String[] getAvailableFieldClassificationAlgorithms() throws RemoteException {
final int userId = UserHandle.getCallingUserId();
synchronized (mLock) {
final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
if (service != null) {
- service.getAvailableFieldClassificationAlgorithms(getCallingUid(), callback);
+ return service.getAvailableFieldClassificationAlgorithms(getCallingUid());
} else {
if (sVerbose) {
Slog.v(TAG, "getAvailableFcAlgorithms(): no service for " + userId);
}
- callback.sendResult(null);
+ return null;
}
}
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index a5bd59a9..6bcfc4b 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -1153,22 +1153,22 @@
return mFieldClassificationStrategy;
}
- void getAvailableFieldClassificationAlgorithms(int callingUid, RemoteCallback callback) {
+ String[] getAvailableFieldClassificationAlgorithms(int callingUid) {
synchronized (mLock) {
if (!isCalledByServiceLocked("getFCAlgorithms()", callingUid)) {
- return;
+ return null;
}
}
- mFieldClassificationStrategy.getAvailableAlgorithms(callback);
+ return mFieldClassificationStrategy.getAvailableAlgorithms();
}
- void getDefaultFieldClassificationAlgorithm(int callingUid, RemoteCallback callback) {
+ String getDefaultFieldClassificationAlgorithm(int callingUid) {
synchronized (mLock) {
if (!isCalledByServiceLocked("getDefaultFCAlgorithm()", callingUid)) {
- return;
+ return null;
}
}
- mFieldClassificationStrategy.getDefaultAlgorithm(callback);
+ return mFieldClassificationStrategy.getDefaultAlgorithm();
}
@Override
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
index 4456087..4d69ef9 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
@@ -191,10 +191,7 @@
if (scores == null) {
pw.println("no score");
} else {
- pw.print("algorithm: ");
- pw.print(scores.getAlgorithm());
- pw.print(" score: ");
- pw.println(scores.getScores()[0][0]);
+ pw.println(scores.scores[0][0]);
}
latch.countDown();
}));
diff --git a/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java b/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java
index 7228f1d..da52201 100644
--- a/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java
+++ b/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java
@@ -15,12 +15,12 @@
*/
package com.android.server.autofill;
-import static android.view.autofill.AutofillManager.EXTRA_AVAILABLE_ALGORITHMS;
-import static android.view.autofill.AutofillManager.EXTRA_DEFAULT_ALGORITHM;
import static android.view.autofill.AutofillManager.FC_SERVICE_TIMEOUT;
import static com.android.server.autofill.Helper.sDebug;
import static com.android.server.autofill.Helper.sVerbose;
+import static android.service.autofill.AutofillFieldClassificationService.SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS;
+import static android.service.autofill.AutofillFieldClassificationService.SERVICE_META_DATA_KEY_DEFAULT_ALGORITHM;
import android.Manifest;
import android.annotation.MainThread;
@@ -33,6 +33,7 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
@@ -49,6 +50,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -80,7 +82,8 @@
mUserId = userId;
}
- private ComponentName getServiceComponentName() {
+ @Nullable
+ private ServiceInfo getServiceInfo() {
final String packageName =
mContext.getPackageManager().getServicesSystemSharedLibraryPackageName();
if (packageName == null) {
@@ -96,9 +99,15 @@
Slog.w(TAG, "No valid components found.");
return null;
}
- final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
- final ComponentName name = new ComponentName(serviceInfo.packageName, serviceInfo.name);
+ return resolveInfo.serviceInfo;
+ }
+ @Nullable
+ private ComponentName getServiceComponentName() {
+ final ServiceInfo serviceInfo = getServiceInfo();
+ if (serviceInfo == null) return null;
+
+ final ComponentName name = new ComponentName(serviceInfo.packageName, serviceInfo.name);
if (!Manifest.permission.BIND_AUTOFILL_FIELD_CLASSIFICATION_SERVICE
.equals(serviceInfo.permission)) {
Slog.w(TAG, name.flattenToShortString() + " does not require permission "
@@ -204,12 +213,40 @@
}
}
- void getAvailableAlgorithms(RemoteCallback callback) {
- connectAndRun((service) -> service.getAvailableAlgorithms(callback));
+ /**
+ * Gets the name of all available algorithms.
+ */
+ @Nullable
+ String[] getAvailableAlgorithms() {
+ return getMetadataValue(SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS,
+ (res, id) -> res.getStringArray(id));
}
- void getDefaultAlgorithm(RemoteCallback callback) {
- connectAndRun((service) -> service.getDefaultAlgorithm(callback));
+ /**
+ * Gets the default algorithm that's used when an algorithm is not specified or is invalid.
+ */
+ @Nullable
+ String getDefaultAlgorithm() {
+ return getMetadataValue(SERVICE_META_DATA_KEY_DEFAULT_ALGORITHM, (res, id) -> res.getString(id));
+ }
+
+ @Nullable
+ private <T> T getMetadataValue(String field, MetadataParser<T> parser) {
+ final ServiceInfo serviceInfo = getServiceInfo();
+ if (serviceInfo == null) return null;
+
+ final PackageManager pm = mContext.getPackageManager();
+
+ final Resources res;
+ try {
+ res = pm.getResourcesForApplication(serviceInfo.applicationInfo);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Error getting application resources for " + serviceInfo, e);
+ return null;
+ }
+
+ final int resourceId = serviceInfo.metaData.getInt(field);
+ return parser.get(res, resourceId);
}
//TODO(b/70291841): rename this method (and all others in the chain) to something like
@@ -237,43 +274,16 @@
}
pw.println(impl.flattenToShortString());
- final CountDownLatch latch = new CountDownLatch(2);
-
- // Lock used to make sure lines don't overlap
- final Object lock = latch;
-
- connectAndRun((service) -> service.getAvailableAlgorithms(new RemoteCallback((bundle) -> {
- synchronized (lock) {
- pw.print(prefix); pw.print("Available algorithms: ");
- pw.println(bundle.getStringArrayList(EXTRA_AVAILABLE_ALGORITHMS));
- }
- latch.countDown();
- })));
-
- connectAndRun((service) -> service.getDefaultAlgorithm(new RemoteCallback((bundle) -> {
- synchronized (lock) {
- pw.print(prefix); pw.print("Default algorithm: ");
- pw.println(bundle.getString(EXTRA_DEFAULT_ALGORITHM));
- }
- latch.countDown();
- })));
-
- try {
- if (!latch.await(FC_SERVICE_TIMEOUT, TimeUnit.MILLISECONDS)) {
- synchronized (lock) {
- pw.print(prefix); pw.print("timeout ("); pw.print(FC_SERVICE_TIMEOUT);
- pw.println("ms) waiting for service");
- }
- }
- } catch (InterruptedException e) {
- synchronized (lock) {
- pw.print(prefix); pw.println("interrupted while waiting for service");
- }
- Thread.currentThread().interrupt();
- }
+ pw.print(prefix); pw.print("Available algorithms: ");
+ pw.println(Arrays.toString(getAvailableAlgorithms()));
+ pw.print(prefix); pw.print("Default algorithm: "); pw.println(getDefaultAlgorithm());
}
- private interface Command {
+ private static interface Command {
void run(IAutofillFieldClassificationService service) throws RemoteException;
}
+
+ private static interface MetadataParser<T> {
+ T get(Resources res, int resId);
+ }
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index a0e23a1..63f8384 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -1172,8 +1172,6 @@
Slog.w(TAG, "No field classification score on " + result);
return;
}
- final float[][] scoresMatrix = scores.getScores();
-
int i = 0, j = 0;
try {
for (i = 0; i < viewsSize; i++) {
@@ -1182,8 +1180,7 @@
ArrayList<Match> matches = null;
for (j = 0; j < userValues.length; j++) {
String remoteId = remoteIds[j];
- final String actualAlgorithm = scores.getAlgorithm();
- final float score = scoresMatrix[i][j];
+ final float score = scores.scores[i][j];
if (score > 0) {
if (sVerbose) {
Slog.v(TAG, "adding score " + score + " at index " + j + " and id "
@@ -1192,7 +1189,7 @@
if (matches == null) {
matches = new ArrayList<>(userValues.length);
}
- matches.add(new Match(remoteId, score, actualAlgorithm));
+ matches.add(new Match(remoteId, score));
}
else if (sVerbose) {
Slog.v(TAG, "skipping score 0 at index " + j + " and id " + fieldId);
@@ -1205,7 +1202,7 @@
}
} catch (ArrayIndexOutOfBoundsException e) {
Slog.wtf(TAG, "Error accessing FC score at " + i + " x " + j + ": "
- + Arrays.toString(scoresMatrix), e);
+ + Arrays.toString(scores.scores), e);
return;
}
diff --git a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
index 5188910..465bb09 100644
--- a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
@@ -3464,16 +3464,21 @@
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BACKUP, "isAppEligibleForBackup");
- String callerLogString = "BMS.isAppEligibleForBackup";
- TransportClient transportClient =
- mTransportManager.getCurrentTransportClient(callerLogString);
- boolean eligible =
- AppBackupUtils.appIsRunningAndEligibleForBackupWithTransport(
- transportClient, packageName, mPackageManager);
- if (transportClient != null) {
- mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
+ long oldToken = Binder.clearCallingIdentity();
+ try {
+ String callerLogString = "BMS.isAppEligibleForBackup";
+ TransportClient transportClient =
+ mTransportManager.getCurrentTransportClient(callerLogString);
+ boolean eligible =
+ AppBackupUtils.appIsRunningAndEligibleForBackupWithTransport(
+ transportClient, packageName, mPackageManager);
+ if (transportClient != null) {
+ mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
+ }
+ return eligible;
+ } finally {
+ Binder.restoreCallingIdentity(oldToken);
}
- return eligible;
}
@Override
@@ -3481,21 +3486,26 @@
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BACKUP, "filterAppsEligibleForBackup");
- String callerLogString = "BMS.filterAppsEligibleForBackup";
- TransportClient transportClient =
- mTransportManager.getCurrentTransportClient(callerLogString);
- List<String> eligibleApps = new LinkedList<>();
- for (String packageName : packages) {
- if (AppBackupUtils
- .appIsRunningAndEligibleForBackupWithTransport(
- transportClient, packageName, mPackageManager)) {
- eligibleApps.add(packageName);
+ long oldToken = Binder.clearCallingIdentity();
+ try {
+ String callerLogString = "BMS.filterAppsEligibleForBackup";
+ TransportClient transportClient =
+ mTransportManager.getCurrentTransportClient(callerLogString);
+ List<String> eligibleApps = new LinkedList<>();
+ for (String packageName : packages) {
+ if (AppBackupUtils
+ .appIsRunningAndEligibleForBackupWithTransport(
+ transportClient, packageName, mPackageManager)) {
+ eligibleApps.add(packageName);
+ }
}
+ if (transportClient != null) {
+ mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
+ }
+ return eligibleApps.toArray(new String[eligibleApps.size()]);
+ } finally {
+ Binder.restoreCallingIdentity(oldToken);
}
- if (transportClient != null) {
- mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
- }
- return eligibleApps.toArray(new String[eligibleApps.size()]);
}
@Override
@@ -3514,6 +3524,9 @@
} else if ("agents".startsWith(arg)) {
dumpAgents(pw);
return;
+ } else if ("transportclients".equals(arg.toLowerCase())) {
+ mTransportManager.dump(pw);
+ return;
}
}
}
@@ -3576,6 +3589,8 @@
}
}
+ mTransportManager.dump(pw);
+
pw.println("Pending init: " + mPendingInits.size());
for (String s : mPendingInits) {
pw.println(" " + s);
diff --git a/services/backup/java/com/android/server/backup/TransportManager.java b/services/backup/java/com/android/server/backup/TransportManager.java
index 6d9231d..7e179e5 100644
--- a/services/backup/java/com/android/server/backup/TransportManager.java
+++ b/services/backup/java/com/android/server/backup/TransportManager.java
@@ -43,6 +43,7 @@
import com.android.server.backup.transport.TransportNotAvailableException;
import com.android.server.backup.transport.TransportNotRegisteredException;
+import java.io.PrintWriter;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -633,6 +634,10 @@
!Thread.holdsLock(mTransportLock), "Can't call transport with transport lock held");
}
+ public void dump(PrintWriter pw) {
+ mTransportClientManager.dump(pw);
+ }
+
private static Predicate<ComponentName> fromPackageFilter(String packageName) {
return transportComponent -> packageName.equals(transportComponent.getPackageName());
}
diff --git a/services/backup/java/com/android/server/backup/transport/TransportClient.java b/services/backup/java/com/android/server/backup/transport/TransportClient.java
index 399f338..7b2e3df 100644
--- a/services/backup/java/com/android/server/backup/transport/TransportClient.java
+++ b/services/backup/java/com/android/server/backup/transport/TransportClient.java
@@ -16,6 +16,8 @@
package com.android.server.backup.transport;
+import static com.android.server.backup.transport.TransportUtils.formatMessage;
+
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.WorkerThread;
@@ -28,6 +30,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.UserHandle;
+import android.text.format.DateFormat;
import android.util.ArrayMap;
import android.util.EventLog;
import android.util.Log;
@@ -41,6 +44,9 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
@@ -65,6 +71,7 @@
*/
public class TransportClient {
private static final String TAG = "TransportClient";
+ private static final int LOG_BUFFER_SIZE = 5;
private final Context mContext;
private final Intent mBindIntent;
@@ -73,6 +80,10 @@
private final Handler mListenerHandler;
private final String mPrefixForLog;
private final Object mStateLock = new Object();
+ private final Object mLogBufferLock = new Object();
+
+ @GuardedBy("mLogBufferLock")
+ private final List<String> mLogBuffer = new LinkedList<>();
@GuardedBy("mStateLock")
private final Map<TransportConnectionListener, String> mListeners = new ArrayMap<>();
@@ -112,7 +123,7 @@
// For logging
String classNameForLog = mTransportComponent.getShortClassName().replaceFirst(".*\\.", "");
- mPrefixForLog = classNameForLog + "#" + mIdentifier + ": ";
+ mPrefixForLog = classNameForLog + "#" + mIdentifier + ":";
}
public ComponentName getTransportComponent() {
@@ -229,7 +240,7 @@
switch (mState) {
case State.UNUSABLE:
- log(Log.DEBUG, caller, "Async connect: UNUSABLE client");
+ log(Log.WARN, caller, "Async connect: UNUSABLE client");
notifyListener(listener, null, caller);
break;
case State.IDLE:
@@ -324,14 +335,14 @@
IBackupTransport transport = mTransport;
if (transport != null) {
- log(Log.DEBUG, caller, "Sync connect: reusing transport");
+ log(Log.INFO, caller, "Sync connect: reusing transport");
return transport;
}
// If it's already UNUSABLE we return straight away, no need to go to main-thread
synchronized (mStateLock) {
if (mState == State.UNUSABLE) {
- log(Log.DEBUG, caller, "Sync connect: UNUSABLE client");
+ log(Log.WARN, caller, "Sync connect: UNUSABLE client");
return null;
}
}
@@ -403,13 +414,16 @@
}
private void notifyListener(
- TransportConnectionListener listener, IBackupTransport transport, String caller) {
- log(Log.VERBOSE, caller, "Notifying listener of transport = " + transport);
+ TransportConnectionListener listener,
+ @Nullable IBackupTransport transport,
+ String caller) {
+ String transportString = (transport != null) ? "IBackupTransport" : "null";
+ log(Log.INFO, "Notifying [" + caller + "] transport = " + transportString);
mListenerHandler.post(() -> listener.onTransportConnectionResult(transport, this));
}
@GuardedBy("mStateLock")
- private void notifyListenersAndClearLocked(IBackupTransport transport) {
+ private void notifyListenersAndClearLocked(@Nullable IBackupTransport transport) {
for (Map.Entry<TransportConnectionListener, String> entry : mListeners.entrySet()) {
TransportConnectionListener listener = entry.getKey();
String caller = entry.getValue();
@@ -509,13 +523,30 @@
}
private void log(int priority, String message) {
- TransportUtils.log(priority, TAG, message);
+ TransportUtils.log(priority, TAG, formatMessage(mPrefixForLog, null, message));
+ saveLogEntry(formatMessage(null, null, message));
}
- private void log(int priority, String caller, String msg) {
- TransportUtils.log(priority, TAG, mPrefixForLog, caller, msg);
- // TODO(brufino): Log in internal list for dump
- // CharSequence time = DateFormat.format("yyyy-MM-dd HH:mm:ss", System.currentTimeMillis());
+ private void log(int priority, String caller, String message) {
+ TransportUtils.log(priority, TAG, formatMessage(mPrefixForLog, caller, message));
+ saveLogEntry(formatMessage(null, caller, message));
+ }
+
+ private void saveLogEntry(String message) {
+ CharSequence time = DateFormat.format("yyyy-MM-dd HH:mm:ss", System.currentTimeMillis());
+ message = time + " " + message;
+ synchronized (mLogBufferLock) {
+ if (mLogBuffer.size() == LOG_BUFFER_SIZE) {
+ mLogBuffer.remove(mLogBuffer.size() - 1);
+ }
+ mLogBuffer.add(0, message);
+ }
+ }
+
+ List<String> getLogBuffer() {
+ synchronized (mLogBufferLock) {
+ return Collections.unmodifiableList(mLogBuffer);
+ }
}
@IntDef({Transition.DOWN, Transition.NO_TRANSITION, Transition.UP})
diff --git a/services/backup/java/com/android/server/backup/transport/TransportClientManager.java b/services/backup/java/com/android/server/backup/transport/TransportClientManager.java
index 1cbe7471..1132bce 100644
--- a/services/backup/java/com/android/server/backup/transport/TransportClientManager.java
+++ b/services/backup/java/com/android/server/backup/transport/TransportClientManager.java
@@ -17,19 +17,20 @@
package com.android.server.backup.transport;
import static com.android.server.backup.TransportManager.SERVICE_ACTION_TRANSPORT_HOST;
+import static com.android.server.backup.transport.TransportUtils.formatMessage;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
-
import com.android.server.backup.TransportManager;
+import java.io.PrintWriter;
+import java.util.Map;
+import java.util.WeakHashMap;
/**
* Manages the creation and disposal of {@link TransportClient}s. The only class that should use
* this is {@link TransportManager}, all the other usages should go to {@link TransportManager}.
- *
- * <p>TODO(brufino): Implement pool of TransportClients
*/
public class TransportClientManager {
private static final String TAG = "TransportClientManager";
@@ -37,6 +38,7 @@
private final Context mContext;
private final Object mTransportClientsLock = new Object();
private int mTransportClientsCreated = 0;
+ private Map<TransportClient, String> mTransportClientsCallerMap = new WeakHashMap<>();
public TransportClientManager(Context context) {
mContext = context;
@@ -62,8 +64,10 @@
bindIntent,
transportComponent,
Integer.toString(mTransportClientsCreated));
+ mTransportClientsCallerMap.put(transportClient, caller);
mTransportClientsCreated++;
- TransportUtils.log(Log.DEBUG, TAG, caller, "Retrieving " + transportClient);
+ TransportUtils.log(
+ Log.DEBUG, TAG, formatMessage(null, caller, "Retrieving " + transportClient));
return transportClient;
}
}
@@ -77,7 +81,25 @@
* details.
*/
public void disposeOfTransportClient(TransportClient transportClient, String caller) {
- TransportUtils.log(Log.DEBUG, TAG, caller, "Disposing of " + transportClient);
transportClient.unbind(caller);
+ synchronized (mTransportClientsLock) {
+ TransportUtils.log(
+ Log.DEBUG, TAG, formatMessage(null, caller, "Disposing of " + transportClient));
+ mTransportClientsCallerMap.remove(transportClient);
+ }
+ }
+
+ public void dump(PrintWriter pw) {
+ pw.println("Transport clients created: " + mTransportClientsCreated);
+ synchronized (mTransportClientsLock) {
+ pw.println("Current transport clients: " + mTransportClientsCallerMap.size());
+ for (TransportClient transportClient : mTransportClientsCallerMap.keySet()) {
+ String caller = mTransportClientsCallerMap.get(transportClient);
+ pw.println(" " + transportClient + " [" + caller + "]");
+ for (String logEntry : transportClient.getLogBuffer()) {
+ pw.println(" " + logEntry);
+ }
+ }
+ }
}
}
diff --git a/services/backup/java/com/android/server/backup/transport/TransportUtils.java b/services/backup/java/com/android/server/backup/transport/TransportUtils.java
index 92bba9b..56b2d44 100644
--- a/services/backup/java/com/android/server/backup/transport/TransportUtils.java
+++ b/services/backup/java/com/android/server/backup/transport/TransportUtils.java
@@ -41,22 +41,21 @@
}
static void log(int priority, String tag, String message) {
- log(priority, tag, null, message);
- }
-
- static void log(int priority, String tag, @Nullable String caller, String message) {
- log(priority, tag, "", caller, message);
- }
-
- static void log(
- int priority, String tag, String prefix, @Nullable String caller, String message) {
if (Log.isLoggable(tag, priority)) {
- if (caller != null) {
- prefix += "[" + caller + "] ";
- }
- Slog.println(priority, tag, prefix + message);
+ Slog.println(priority, tag, message);
}
}
+ static String formatMessage(@Nullable String prefix, @Nullable String caller, String message) {
+ StringBuilder string = new StringBuilder();
+ if (prefix != null) {
+ string.append(prefix).append(" ");
+ }
+ if (caller != null) {
+ string.append("[").append(caller).append("] ");
+ }
+ return string.append(message).toString();
+ }
+
private TransportUtils() {}
}
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 4ffa5f1..f4675fd 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -1316,14 +1316,8 @@
isPrivileged = (appInfo.privateFlags
& ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
} else {
- if ("media".equals(packageName)) {
- pkgUid = Process.MEDIA_UID;
- isPrivileged = false;
- } else if ("audioserver".equals(packageName)) {
- pkgUid = Process.AUDIOSERVER_UID;
- isPrivileged = false;
- } else if ("cameraserver".equals(packageName)) {
- pkgUid = Process.CAMERASERVER_UID;
+ pkgUid = resolveUid(packageName);
+ if (pkgUid >= 0) {
isPrivileged = false;
}
}
@@ -1957,9 +1951,8 @@
if (nonpackageUid != -1) {
packageName = null;
} else {
- if ("root".equals(packageName)) {
- packageUid = 0;
- } else {
+ packageUid = resolveUid(packageName);
+ if (packageUid < 0) {
packageUid = AppGlobals.getPackageManager().getPackageUid(packageName,
PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
}
@@ -2052,6 +2045,10 @@
}
if (ops == null || ops.size() <= 0) {
pw.println("No operations.");
+ if (shell.op > AppOpsManager.OP_NONE && shell.op < AppOpsManager._NUM_OP) {
+ pw.println("Default mode: " + AppOpsManager.modeToString(
+ AppOpsManager.opToDefaultMode(shell.op)));
+ }
return 0;
}
final long now = System.currentTimeMillis();
@@ -2061,24 +2058,7 @@
AppOpsManager.OpEntry ent = entries.get(j);
pw.print(AppOpsManager.opToName(ent.getOp()));
pw.print(": ");
- switch (ent.getMode()) {
- case AppOpsManager.MODE_ALLOWED:
- pw.print("allow");
- break;
- case AppOpsManager.MODE_IGNORED:
- pw.print("ignore");
- break;
- case AppOpsManager.MODE_ERRORED:
- pw.print("deny");
- break;
- case AppOpsManager.MODE_DEFAULT:
- pw.print("default");
- break;
- default:
- pw.print("mode=");
- pw.print(ent.getMode());
- break;
- }
+ pw.print(AppOpsManager.modeToString(ent.getMode()));
if (ent.getTime() != 0) {
pw.print("; time=");
TimeUtils.formatDuration(now - ent.getTime(), pw);
@@ -2563,16 +2543,41 @@
}
private static String resolvePackageName(int uid, String packageName) {
- if (uid == 0) {
+ if (uid == Process.ROOT_UID) {
return "root";
} else if (uid == Process.SHELL_UID) {
return "com.android.shell";
+ } else if (uid == Process.MEDIA_UID) {
+ return "media";
+ } else if (uid == Process.AUDIOSERVER_UID) {
+ return "audioserver";
+ } else if (uid == Process.CAMERASERVER_UID) {
+ return "cameraserver";
} else if (uid == Process.SYSTEM_UID && packageName == null) {
return "android";
}
return packageName;
}
+ private static int resolveUid(String packageName) {
+ if (packageName == null) {
+ return -1;
+ }
+ switch (packageName) {
+ case "root":
+ return Process.ROOT_UID;
+ case "shell":
+ return Process.SHELL_UID;
+ case "media":
+ return Process.MEDIA_UID;
+ case "audioserver":
+ return Process.AUDIOSERVER_UID;
+ case "cameraserver":
+ return Process.CAMERASERVER_UID;
+ }
+ return -1;
+ }
+
private static String[] getPackagesForUid(int uid) {
String[] packageNames = null;
try {
diff --git a/services/core/java/com/android/server/ForceAppStandbyTracker.java b/services/core/java/com/android/server/ForceAppStandbyTracker.java
index 0b3bc72..de113a6 100644
--- a/services/core/java/com/android/server/ForceAppStandbyTracker.java
+++ b/services/core/java/com/android/server/ForceAppStandbyTracker.java
@@ -818,21 +818,23 @@
* @return whether alarms should be restricted for a UID package-name.
*/
public boolean areAlarmsRestricted(int uid, @NonNull String packageName) {
- return isRestricted(uid, packageName, /*useTempWhitelistToo=*/ false);
+ return isRestricted(uid, packageName, /*useTempWhitelistToo=*/ false,
+ /* exemptOnBatterySaver =*/ false);
}
/**
* @return whether jobs should be restricted for a UID package-name.
*/
public boolean areJobsRestricted(int uid, @NonNull String packageName) {
- return isRestricted(uid, packageName, /*useTempWhitelistToo=*/ true);
+ return isRestricted(uid, packageName, /*useTempWhitelistToo=*/ true,
+ /* exemptOnBatterySaver =*/ false);
}
/**
* @return whether force-app-standby is effective for a UID package-name.
*/
private boolean isRestricted(int uid, @NonNull String packageName,
- boolean useTempWhitelistToo) {
+ boolean useTempWhitelistToo, boolean exemptOnBatterySaver) {
if (isInForeground(uid)) {
return false;
}
@@ -846,12 +848,13 @@
ArrayUtils.contains(mTempWhitelistedAppIds, appId)) {
return false;
}
-
- if (mForceAllAppsStandby) {
+ if (mForcedAppStandbyEnabled && isRunAnyRestrictedLocked(uid, packageName)) {
return true;
}
-
- return mForcedAppStandbyEnabled && isRunAnyRestrictedLocked(uid, packageName);
+ if (exemptOnBatterySaver) {
+ return false;
+ }
+ return mForceAllAppsStandby;
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 29d33ce..c0c684c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8601,6 +8601,16 @@
}
return false;
}
+
+ @Override
+ public int getPackageUid(String packageName, int flags) {
+ try {
+ return mActivityManagerService.mContext.getPackageManager()
+ .getPackageUid(packageName, flags);
+ } catch (NameNotFoundException nnfe) {
+ return -1;
+ }
+ }
}
class IntentFirewallInterface implements IntentFirewall.AMSInterface {
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 81e8eb0..207aaa7 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -40,6 +40,7 @@
import android.os.WorkSource;
import android.os.WorkSource.WorkChain;
import android.os.connectivity.CellularBatteryStats;
+import android.os.connectivity.GpsBatteryStats;
import android.os.health.HealthStatsParceler;
import android.os.health.HealthStatsWriter;
import android.os.health.UidHealthStats;
@@ -594,6 +595,12 @@
}
}
+ public void noteGpsSignalQuality(int signalLevel) {
+ synchronized (mStats) {
+ mStats.noteGpsSignalQualityLocked(signalLevel);
+ }
+ }
+
public void noteScreenState(int state) {
enforceCallingPermission();
if (DBG) Slog.d(TAG, "begin noteScreenState");
@@ -1448,6 +1455,16 @@
}
/**
+ * Gets a snapshot of Gps stats
+ * @hide
+ */
+ public GpsBatteryStats getGpsBatteryStats() {
+ synchronized (mStats) {
+ return mStats.getGpsBatteryStats();
+ }
+ }
+
+ /**
* Gets a snapshot of the system health for a particular uid.
*/
@Override
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 799f2a9..a714720 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1047,9 +1047,11 @@
private void checkMuteAffectedStreams() {
// any stream with a min level > 0 is not muteable by definition
+ // STREAM_VOICE_CALL can be muted by applications that has the the MODIFY_PHONE_STATE permission.
for (int i = 0; i < mStreamStates.length; i++) {
final VolumeStreamState vss = mStreamStates[i];
- if (vss.mIndexMin > 0) {
+ if (vss.mIndexMin > 0 &&
+ vss.mStreamType != AudioSystem.STREAM_VOICE_CALL) {
mMuteAffectedStreams &= ~(1 << vss.mStreamType);
}
}
@@ -1412,6 +1414,18 @@
return;
}
+ // If adjust is mute and the stream is STREAM_VOICE_CALL, make sure
+ // that the calling app have the MODIFY_PHONE_STATE permission.
+ if (isMuteAdjust &&
+ streamType == AudioSystem.STREAM_VOICE_CALL &&
+ mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.MODIFY_PHONE_STATE)
+ != PackageManager.PERMISSION_GRANTED) {
+ Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: adjustStreamVolume from pid="
+ + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+
// use stream type alias here so that streams with same alias have the same behavior,
// including with regard to silent mode control (e.g the use of STREAM_RING below and in
// checkForRingerModeChange() in place of STREAM_RING or STREAM_NOTIFICATION)
@@ -1712,6 +1726,15 @@
+ " CHANGE_ACCESSIBILITY_VOLUME callingPackage=" + callingPackage);
return;
}
+ if ((streamType == AudioManager.STREAM_VOICE_CALL) &&
+ (index == 0) &&
+ (mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.MODIFY_PHONE_STATE)
+ != PackageManager.PERMISSION_GRANTED)) {
+ Log.w(TAG, "Trying to call setStreamVolume() for STREAM_VOICE_CALL and index 0 without"
+ + " MODIFY_PHONE_STATE callingPackage=" + callingPackage);
+ return;
+ }
mVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_SET_STREAM_VOL, streamType,
index/*val1*/, flags/*val2*/, callingPackage));
setStreamVolume(streamType, index, flags, callingPackage, callingPackage,
@@ -4132,22 +4155,30 @@
public int setBluetoothA2dpDeviceConnectionState(BluetoothDevice device, int state, int profile)
{
+ return setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
+ device, state, profile, false /* suppressNoisyIntent */);
+ }
+
+ public int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(BluetoothDevice device,
+ int state, int profile, boolean suppressNoisyIntent)
+ {
if (mAudioHandler.hasMessages(MSG_SET_A2DP_SINK_CONNECTION_STATE, device)) {
return 0;
}
return setBluetoothA2dpDeviceConnectionStateInt(
- device, state, profile, AudioSystem.DEVICE_NONE);
+ device, state, profile, suppressNoisyIntent, AudioSystem.DEVICE_NONE);
}
public int setBluetoothA2dpDeviceConnectionStateInt(
- BluetoothDevice device, int state, int profile, int musicDevice)
+ BluetoothDevice device, int state, int profile, boolean suppressNoisyIntent,
+ int musicDevice)
{
int delay;
if (profile != BluetoothProfile.A2DP && profile != BluetoothProfile.A2DP_SINK) {
throw new IllegalArgumentException("invalid profile " + profile);
}
synchronized (mConnectedDevices) {
- if (profile == BluetoothProfile.A2DP) {
+ if (profile == BluetoothProfile.A2DP && !suppressNoisyIntent) {
int intState = (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0;
delay = checkSendBecomingNoisyIntent(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
intState, musicDevice);
@@ -4503,27 +4534,30 @@
if (mStreamType == srcStream.mStreamType) {
return;
}
- synchronized (VolumeStreamState.class) {
- int srcStreamType = srcStream.getStreamType();
- // apply default device volume from source stream to all devices first in case
- // some devices are present in this stream state but not in source stream state
- int index = srcStream.getIndex(AudioSystem.DEVICE_OUT_DEFAULT);
- index = rescaleIndex(index, srcStreamType, mStreamType);
- for (int i = 0; i < mIndexMap.size(); i++) {
- mIndexMap.put(mIndexMap.keyAt(i), index);
- }
- // Now apply actual volume for devices in source stream state
- SparseIntArray srcMap = srcStream.mIndexMap;
- for (int i = 0; i < srcMap.size(); i++) {
- int device = srcMap.keyAt(i);
- index = srcMap.valueAt(i);
+ synchronized (mSettingsLock) {
+ synchronized (VolumeStreamState.class) {
+ int srcStreamType = srcStream.getStreamType();
+ // apply default device volume from source stream to all devices first in case
+ // some devices are present in this stream state but not in source stream state
+ int index = srcStream.getIndex(AudioSystem.DEVICE_OUT_DEFAULT);
index = rescaleIndex(index, srcStreamType, mStreamType);
+ for (int i = 0; i < mIndexMap.size(); i++) {
+ mIndexMap.put(mIndexMap.keyAt(i), index);
+ }
+ // Now apply actual volume for devices in source stream state
+ SparseIntArray srcMap = srcStream.mIndexMap;
+ for (int i = 0; i < srcMap.size(); i++) {
+ int device = srcMap.keyAt(i);
+ index = srcMap.valueAt(i);
+ index = rescaleIndex(index, srcStreamType, mStreamType);
- setIndex(index, device, caller);
+ setIndex(index, device, caller);
+ }
}
}
}
+ @GuardedBy("mSettingsLock")
public void setAllIndexesToMax() {
synchronized (VolumeStreamState.class) {
for (int i = 0; i < mIndexMap.size(); i++) {
@@ -5397,7 +5431,7 @@
// consistent with audio policy manager state
setBluetoothA2dpDeviceConnectionStateInt(
btDevice, BluetoothA2dp.STATE_DISCONNECTED, BluetoothProfile.A2DP,
- musicDevice);
+ false /* suppressNoisyIntent */, musicDevice);
}
}
}
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
index e093c9d..1ae7d20 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
@@ -222,7 +222,7 @@
MutableInt halResult = new MutableInt(Result.UNKNOWN_ERROR);
MutableBoolean flagState = new MutableBoolean(false);
try {
- mHwSession.getConfigFlag(flag, (int result, boolean value) -> {
+ mHwSession.isConfigFlagSet(flag, (int result, boolean value) -> {
halResult.value = result;
flagState.value = value;
});
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 6dc5403..48d275c 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -827,7 +827,7 @@
return isEnabled();
}
};
- mGnssMetrics = new GnssMetrics();
+ mGnssMetrics = new GnssMetrics(mBatteryStats);
}
/**
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index e458f48..971ac8b 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -66,4 +66,9 @@
* given network.
*/
public abstract long getSubscriptionOpportunisticQuota(Network network, int quotaType);
+
+ /**
+ * Informs that admin data is loaded and available.
+ */
+ public abstract void onAdminDataAvailable();
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 96b06c2..e406d51 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -199,6 +199,7 @@
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.IndentingPrintWriter;
@@ -333,6 +334,11 @@
private static final long TIME_CACHE_MAX_AGE = DAY_IN_MILLIS;
+ /**
+ * Indicates the maximum wait time for admin data to be available;
+ */
+ private static final long WAIT_FOR_ADMIN_DATA_TIMEOUT_MS = 10_000;
+
private static final int MSG_RULES_CHANGED = 1;
private static final int MSG_METERED_IFACES_CHANGED = 2;
private static final int MSG_LIMIT_REACHED = 5;
@@ -384,6 +390,8 @@
private final boolean mSuppressDefaultPolicy;
+ private final CountDownLatch mAdminDataAvailableLatch = new CountDownLatch(1);
+
/** Defined network policies. */
@GuardedBy("mNetworkPoliciesSecondLock")
final ArrayMap<NetworkTemplate, NetworkPolicy> mNetworkPolicy = new ArrayMap<>();
@@ -672,6 +680,8 @@
mSystemReady = true;
+ waitForAdminData();
+
// read policy from disk
readPolicyAL();
@@ -4590,6 +4600,11 @@
return mSubscriptionOpportunisticQuota.get(getSubIdLocked(network));
}
}
+
+ @Override
+ public void onAdminDataAvailable() {
+ mAdminDataAvailableLatch.countDown();
+ }
}
private int parseSubId(NetworkState state) {
@@ -4617,6 +4632,16 @@
return ArrayUtils.isEmpty(plans) ? null : plans[0];
}
+ /**
+ * This will only ever be called once - during device boot.
+ */
+ private void waitForAdminData() {
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)) {
+ ConcurrentUtils.waitForCountDownNoInterrupt(mAdminDataAvailableLatch,
+ WAIT_FOR_ADMIN_DATA_TIMEOUT_MS, "Wait for admin data");
+ }
+ }
+
private static boolean hasRule(int uidRules, int rule) {
return (uidRules & rule) != 0;
}
diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java b/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java
index fbf11fc..7387ad4 100644
--- a/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java
+++ b/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java
@@ -161,7 +161,7 @@
public boolean containsDomain(String domain) {
final CrcShaDigests domainDigests = mDomainDigests;
if (domainDigests == null) {
- Slog.wtf(TAG, "domainDigests should not be null");
+ // mDomainDigests is not initialized
return false;
}
// First it does a quick CRC32 check.
@@ -177,7 +177,7 @@
public boolean containsIp(String ip) {
final CrcShaDigests ipDigests = mIpDigests;
if (ipDigests == null) {
- Slog.wtf(TAG, "ipDigests should not be null");
+ // mIpDigests is not initialized
return false;
}
// First it does a quick CRC32 check.
@@ -234,12 +234,20 @@
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("Domain CRC32 digest list:");
- mDomainDigests.crc32Digests.dump(fd, pw, args);
+ if (mDomainDigests != null) {
+ mDomainDigests.crc32Digests.dump(fd, pw, args);
+ }
pw.println("Domain SHA256 digest list:");
- mDomainDigests.sha256Digests.dump(fd, pw, args);
+ if (mDomainDigests != null) {
+ mDomainDigests.sha256Digests.dump(fd, pw, args);
+ }
pw.println("Ip CRC32 digest list:");
- mIpDigests.crc32Digests.dump(fd, pw, args);
+ if (mIpDigests != null) {
+ mIpDigests.crc32Digests.dump(fd, pw, args);
+ }
pw.println("Ip SHA256 digest list:");
- mIpDigests.sha256Digests.dump(fd, pw, args);
+ if (mIpDigests != null) {
+ mIpDigests.sha256Digests.dump(fd, pw, args);
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 2585cf3..10b377d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -48,7 +48,6 @@
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
-import static android.content.pm.PackageManager.INSTALL_FAILED_NEWER_SDK;
import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
import static android.content.pm.PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE;
import static android.content.pm.PackageManager.INSTALL_FAILED_SANDBOX_VERSION_DOWNGRADE;
@@ -16495,16 +16494,6 @@
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
- // App targetSdkVersion is below min supported version
- if (!forceSdk && pkg.applicationInfo.isTargetingDeprecatedSdkVersion()) {
- Slog.w(TAG, "App " + pkg.packageName + " targets deprecated sdk");
-
- res.setError(INSTALL_FAILED_NEWER_SDK,
- "App is targeting deprecated sdk (targetSdkVersion should be at least "
- + Build.VERSION.MIN_SUPPORTED_TARGET_SDK_INT + ").");
- return;
- }
-
// Instant apps have several additional install-time checks.
if (instantApp) {
if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.O) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 6344997..6599fd9 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -66,7 +66,6 @@
import static android.view.WindowManager.LayoutParams.LAST_SYSTEM_WINDOW;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
@@ -163,13 +162,10 @@
import android.app.UiModeManager;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
-import android.content.ComponentCallbacks;
-import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.ServiceConnection;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -202,7 +198,6 @@
import android.os.IDeviceIdleController;
import android.os.Looper;
import android.os.Message;
-import android.os.Messenger;
import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.Process;
@@ -1030,8 +1025,10 @@
public void run() {
// send interaction hint to improve redraw performance
mPowerManagerInternal.powerHint(PowerHint.INTERACTION, 0);
- if (showRotationChoice(mCurrentAppOrientation, mRotation)) {
- sendProposedRotationChangeToStatusBarInternal(mRotation);
+ if (isRotationChoiceEnabled()) {
+ final boolean isValid = isValidRotationChoice(mCurrentAppOrientation,
+ mRotation);
+ sendProposedRotationChangeToStatusBarInternal(mRotation, isValid);
} else {
updateRotation(false);
}
@@ -4498,7 +4495,7 @@
mHandler.obtainMessage(MSG_DISPOSE_INPUT_CONSUMER, mInputConsumer));
mInputConsumer = null;
}
- } else if (mInputConsumer == null) {
+ } else if (mInputConsumer == null && mStatusBar != null && canHideNavigationBar()) {
mInputConsumer = mWindowManagerFuncs.createInputConsumer(mHandler.getLooper(),
INPUT_CONSUMER_NAVIGATION,
(channel, looper) -> new HideNavInputEventReceiver(channel, looper));
@@ -6193,10 +6190,10 @@
/**
* Notify the StatusBar that system rotation suggestion has changed.
*/
- private void sendProposedRotationChangeToStatusBarInternal(int rotation) {
+ private void sendProposedRotationChangeToStatusBarInternal(int rotation, boolean isValid) {
StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
if (statusBar != null) {
- statusBar.onProposedRotationChanged(rotation);
+ statusBar.onProposedRotationChanged(rotation, isValid);
}
}
@@ -7142,15 +7139,14 @@
mOrientationListener.setCurrentRotation(rotation);
}
- public boolean showRotationChoice(int orientation, final int preferredRotation) {
+ public boolean isRotationChoiceEnabled() {
// Rotation choice is only shown when the user is in locked mode.
if (mUserRotationMode != WindowManagerPolicy.USER_ROTATION_LOCKED) return false;
- // We should only show a rotation choice if:
- // 1. The rotation isn't forced by the lid, dock, demo, hdmi, vr, etc mode
- // 2. The user choice won't be ignored due to screen orientation settings
+ // We should only enable rotation choice if the rotation isn't forced by the lid, dock,
+ // demo, hdmi, vr, etc mode
- // Determine if the rotation currently forced
+ // Determine if the rotation is currently forced
if (mForceDefaultOrientation) {
return false; // Rotation is forced to default orientation
@@ -7183,7 +7179,14 @@
return false;
}
- // Determine if the orientation will ignore user choice
+ // Rotation isn't forced, enable choice
+ return true;
+ }
+
+ public boolean isValidRotationChoice(int orientation, final int preferredRotation) {
+ // Determine if the given app orientation can be chosen and, if so, if it is compatible
+ // with the provided rotation choice
+
switch (orientation) {
case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
diff --git a/services/core/java/com/android/server/slice/PinnedSliceState.java b/services/core/java/com/android/server/slice/PinnedSliceState.java
index cf930f5..09f6da9 100644
--- a/services/core/java/com/android/server/slice/PinnedSliceState.java
+++ b/services/core/java/com/android/server/slice/PinnedSliceState.java
@@ -22,6 +22,7 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.RemoteException;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
@@ -51,6 +52,8 @@
private final ArraySet<ISliceListener> mListeners = new ArraySet<>();
@GuardedBy("mLock")
private SliceSpec[] mSupportedSpecs = null;
+ @GuardedBy("mLock")
+ private final ArrayMap<ISliceListener, String> mPkgMap = new ArrayMap<>();
public PinnedSliceState(SliceManagerService service, Uri uri) {
mService = service;
@@ -102,17 +105,19 @@
mService.getHandler().post(this::handleBind);
}
- public void addSliceListener(ISliceListener listener, SliceSpec[] specs) {
+ public void addSliceListener(ISliceListener listener, String pkg, SliceSpec[] specs) {
synchronized (mLock) {
if (mListeners.add(listener) && mListeners.size() == 1) {
mService.listen(mUri);
}
+ mPkgMap.put(listener, pkg);
mergeSpecs(specs);
}
}
public boolean removeSliceListener(ISliceListener listener) {
synchronized (mLock) {
+ mPkgMap.remove(listener);
if (mListeners.remove(listener) && mListeners.size() == 0) {
mService.unlisten(mUri);
}
@@ -155,25 +160,16 @@
}
private void handleBind() {
- Slice s;
- try (ContentProviderClient client = getClient()) {
- Bundle extras = new Bundle();
- extras.putParcelable(SliceProvider.EXTRA_BIND_URI, mUri);
- extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
- new ArrayList<>(Arrays.asList(mSupportedSpecs)));
- final Bundle res;
- try {
- res = client.call(SliceProvider.METHOD_SLICE, null, extras);
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to bind slice " + mUri, e);
- return;
- }
- if (res == null) return;
- Bundle.setDefusable(res, true);
- s = res.getParcelable(SliceProvider.EXTRA_SLICE);
- }
+ Slice cachedSlice = doBind(null);
synchronized (mLock) {
mListeners.removeIf(l -> {
+ Slice s = cachedSlice;
+ if (s == null || s.hasHint(Slice.HINT_CALLER_NEEDED)) {
+ s = doBind(mPkgMap.get(l));
+ }
+ if (s == null) {
+ return true;
+ }
try {
l.onSliceUpdated(s);
return false;
@@ -189,6 +185,26 @@
}
}
+ private Slice doBind(String overridePkg) {
+ try (ContentProviderClient client = getClient()) {
+ Bundle extras = new Bundle();
+ extras.putParcelable(SliceProvider.EXTRA_BIND_URI, mUri);
+ extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
+ new ArrayList<>(Arrays.asList(mSupportedSpecs)));
+ extras.putString(SliceProvider.EXTRA_OVERRIDE_PKG, overridePkg);
+ final Bundle res;
+ try {
+ res = client.call(SliceProvider.METHOD_SLICE, null, extras);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to bind slice " + mUri, e);
+ return null;
+ }
+ if (res == null) return null;
+ Bundle.setDefusable(res, true);
+ return res.getParcelable(SliceProvider.EXTRA_SLICE);
+ }
+ }
+
private void handleSendPinned() {
try (ContentProviderClient client = getClient()) {
Bundle b = new Bundle();
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index 2d9e772..ca7632c 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -16,27 +16,35 @@
package com.android.server.slice;
+import static android.content.ContentProvider.getUriWithoutUserId;
import static android.content.ContentProvider.getUserIdFromUri;
import static android.content.ContentProvider.maybeAddUserId;
import android.Manifest.permission;
+import android.app.ActivityManager;
import android.app.AppOpsManager;
+import android.app.ContentProviderHolder;
+import android.app.IActivityManager;
import android.app.slice.ISliceListener;
import android.app.slice.ISliceManager;
+import android.app.slice.SliceManager;
import android.app.slice.SliceSpec;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Binder;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Looper;
import android.os.Process;
import android.os.RemoteException;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
@@ -63,6 +71,8 @@
@GuardedBy("mLock")
private final ArrayMap<Uri, PinnedSliceState> mPinnedSlicesByUri = new ArrayMap<>();
+ @GuardedBy("mLock")
+ private final ArraySet<SliceGrant> mUserGrants = new ArraySet<>();
private final Handler mHandler;
private final ContentObserver mObserver;
@@ -111,7 +121,7 @@
verifyCaller(pkg);
uri = maybeAddUserId(uri, Binder.getCallingUserHandle().getIdentifier());
enforceAccess(pkg, uri);
- getOrCreatePinnedSlice(uri).addSliceListener(listener, specs);
+ getOrCreatePinnedSlice(uri).addSliceListener(listener, pkg, specs);
}
@Override
@@ -156,6 +166,43 @@
return getPinnedSlice(uri).getSpecs();
}
+ @Override
+ public int checkSlicePermission(Uri uri, String pkg, int pid, int uid) throws RemoteException {
+ if (mContext.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
+ == PackageManager.PERMISSION_GRANTED) {
+ return SliceManager.PERMISSION_GRANTED;
+ }
+ if (hasFullSliceAccess(pkg, uid)) {
+ return SliceManager.PERMISSION_GRANTED;
+ }
+ synchronized (mLock) {
+ if (mUserGrants.contains(new SliceGrant(uri, pkg))) {
+ return SliceManager.PERMISSION_USER_GRANTED;
+ }
+ }
+ return SliceManager.PERMISSION_DENIED;
+ }
+
+ @Override
+ public void grantPermissionFromUser(Uri uri, String pkg, String callingPkg, boolean allSlices) {
+ verifyCaller(callingPkg);
+ getContext().enforceCallingOrSelfPermission(permission.MANAGE_SLICE_PERMISSIONS,
+ "Slice granting requires MANAGE_SLICE_PERMISSIONS");
+ if (allSlices) {
+ // TODO: Manage full access grants.
+ } else {
+ synchronized (mLock) {
+ mUserGrants.add(new SliceGrant(uri, pkg));
+ }
+ long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.getContentResolver().notifyChange(uri, null);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
/// ----- internal code -----
void removePinnedSlice(Uri uri) {
synchronized (mLock) {
@@ -202,12 +249,45 @@
return mHandler;
}
- private void enforceAccess(String pkg, Uri uri) {
- getContext().enforceUriPermission(uri, permission.BIND_SLICE,
- permission.BIND_SLICE, Binder.getCallingPid(), Binder.getCallingUid(),
- Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
- "Slice binding requires the permission BIND_SLICE");
+ private void enforceAccess(String pkg, Uri uri) throws RemoteException {
int user = Binder.getCallingUserHandle().getIdentifier();
+ // Check for default launcher/assistant.
+ if (!hasFullSliceAccess(pkg, Binder.getCallingUid())) {
+ try {
+ // Also allow things with uri access.
+ getContext().enforceUriPermission(uri, Binder.getCallingPid(),
+ Binder.getCallingUid(),
+ Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
+ "Slice binding requires permission to the Uri");
+ } catch (SecurityException e) {
+ // Last fallback (if the calling app owns the authority, then it can have access).
+ long ident = Binder.clearCallingIdentity();
+ try {
+ IBinder token = new Binder();
+ IActivityManager activityManager = ActivityManager.getService();
+ ContentProviderHolder holder = null;
+ String providerName = getUriWithoutUserId(uri).getAuthority();
+ try {
+ holder = activityManager.getContentProviderExternal(
+ providerName, getUserIdFromUri(uri, user), token);
+ if (holder == null || holder.info == null
+ || !Objects.equals(holder.info.packageName, pkg)) {
+ // No more fallbacks, no access.
+ throw e;
+ }
+ } finally {
+ if (holder != null && holder.provider != null) {
+ activityManager.removeContentProviderExternal(providerName, token);
+ }
+ }
+ } finally {
+ // I know, the double finally seems ugly, but seems safest for the identity.
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+ // Lastly check for any multi-userness. Any return statements above here will break this
+ // important check.
if (getUserIdFromUri(uri, user) != user) {
getContext().enforceCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS_FULL,
"Slice interaction across users requires INTERACT_ACROSS_USERS_FULL");
@@ -230,8 +310,14 @@
}
private boolean hasFullSliceAccess(String pkg, int userId) {
- return isDefaultHomeApp(pkg, userId) || isAssistant(pkg, userId)
- || isGrantedFullAccess(pkg, userId);
+ long ident = Binder.clearCallingIdentity();
+ try {
+ boolean ret = isDefaultHomeApp(pkg, userId) || isAssistant(pkg, userId)
+ || isGrantedFullAccess(pkg, userId);
+ return ret;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
private boolean isAssistant(String pkg, int userId) {
@@ -259,7 +345,8 @@
private boolean isDefaultHomeApp(String pkg, int userId) {
String defaultHome = getDefaultHome(userId);
- return Objects.equals(pkg, defaultHome);
+
+ return pkg != null && Objects.equals(pkg, defaultHome);
}
// Based on getDefaultHome in ShortcutService.
@@ -301,7 +388,7 @@
lastPriority = ri.priority;
}
}
- return detected.getPackageName();
+ return detected != null ? detected.getPackageName() : null;
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -349,4 +436,26 @@
mService.onStopUser(userHandle);
}
}
+
+ private class SliceGrant {
+ private final Uri mUri;
+ private final String mPkg;
+
+ public SliceGrant(Uri uri, String pkg) {
+ mUri = uri;
+ mPkg = pkg;
+ }
+
+ @Override
+ public int hashCode() {
+ return mUri.hashCode() + mPkg.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof SliceGrant)) return false;
+ SliceGrant other = (SliceGrant) obj;
+ return Objects.equals(other.mUri, mUri) && Objects.equals(other.mPkg, mPkg);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 2f5e2f8..4cb5e08 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -28,6 +28,7 @@
import android.net.NetworkStats;
import android.net.wifi.IWifiManager;
import android.net.wifi.WifiActivityEnergyInfo;
+import android.os.SystemClock;
import android.telephony.ModemActivityInfo;
import android.telephony.TelephonyManager;
import android.os.BatteryStatsInternal;
@@ -621,6 +622,20 @@
}
break;
}
+ case StatsLog.CPU_SUSPEND_TIME: {
+ List<StatsLogEventWrapper> ret = new ArrayList();
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 1);
+ e.writeLong(SystemClock.elapsedRealtime());
+ ret.add(e);
+ break;
+ }
+ case StatsLog.CPU_IDLE_TIME: {
+ List<StatsLogEventWrapper> ret = new ArrayList();
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 1);
+ e.writeLong(SystemClock.uptimeMillis());
+ ret.add(e);
+ break;
+ }
default:
Slog.w(TAG, "No such tagId data as " + tagId);
return null;
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index b5d0c60..95006ff 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -95,7 +95,7 @@
*
* @param rotation rotation suggestion
*/
- void onProposedRotationChanged(int rotation);
+ void onProposedRotationChanged(int rotation, boolean isValid);
public interface GlobalActionsListener {
/**
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 79d3dbb..c58c208 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -408,10 +408,10 @@
}
@Override
- public void onProposedRotationChanged(int rotation) {
+ public void onProposedRotationChanged(int rotation, boolean isValid) {
if (mBar != null){
try {
- mBar.onProposedRotationChanged(rotation);
+ mBar.onProposedRotationChanged(rotation, isValid);
} catch (RemoteException ex) {}
}
}
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index b474c62..ce3f512 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -463,10 +463,14 @@
mService.mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(token);
}
- // Update the client visibility if we are not running an animation. Otherwise, we'll
- // update client visibility state in onAnimationFinished.
- if (!visible && !delayed) {
- setClientHidden(true);
+ // If we're becoming visible, immediately change client visibility as well although it
+ // usually gets changed in AppWindowContainerController.setVisibility already. However,
+ // there seem to be some edge cases where we change our visibility but client visibility
+ // never gets updated.
+ // If we're becoming invisible, update the client visibility if we are not running an
+ // animation. Otherwise, we'll update client visibility in onAnimationFinished.
+ if (visible || !delayed) {
+ setClientHidden(!visible);
}
// If we are hidden but there is no delay needed we immediately
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index fba404e..6dc384a 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -49,6 +49,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
@@ -3180,6 +3181,21 @@
*/
SurfaceControl mAppAnimationLayer = null;
+ /**
+ * Given that the split-screen divider does not have an AppWindowToken, it
+ * will have to live inside of a "NonAppWindowContainer", in particular
+ * {@link DisplayContent#mAboveAppWindowsContainers}. However, in visual Z order
+ * it will need to be interleaved with some of our children, appearing on top of
+ * both docked stacks but underneath any assistant stacks.
+ *
+ * To solve this problem we have this anchor control, which will always exist so
+ * we can always assign it the correct value in our {@link #assignChildLayers}.
+ * Likewise since it always exists, {@link AboveAppWindowContainers} can always
+ * assign the divider a layer relative to it. This way we prevent linking lifecycle
+ * events between the two containers.
+ */
+ SurfaceControl mSplitScreenDividerAnchor = null;
+
// Cached reference to some special stacks we tend to get a lot so we don't need to loop
// through the list to find them.
private TaskStack mHomeStack = null;
@@ -3496,37 +3512,39 @@
@Override
void assignChildLayers(SurfaceControl.Transaction t) {
+
+ final int HOME_STACK_STATE = 0;
+ final int NORMAL_STACK_STATE = 1;
+ final int ALWAYS_ON_TOP_STATE = 2;
+
int layer = 0;
-
- // We allow stacks to change visual order from the AM specified order due to
- // Z-boosting during animations. However we must take care to ensure TaskStacks
- // which are marked as alwaysOnTop remain that way.
- for (int i = 0; i < mChildren.size(); i++) {
- final TaskStack s = mChildren.get(i);
- s.assignChildLayers();
- if (!s.needsZBoost() && !s.isAlwaysOnTop()) {
- s.assignLayer(t, layer++);
+ for (int state = 0; state <= ALWAYS_ON_TOP_STATE; state++) {
+ for (int i = 0; i < mChildren.size(); i++) {
+ final TaskStack s = mChildren.get(i);
+ if (state == HOME_STACK_STATE && s.isActivityTypeHome()) {
+ s.assignLayer(t, layer++);
+ } else if (state == NORMAL_STACK_STATE && !s.isActivityTypeHome()
+ && !s.isAlwaysOnTop()) {
+ s.assignLayer(t, layer++);
+ if (s.inSplitScreenWindowingMode() && mSplitScreenDividerAnchor != null) {
+ t.setLayer(mSplitScreenDividerAnchor, layer++);
+ }
+ } else if (state == ALWAYS_ON_TOP_STATE && s.isAlwaysOnTop()) {
+ s.assignLayer(t, layer++);
+ }
+ }
+ // The appropriate place for App-Transitions to occur is right
+ // above all other animations but still below things in the Picture-and-Picture
+ // windowing mode.
+ if (state == NORMAL_STACK_STATE && mAppAnimationLayer != null) {
+ t.setLayer(mAppAnimationLayer, layer++);
}
}
for (int i = 0; i < mChildren.size(); i++) {
final TaskStack s = mChildren.get(i);
- if (s.needsZBoost() && !s.isAlwaysOnTop()) {
- s.assignLayer(t, layer++);
- }
- }
- for (int i = 0; i < mChildren.size(); i++) {
- final TaskStack s = mChildren.get(i);
- if (s.isAlwaysOnTop()) {
- s.assignLayer(t, layer++);
- }
+ s.assignChildLayers(t);
}
- // The appropriate place for App-Transitions to occur is right
- // above all other animations but still below things in the Picture-and-Picture
- // windowing mode.
- if (mAppAnimationLayer != null) {
- t.setLayer(mAppAnimationLayer, layer++);
- }
}
@Override
@@ -3534,6 +3552,10 @@
return mAppAnimationLayer;
}
+ SurfaceControl getSplitScreenDividerAnchor() {
+ return mSplitScreenDividerAnchor;
+ }
+
@Override
void onParentSet() {
super.onParentSet();
@@ -3541,11 +3563,18 @@
mAppAnimationLayer = makeChildSurface(null)
.setName("animationLayer")
.build();
- getPendingTransaction().show(mAppAnimationLayer);
+ mSplitScreenDividerAnchor = makeChildSurface(null)
+ .setName("splitScreenDividerAnchor")
+ .build();
+ getPendingTransaction()
+ .show(mAppAnimationLayer)
+ .show(mSplitScreenDividerAnchor);
scheduleAnimation();
} else {
mAppAnimationLayer.destroy();
mAppAnimationLayer = null;
+ mSplitScreenDividerAnchor.destroy();
+ mSplitScreenDividerAnchor = null;
}
}
}
@@ -3560,6 +3589,12 @@
&& imeContainer.getSurfaceControl() != null;
for (int j = 0; j < mChildren.size(); ++j) {
final WindowToken wt = mChildren.get(j);
+
+ // See {@link mSplitScreenDividerAnchor}
+ if (wt.windowType == TYPE_DOCK_DIVIDER) {
+ wt.assignRelativeLayer(t, mTaskStackContainers.getSplitScreenDividerAnchor(), 1);
+ continue;
+ }
wt.assignLayer(t, j);
wt.assignChildLayers(t);
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index b18c1a0..0bc58e0 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -55,6 +55,17 @@
"frameworks/native/services",
"system/gatekeeper/include",
],
+
+ product_variables: {
+ arc: {
+ cflags: [
+ "-DUSE_ARC",
+ ],
+ srcs: [
+ "com_android_server_ArcVideoService.cpp",
+ ],
+ }
+ }
}
cc_defaults {
@@ -119,4 +130,17 @@
"android.hardware.broadcastradio@common-utils-1x-lib",
"libscrypt_static",
],
+
+ product_variables: {
+ arc: {
+ // TODO: remove the suffix "_bp" after finishing migration to Android.bp.
+ shared_libs: [
+ "libarcbridge",
+ "libarcbridgeservice",
+ "libarcvideobridge",
+ "libchrome",
+ "libmojo_bp",
+ ],
+ }
+ }
}
diff --git a/services/core/jni/com_android_server_ArcVideoService.cpp b/services/core/jni/com_android_server_ArcVideoService.cpp
new file mode 100644
index 0000000..7df8276
--- /dev/null
+++ b/services/core/jni/com_android_server_ArcVideoService.cpp
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2016, 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ArcVideoService"
+
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <media/arcvideobridge/IArcVideoBridge.h>
+#include <utils/Log.h>
+
+#include <base/bind.h>
+#include <base/bind_helpers.h>
+#include <mojo/edk/embedder/embedder.h>
+#include <mojo/public/cpp/bindings/binding.h>
+
+#include <arc/ArcBridgeSupport.h>
+#include <arc/ArcService.h>
+#include <arc/Future.h>
+#include <arc/IArcBridgeService.h>
+#include <arc/MojoProcessSupport.h>
+#include <video.mojom.h>
+
+namespace {
+
+// [MinVersion] of OnVideoInstanceReady method in arc_bridge.mojom.
+constexpr int kMinimumArcBridgeHostVersion = 6;
+
+void onCaptureResult(arc::Future<arc::MojoBootstrapResult>* future, uint32_t version,
+ mojo::ScopedHandle handle, const std::string& token) {
+ mojo::edk::ScopedPlatformHandle scoped_platform_handle;
+ MojoResult result =
+ mojo::edk::PassWrappedPlatformHandle(handle.release().value(), &scoped_platform_handle);
+ if (result != MOJO_RESULT_OK) {
+ ALOGE("Received invalid file descriptor.");
+ future->set(arc::MojoBootstrapResult());
+ return;
+ }
+
+ base::ScopedFD fd(scoped_platform_handle.release().handle);
+ future->set(arc::MojoBootstrapResult(std::move(fd), token, version));
+}
+
+} // namespace
+
+namespace arc {
+
+class VideoService : public mojom::VideoInstance,
+ public ArcService,
+ public android::BnArcVideoBridge {
+public:
+ explicit VideoService(MojoProcessSupport* mojoProcessSupport)
+ : mMojoProcessSupport(mojoProcessSupport), mBinding(this) {
+ mMojoProcessSupport->arc_bridge_support().requestArcBridgeProxyAsync(
+ this, kMinimumArcBridgeHostVersion);
+ }
+
+ ~VideoService() override { mMojoProcessSupport->disconnect(&mBinding, &mHostPtr); }
+
+ // VideoInstance overrides:
+ void InitDeprecated(mojom::VideoHostPtr hostPtr) override {
+ Init(std::move(hostPtr), base::Bind(&base::DoNothing));
+ }
+
+ void Init(mojom::VideoHostPtr hostPtr, const InitCallback& callback) override {
+ ALOGV("Init");
+ mHostPtr = std::move(hostPtr);
+ // A method must be called while we are still in a Mojo thread so the
+ // proxy can perform lazy initialization and be able to be called from
+ // non-Mojo threads later.
+ // This also caches the version number so it can be obtained by calling
+ // .version().
+ mHostPtr.QueryVersion(base::Bind(
+ [](const InitCallback& callback, uint32_t version) {
+ ALOGI("VideoService ready (version=%d)", version);
+ callback.Run();
+ },
+ callback));
+ ALOGV("Init done");
+ }
+
+ // ArcService overrides:
+ void ready(mojom::ArcBridgeHostPtr* bridgeHost) override {
+ (*bridgeHost)->OnVideoInstanceReady(mBinding.CreateInterfacePtrAndBind());
+ }
+
+ void versionMismatch(uint32_t version) override {
+ ALOGE("ArcBridgeHost version %d, does not support video (version %d)\n", version,
+ kMinimumArcBridgeHostVersion);
+ }
+
+ // BnArcVideoBridge overrides:
+ MojoBootstrapResult bootstrapVideoAcceleratorFactory() override {
+ ALOGV("VideoService::bootstrapVideoAcceleratorFactory");
+
+ Future<MojoBootstrapResult> future;
+ mMojoProcessSupport->mojo_thread().getTaskRunner()->PostTask(
+ FROM_HERE, base::Bind(&VideoService::bootstrapVideoAcceleratorFactoryOnMojoThread,
+ base::Unretained(this), &future));
+ return future.get();
+ }
+
+ int32_t hostVersion() override {
+ ALOGV("VideoService::hostVersion");
+ return mHostPtr.version();
+ }
+
+private:
+ void bootstrapVideoAcceleratorFactoryOnMojoThread(Future<MojoBootstrapResult>* future) {
+ if (!mHostPtr) {
+ ALOGE("mHostPtr is not ready yet");
+ future->set(MojoBootstrapResult());
+ return;
+ }
+ mHostPtr->OnBootstrapVideoAcceleratorFactory(
+ base::Bind(&onCaptureResult, base::Unretained(future), mHostPtr.version()));
+ }
+
+ // Outlives VideoService.
+ MojoProcessSupport* const mMojoProcessSupport;
+ mojo::Binding<mojom::VideoInstance> mBinding;
+ mojom::VideoHostPtr mHostPtr;
+};
+
+} // namespace arc
+
+namespace android {
+
+int register_android_server_ArcVideoService() {
+ defaultServiceManager()->addService(
+ String16("android.os.IArcVideoBridge"),
+ new arc::VideoService(arc::MojoProcessSupport::getLeakyInstance()));
+ return 0;
+}
+
+} // namespace android
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 071b6b8..07ddb05 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -52,6 +52,9 @@
int register_android_server_GraphicsStatsService(JNIEnv* env);
int register_android_hardware_display_DisplayViewport(JNIEnv* env);
int register_android_server_net_NetworkStatsService(JNIEnv* env);
+#ifdef USE_ARC
+int register_android_server_ArcVideoService();
+#endif
};
using namespace android;
@@ -97,6 +100,8 @@
register_android_server_GraphicsStatsService(env);
register_android_hardware_display_DisplayViewport(env);
register_android_server_net_NetworkStatsService(env);
-
+#ifdef USE_ARC
+ register_android_server_ArcVideoService();
+#endif
return JNI_VERSION_1_4;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 99275e8..7a0b1bf 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -39,12 +39,6 @@
*/
abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub {
/**
- * To be called by {@link DevicePolicyManagerService#Lifecycle} when the service is started.
- *
- * @see {@link SystemService#onStart}.
- */
- abstract void handleStart();
- /**
* To be called by {@link DevicePolicyManagerService#Lifecycle} during the various boot phases.
*
* @see {@link SystemService#onBootPhase}.
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 77b87b6..6bee9d6 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -198,8 +198,10 @@
import com.android.internal.util.XmlUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.LocalServices;
+import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemService;
import com.android.server.devicepolicy.DevicePolicyManagerService.ActiveAdmin.TrustAgentInfo;
+import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.pm.UserRestrictionsUtils;
import com.google.android.collect.Sets;
@@ -511,7 +513,6 @@
@Override
public void onStart() {
publishBinderService(Context.DEVICE_POLICY_SERVICE, mService);
- mService.handleStart();
}
@Override
@@ -1743,6 +1744,10 @@
return LocalServices.getService(UsageStatsManagerInternal.class);
}
+ NetworkPolicyManagerInternal getNetworkPolicyManagerInternal() {
+ return LocalServices.getService(NetworkPolicyManagerInternal.class);
+ }
+
NotificationManager getNotificationManager() {
return mContext.getSystemService(NotificationManager.class);
}
@@ -1996,6 +2001,10 @@
KeyChainConnection keyChainBindAsUser(UserHandle user) throws InterruptedException {
return KeyChain.bindAsUser(mContext, user);
}
+
+ void postOnSystemServerInitThreadPool(Runnable runnable) {
+ SystemServerInitThreadPool.get().submit(runnable, LOG_TAG);
+ }
}
/**
@@ -3239,6 +3248,7 @@
switch (phase) {
case SystemService.PHASE_LOCK_SETTINGS_READY:
onLockSettingsReady();
+ loadAdminDataAsync();
break;
case SystemService.PHASE_BOOT_COMPLETED:
ensureDeviceOwnerUserStarted(); // TODO Consider better place to do this.
@@ -3307,11 +3317,6 @@
}
@Override
- void handleStart() {
- pushActiveAdminPackages();
- }
-
- @Override
void handleStartUser(int userId) {
updateScreenCaptureDisabledInWindowManager(userId,
getScreenCaptureDisabled(null, userId));
@@ -3493,6 +3498,14 @@
}
}
+ private void loadAdminDataAsync() {
+ mInjector.postOnSystemServerInitThreadPool(() -> {
+ pushActiveAdminPackages();
+ mUsageStatsManagerInternal.onAdminDataAvailable();
+ mInjector.getNetworkPolicyManagerInternal().onAdminDataAvailable();
+ });
+ }
+
private void pushActiveAdminPackages() {
synchronized (this) {
final List<UserInfo> users = mUserManager.getUsers();
@@ -10216,7 +10229,8 @@
final int userId = UserHandle.getUserId(uid);
Intent intent = null;
if (DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction) ||
- DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction)) {
+ DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction) ||
+ DevicePolicyManager.POLICY_MANDATORY_BACKUPS.equals(restriction)) {
synchronized(this) {
final DevicePolicyData policy = getUserData(userId);
final int N = policy.mAdminList.size();
@@ -10225,7 +10239,9 @@
if ((admin.disableCamera &&
DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction)) ||
(admin.disableScreenCapture && DevicePolicyManager
- .POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction))) {
+ .POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction)) ||
+ (admin.mandatoryBackupTransport != null && DevicePolicyManager
+ .POLICY_MANDATORY_BACKUPS.equals(restriction))) {
intent = createShowAdminSupportIntent(admin.info.getComponent(), userId);
break;
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/GlobalActionPerformerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/GlobalActionPerformerTest.java
index 1213e81..e72e460 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/GlobalActionPerformerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/GlobalActionPerformerTest.java
@@ -16,13 +16,18 @@
package com.android.server.accessibility;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.accessibilityservice.AccessibilityService;
import android.app.StatusBarManager;
import android.content.Context;
+import android.os.Handler;
+import com.android.internal.util.ScreenshotHelper;
import com.android.server.wm.WindowManagerInternal;
import org.junit.Before;
@@ -39,6 +44,7 @@
@Mock Context mMockContext;
@Mock WindowManagerInternal mMockWindowManagerInternal;
@Mock StatusBarManager mMockStatusBarManager;
+ @Mock ScreenshotHelper mMockScreenshotHelper;
@Before
public void setup() {
@@ -48,7 +54,8 @@
.thenReturn(mMockStatusBarManager);
mGlobalActionPerformer =
- new GlobalActionPerformer(mMockContext, mMockWindowManagerInternal);
+ new GlobalActionPerformer(mMockContext, mMockWindowManagerInternal,
+ () -> mMockScreenshotHelper);
}
@Test
@@ -70,4 +77,13 @@
mGlobalActionPerformer.performGlobalAction(AccessibilityService.GLOBAL_ACTION_POWER_DIALOG);
verify(mMockWindowManagerInternal).showGlobalActions();
}
+
+ @Test
+ public void testScreenshot_requestsFromScreenshotHelper() {
+ mGlobalActionPerformer.performGlobalAction(
+ AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT);
+ verify(mMockScreenshotHelper).takeScreenshot(
+ eq(android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN), anyBoolean(),
+ anyBoolean(), any(Handler.class));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index 5134f52..06f138b 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -45,6 +45,7 @@
import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
import com.android.internal.widget.LockPatternUtils;
+import com.android.server.net.NetworkPolicyManagerInternal;
import java.io.File;
import java.io.IOException;
@@ -159,6 +160,11 @@
}
@Override
+ NetworkPolicyManagerInternal getNetworkPolicyManagerInternal() {
+ return services.networkPolicyManagerInternal;
+ }
+
+ @Override
PackageManagerInternal getPackageManagerInternal() {
return services.packageManagerInternal;
}
@@ -438,5 +444,10 @@
KeyChain.KeyChainConnection keyChainBindAsUser(UserHandle user) {
return services.keyChainConnection;
}
+
+ @Override
+ void postOnSystemServerInitThreadPool(Runnable runnable) {
+ runnable.run();
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 1df0ff2..725ede8 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -186,6 +186,7 @@
initializeDpms();
Mockito.reset(getServices().usageStatsManagerInternal);
+ Mockito.reset(getServices().networkPolicyManagerInternal);
setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_UID);
setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_UID);
setUpPackageManagerForAdmin(admin3, DpmMockContext.CALLER_UID);
@@ -211,7 +212,6 @@
LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
dpms = new DevicePolicyManagerServiceTestable(getServices(), mContext);
- dpms.handleStart();
dpms.systemReady(SystemService.PHASE_LOCK_SETTINGS_READY);
dpms.systemReady(SystemService.PHASE_BOOT_COMPLETED);
@@ -283,7 +283,7 @@
assertNull(LocalServices.getService(DevicePolicyManagerInternal.class));
}
- public void testHandleStart() throws Exception {
+ public void testLoadAdminData() throws Exception {
// Device owner in SYSTEM_USER
setDeviceOwner();
// Profile owner in CALLER_USER_HANDLE
@@ -307,6 +307,23 @@
MockUtils.checkAdminApps(admin2.getPackageName(),
adminAnotherPackage.getPackageName()),
eq(DpmMockContext.CALLER_USER_HANDLE));
+ verify(getServices().usageStatsManagerInternal).onAdminDataAvailable();
+ verify(getServices().networkPolicyManagerInternal).onAdminDataAvailable();
+ }
+
+ public void testLoadAdminData_noAdmins() throws Exception {
+ final int ANOTHER_USER_ID = 15;
+ getServices().addUser(ANOTHER_USER_ID, 0);
+
+ initializeDpms();
+
+ // Verify
+ verify(getServices().usageStatsManagerInternal).setActiveAdminApps(
+ null, DpmMockContext.CALLER_USER_HANDLE);
+ verify(getServices().usageStatsManagerInternal).setActiveAdminApps(
+ null, ANOTHER_USER_ID);
+ verify(getServices().usageStatsManagerInternal).onAdminDataAvailable();
+ verify(getServices().networkPolicyManagerInternal).onAdminDataAvailable();
}
/**
@@ -2119,8 +2136,8 @@
assertEquals(UserManager.DISALLOW_ADJUST_VOLUME,
intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION));
- // Try with POLICY_DISABLE_CAMERA and POLICY_DISABLE_SCREEN_CAPTURE, which are not
- // user restrictions
+ // Try with POLICY_DISABLE_CAMERA, POLICY_DISABLE_SCREEN_CAPTURE and
+ // POLICY_MANDATORY_BACKUPS, which are not user restrictions
// Camera is not disabled
intent = dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_DISABLE_CAMERA);
@@ -2144,6 +2161,19 @@
assertEquals(DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE,
intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION));
+ // Backups are not mandatory
+ intent = dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_MANDATORY_BACKUPS);
+ assertNull(intent);
+
+ // Backups are mandatory
+ ComponentName transportComponent = ComponentName.unflattenFromString(
+ "android/com.android.internal.backup.LocalTransport");
+ dpm.setMandatoryBackupTransport(admin1, transportComponent);
+ intent = dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_MANDATORY_BACKUPS);
+ assertNotNull(intent);
+ assertEquals(DevicePolicyManager.POLICY_MANDATORY_BACKUPS,
+ intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION));
+
// Same checks for different user
mContext.binder.callingUid = DpmMockContext.CALLER_UID;
// Camera should be disabled by device owner
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index 268d424..0343a52 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -58,6 +58,7 @@
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.widget.LockPatternUtils;
+import com.android.server.net.NetworkPolicyManagerInternal;
import java.io.File;
import java.io.IOException;
@@ -76,6 +77,7 @@
public final UserManager userManager;
public final UserManagerInternal userManagerInternal;
public final UsageStatsManagerInternal usageStatsManagerInternal;
+ public final NetworkPolicyManagerInternal networkPolicyManagerInternal;
public final PackageManagerInternal packageManagerInternal;
public final UserManagerForMock userManagerForMock;
public final PowerManagerForMock powerManager;
@@ -113,6 +115,8 @@
userManager = mock(UserManager.class);
userManagerInternal = mock(UserManagerInternal.class);
usageStatsManagerInternal = mock(UsageStatsManagerInternal.class);
+ networkPolicyManagerInternal = mock(NetworkPolicyManagerInternal.class);
+
userManagerForMock = mock(UserManagerForMock.class);
packageManagerInternal = mock(PackageManagerInternal.class);
powerManager = mock(PowerManagerForMock.class);
diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistConfigTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistConfigTests.java
index 851d2c6..654acc2 100644
--- a/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistConfigTests.java
+++ b/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistConfigTests.java
@@ -33,12 +33,15 @@
import org.junit.runner.RunWith;
import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
+import java.io.PrintWriter;
import java.util.Arrays;
+
/**
* runtest frameworks-services -c com.android.server.net.watchlist.WatchlistConfigTests
*/
@@ -117,6 +120,15 @@
assertEquals(TEST_XML_1_HASH, HexDump.toHexString(config.getWatchlistConfigHash()));
}
+ @Test
+ public void testWatchlistConfig_testDumpDoesNotCrash() throws Exception {
+ WatchlistConfig config = new WatchlistConfig(new File("/not_exist_path.xml"));
+ ByteArrayOutputStream bs = new ByteArrayOutputStream(2048);
+ PrintWriter pw = new PrintWriter(bs);
+ // Make sure dump still works even watchlist does not exist
+ config.dump(null, pw, null);
+ }
+
private static void copyWatchlistConfigXml(Context context, String xmlAsset, File outFile)
throws IOException {
writeToFile(outFile, readAsset(context, xmlAsset));
diff --git a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
index 6468763..5f44fb6 100644
--- a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
@@ -17,14 +17,17 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
@@ -74,11 +77,11 @@
return super.setRelativeLayer(sc, relativeTo, layer);
}
- int getLayer(SurfaceControl sc) {
+ private int getLayer(SurfaceControl sc) {
return mLayersForControl.getOrDefault(sc, 0);
}
- SurfaceControl getRelativeLayer(SurfaceControl sc) {
+ private SurfaceControl getRelativeLayer(SurfaceControl sc) {
return mRelativeLayersForControl.get(sc);
}
};
@@ -146,8 +149,9 @@
return p;
}
- void assertZOrderGreaterThan(LayerRecordingTransaction t,
- SurfaceControl left, SurfaceControl right) throws Exception {
+
+ void assertZOrderGreaterThan(LayerRecordingTransaction t, SurfaceControl left,
+ SurfaceControl right) throws Exception {
final LinkedList<SurfaceControl> leftParentChain = getAncestors(t, left);
final LinkedList<SurfaceControl> rightParentChain = getAncestors(t, right);
@@ -171,9 +175,12 @@
}
}
- void assertWindowLayerGreaterThan(LayerRecordingTransaction t,
- WindowState left, WindowState right) throws Exception {
- assertZOrderGreaterThan(t, left.getSurfaceControl(), right.getSurfaceControl());
+ void assertWindowHigher(WindowState left, WindowState right) throws Exception {
+ assertZOrderGreaterThan(mTransaction, left.getSurfaceControl(), right.getSurfaceControl());
+ }
+
+ WindowState createWindow(String name) {
+ return createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, name);
}
@Test
@@ -184,38 +191,37 @@
// The Ime has an higher base layer than app windows and lower base layer than system
// windows, so it should be above app windows and below system windows if there isn't an IME
// target.
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
- assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
- assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+ assertWindowHigher(mImeWindow, mChildAppWindowAbove);
+ assertWindowHigher(mImeWindow, mAppWindow);
+ assertWindowHigher(mNavBarWindow, mImeWindow);
+ assertWindowHigher(mStatusBarWindow, mImeWindow);
// And, IME dialogs should always have an higher layer than the IME.
- assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+ assertWindowHigher(mImeDialogWindow, mImeWindow);
}
@Test
public void testAssignWindowLayers_ForImeWithAppTarget() throws Exception {
- final WindowState imeAppTarget =
- createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
+ final WindowState imeAppTarget = createWindow("imeAppTarget");
sWm.mInputMethodTarget = imeAppTarget;
+
mDisplayContent.assignChildLayers(mTransaction);
// Ime should be above all app windows and below system windows if it is targeting an app
// window.
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget);
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
- assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
- assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+ assertWindowHigher(mImeWindow, imeAppTarget);
+ assertWindowHigher(mImeWindow, mChildAppWindowAbove);
+ assertWindowHigher(mImeWindow, mAppWindow);
+ assertWindowHigher(mNavBarWindow, mImeWindow);
+ assertWindowHigher(mStatusBarWindow, mImeWindow);
// And, IME dialogs should always have an higher layer than the IME.
- assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+ assertWindowHigher(mImeDialogWindow, mImeWindow);
}
@Test
public void testAssignWindowLayers_ForImeWithAppTargetWithChildWindows() throws Exception {
- final WindowState imeAppTarget =
- createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
+ final WindowState imeAppTarget = createWindow("imeAppTarget");
final WindowState imeAppTargetChildAboveWindow = createWindow(imeAppTarget,
TYPE_APPLICATION_ATTACHED_DIALOG, imeAppTarget.mToken,
"imeAppTargetChildAboveWindow");
@@ -228,41 +234,38 @@
// Ime should be above all app windows except for child windows that are z-ordered above it
// and below system windows if it is targeting an app window.
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget);
- assertWindowLayerGreaterThan(mTransaction, imeAppTargetChildAboveWindow, mImeWindow);
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
- assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
- assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+ assertWindowHigher(mImeWindow, imeAppTarget);
+ assertWindowHigher(imeAppTargetChildAboveWindow, mImeWindow);
+ assertWindowHigher(mImeWindow, mChildAppWindowAbove);
+ assertWindowHigher(mImeWindow, mAppWindow);
+ assertWindowHigher(mNavBarWindow, mImeWindow);
+ assertWindowHigher(mStatusBarWindow, mImeWindow);
// And, IME dialogs should always have an higher layer than the IME.
- assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+ assertWindowHigher(mImeDialogWindow, mImeWindow);
}
@Test
public void testAssignWindowLayers_ForImeWithAppTargetAndAppAbove() throws Exception {
- final WindowState appBelowImeTarget =
- createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appBelowImeTarget");
- final WindowState imeAppTarget =
- createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
- final WindowState appAboveImeTarget =
- createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appAboveImeTarget");
+ final WindowState appBelowImeTarget = createWindow("appBelowImeTarget");
+ final WindowState imeAppTarget = createWindow("imeAppTarget");
+ final WindowState appAboveImeTarget = createWindow("appAboveImeTarget");
sWm.mInputMethodTarget = imeAppTarget;
mDisplayContent.assignChildLayers(mTransaction);
// Ime should be above all app windows except for non-fullscreen app window above it and
// below system windows if it is targeting an app window.
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget);
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, appBelowImeTarget);
- assertWindowLayerGreaterThan(mTransaction, appAboveImeTarget, mImeWindow);
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
- assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
- assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+ assertWindowHigher(mImeWindow, imeAppTarget);
+ assertWindowHigher(mImeWindow, appBelowImeTarget);
+ assertWindowHigher(appAboveImeTarget, mImeWindow);
+ assertWindowHigher(mImeWindow, mChildAppWindowAbove);
+ assertWindowHigher(mImeWindow, mAppWindow);
+ assertWindowHigher(mNavBarWindow, mImeWindow);
+ assertWindowHigher(mStatusBarWindow, mImeWindow);
// And, IME dialogs should always have an higher layer than the IME.
- assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+ assertWindowHigher(mImeDialogWindow, mImeWindow);
}
@Test
@@ -276,20 +279,20 @@
// The IME target base layer is higher than all window except for the nav bar window, so the
// IME should be above all windows except for the nav bar.
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeSystemOverlayTarget);
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, mDockedDividerWindow);
+ assertWindowHigher(mImeWindow, imeSystemOverlayTarget);
+ assertWindowHigher(mImeWindow, mChildAppWindowAbove);
+ assertWindowHigher(mImeWindow, mAppWindow);
+ assertWindowHigher(mImeWindow, mDockedDividerWindow);
// The IME has a higher base layer than the status bar so we may expect it to go
// above the status bar once they are both in the Non-App layer, as past versions of this
// test enforced. However this seems like the wrong behavior unless the status bar is the
// IME target.
- assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
- assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+ assertWindowHigher(mNavBarWindow, mImeWindow);
+ assertWindowHigher(mStatusBarWindow, mImeWindow);
// And, IME dialogs should always have an higher layer than the IME.
- assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+ assertWindowHigher(mImeDialogWindow, mImeWindow);
}
@Test
@@ -297,17 +300,18 @@
sWm.mInputMethodTarget = mStatusBarWindow;
mDisplayContent.assignChildLayers(mTransaction);
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, mDockedDividerWindow);
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, mStatusBarWindow);
+ assertWindowHigher(mImeWindow, mChildAppWindowAbove);
+ assertWindowHigher(mImeWindow, mAppWindow);
+ assertWindowHigher(mImeWindow, mDockedDividerWindow);
+ assertWindowHigher(mImeWindow, mStatusBarWindow);
// And, IME dialogs should always have an higher layer than the IME.
- assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+ assertWindowHigher(mImeDialogWindow, mImeWindow);
}
@Test
public void testStackLayers() throws Exception {
+ final WindowState anyWindow1 = createWindow("anyWindow");
final WindowState pinnedStackWindow = createWindowOnStack(null, WINDOWING_MODE_PINNED,
ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent,
"pinnedStackWindow");
@@ -317,12 +321,22 @@
final WindowState assistantStackWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION,
mDisplayContent, "assistantStackWindow");
+ final WindowState homeActivityWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_HOME, TYPE_BASE_APPLICATION,
+ mDisplayContent, "homeActivityWindow");
+ final WindowState anyWindow2 = createWindow("anyWindow2");
mDisplayContent.assignChildLayers(mTransaction);
- assertWindowLayerGreaterThan(mTransaction, dockedStackWindow, mAppWindow);
- assertWindowLayerGreaterThan(mTransaction, assistantStackWindow, dockedStackWindow);
- assertWindowLayerGreaterThan(mTransaction, pinnedStackWindow, assistantStackWindow);
+ assertWindowHigher(dockedStackWindow, homeActivityWindow);
+ assertWindowHigher(assistantStackWindow, homeActivityWindow);
+ assertWindowHigher(pinnedStackWindow, homeActivityWindow);
+ assertWindowHigher(anyWindow1, homeActivityWindow);
+ assertWindowHigher(anyWindow2, homeActivityWindow);
+ assertWindowHigher(pinnedStackWindow, anyWindow1);
+ assertWindowHigher(pinnedStackWindow, anyWindow2);
+ assertWindowHigher(pinnedStackWindow, dockedStackWindow);
+ assertWindowHigher(pinnedStackWindow, assistantStackWindow);
}
@Test
@@ -337,9 +351,9 @@
// Ime should be above all app windows and below system windows if it is targeting an app
// window.
- assertWindowLayerGreaterThan(mTransaction, navBarPanel, mNavBarWindow);
- assertWindowLayerGreaterThan(mTransaction, statusBarPanel, mStatusBarWindow);
- assertWindowLayerGreaterThan(mTransaction, statusBarSubPanel, statusBarPanel);
+ assertWindowHigher(navBarPanel, mNavBarWindow);
+ assertWindowHigher(statusBarPanel, mStatusBarWindow);
+ assertWindowHigher(statusBarSubPanel, statusBarPanel);
}
@Test
@@ -347,8 +361,7 @@
// TODO(b/70040778): We should aim to eliminate the last user of TYPE_APPLICATION_MEDIA
// then we can drop all negative layering on the windowing side.
- final WindowState anyWindow =
- createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "anyWindow");
+ final WindowState anyWindow = createWindow("anyWindow");
final WindowState child = createWindow(anyWindow, TYPE_APPLICATION_MEDIA, mDisplayContent,
"TypeApplicationMediaChild");
final WindowState mediaOverlayChild = createWindow(anyWindow, TYPE_APPLICATION_MEDIA_OVERLAY,
@@ -356,7 +369,29 @@
mDisplayContent.assignChildLayers(mTransaction);
- assertWindowLayerGreaterThan(mTransaction, anyWindow, mediaOverlayChild);
- assertWindowLayerGreaterThan(mTransaction, mediaOverlayChild, child);
+ assertWindowHigher(anyWindow, mediaOverlayChild);
+ assertWindowHigher(mediaOverlayChild, child);
+ }
+
+ @Test
+ public void testDockedDividerPosition() throws Exception {
+ final WindowState pinnedStackWindow = createWindowOnStack(null, WINDOWING_MODE_PINNED,
+ ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent,
+ "pinnedStackWindow");
+ final WindowState splitScreenWindow = createWindowOnStack(null,
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
+ mDisplayContent, "splitScreenWindow");
+ final WindowState splitScreenSecondaryWindow = createWindowOnStack(null,
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD,
+ TYPE_BASE_APPLICATION, mDisplayContent, "splitScreenSecondaryWindow");
+ final WindowState assistantStackWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION,
+ mDisplayContent, "assistantStackWindow");
+
+ mDisplayContent.assignChildLayers(mTransaction);
+
+ assertWindowHigher(mDockedDividerWindow, splitScreenWindow);
+ assertWindowHigher(mDockedDividerWindow, splitScreenSecondaryWindow);
+ assertWindowHigher(pinnedStackWindow, mDockedDividerWindow);
}
}
diff --git a/services/tests/uiservicestests/AndroidManifest.xml b/services/tests/uiservicestests/AndroidManifest.xml
index f022dcf..3475572 100644
--- a/services/tests/uiservicestests/AndroidManifest.xml
+++ b/services/tests/uiservicestests/AndroidManifest.xml
@@ -25,6 +25,7 @@
<uses-permission android:name="android.permission.ACCESS_NOTIFICATIONS" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
+ <uses-permission android:name="android.permission.ACCESS_VOICE_INTERACTION_SERVICE" />
<application>
<uses-library android:name="android.test.runner" />
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
index ce328c2..aada682 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
@@ -149,7 +149,7 @@
ISliceListener listener = mock(ISliceListener.class);
assertFalse(mPinnedSliceManager.isPinned());
- mPinnedSliceManager.addSliceListener(listener, FIRST_SPECS);
+ mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS);
assertTrue(mPinnedSliceManager.isPinned());
assertTrue(mPinnedSliceManager.removeSliceListener(listener));
@@ -162,9 +162,9 @@
ISliceListener listener2 = mock(ISliceListener.class);
assertFalse(mPinnedSliceManager.isPinned());
- mPinnedSliceManager.addSliceListener(listener, FIRST_SPECS);
+ mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS);
assertTrue(mPinnedSliceManager.isPinned());
- mPinnedSliceManager.addSliceListener(listener2, FIRST_SPECS);
+ mPinnedSliceManager.addSliceListener(listener2, mContext.getPackageName(), FIRST_SPECS);
assertFalse(mPinnedSliceManager.removeSliceListener(listener));
assertTrue(mPinnedSliceManager.removeSliceListener(listener2));
@@ -176,7 +176,7 @@
ISliceListener listener = mock(ISliceListener.class);
assertFalse(mPinnedSliceManager.isPinned());
- mPinnedSliceManager.addSliceListener(listener, FIRST_SPECS);
+ mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS);
assertTrue(mPinnedSliceManager.isPinned());
mPinnedSliceManager.pin("pkg", FIRST_SPECS);
@@ -199,7 +199,7 @@
assertFalse(mPinnedSliceManager.isPinned());
- mPinnedSliceManager.addSliceListener(listener, FIRST_SPECS);
+ mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS);
mPinnedSliceManager.onChange();
TestableLooper.get(this).processAllMessages();
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index ff3d586..6782188 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -78,6 +78,7 @@
import com.android.internal.app.IBatteryStats;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices;
@@ -90,6 +91,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.CountDownLatch;
/**
* Manages the standby state of an app, listening to various events.
@@ -128,6 +130,11 @@
// Expiration time for predicted bucket
private static final long PREDICTION_TIMEOUT = 12 * ONE_HOUR;
+ /**
+ * Indicates the maximum wait time for admin data to be available;
+ */
+ private static final long WAIT_FOR_ADMIN_DATA_TIMEOUT_MS = 10_000;
+
// To name the lock for stack traces
static class Lock {}
@@ -153,6 +160,8 @@
@GuardedBy("mActiveAdminApps")
private final SparseArray<Set<String>> mActiveAdminApps = new SparseArray<>();
+ private final CountDownLatch mAdminDataAvailableLatch = new CountDownLatch(1);
+
// Messages for the handler
static final int MSG_INFORM_LISTENERS = 3;
static final int MSG_FORCE_IDLE_STATE = 4;
@@ -895,6 +904,20 @@
}
}
+ public void onAdminDataAvailable() {
+ mAdminDataAvailableLatch.countDown();
+ }
+
+ /**
+ * This will only ever be called once - during device boot.
+ */
+ private void waitForAdminData() {
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)) {
+ ConcurrentUtils.waitForCountDownNoInterrupt(mAdminDataAvailableLatch,
+ WAIT_FOR_ADMIN_DATA_TIMEOUT_MS, "Wait for admin data");
+ }
+ }
+
Set<String> getActiveAdminAppsForTest(int userId) {
synchronized (mActiveAdminApps) {
return mActiveAdminApps.get(userId);
@@ -1224,6 +1247,7 @@
case MSG_ONE_TIME_CHECK_IDLE_STATES:
mHandler.removeMessages(MSG_ONE_TIME_CHECK_IDLE_STATES);
+ waitForAdminData();
checkIdleStates(UserHandle.USER_ALL);
break;
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 78cc81f..979feaa 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -1030,5 +1030,10 @@
public void setActiveAdminApps(Set<String> packageNames, int userId) {
mAppStandby.setActiveAdminApps(packageNames, userId);
}
+
+ @Override
+ public void onAdminDataAvailable() {
+ mAppStandby.onAdminDataAvailable();
+ }
}
}
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 4e1c15f..38408fe 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -126,14 +126,31 @@
private UiccAccessRule[] mAccessRules;
/**
+ * The ID of the SIM card. It is the ICCID of the active profile for a UICC card and the EID
+ * for an eUICC card.
+ */
+ private String mCardId;
+
+ /**
+ * @hide
+ */
+ public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
+ CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
+ Bitmap icon, int mcc, int mnc, String countryIso) {
+ this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
+ roaming, icon, mcc, mnc, countryIso, false /* isEmbedded */,
+ null /* accessRules */, null /* accessRules */);
+ }
+
+ /**
* @hide
*/
public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
- Bitmap icon, int mcc, int mnc, String countryIso) {
+ Bitmap icon, int mcc, int mnc, String countryIso, boolean isEmbedded,
+ @Nullable UiccAccessRule[] accessRules) {
this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
- roaming, icon, mcc, mnc, countryIso, false /* isEmbedded */,
- null /* accessRules */);
+ roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, null /* cardId */);
}
/**
@@ -142,7 +159,7 @@
public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
Bitmap icon, int mcc, int mnc, String countryIso, boolean isEmbedded,
- @Nullable UiccAccessRule[] accessRules) {
+ @Nullable UiccAccessRule[] accessRules, String cardId) {
this.mId = id;
this.mIccId = iccId;
this.mSimSlotIndex = simSlotIndex;
@@ -158,6 +175,7 @@
this.mCountryIso = countryIso;
this.mIsEmbedded = isEmbedded;
this.mAccessRules = accessRules;
+ this.mCardId = cardId;
}
/**
@@ -387,6 +405,14 @@
return mAccessRules;
}
+ /**
+ * @return the ID of the SIM card which contains the subscription.
+ * @hide
+ */
+ public String getCardId() {
+ return this.mCardId;
+ }
+
public static final Parcelable.Creator<SubscriptionInfo> CREATOR = new Parcelable.Creator<SubscriptionInfo>() {
@Override
public SubscriptionInfo createFromParcel(Parcel source) {
@@ -405,10 +431,11 @@
Bitmap iconBitmap = Bitmap.CREATOR.createFromParcel(source);
boolean isEmbedded = source.readBoolean();
UiccAccessRule[] accessRules = source.createTypedArray(UiccAccessRule.CREATOR);
+ String cardId = source.readString();
return new SubscriptionInfo(id, iccId, simSlotIndex, displayName, carrierName,
nameSource, iconTint, number, dataRoaming, iconBitmap, mcc, mnc, countryIso,
- isEmbedded, accessRules);
+ isEmbedded, accessRules, cardId);
}
@Override
@@ -434,6 +461,7 @@
mIconBitmap.writeToParcel(dest, flags);
dest.writeBoolean(mIsEmbedded);
dest.writeTypedArray(mAccessRules, flags);
+ dest.writeString(mCardId);
}
@Override
@@ -459,11 +487,13 @@
@Override
public String toString() {
String iccIdToPrint = givePrintableIccid(mIccId);
+ String cardIdToPrint = givePrintableIccid(mCardId);
return "{id=" + mId + ", iccId=" + iccIdToPrint + " simSlotIndex=" + mSimSlotIndex
+ " displayName=" + mDisplayName + " carrierName=" + mCarrierName
+ " nameSource=" + mNameSource + " iconTint=" + mIconTint
+ " dataRoaming=" + mDataRoaming + " iconBitmap=" + mIconBitmap + " mcc " + mMcc
+ " mnc " + mMnc + " isEmbedded " + mIsEmbedded
- + " accessRules " + Arrays.toString(mAccessRules) + "}";
+ + " accessRules " + Arrays.toString(mAccessRules)
+ + " cardId=" + cardIdToPrint + "}";
}
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 1406093..57f4cf2 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -284,6 +284,14 @@
public static final String IS_EMBEDDED = "is_embedded";
/**
+ * TelephonyProvider column name for SIM card identifier. For UICC card it is the ICCID of the
+ * current enabled profile on the card, while for eUICC card it is the EID of the card.
+ * <P>Type: TEXT (String)</P>
+ * @hide
+ */
+ public static final String CARD_ID = "card_id";
+
+ /**
* TelephonyProvider column name for the encoded {@link UiccAccessRule}s from
* {@link UiccAccessRule#encodeRules}. Only present if {@link #IS_EMBEDDED} is 1.
* <p>TYPE: BLOB
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
index 10d6deb..9f2cb92 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -66,6 +66,7 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -174,6 +175,7 @@
}
@Test
+ @Ignore
public void testDefaultNetworkEvents() throws Exception {
final long cell = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_CELLULAR});
final long wifi = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_WIFI});
@@ -292,6 +294,7 @@
}
@Test
+ @Ignore
public void testEndToEndLogging() throws Exception {
// TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto.
IpConnectivityLog logger = new IpConnectivityLog(mService.impl);
diff --git a/tests/permission/src/com/android/framework/permission/tests/ServiceManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/ServiceManagerPermissionTests.java
index 4098b98..0504c79 100644
--- a/tests/permission/src/com/android/framework/permission/tests/ServiceManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/ServiceManagerPermissionTests.java
@@ -62,6 +62,11 @@
public boolean isRuntimePermission(String permission) {
return false;
}
+
+ @Override
+ public int getPackageUid(String packageName, int flags) {
+ return -1;
+ }
};
ServiceManagerNative.asInterface(BinderInternal.getContextObject())
.setPermissionController(pc);
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 6438631..2c60118 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -267,8 +267,15 @@
public static final int AP_BAND_5GHZ = 1;
/**
+ * Device is allowed to choose the optimal band (2Ghz or 5Ghz) based on device capability,
+ * operating country code and current radio conditions.
+ * @hide
+ */
+ public static final int AP_BAND_ANY = -1;
+
+ /**
* The band which AP resides on
- * 0-2G 1-5G
+ * -1:Any 0:2G 1:5G
* By default, 2G is chosen
* @hide
*/