diff options
342 files changed, 11716 insertions, 3272 deletions
diff --git a/Android.bp b/Android.bp index 19c0580e21d2..d1332bb4fe87 100644 --- a/Android.bp +++ b/Android.bp @@ -230,6 +230,7 @@ java_library { "core/java/android/os/ISchedulingPolicyService.aidl", "core/java/android/os/IStatsCompanionService.aidl", "core/java/android/os/IStatsManager.aidl", + "core/java/android/os/ISystemUpdateManager.aidl", "core/java/android/os/IThermalEventListener.aidl", "core/java/android/os/IThermalService.aidl", "core/java/android/os/IUpdateLock.aidl", @@ -689,7 +690,10 @@ gensrcs { " $(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 298b85ddf084..2254008422e0 100644 --- a/Android.mk +++ b/Android.mk @@ -331,6 +331,8 @@ LOCAL_DROIDDOC_OPTIONS:=\ $(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 @@ LOCAL_UNINSTALLABLE_MODULE := true 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 @@ LOCAL_DROIDDOC_OPTIONS:=\ -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 @@ LOCAL_UNINSTALLABLE_MODULE := true 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_PROTOC_FLAGS := \ 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 @@ LOCAL_PROTO_JAVA_OUTPUT_PARAMS := \ 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_OPTIMIZE_TYPE := lite 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 bd0b944e1233..132a2f9b2d0c 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 000000000000..145fbcd2d5d7 --- /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 d292e7dc3b96..000000000000 --- 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/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java index 6975609d990a..682885b3120d 100644 --- a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java +++ b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java @@ -25,9 +25,12 @@ import android.support.test.filters.LargeTest; import android.support.test.runner.AndroidJUnit4; import android.content.res.ColorStateList; +import android.graphics.Canvas; import android.graphics.Typeface; import android.text.Layout; import android.text.style.TextAppearanceSpan; +import android.view.DisplayListCanvas; +import android.view.RenderNode; import org.junit.Before; import org.junit.Rule; @@ -285,4 +288,157 @@ public class StaticLayoutPerfTest { .build(); } } + + @Test + public void testDraw_FixedText_NoStyled() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); + final RenderNode node = RenderNode.create("benchmark", null); + while (state.keepRunning()) { + state.pauseTiming(); + final StaticLayout layout = + StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); + final DisplayListCanvas c = node.start(1200, 200); + state.resumeTiming(); + + layout.draw(c); + } + } + + @Test + public void testDraw_RandomText_Styled() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final RenderNode node = RenderNode.create("benchmark", null); + while (state.keepRunning()) { + state.pauseTiming(); + final CharSequence text = generateRandomParagraph(WORD_LENGTH, STYLE_TEXT); + final StaticLayout layout = + StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); + final DisplayListCanvas c = node.start(1200, 200); + state.resumeTiming(); + + layout.draw(c); + } + } + + @Test + public void testDraw_RandomText_NoStyled() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final RenderNode node = RenderNode.create("benchmark", null); + while (state.keepRunning()) { + state.pauseTiming(); + final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); + final StaticLayout layout = + StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); + final DisplayListCanvas c = node.start(1200, 200); + state.resumeTiming(); + + layout.draw(c); + } + } + + @Test + public void testDraw_RandomText_Styled_WithoutCache() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final RenderNode node = RenderNode.create("benchmark", null); + while (state.keepRunning()) { + state.pauseTiming(); + final CharSequence text = generateRandomParagraph(WORD_LENGTH, STYLE_TEXT); + final StaticLayout layout = + StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); + final DisplayListCanvas c = node.start(1200, 200); + Canvas.freeTextLayoutCaches(); + state.resumeTiming(); + + layout.draw(c); + } + } + + @Test + public void testDraw_RandomText_NoStyled_WithoutCache() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final RenderNode node = RenderNode.create("benchmark", null); + while (state.keepRunning()) { + state.pauseTiming(); + final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); + final StaticLayout layout = + StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); + final DisplayListCanvas c = node.start(1200, 200); + Canvas.freeTextLayoutCaches(); + state.resumeTiming(); + + layout.draw(c); + } + } + + @Test + public void testDraw_MeasuredText_Styled() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final RenderNode node = RenderNode.create("benchmark", null); + while (state.keepRunning()) { + state.pauseTiming(); + final MeasuredText text = new MeasuredText.Builder( + generateRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT).build(); + final StaticLayout layout = + StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); + final DisplayListCanvas c = node.start(1200, 200); + state.resumeTiming(); + + layout.draw(c); + } + } + + @Test + public void testDraw_MeasuredText_NoStyled() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final RenderNode node = RenderNode.create("benchmark", null); + while (state.keepRunning()) { + state.pauseTiming(); + final MeasuredText text = new MeasuredText.Builder( + generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT).build(); + final StaticLayout layout = + StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); + final DisplayListCanvas c = node.start(1200, 200); + state.resumeTiming(); + + layout.draw(c); + } + } + + @Test + public void testDraw_MeasuredText_Styled_WithoutCache() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final RenderNode node = RenderNode.create("benchmark", null); + while (state.keepRunning()) { + state.pauseTiming(); + final MeasuredText text = new MeasuredText.Builder( + generateRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT).build(); + final StaticLayout layout = + StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); + final DisplayListCanvas c = node.start(1200, 200); + Canvas.freeTextLayoutCaches(); + state.resumeTiming(); + + layout.draw(c); + } + } + + @Test + public void testDraw_MeasuredText_NoStyled_WithoutCache() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final RenderNode node = RenderNode.create("benchmark", null); + while (state.keepRunning()) { + state.pauseTiming(); + final MeasuredText text = new MeasuredText.Builder( + generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT).build(); + final StaticLayout layout = + StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); + final DisplayListCanvas c = node.start(1200, 200); + Canvas.freeTextLayoutCaches(); + state.resumeTiming(); + + layout.draw(c); + } + } + } diff --git a/api/current.txt b/api/current.txt index 86f1a3b52583..8d26717af988 100644 --- a/api/current.txt +++ b/api/current.txt @@ -2796,6 +2796,7 @@ package android.accessibilityservice { 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"; @@ -6473,6 +6474,7 @@ package android.app.admin { method public boolean isMasterVolumeMuted(android.content.ComponentName); method public boolean isNetworkLoggingEnabled(android.content.ComponentName); method public boolean isPackageSuspended(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException; + method public boolean isPrintingEnabled(); method public boolean isProfileOwnerApp(java.lang.String); method public boolean isProvisioningAllowed(java.lang.String); method public boolean isResetPasswordTokenActive(android.content.ComponentName); @@ -6542,6 +6544,7 @@ package android.app.admin { method public boolean setPermittedAccessibilityServices(android.content.ComponentName, java.util.List<java.lang.String>); method public boolean setPermittedCrossProfileNotificationListeners(android.content.ComponentName, java.util.List<java.lang.String>); method public boolean setPermittedInputMethods(android.content.ComponentName, java.util.List<java.lang.String>); + method public void setPrintingEnabled(android.content.ComponentName, boolean); method public void setProfileEnabled(android.content.ComponentName); method public void setProfileName(android.content.ComponentName, java.lang.String); method public void setRecommendedGlobalProxy(android.content.ComponentName, android.net.ProxyInfo); @@ -6672,6 +6675,7 @@ package android.app.admin { 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 +7256,7 @@ package android.app.usage { 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 +7268,9 @@ package android.app.usage { 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 +15241,26 @@ package android.hardware { 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 @@ -15559,8 +15575,10 @@ package android.hardware.camera2 { method public <T> T get(android.hardware.camera2.CameraCharacteristics.Key<T>); method public java.util.List<android.hardware.camera2.CaptureRequest.Key<?>> getAvailableCaptureRequestKeys(); method public java.util.List<android.hardware.camera2.CaptureResult.Key<?>> getAvailableCaptureResultKeys(); + method public java.util.List<android.hardware.camera2.CaptureRequest.Key<?>> getAvailablePhysicalCameraRequestKeys(); method public java.util.List<android.hardware.camera2.CaptureRequest.Key<?>> getAvailableSessionKeys(); method public java.util.List<android.hardware.camera2.CameraCharacteristics.Key<?>> getKeys(); + method public java.util.List<java.lang.String> getPhysicalCameraIds(); field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES; field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AE_AVAILABLE_ANTIBANDING_MODES; field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AE_AVAILABLE_MODES; @@ -15599,6 +15617,7 @@ package android.hardware.camera2 { field public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_POSE_ROTATION; field public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_POSE_TRANSLATION; field public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_RADIAL_DISTORTION; + field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE; field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES; field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REPROCESS_MAX_CAPTURE_STALL; field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> REQUEST_AVAILABLE_CAPABILITIES; @@ -15639,6 +15658,7 @@ package android.hardware.camera2 { field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES; field public static final android.hardware.camera2.CameraCharacteristics.Key<boolean[]> STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES; field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES; + field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> STATISTICS_INFO_AVAILABLE_OIS_DATA_MODES; field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> STATISTICS_INFO_MAX_FACE_COUNT; field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SYNC_MAX_LATENCY; field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> TONEMAP_AVAILABLE_TONE_MAP_MODES; @@ -15659,6 +15679,7 @@ package android.hardware.camera2 { public abstract class CameraDevice implements java.lang.AutoCloseable { method public abstract void close(); method public abstract android.hardware.camera2.CaptureRequest.Builder createCaptureRequest(int) throws android.hardware.camera2.CameraAccessException; + method public android.hardware.camera2.CaptureRequest.Builder createCaptureRequest(int, java.util.Set<java.lang.String>) throws android.hardware.camera2.CameraAccessException; method public abstract void createCaptureSession(java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException; method public void createCaptureSession(android.hardware.camera2.params.SessionConfiguration) throws android.hardware.camera2.CameraAccessException; method public abstract void createCaptureSessionByOutputConfigurations(java.util.List<android.hardware.camera2.params.OutputConfiguration>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException; @@ -15829,6 +15850,7 @@ package android.hardware.camera2 { field public static final int HOT_PIXEL_MODE_HIGH_QUALITY = 2; // 0x2 field public static final int HOT_PIXEL_MODE_OFF = 0; // 0x0 field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_3 = 3; // 0x3 + field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL = 4; // 0x4 field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_FULL = 1; // 0x1 field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY = 2; // 0x2 field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED = 0; // 0x0 @@ -15844,6 +15866,8 @@ package android.hardware.camera2 { field public static final int LENS_POSE_REFERENCE_PRIMARY_CAMERA = 0; // 0x0 field public static final int LENS_STATE_MOVING = 1; // 0x1 field public static final int LENS_STATE_STATIONARY = 0; // 0x0 + field public static final int LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_APPROXIMATE = 0; // 0x0 + field public static final int LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_CALIBRATED = 1; // 0x1 field public static final int NOISE_REDUCTION_MODE_FAST = 1; // 0x1 field public static final int NOISE_REDUCTION_MODE_HIGH_QUALITY = 2; // 0x2 field public static final int NOISE_REDUCTION_MODE_MINIMAL = 3; // 0x3 @@ -15853,6 +15877,7 @@ package android.hardware.camera2 { field public static final int REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE = 6; // 0x6 field public static final int REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO = 9; // 0x9 field public static final int REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT = 8; // 0x8 + field public static final int REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA = 11; // 0xb field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING = 2; // 0x2 field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR = 1; // 0x1 field public static final int REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING = 10; // 0xa @@ -15902,6 +15927,8 @@ package android.hardware.camera2 { field public static final int STATISTICS_FACE_DETECT_MODE_SIMPLE = 1; // 0x1 field public static final int STATISTICS_LENS_SHADING_MAP_MODE_OFF = 0; // 0x0 field public static final int STATISTICS_LENS_SHADING_MAP_MODE_ON = 1; // 0x1 + field public static final int STATISTICS_OIS_DATA_MODE_OFF = 0; // 0x0 + field public static final int STATISTICS_OIS_DATA_MODE_ON = 1; // 0x1 field public static final int STATISTICS_SCENE_FLICKER_50HZ = 1; // 0x1 field public static final int STATISTICS_SCENE_FLICKER_60HZ = 2; // 0x2 field public static final int STATISTICS_SCENE_FLICKER_NONE = 0; // 0x0 @@ -15984,6 +16011,7 @@ package android.hardware.camera2 { field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> STATISTICS_FACE_DETECT_MODE; field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Boolean> STATISTICS_HOT_PIXEL_MAP_MODE; field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> STATISTICS_LENS_SHADING_MAP_MODE; + field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> STATISTICS_OIS_DATA_MODE; field public static final android.hardware.camera2.CaptureRequest.Key<android.hardware.camera2.params.TonemapCurve> TONEMAP_CURVE; field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Float> TONEMAP_GAMMA; field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> TONEMAP_MODE; @@ -15994,8 +16022,10 @@ package android.hardware.camera2 { method public void addTarget(android.view.Surface); method public android.hardware.camera2.CaptureRequest build(); method public <T> T get(android.hardware.camera2.CaptureRequest.Key<T>); + method public <T> T getPhysicalCameraKey(android.hardware.camera2.CaptureRequest.Key<T>, java.lang.String); method public void removeTarget(android.view.Surface); method public <T> void set(android.hardware.camera2.CaptureRequest.Key<T>, T); + method public <T> android.hardware.camera2.CaptureRequest.Builder setPhysicalCameraKey(android.hardware.camera2.CaptureRequest.Key<T>, T, java.lang.String); method public void setTag(java.lang.Object); } @@ -16083,6 +16113,10 @@ package android.hardware.camera2 { field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Boolean> STATISTICS_HOT_PIXEL_MAP_MODE; field public static final android.hardware.camera2.CaptureResult.Key<android.hardware.camera2.params.LensShadingMap> STATISTICS_LENS_SHADING_CORRECTION_MAP; field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> STATISTICS_LENS_SHADING_MAP_MODE; + field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> STATISTICS_OIS_DATA_MODE; + field public static final android.hardware.camera2.CaptureResult.Key<long[]> STATISTICS_OIS_TIMESTAMPS; + field public static final android.hardware.camera2.CaptureResult.Key<float[]> STATISTICS_OIS_X_SHIFTS; + field public static final android.hardware.camera2.CaptureResult.Key<float[]> STATISTICS_OIS_Y_SHIFTS; field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> STATISTICS_SCENE_FLICKER; field public static final android.hardware.camera2.CaptureResult.Key<android.hardware.camera2.params.TonemapCurve> TONEMAP_CURVE; field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> TONEMAP_GAMMA; @@ -16191,6 +16225,7 @@ package android.hardware.camera2.params { method public int getSurfaceGroupId(); method public java.util.List<android.view.Surface> getSurfaces(); method public void removeSurface(android.view.Surface); + method public void setPhysicalCameraId(java.lang.String); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.hardware.camera2.params.OutputConfiguration> CREATOR; field public static final int SURFACE_GROUP_ID_NONE = -1; // 0xffffffff @@ -21737,7 +21772,13 @@ package android.media { 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 +21786,7 @@ package android.media { 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 +21829,7 @@ package android.media { 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 +22130,7 @@ package android.media { 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 +22144,7 @@ package android.media { 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 +22180,7 @@ package android.media { 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 +22196,12 @@ package android.media { 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); @@ -27345,7 +27397,7 @@ package android.net.wifi { method public int addNetwork(android.net.wifi.WifiConfiguration); method public void addOrUpdatePasspointConfiguration(android.net.wifi.hotspot2.PasspointConfiguration); method public static int calculateSignalLevel(int, int); - method public void cancelWps(android.net.wifi.WifiManager.WpsCallback); + method public deprecated void cancelWps(android.net.wifi.WifiManager.WpsCallback); method public static int compareSignalLevel(int, int); method public android.net.wifi.WifiManager.MulticastLock createMulticastLock(java.lang.String); method public android.net.wifi.WifiManager.WifiLock createWifiLock(int, java.lang.String); @@ -27378,7 +27430,7 @@ package android.net.wifi { method public boolean setWifiEnabled(boolean); method public void startLocalOnlyHotspot(android.net.wifi.WifiManager.LocalOnlyHotspotCallback, android.os.Handler); method public deprecated boolean startScan(); - method public void startWps(android.net.wifi.WpsInfo, android.net.wifi.WifiManager.WpsCallback); + method public deprecated void startWps(android.net.wifi.WpsInfo, android.net.wifi.WifiManager.WpsCallback); method public int updateNetwork(android.net.wifi.WifiConfiguration); field public static final java.lang.String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK"; field public static final java.lang.String ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE = "android.net.wifi.action.REQUEST_SCAN_ALWAYS_AVAILABLE"; @@ -27408,11 +27460,11 @@ package android.net.wifi { field public static final int WIFI_STATE_ENABLED = 3; // 0x3 field public static final int WIFI_STATE_ENABLING = 2; // 0x2 field public static final int WIFI_STATE_UNKNOWN = 4; // 0x4 - field public static final int WPS_AUTH_FAILURE = 6; // 0x6 - field public static final int WPS_OVERLAP_ERROR = 3; // 0x3 - field public static final int WPS_TIMED_OUT = 7; // 0x7 - field public static final int WPS_TKIP_ONLY_PROHIBITED = 5; // 0x5 - field public static final int WPS_WEP_PROHIBITED = 4; // 0x4 + field public static final deprecated int WPS_AUTH_FAILURE = 6; // 0x6 + field public static final deprecated int WPS_OVERLAP_ERROR = 3; // 0x3 + field public static final deprecated int WPS_TIMED_OUT = 7; // 0x7 + field public static final deprecated int WPS_TKIP_ONLY_PROHIBITED = 5; // 0x5 + field public static final deprecated int WPS_WEP_PROHIBITED = 4; // 0x4 } public static class WifiManager.LocalOnlyHotspotCallback { @@ -27446,11 +27498,11 @@ package android.net.wifi { method public void setWorkSource(android.os.WorkSource); } - public static abstract class WifiManager.WpsCallback { + public static abstract deprecated class WifiManager.WpsCallback { ctor public WifiManager.WpsCallback(); - method public abstract void onFailed(int); - method public abstract void onStarted(java.lang.String); - method public abstract void onSucceeded(); + method public abstract deprecated void onFailed(int); + method public abstract deprecated void onStarted(java.lang.String); + method public abstract deprecated void onSucceeded(); } public class WpsInfo implements android.os.Parcelable { @@ -27892,6 +27944,29 @@ package android.net.wifi.p2p.nsd { package android.net.wifi.rtt { + public final class LocationCivic implements android.os.Parcelable { + method public int describeContents(); + method public byte[] getData(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.net.wifi.rtt.LocationCivic> CREATOR; + } + + public final class LocationConfigurationInformation implements android.os.Parcelable { + method public int describeContents(); + method public double getAltitude(); + method public int getAltitudeType(); + method public double getAltitudeUncertainty(); + method public double getLatitude(); + method public double getLatitudeUncertainty(); + method public double getLongitude(); + method public double getLongitudeUncertainty(); + method public void writeToParcel(android.os.Parcel, int); + field public static final int ALTITUDE_IN_FLOORS = 2; // 0x2 + field public static final int ALTITUDE_IN_METERS = 1; // 0x1 + field public static final int ALTITUDE_UNKNOWN = 0; // 0x0 + field public static final android.os.Parcelable.Creator<android.net.wifi.rtt.LocationConfigurationInformation> CREATOR; + } + public final class RangingRequest implements android.os.Parcelable { method public int describeContents(); method public static int getMaxPeers(); @@ -27915,6 +27990,8 @@ package android.net.wifi.rtt { method public android.net.MacAddress getMacAddress(); method public android.net.wifi.aware.PeerHandle getPeerHandle(); method public long getRangingTimestampUs(); + method public android.net.wifi.rtt.LocationCivic getReportedLocationCivic(); + method public android.net.wifi.rtt.LocationConfigurationInformation getReportedLocationConfigurationInformation(); method public int getRssi(); method public int getStatus(); method public void writeToParcel(android.os.Parcel, int); @@ -31516,7 +31593,7 @@ package android.os { } public final class Debug { - method public static void attachJvmtiAgent(java.lang.String, java.lang.String) throws java.io.IOException; + method public static void attachJvmtiAgent(java.lang.String, java.lang.String, java.lang.ClassLoader) throws java.io.IOException; method public static deprecated void changeDebugPort(int); method public static void dumpHprofData(java.lang.String) throws java.io.IOException; method public static boolean dumpService(java.lang.String, java.io.FileDescriptor, java.lang.String[]); @@ -33993,9 +34070,10 @@ package android.provider { field public static final java.lang.String DURATION = "duration"; field public static final java.lang.String EXTRA_CALL_TYPE_FILTER = "android.provider.extra.CALL_TYPE_FILTER"; field public static final java.lang.String FEATURES = "features"; + field public static final int FEATURES_ASSISTED_DIALING_USED = 16; // 0x10 field public static final int FEATURES_HD_CALL = 4; // 0x4 field public static final int FEATURES_PULLED_EXTERNALLY = 2; // 0x2 - field public static final int FEATURES_RTT = 16; // 0x10 + field public static final int FEATURES_RTT = 32; // 0x20 field public static final int FEATURES_VIDEO = 1; // 0x1 field public static final int FEATURES_WIFI = 8; // 0x8 field public static final java.lang.String GEOCODED_LOCATION = "geocoded_location"; @@ -37910,7 +37988,6 @@ package android.service.autofill { } public static final class FieldClassification.Match { - method public java.lang.String getAlgorithm(); method public java.lang.String getRemoteId(); method public float getScore(); } @@ -39844,6 +39921,7 @@ package android.telecom { field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 2048; // 0x800 field public static final int CAPABILITY_SUPPORT_HOLD = 2; // 0x2 field public static final int CAPABILITY_SWAP_CONFERENCE = 8; // 0x8 + field public static final int PROPERTY_ASSISTED_DIALING_USED = 512; // 0x200 field public static final int PROPERTY_CONFERENCE = 1; // 0x1 field public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 4; // 0x4 field public static final int PROPERTY_ENTERPRISE_CALL = 32; // 0x20 @@ -40064,6 +40142,7 @@ package android.telecom { field public static final java.lang.String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT"; field public static final java.lang.String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS"; field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER"; + field public static final int PROPERTY_ASSISTED_DIALING_USED = 512; // 0x200 field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20 field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10 field public static final int PROPERTY_IS_RTT = 256; // 0x100 @@ -40486,6 +40565,7 @@ package android.telecom { field public static final java.lang.String ACTION_SHOW_RESPOND_VIA_SMS_SETTINGS = "android.telecom.action.SHOW_RESPOND_VIA_SMS_SETTINGS"; field public static final char DTMF_CHARACTER_PAUSE = 44; // 0x002c ',' field public static final char DTMF_CHARACTER_WAIT = 59; // 0x003b ';' + field public static final java.lang.String EXTRA_ASSISTED_DIALING_TRANSFORMATION_INFO = "android.telecom.extra.ASSISTED_DIALING_TRANSFORMATION_INFO"; field public static final java.lang.String EXTRA_CALL_BACK_NUMBER = "android.telecom.extra.CALL_BACK_NUMBER"; field public static final java.lang.String EXTRA_CALL_DISCONNECT_CAUSE = "android.telecom.extra.CALL_DISCONNECT_CAUSE"; field public static final java.lang.String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecom.extra.CALL_DISCONNECT_MESSAGE"; @@ -40501,6 +40581,7 @@ package android.telecom { field public static final java.lang.String EXTRA_START_CALL_WITH_RTT = "android.telecom.extra.START_CALL_WITH_RTT"; field public static final java.lang.String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.telecom.extra.START_CALL_WITH_SPEAKERPHONE"; field public static final java.lang.String EXTRA_START_CALL_WITH_VIDEO_STATE = "android.telecom.extra.START_CALL_WITH_VIDEO_STATE"; + field public static final java.lang.String EXTRA_USE_ASSISTED_DIALING = "android.telecom.extra.USE_ASSISTED_DIALING"; field public static final java.lang.String GATEWAY_ORIGINAL_ADDRESS = "android.telecom.extra.GATEWAY_ORIGINAL_ADDRESS"; field public static final java.lang.String GATEWAY_PROVIDER_PACKAGE = "android.telecom.extra.GATEWAY_PROVIDER_PACKAGE"; field public static final java.lang.String METADATA_INCLUDE_EXTERNAL_CALLS = "android.telecom.INCLUDE_EXTERNAL_CALLS"; @@ -40513,6 +40594,18 @@ package android.telecom { field public static final int PRESENTATION_UNKNOWN = 3; // 0x3 } + public final class TransformationInfo implements android.os.Parcelable { + ctor public TransformationInfo(java.lang.String, java.lang.String, java.lang.String, java.lang.String, int); + method public int describeContents(); + method public java.lang.String getOriginalNumber(); + method public java.lang.String getTransformedNumber(); + method public int getTransformedNumberCountryCallingCode(); + method public java.lang.String getUserHomeCountryCode(); + method public java.lang.String getUserRoamingCountryCode(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telecom.TransformationInfo> CREATOR; + } + public class VideoProfile implements android.os.Parcelable { ctor public VideoProfile(int); ctor public VideoProfile(int, int); @@ -40676,6 +40769,7 @@ package android.telephony { field public static final java.lang.String KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL = "allow_non_emergency_calls_in_ecm_bool"; field public static final java.lang.String KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL = "always_show_emergency_alert_onoff_bool"; field public static final java.lang.String KEY_APN_EXPAND_BOOL = "apn_expand_bool"; + field public static final java.lang.String KEY_ASSISTED_DIALING_ENABLED_BOOL = "assisted_dialing_enabled_bool"; field public static final java.lang.String KEY_AUTO_RETRY_ENABLED_BOOL = "auto_retry_enabled_bool"; field public static final java.lang.String KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY = "call_forwarding_blocks_while_roaming_string_array"; field public static final java.lang.String KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL = "carrier_allow_turnoff_ims_bool"; @@ -41421,6 +41515,8 @@ package android.telephony { method public android.telephony.TelephonyManager createForPhoneAccountHandle(android.telecom.PhoneAccountHandle); method public android.telephony.TelephonyManager createForSubscriptionId(int); method public java.util.List<android.telephony.CellInfo> getAllCellInfo(); + method public int getAndroidCarrierIdForSubscription(); + method public java.lang.CharSequence getAndroidCarrierNameForSubscription(); method public int getCallState(); method public android.os.PersistableBundle getCarrierConfig(); method public deprecated android.telephony.CellLocation getCellLocation(); @@ -41458,8 +41554,6 @@ package android.telephony { method public int getSimState(); method public int getSimState(int); method public java.lang.String getSubscriberId(); - method public int getSubscriptionCarrierId(); - method public java.lang.String getSubscriptionCarrierName(); method public java.lang.String getVisualVoicemailPackageName(); method public java.lang.String getVoiceMailAlphaTag(); method public java.lang.String getVoiceMailNumber(); @@ -41677,6 +41771,7 @@ package android.telephony.data { method public java.net.InetAddress getMmsProxy(); method public java.net.URL getMmsc(); method public java.lang.String getMvnoType(); + method public int getNetworkTypeBitmask(); method public java.lang.String getOperatorNumeric(); method public java.lang.String getPassword(); method public int getPort(); @@ -41720,11 +41815,11 @@ package android.telephony.data { method public android.telephony.data.ApnSetting.Builder setAuthType(int); method public android.telephony.data.ApnSetting.Builder setCarrierEnabled(boolean); method public android.telephony.data.ApnSetting.Builder setEntryName(java.lang.String); - method public android.telephony.data.ApnSetting.Builder setId(int); method public android.telephony.data.ApnSetting.Builder setMmsPort(int); method public android.telephony.data.ApnSetting.Builder setMmsProxy(java.net.InetAddress); method public android.telephony.data.ApnSetting.Builder setMmsc(java.net.URL); method public android.telephony.data.ApnSetting.Builder setMvnoType(java.lang.String); + method public android.telephony.data.ApnSetting.Builder setNetworkTypeBitmask(int); method public android.telephony.data.ApnSetting.Builder setOperatorNumeric(java.lang.String); method public android.telephony.data.ApnSetting.Builder setPassword(java.lang.String); method public android.telephony.data.ApnSetting.Builder setPort(int); diff --git a/api/system-current.txt b/api/system-current.txt index f35984aaf8d1..ba9f2ac0983e 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -75,6 +75,7 @@ package android { field public static final java.lang.String INSTALL_GRANT_RUNTIME_PERMISSIONS = "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS"; field public static final java.lang.String INSTALL_LOCATION_PROVIDER = "android.permission.INSTALL_LOCATION_PROVIDER"; field public static final java.lang.String INSTALL_PACKAGES = "android.permission.INSTALL_PACKAGES"; + field public static final java.lang.String INSTALL_PACKAGE_UPDATES = "android.permission.INSTALL_PACKAGE_UPDATES"; field public static final java.lang.String INSTALL_SELF_UPDATES = "android.permission.INSTALL_SELF_UPDATES"; field public static final java.lang.String INTENT_FILTER_VERIFICATION_AGENT = "android.permission.INTENT_FILTER_VERIFICATION_AGENT"; field public static final java.lang.String INTERACT_ACROSS_USERS = "android.permission.INTERACT_ACROSS_USERS"; @@ -131,6 +132,7 @@ package android { field public static final java.lang.String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE"; field public static final java.lang.String READ_RUNTIME_PROFILES = "android.permission.READ_RUNTIME_PROFILES"; field public static final java.lang.String READ_SEARCH_INDEXABLES = "android.permission.READ_SEARCH_INDEXABLES"; + field public static final java.lang.String READ_SYSTEM_UPDATE_INFO = "android.permission.READ_SYSTEM_UPDATE_INFO"; field public static final java.lang.String READ_WALLPAPER_INTERNAL = "android.permission.READ_WALLPAPER_INTERNAL"; field public static final java.lang.String READ_WIFI_CREDENTIAL = "android.permission.READ_WIFI_CREDENTIAL"; field public static final java.lang.String REAL_GET_TASKS = "android.permission.REAL_GET_TASKS"; @@ -380,6 +382,7 @@ package android.app.admin { method public java.lang.CharSequence getDeviceOwnerOrganizationName(); method public java.util.List<java.lang.String> getPermittedAccessibilityServices(int); method public java.util.List<java.lang.String> getPermittedInputMethodsForCurrentUser(); + method public java.lang.CharSequence getPrintingDisabledReason(); method public android.content.ComponentName getProfileOwner() throws java.lang.IllegalArgumentException; method public java.lang.String getProfileOwnerNameAsUser(int) throws java.lang.IllegalArgumentException; method public int getUserProvisioningState(); @@ -760,6 +763,7 @@ package android.content { field public static final java.lang.String OEM_LOCK_SERVICE = "oem_lock"; field public static final java.lang.String PERSISTENT_DATA_BLOCK_SERVICE = "persistent_data_block"; field public static final java.lang.String STATS_MANAGER = "stats"; + field public static final java.lang.String SYSTEM_UPDATE_SERVICE = "system_update"; field public static final java.lang.String VR_SERVICE = "vrmanager"; field public static final java.lang.String WIFI_RTT_SERVICE = "rttmanager"; field public static final java.lang.String WIFI_SCANNING_SERVICE = "wifiscanner"; @@ -1777,9 +1781,9 @@ package android.hardware.radio { 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 +1791,31 @@ package android.hardware.radio { 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 +1830,7 @@ package android.hardware.radio { 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 +2868,18 @@ package android.net { 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 { @@ -3489,6 +3506,22 @@ package android.os { method public abstract void onResult(android.os.Bundle); } + public class SystemUpdateManager { + method public android.os.Bundle retrieveSystemUpdateInfo(); + method public void updateSystemUpdateInfo(android.os.PersistableBundle); + field public static final java.lang.String KEY_IS_SECURITY_UPDATE = "is_security_update"; + field public static final java.lang.String KEY_STATUS = "status"; + field public static final java.lang.String KEY_TARGET_BUILD_FINGERPRINT = "target_build_fingerprint"; + field public static final java.lang.String KEY_TARGET_SECURITY_PATCH_LEVEL = "target_security_patch_level"; + field public static final java.lang.String KEY_TITLE = "title"; + field public static final int STATUS_IDLE = 1; // 0x1 + field public static final int STATUS_IN_PROGRESS = 3; // 0x3 + field public static final int STATUS_UNKNOWN = 0; // 0x0 + field public static final int STATUS_WAITING_DOWNLOAD = 2; // 0x2 + field public static final int STATUS_WAITING_INSTALL = 4; // 0x4 + field public static final int STATUS_WAITING_REBOOT = 5; // 0x5 + } + public class UpdateEngine { ctor public UpdateEngine(); method public void applyPayload(java.lang.String, long, long, java.lang.String[]); @@ -3862,20 +3895,10 @@ package android.service.autofill { 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 c41612de4eb3..166796673e25 100644 --- a/cmds/incident_helper/src/TextParserBase.h +++ b/cmds/incident_helper/src/TextParserBase.h @@ -68,4 +68,4 @@ public: 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 8420bc8f7ac7..368a70bc3898 100644 --- a/cmds/incidentd/Android.mk +++ b/cmds/incidentd/Android.mk @@ -134,6 +134,7 @@ LOCAL_SHARED_LIBRARIES := \ libcutils \ libincident \ liblog \ + libprotobuf-cpp-lite \ libprotoutil \ libselinux \ libservices \ diff --git a/cmds/incidentd/README.md b/cmds/incidentd/README.md index ad0fa08c7326..71c6deb18aac 100644 --- a/cmds/incidentd/README.md +++ b/cmds/incidentd/README.md @@ -12,8 +12,8 @@ Run the test on a device manually ``` 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 66667dca2982..1bd146850ea9 100644 --- a/cmds/incidentd/incidentd.rc +++ b/cmds/incidentd/incidentd.rc @@ -19,4 +19,4 @@ service incidentd /system/bin/incidentd 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 30dd339a629b..0fff4e6dc4a0 100644 --- a/cmds/incidentd/src/FdBuffer.cpp +++ b/cmds/incidentd/src/FdBuffer.cpp @@ -63,12 +63,14 @@ FdBuffer::read(int fd, int64_t timeout) 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 @@ FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeou 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 @@ FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeou // 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 c4b54bbbc022..1d5ab59f9ba8 100644 --- a/cmds/incidentd/src/IncidentService.cpp +++ b/cmds/incidentd/src/IncidentService.cpp @@ -43,24 +43,44 @@ String16 const DUMP_PERMISSION("android.permission.DUMP"); 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 @@ ReportRequestQueue::~ReportRequestQueue() } 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 @@ IncidentService::reportIncident(const IncidentReportArgs& args) { ALOGI("reportIncident"); - Status status = checkIncidentPermissions(); + Status status = checkIncidentPermissions(args); if (!status.isOk()) { return status; } @@ -212,7 +232,7 @@ IncidentService::reportIncidentToStream(const IncidentReportArgs& args, { 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 34930aa57321..bd559d6980f1 100644 --- a/cmds/incidentd/src/Reporter.cpp +++ b/cmds/incidentd/src/Reporter.cpp @@ -251,7 +251,7 @@ Reporter::create_file(int* fd) // 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 61d16f815e65..0827785811b6 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 @@ static pid_t 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 @@ fork_execute_incident_helper(const int id, const char* name, Fpipe& p2cPipe, Fpi } // ================================================================================ +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 @@ static status_t wait_child(pid_t pid) { 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 @@ FileSection::Execute(ReportRequestSet* requests) const 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 @@ CommandSection::Execute(ReportRequestSet* requests) const 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 af4a35cc0015..90f543e30ff7 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 65030b3a1799..20111d8ae89a 100644 --- a/cmds/incidentd/src/report_directory.cpp +++ b/cmds/incidentd/src/report_directory.cpp @@ -58,26 +58,9 @@ create_directory(const char* directory) 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 @@ create_directory(const char* directory) 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 531c9f29bf03..c494bd646b0b 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 @@ TEST_F(ReporterTest, RunReportWithHeaders) { 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 @@ TEST_F(ReporterTest, RunReportWithHeaders) { 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, RunReportWithHeaders) { 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 cbfb89685de0..2cfd7df6be84 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 @@ const string FIX64_FIELD_3 = "\x19\xff\xff\xff\xff\xff\xff\xff\xff"; // -1 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 @@ TEST(SectionTest, HeaderSection) { 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 @@ TEST(SectionTest, HeaderSection) { 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 5eff54887be1..01f4a84bbb93 100644 --- a/cmds/statsd/Android.mk +++ b/cmds/statsd/Android.mk @@ -34,6 +34,7 @@ statsd_common_src := \ 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 @@ statsd_common_src := \ 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_MODULE := statsdprotolite 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/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index 9d6d8a1f2b73..7a7a2f617e6a 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -127,6 +127,10 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event) { StatsdStats::getInstance().noteAtomLogged( event->GetTagId(), event->GetTimestampNs() / NS_PER_SEC); + if (mMetricsManagers.empty()) { + return; + } + // Hard-coded logic to update the isolated uid's in the uid-map. // The field numbers need to be currently updated by hand with atoms.proto if (event->GetTagId() == android::util::ISOLATED_UID_CHANGED) { diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp index f10b2cf618cd..e34aed33a3cb 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 @@ void AnomalyTracker::informSubscribers(const HashableDimensionKey& key) { } break; case Subscription::SubscriberInformationCase::kPerfettoDetails: - ALOGW("Perfetto reports not implemented."); + CollectPerfettoTraceAndUploadToDropbox(subscription.perfetto_details()); break; default: break; @@ -253,10 +255,10 @@ void AnomalyTracker::informSubscribers(const HashableDimensionKey& key) { 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 7d513cd01694..a07bd2f50607 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -84,6 +84,7 @@ message Atom { 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 @@ message Atom { 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; } } @@ -321,8 +327,9 @@ message ScheduledJobStateChanged { optional string name = 2; enum State { - OFF = 0; - ON = 1; + FINISHED = 0; + STARTED = 1; + SCHEDULED = 2; } optional State state = 3; @@ -1177,3 +1184,91 @@ message ModemActivityInfo { // 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 a61afb429f54..a75127324745 100644 --- a/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp +++ b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp @@ -34,7 +34,7 @@ namespace android { 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 000000000000..7a2d1991a538 --- /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 000000000000..fcae35fa6eb5 --- /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 000000000000..7426e743c40f --- /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 000000000000..90236ae00762 --- /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 000000000000..f7b33e77c557 --- /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 000000000000..e2e02533f17f --- /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 bb2e8c08dfe6..79f1a5d84174 100644 --- a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp +++ b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp @@ -64,6 +64,8 @@ StatsPullerManagerImpl::StatsPullerManagerImpl() 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/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp index 6782f3fd6fcf..34fa3c404d10 100644 --- a/cmds/statsd/src/logd/LogEvent.cpp +++ b/cmds/statsd/src/logd/LogEvent.cpp @@ -36,13 +36,13 @@ using std::string; using android::util::ProtoOutputStream; LogEvent::LogEvent(log_msg& msg) { - android_log_context context = + mContext = create_android_log_parser(msg.msg() + sizeof(uint32_t), msg.len() - sizeof(uint32_t)); mTimestampNs = msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec; mLogUid = msg.entry_v4.uid; - init(context); - if (context) { - android_log_destroy(&context); + init(mContext); + if (mContext) { + android_log_destroy(&mContext); } } diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index c7550f7d4711..e98587356857 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -285,8 +285,15 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked( 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 000000000000..dc868f997a63 --- /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 624785486043..cd60ee74e3ad 100644 --- a/cmds/statsd/src/statsd_config.proto +++ b/cmds/statsd/src/statsd_config.proto @@ -22,6 +22,8 @@ package android.os.statsd; 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 IncidentdDetails { } 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 a4c080063284..32a85b136023 100644 --- a/cmds/statsd/tools/dogfood/Android.mk +++ b/cmds/statsd/tools/dogfood/Android.mk @@ -19,13 +19,10 @@ include $(CLEAR_VARS) 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 0a0fd6647fbb..f5722c2c3d19 100644 --- a/cmds/statsd/tools/loadtest/Android.mk +++ b/cmds/statsd/tools/loadtest/Android.mk @@ -21,6 +21,7 @@ LOCAL_PACKAGE_NAME := StatsdLoadtest 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 97dcb90bbb99..0a4541ba4777 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -363,6 +363,11 @@ public abstract class AccessibilityService extends Service { */ 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/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index ccb54f93ba33..da9f72855e09 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -339,4 +339,9 @@ public abstract class ActivityManagerInternal { * Returns maximum number of users that can run simultaneously. */ public abstract int getMaxRunningUsers(); + + /** + * Returns is the caller has the same uid as the Recents component + */ + public abstract boolean isCallerRecents(int callingUid); } diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 57f9f67b4f9c..7ca680239eef 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -1326,6 +1326,25 @@ public class AppOpsManager { } /** + * 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 b469de56d857..c5a58f2eef30 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 @@ public class Instrumentation { * * @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/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 33277eae0520..fb8d1017e205 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -112,6 +112,7 @@ import android.os.IBinder; import android.os.IHardwarePropertiesManager; import android.os.IPowerManager; import android.os.IRecoverySystem; +import android.os.ISystemUpdateManager; import android.os.IUserManager; import android.os.IncidentManager; import android.os.PowerManager; @@ -119,6 +120,7 @@ import android.os.Process; import android.os.RecoverySystem; import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; +import android.os.SystemUpdateManager; import android.os.SystemVibrator; import android.os.UserHandle; import android.os.UserManager; @@ -485,6 +487,17 @@ final class SystemServiceRegistry { return new StorageStatsManager(ctx, service); }}); + registerService(Context.SYSTEM_UPDATE_SERVICE, SystemUpdateManager.class, + new CachedServiceFetcher<SystemUpdateManager>() { + @Override + public SystemUpdateManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow( + Context.SYSTEM_UPDATE_SERVICE); + ISystemUpdateManager service = ISystemUpdateManager.Stub.asInterface(b); + return new SystemUpdateManager(service); + }}); + registerService(Context.TELEPHONY_SERVICE, TelephonyManager.class, new CachedServiceFetcher<TelephonyManager>() { @Override diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 0be55642d4cf..7fccda8c04e3 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -1157,9 +1157,17 @@ public class DevicePolicyManager { 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 @@ public class DevicePolicyManager { * @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. */ @@ -9201,10 +9210,13 @@ public class DevicePolicyManager { /** * Allows/disallows printing. * + * Called by a device owner or a profile owner. + * Device owner changes policy for all users. Profile owner can override it if present. + * Printing is enabled by default. If {@code FEATURE_PRINTING} is absent, the call is ignored. + * * @param admin which {@link DeviceAdminReceiver} this request is associated with. * @param enabled whether printing should be allowed or not. * @throws SecurityException if {@code admin} is neither device, nor profile owner. - * @hide */ public void setPrintingEnabled(@NonNull ComponentName admin, boolean enabled) { try { @@ -9215,10 +9227,12 @@ public class DevicePolicyManager { } /** - * Returns whether printing is enabled for current user. + * Returns whether printing is enabled for this user. + * + * Always {@code false} if {@code FEATURE_PRINTING} is absent. + * Otherwise, {@code true} by default. * * @return {@code true} iff printing is enabled. - * @hide */ public boolean isPrintingEnabled() { try { @@ -9233,9 +9247,9 @@ public class DevicePolicyManager { * * Used only by PrintService. * @return Localized error message. - * @throws SecurityException if caller is not system. * @hide */ + @SystemApi public CharSequence getPrintingDisabledReason() { try { return mService.getPrintingDisabledReason(); diff --git a/core/java/android/app/slice/ISliceManager.aidl b/core/java/android/app/slice/ISliceManager.aidl index 5f0e542f4b12..4461b16fe15c 100644 --- a/core/java/android/app/slice/ISliceManager.aidl +++ b/core/java/android/app/slice/ISliceManager.aidl @@ -29,4 +29,6 @@ interface ISliceManager { 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 6093a4a02fbe..5bd3440d09f8 100644 --- a/core/java/android/app/slice/Slice.java +++ b/core/java/android/app/slice/Slice.java @@ -156,6 +156,13 @@ public final class Slice implements Parcelable { */ 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 74864cb1a371..09c420c3e66a 100644 --- a/core/java/android/app/slice/SliceManager.java +++ b/core/java/android/app/slice/SliceManager.java @@ -53,12 +53,34 @@ public class SliceManager { 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 @@ public class SliceManager { 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 @@ public class SliceManager { 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 @@ public class SliceManager { } /** + * 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 aa41f14d8cb5..c4316a0ba086 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 @@ public abstract class SliceProvider extends ContentProvider { * @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> @@ -261,54 +299,47 @@ public abstract class SliceProvider extends ContentProvider { @Override 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"); - } + Uri uri = getUriWithoutUserId(extras.getParcelable(EXTRA_BIND_URI)); 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); } 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"); + Uri uri = getUriWithoutUserId(extras.getParcelable(EXTRA_BIND_URI)); + 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"); + Uri uri = getUriWithoutUserId(extras.getParcelable(EXTRA_BIND_URI)); + 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)) { - Uri uri = extras.getParcelable(EXTRA_BIND_URI); + Uri uri = getUriWithoutUserId(extras.getParcelable(EXTRA_BIND_URI)); Bundle b = new Bundle(); b.putParcelableArrayList(EXTRA_SLICE_DESCENDANTS, new ArrayList<>(handleGetDescendants(uri))); @@ -370,14 +401,27 @@ public abstract class SliceProvider extends ContentProvider { } } - 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 @@ public abstract class SliceProvider extends ContentProvider { } } - 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 2e44a630b056..da36157d85f8 100644 --- a/core/java/android/app/usage/NetworkStats.java +++ b/core/java/android/app/usage/NetworkStats.java @@ -227,6 +227,30 @@ public final class NetworkStats implements AutoCloseable { */ 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 @@ public final class NetworkStats implements AutoCloseable { 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 @@ public final class NetworkStats implements AutoCloseable { 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 @@ public final class NetworkStats implements AutoCloseable { } /** + * 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 @@ public final class NetworkStats implements AutoCloseable { 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 @@ public final class NetworkStats implements AutoCloseable { 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 853b00331a4d..90b514e272f4 100644 --- a/core/java/android/app/usage/NetworkStatsManager.java +++ b/core/java/android/app/usage/NetworkStatsManager.java @@ -60,10 +60,11 @@ import android.util.Log; * {@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 @@ public class NetworkStatsManager { * 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 @@ public class NetworkStatsManager { /** * 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 @@ public class NetworkStatsManager { * 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 @@ public class NetworkStatsManager { * 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 5cd0981f72e3..bd978e3df863 100644 --- a/core/java/android/app/usage/UsageStatsManagerInternal.java +++ b/core/java/android/app/usage/UsageStatsManagerInternal.java @@ -176,6 +176,12 @@ public abstract class UsageStatsManagerInternal { 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/Context.java b/core/java/android/content/Context.java index 265f7c7425db..f69aab01d821 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3024,7 +3024,8 @@ public abstract class Context { //@hide: INCIDENT_SERVICE, //@hide: STATS_COMPANION_SERVICE, COMPANION_DEVICE_SERVICE, - CROSS_PROFILE_APPS_SERVICE + CROSS_PROFILE_APPS_SERVICE, + //@hide: SYSTEM_UPDATE_SERVICE, }) @Retention(RetentionPolicy.SOURCE) public @interface ServiceName {} @@ -3242,6 +3243,17 @@ public abstract class Context { /** * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.os.SystemUpdateManager} for accessing the system update + * manager service. + * + * @see #getSystemService(String) + * @hide + */ + @SystemApi + public static final String SYSTEM_UPDATE_SERVICE = "system_update"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a * {@link android.view.WindowManager} for accessing the system's window * manager. * diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 15e119b20a2e..84cbdb472c13 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -26,7 +26,6 @@ import android.content.Context; 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 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { 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 7866b52cc68c..9aa3f40a8f0a 100644 --- a/core/java/android/hardware/HardwareBuffer.java +++ b/core/java/android/hardware/HardwareBuffer.java @@ -25,11 +25,11 @@ import android.os.Parcelable; 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 @@ import libcore.util.NativeAllocationRegistry; 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 final class HardwareBuffer implements Parcelable, AutoCloseable { 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 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable { @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 final class HardwareBuffer implements Parcelable, AutoCloseable { 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 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable { * * @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 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable { 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 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable { } /** - * 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 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable { 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/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 1201ef48220a..96d043c2a894 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -22,9 +22,11 @@ import android.hardware.camera2.impl.CameraMetadataNative; import android.hardware.camera2.impl.PublicKey; import android.hardware.camera2.impl.SyntheticKey; import android.hardware.camera2.params.SessionConfiguration; +import android.hardware.camera2.utils.ArrayUtils; import android.hardware.camera2.utils.TypeReference; import android.util.Rational; +import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -171,6 +173,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri private List<CameraCharacteristics.Key<?>> mKeys; private List<CaptureRequest.Key<?>> mAvailableRequestKeys; private List<CaptureRequest.Key<?>> mAvailableSessionKeys; + private List<CaptureRequest.Key<?>> mAvailablePhysicalRequestKeys; private List<CaptureResult.Key<?>> mAvailableResultKeys; /** @@ -314,6 +317,45 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri } /** + * <p>Returns a subset of {@link #getAvailableCaptureRequestKeys} keys that can + * be overriden for physical devices backing a logical multi-camera.</p> + * + * <p>This is a subset of android.request.availableRequestKeys which contains a list + * of keys that can be overriden using {@link CaptureRequest.Builder#setPhysicalCameraKey }. + * The respective value of such request key can be obtained by calling + * {@link CaptureRequest.Builder#getPhysicalCameraKey }. Capture requests that contain + * individual physical device requests must be built via + * {@link android.hardware.camera2.CameraDevice#createCaptureRequest(int, Set)}. + * Such extended capture requests can be passed only to + * {@link CameraCaptureSession#capture } or {@link CameraCaptureSession#captureBurst } and + * not to {@link CameraCaptureSession#setRepeatingRequest } or + * {@link CameraCaptureSession#setRepeatingBurst }.</p> + * + * <p>The list returned is not modifiable, so any attempts to modify it will throw + * a {@code UnsupportedOperationException}.</p> + * + * <p>Each key is only listed once in the list. The order of the keys is undefined.</p> + * + * @return List of keys that can be overriden in individual physical device requests. + * In case the camera device doesn't support such keys the list can be null. + */ + @SuppressWarnings({"unchecked"}) + public List<CaptureRequest.Key<?>> getAvailablePhysicalCameraRequestKeys() { + if (mAvailableSessionKeys == null) { + Object crKey = CaptureRequest.Key.class; + Class<CaptureRequest.Key<?>> crKeyTyped = (Class<CaptureRequest.Key<?>>)crKey; + + int[] filterTags = get(REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS); + if (filterTags == null) { + return null; + } + mAvailablePhysicalRequestKeys = + getAvailableKeyList(CaptureRequest.class, crKeyTyped, filterTags); + } + return mAvailablePhysicalRequestKeys; + } + + /** * Returns the list of keys supported by this {@link CameraDevice} for querying * with a {@link CaptureRequest}. * @@ -407,6 +449,47 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri return Collections.unmodifiableList(staticKeyList); } + /** + * Returns the list of physical camera ids that this logical {@link CameraDevice} is + * made up of. + * + * <p>A camera device is a logical camera if it has + * REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA capability. If the camera device + * doesn't have the capability, the return value will be an empty list. </p> + * + * <p>The list returned is not modifiable, so any attempts to modify it will throw + * a {@code UnsupportedOperationException}.</p> + * + * <p>Each physical camera id is only listed once in the list. The order of the keys + * is undefined.</p> + * + * @return List of physical camera ids for this logical camera device. + */ + @NonNull + public List<String> getPhysicalCameraIds() { + int[] availableCapabilities = get(REQUEST_AVAILABLE_CAPABILITIES); + if (availableCapabilities == null) { + throw new AssertionError("android.request.availableCapabilities must be non-null " + + "in the characteristics"); + } + + if (!ArrayUtils.contains(availableCapabilities, + REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA)) { + return Collections.emptyList(); + } + byte[] physicalCamIds = get(LOGICAL_MULTI_CAMERA_PHYSICAL_IDS); + + String physicalCamIdString = null; + try { + physicalCamIdString = new String(physicalCamIds, "UTF-8"); + } catch (java.io.UnsupportedEncodingException e) { + throw new AssertionError("android.logicalCam.physicalIds must be UTF-8 string"); + } + String[] physicalCameraIdList = physicalCamIdString.split("\0"); + + return Collections.unmodifiableList(Arrays.asList(physicalCameraIdList)); + } + /*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~ * The key entries below this point are generated from metadata * definitions in /system/media/camera/docs. Do not modify by hand or @@ -1579,6 +1662,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT DEPTH_OUTPUT}</li> * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO CONSTRAINED_HIGH_SPEED_VIDEO}</li> * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING MOTION_TRACKING}</li> + * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA LOGICAL_MULTI_CAMERA}</li> * </ul></p> * <p>This key is available on all devices.</p> * @@ -1594,6 +1678,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * @see #REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT * @see #REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO * @see #REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING + * @see #REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA */ @PublicKey public static final Key<int[]> REQUEST_AVAILABLE_CAPABILITIES = @@ -1701,6 +1786,30 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri new Key<int[]>("android.request.availableSessionKeys", int[].class); /** + * <p>A subset of the available request keys that can be overriden for + * physical devices backing a logical multi-camera.</p> + * <p>This is a subset of android.request.availableRequestKeys which contains a list + * of keys that can be overriden using {@link CaptureRequest.Builder#setPhysicalCameraKey }. + * The respective value of such request key can be obtained by calling + * {@link CaptureRequest.Builder#getPhysicalCameraKey }. Capture requests that contain + * individual physical device requests must be built via + * {@link android.hardware.camera2.CameraDevice#createCaptureRequest(int, Set)}. + * Such extended capture requests can be passed only to + * {@link CameraCaptureSession#capture } or {@link CameraCaptureSession#captureBurst } and + * not to {@link CameraCaptureSession#setRepeatingRequest } or + * {@link CameraCaptureSession#setRepeatingBurst }.</p> + * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * <p><b>Limited capability</b> - + * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the + * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p> + * + * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL + * @hide + */ + public static final Key<int[]> REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS = + new Key<int[]>("android.request.availablePhysicalCameraRequestKeys", int[].class); + + /** * <p>The list of image formats that are supported by this * camera device for output streams.</p> * <p>All camera devices will support JPEG and YUV_420_888 formats.</p> @@ -2870,6 +2979,21 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri new Key<int[]>("android.statistics.info.availableLensShadingMapModes", int[].class); /** + * <p>List of OIS data output modes for {@link CaptureRequest#STATISTICS_OIS_DATA_MODE android.statistics.oisDataMode} that + * are supported by this camera device.</p> + * <p>If no OIS data output is available for this camera device, this key will + * contain only OFF.</p> + * <p><b>Range of valid values:</b><br> + * Any value listed in {@link CaptureRequest#STATISTICS_OIS_DATA_MODE android.statistics.oisDataMode}</p> + * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * + * @see CaptureRequest#STATISTICS_OIS_DATA_MODE + */ + @PublicKey + public static final Key<int[]> STATISTICS_INFO_AVAILABLE_OIS_DATA_MODES = + new Key<int[]>("android.statistics.info.availableOisDataModes", int[].class); + + /** * <p>Maximum number of supported points in the * tonemap curve that can be used for {@link CaptureRequest#TONEMAP_CURVE android.tonemap.curve}.</p> * <p>If the actual number of points provided by the application (in {@link CaptureRequest#TONEMAP_CURVE android.tonemap.curve}*) is @@ -2978,6 +3102,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <li>{@link #INFO_SUPPORTED_HARDWARE_LEVEL_FULL FULL}</li> * <li>{@link #INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY LEGACY}</li> * <li>{@link #INFO_SUPPORTED_HARDWARE_LEVEL_3 3}</li> + * <li>{@link #INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL EXTERNAL}</li> * </ul></p> * <p>This key is available on all devices.</p> * @@ -2991,6 +3116,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * @see #INFO_SUPPORTED_HARDWARE_LEVEL_FULL * @see #INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY * @see #INFO_SUPPORTED_HARDWARE_LEVEL_3 + * @see #INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL */ @PublicKey public static final Key<Integer> INFO_SUPPORTED_HARDWARE_LEVEL = @@ -3167,6 +3293,54 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri public static final Key<Boolean> DEPTH_DEPTH_IS_EXCLUSIVE = new Key<Boolean>("android.depth.depthIsExclusive", boolean.class); + /** + * <p>String containing the ids of the underlying physical cameras.</p> + * <p>For a logical camera, this is concatenation of all underlying physical camera ids. + * The null terminator for physical camera id must be preserved so that the whole string + * can be tokenized using '\0' to generate list of physical camera ids.</p> + * <p>For example, if the physical camera ids of the logical camera are "2" and "3", the + * value of this tag will be ['2', '\0', '3', '\0'].</p> + * <p>The number of physical camera ids must be no less than 2.</p> + * <p><b>Units</b>: UTF-8 null-terminated string</p> + * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * <p><b>Limited capability</b> - + * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the + * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p> + * + * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL + * @hide + */ + public static final Key<byte[]> LOGICAL_MULTI_CAMERA_PHYSICAL_IDS = + new Key<byte[]>("android.logicalMultiCamera.physicalIds", byte[].class); + + /** + * <p>The accuracy of frame timestamp synchronization between physical cameras</p> + * <p>The accuracy of the frame timestamp synchronization determines the physical cameras' + * ability to start exposure at the same time. If the sensorSyncType is CALIBRATED, + * the physical camera sensors usually run in master-slave mode so that their shutter + * time is synchronized. For APPROXIMATE sensorSyncType, the camera sensors usually run in + * master-master mode, and there could be offset between their start of exposure.</p> + * <p>In both cases, all images generated for a particular capture request still carry the same + * timestamps, so that they can be used to look up the matching frame number and + * onCaptureStarted callback.</p> + * <p><b>Possible values:</b> + * <ul> + * <li>{@link #LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_APPROXIMATE APPROXIMATE}</li> + * <li>{@link #LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_CALIBRATED CALIBRATED}</li> + * </ul></p> + * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * <p><b>Limited capability</b> - + * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the + * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p> + * + * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL + * @see #LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_APPROXIMATE + * @see #LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_CALIBRATED + */ + @PublicKey + public static final Key<Integer> LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE = + new Key<Integer>("android.logicalMultiCamera.sensorSyncType", int.class); + /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~ * End generated code *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/ diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index 639795ab8080..40ee8348ac4c 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -31,6 +31,7 @@ import android.os.Handler; import android.view.Surface; import java.util.List; +import java.util.Set; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -910,6 +911,47 @@ public abstract class CameraDevice implements AutoCloseable { throws CameraAccessException; /** + * <p>Create a {@link CaptureRequest.Builder} for new capture requests, + * initialized with template for a target use case. This methods allows + * clients to pass physical camera ids which can be used to customize the + * request for a specific physical camera. The settings are chosen + * to be the best options for the specific logical camera device. If + * additional physical camera ids are passed, then they will also use the + * same settings template. Requests containing individual physical camera + * settings can be passed only to {@link CameraCaptureSession#capture} or + * {@link CameraCaptureSession#captureBurst} and not to + * {@link CameraCaptureSession#setRepeatingRequest} or + * {@link CameraCaptureSession#setRepeatingBurst}</p> + * + * @param templateType An enumeration selecting the use case for this request. Not all template + * types are supported on every device. See the documentation for each template type for + * details. + * @param physicalCameraIdSet A set of physical camera ids that can be used to customize + * the request for a specific physical camera. + * @return a builder for a capture request, initialized with default + * settings for that template, and no output streams + * + * @throws IllegalArgumentException if the templateType is not supported by + * this device, or one of the physical id arguments matches with logical camera id. + * @throws CameraAccessException if the camera device is no longer connected or has + * encountered a fatal error + * @throws IllegalStateException if the camera device has been closed + * + * @see #TEMPLATE_PREVIEW + * @see #TEMPLATE_RECORD + * @see #TEMPLATE_STILL_CAPTURE + * @see #TEMPLATE_VIDEO_SNAPSHOT + * @see #TEMPLATE_MANUAL + * @see CaptureRequest.Builder#setKey + * @see CaptureRequest.Builder#getKey + */ + @NonNull + public CaptureRequest.Builder createCaptureRequest(@RequestTemplate int templateType, + Set<String> physicalCameraIdSet) throws CameraAccessException { + throw new UnsupportedOperationException("Subclasses must override this method"); + } + + /** * <p>Create a {@link CaptureRequest.Builder} for a new reprocess {@link CaptureRequest} from a * {@link TotalCaptureResult}. * diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index 90bf896c2225..a2bc91e0cda6 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -996,7 +996,12 @@ public final class CameraManager { return; } - Integer oldStatus = mDeviceStatus.put(id, status); + Integer oldStatus; + if (status == ICameraServiceListener.STATUS_NOT_PRESENT) { + oldStatus = mDeviceStatus.remove(id); + } else { + oldStatus = mDeviceStatus.put(id, status); + } if (oldStatus != null && oldStatus == status) { if (DEBUG) { diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index 2294ec56525f..e7c8961186ec 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -845,6 +845,53 @@ public abstract class CameraMetadata<TKey> { */ public static final int REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING = 10; + /** + * <p>The camera device is a logical camera backed by two or more physical cameras that are + * also exposed to the application.</p> + * <p>This capability requires the camera device to support the following:</p> + * <ul> + * <li>This camera device must list the following static metadata entries in {@link android.hardware.camera2.CameraCharacteristics }:<ul> + * <li>android.logicalMultiCamera.physicalIds</li> + * <li>{@link CameraCharacteristics#LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE android.logicalMultiCamera.sensorSyncType}</li> + * </ul> + * </li> + * <li>The underlying physical cameras' static metadata must list the following entries, + * so that the application can correlate pixels from the physical streams:<ul> + * <li>{@link CameraCharacteristics#LENS_POSE_REFERENCE android.lens.poseReference}</li> + * <li>{@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}</li> + * <li>{@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}</li> + * <li>{@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration}</li> + * <li>{@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion}</li> + * </ul> + * </li> + * <li>The logical camera device must be LIMITED or higher device.</li> + * </ul> + * <p>Both the logical camera device and its underlying physical devices support the + * mandatory stream combinations required for their device levels.</p> + * <p>Additionally, for each guaranteed stream combination, the logical camera supports:</p> + * <ul> + * <li>Replacing one logical {@link android.graphics.ImageFormat#YUV_420_888 YUV_420_888} + * or raw stream with two physical streams of the same size and format, each from a + * separate physical camera, given that the size and format are supported by both + * physical cameras.</li> + * <li>Adding two raw streams, each from one physical camera, if the logical camera doesn't + * advertise RAW capability, but the underlying physical cameras do. This is usually + * the case when the physical cameras have different sensor sizes.</li> + * </ul> + * <p>Using physical streams in place of a logical stream of the same size and format will + * not slow down the frame rate of the capture, as long as the minimum frame duration + * of the physical and logical streams are the same.</p> + * + * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION + * @see CameraCharacteristics#LENS_POSE_REFERENCE + * @see CameraCharacteristics#LENS_POSE_ROTATION + * @see CameraCharacteristics#LENS_POSE_TRANSLATION + * @see CameraCharacteristics#LENS_RADIAL_DISTORTION + * @see CameraCharacteristics#LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE + * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES + */ + public static final int REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA = 11; + // // Enumeration values for CameraCharacteristics#SCALER_CROPPING_TYPE // @@ -1134,6 +1181,38 @@ public abstract class CameraMetadata<TKey> { */ public static final int INFO_SUPPORTED_HARDWARE_LEVEL_3 = 3; + /** + * <p>This camera device is backed by an external camera connected to this Android device.</p> + * <p>The device has capability identical to a LIMITED level device, with the following + * exceptions:</p> + * <ul> + * <li>The device may not report lens/sensor related information such as<ul> + * <li>{@link CaptureRequest#LENS_FOCAL_LENGTH android.lens.focalLength}</li> + * <li>{@link CameraCharacteristics#LENS_INFO_HYPERFOCAL_DISTANCE android.lens.info.hyperfocalDistance}</li> + * <li>{@link CameraCharacteristics#SENSOR_INFO_PHYSICAL_SIZE android.sensor.info.physicalSize}</li> + * <li>{@link CameraCharacteristics#SENSOR_INFO_WHITE_LEVEL android.sensor.info.whiteLevel}</li> + * <li>{@link CameraCharacteristics#SENSOR_BLACK_LEVEL_PATTERN android.sensor.blackLevelPattern}</li> + * <li>{@link CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT android.sensor.info.colorFilterArrangement}</li> + * <li>{@link CaptureResult#SENSOR_ROLLING_SHUTTER_SKEW android.sensor.rollingShutterSkew}</li> + * </ul> + * </li> + * <li>The device will report 0 for {@link CameraCharacteristics#SENSOR_ORIENTATION android.sensor.orientation}</li> + * <li>The device has less guarantee on stable framerate, as the framerate partly depends + * on the external camera being used.</li> + * </ul> + * + * @see CaptureRequest#LENS_FOCAL_LENGTH + * @see CameraCharacteristics#LENS_INFO_HYPERFOCAL_DISTANCE + * @see CameraCharacteristics#SENSOR_BLACK_LEVEL_PATTERN + * @see CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT + * @see CameraCharacteristics#SENSOR_INFO_PHYSICAL_SIZE + * @see CameraCharacteristics#SENSOR_INFO_WHITE_LEVEL + * @see CameraCharacteristics#SENSOR_ORIENTATION + * @see CaptureResult#SENSOR_ROLLING_SHUTTER_SKEW + * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL + */ + public static final int INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL = 4; + // // Enumeration values for CameraCharacteristics#SYNC_MAX_LATENCY // @@ -1160,6 +1239,26 @@ public abstract class CameraMetadata<TKey> { public static final int SYNC_MAX_LATENCY_UNKNOWN = -1; // + // Enumeration values for CameraCharacteristics#LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE + // + + /** + * <p>A software mechanism is used to synchronize between the physical cameras. As a result, + * the timestamp of an image from a physical stream is only an approximation of the + * image sensor start-of-exposure time.</p> + * @see CameraCharacteristics#LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE + */ + public static final int LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_APPROXIMATE = 0; + + /** + * <p>The camera device supports frame timestamp synchronization at the hardware level, + * and the timestamp of a physical stream image accurately reflects its + * start-of-exposure time.</p> + * @see CameraCharacteristics#LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE + */ + public static final int LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_CALIBRATED = 1; + + // // Enumeration values for CaptureRequest#COLOR_CORRECTION_MODE // @@ -2566,6 +2665,22 @@ public abstract class CameraMetadata<TKey> { public static final int STATISTICS_LENS_SHADING_MAP_MODE_ON = 1; // + // Enumeration values for CaptureRequest#STATISTICS_OIS_DATA_MODE + // + + /** + * <p>Do not include OIS data in the capture result.</p> + * @see CaptureRequest#STATISTICS_OIS_DATA_MODE + */ + public static final int STATISTICS_OIS_DATA_MODE_OFF = 0; + + /** + * <p>Include OIS data in the capture result.</p> + * @see CaptureRequest#STATISTICS_OIS_DATA_MODE + */ + public static final int STATISTICS_OIS_DATA_MODE_ON = 1; + + // // Enumeration values for CaptureRequest#TONEMAP_MODE // diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index ce75fa52eff7..481b7649610a 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -33,9 +33,11 @@ import android.view.Surface; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; - +import java.util.Set; /** * <p>An immutable package of settings and outputs needed to capture a single @@ -219,7 +221,11 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> private static final ArraySet<Surface> mEmptySurfaceSet = new ArraySet<Surface>(); - private final CameraMetadataNative mSettings; + private String mLogicalCameraId; + private CameraMetadataNative mLogicalCameraSettings; + private final HashMap<String, CameraMetadataNative> mPhysicalCameraSettings = + new HashMap<String, CameraMetadataNative>(); + private boolean mIsReprocess; // If this request is part of constrained high speed request list that was created by // {@link android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList} @@ -236,8 +242,6 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * Used by Binder to unparcel this object only. */ private CaptureRequest() { - mSettings = new CameraMetadataNative(); - setNativeInstance(mSettings); mIsReprocess = false; mReprocessableSessionId = CameraCaptureSession.SESSION_ID_NONE; } @@ -249,8 +253,14 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> */ @SuppressWarnings("unchecked") private CaptureRequest(CaptureRequest source) { - mSettings = new CameraMetadataNative(source.mSettings); - setNativeInstance(mSettings); + mLogicalCameraId = new String(source.mLogicalCameraId); + for (Map.Entry<String, CameraMetadataNative> entry : + source.mPhysicalCameraSettings.entrySet()) { + mPhysicalCameraSettings.put(new String(entry.getKey()), + new CameraMetadataNative(entry.getValue())); + } + mLogicalCameraSettings = mPhysicalCameraSettings.get(mLogicalCameraId); + setNativeInstance(mLogicalCameraSettings); mSurfaceSet.addAll(source.mSurfaceSet); mIsReprocess = source.mIsReprocess; mIsPartOfCHSRequestList = source.mIsPartOfCHSRequestList; @@ -272,16 +282,35 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * reprocess capture request to the same session where * the {@link TotalCaptureResult}, used to create the reprocess * capture, came from. + * @param logicalCameraId Camera Id of the actively open camera that instantiates the + * Builder. + * + * @param physicalCameraIdSet A set of physical camera ids that can be used to customize + * the request for a specific physical camera. * * @throws IllegalArgumentException If creating a reprocess capture request with an invalid - * reprocessableSessionId. + * reprocessableSessionId, or multiple physical cameras. * * @see CameraDevice#createReprocessCaptureRequest */ private CaptureRequest(CameraMetadataNative settings, boolean isReprocess, - int reprocessableSessionId) { - mSettings = CameraMetadataNative.move(settings); - setNativeInstance(mSettings); + int reprocessableSessionId, String logicalCameraId, Set<String> physicalCameraIdSet) { + if ((physicalCameraIdSet != null) && isReprocess) { + throw new IllegalArgumentException("Create a reprocess capture request with " + + "with more than one physical camera is not supported!"); + } + + mLogicalCameraId = logicalCameraId; + mLogicalCameraSettings = CameraMetadataNative.move(settings); + mPhysicalCameraSettings.put(mLogicalCameraId, mLogicalCameraSettings); + if (physicalCameraIdSet != null) { + for (String physicalId : physicalCameraIdSet) { + mPhysicalCameraSettings.put(physicalId, new CameraMetadataNative( + mLogicalCameraSettings)); + } + } + + setNativeInstance(mLogicalCameraSettings); mIsReprocess = isReprocess; if (isReprocess) { if (reprocessableSessionId == CameraCaptureSession.SESSION_ID_NONE) { @@ -309,7 +338,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> */ @Nullable public <T> T get(Key<T> key) { - return mSettings.get(key); + return mLogicalCameraSettings.get(key); } /** @@ -319,7 +348,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> @SuppressWarnings("unchecked") @Override protected <T> T getProtected(Key<?> key) { - return (T) mSettings.get(key); + return (T) mLogicalCameraSettings.get(key); } /** @@ -403,7 +432,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * @hide */ public CameraMetadataNative getNativeCopy() { - return new CameraMetadataNative(mSettings); + return new CameraMetadataNative(mLogicalCameraSettings); } /** @@ -444,14 +473,16 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> return other != null && Objects.equals(mUserTag, other.mUserTag) && mSurfaceSet.equals(other.mSurfaceSet) - && mSettings.equals(other.mSettings) + && mPhysicalCameraSettings.equals(other.mPhysicalCameraSettings) + && mLogicalCameraId.equals(other.mLogicalCameraId) + && mLogicalCameraSettings.equals(other.mLogicalCameraSettings) && mIsReprocess == other.mIsReprocess && mReprocessableSessionId == other.mReprocessableSessionId; } @Override public int hashCode() { - return HashCodeHelpers.hashCodeGeneric(mSettings, mSurfaceSet, mUserTag); + return HashCodeHelpers.hashCodeGeneric(mPhysicalCameraSettings, mSurfaceSet, mUserTag); } public static final Parcelable.Creator<CaptureRequest> CREATOR = @@ -479,8 +510,25 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * @hide */ private void readFromParcel(Parcel in) { - mSettings.readFromParcel(in); - setNativeInstance(mSettings); + int physicalCameraCount = in.readInt(); + if (physicalCameraCount <= 0) { + throw new RuntimeException("Physical camera count" + physicalCameraCount + + " should always be positive"); + } + + //Always start with the logical camera id + mLogicalCameraId = in.readString(); + mLogicalCameraSettings = new CameraMetadataNative(); + mLogicalCameraSettings.readFromParcel(in); + setNativeInstance(mLogicalCameraSettings); + mPhysicalCameraSettings.put(mLogicalCameraId, mLogicalCameraSettings); + for (int i = 1; i < physicalCameraCount; i++) { + String physicalId = in.readString(); + CameraMetadataNative physicalCameraSettings = new CameraMetadataNative(); + physicalCameraSettings.readFromParcel(in); + mPhysicalCameraSettings.put(physicalId, physicalCameraSettings); + } + mIsReprocess = (in.readInt() == 0) ? false : true; mReprocessableSessionId = CameraCaptureSession.SESSION_ID_NONE; @@ -509,7 +557,19 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> @Override public void writeToParcel(Parcel dest, int flags) { - mSettings.writeToParcel(dest, flags); + int physicalCameraCount = mPhysicalCameraSettings.size(); + dest.writeInt(physicalCameraCount); + //Logical camera id and settings always come first. + dest.writeString(mLogicalCameraId); + mLogicalCameraSettings.writeToParcel(dest, flags); + for (Map.Entry<String, CameraMetadataNative> entry : mPhysicalCameraSettings.entrySet()) { + if (entry.getKey().equals(mLogicalCameraId)) { + continue; + } + dest.writeString(entry.getKey()); + entry.getValue().writeToParcel(dest, flags); + } + dest.writeInt(mIsReprocess ? 1 : 0); synchronized (mSurfacesLock) { @@ -542,6 +602,14 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> } /** + * Retrieves the logical camera id. + * @hide + */ + public String getLogicalCameraId() { + return mLogicalCameraId; + } + + /** * @hide */ public void convertSurfaceToStreamId( @@ -633,14 +701,20 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * submits a reprocess capture request to the same session * where the {@link TotalCaptureResult}, used to create the * reprocess capture, came from. + * @param logicalCameraId Camera Id of the actively open camera that instantiates the + * Builder. + * @param physicalCameraIdSet A set of physical camera ids that can be used to customize + * the request for a specific physical camera. * * @throws IllegalArgumentException If creating a reprocess capture request with an invalid * reprocessableSessionId. * @hide */ public Builder(CameraMetadataNative template, boolean reprocess, - int reprocessableSessionId) { - mRequest = new CaptureRequest(template, reprocess, reprocessableSessionId); + int reprocessableSessionId, String logicalCameraId, + Set<String> physicalCameraIdSet) { + mRequest = new CaptureRequest(template, reprocess, reprocessableSessionId, + logicalCameraId, physicalCameraIdSet); } /** @@ -682,7 +756,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * type to the key. */ public <T> void set(@NonNull Key<T> key, T value) { - mRequest.mSettings.set(key, value); + mRequest.mLogicalCameraSettings.set(key, value); } /** @@ -696,7 +770,71 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> */ @Nullable public <T> T get(Key<T> key) { - return mRequest.mSettings.get(key); + return mRequest.mLogicalCameraSettings.get(key); + } + + /** + * Set a capture request field to a value. The field definitions can be + * found in {@link CaptureRequest}. + * + * <p>Setting a field to {@code null} will remove that field from the capture request. + * Unless the field is optional, removing it will likely produce an error from the camera + * device when the request is submitted.</p> + * + *<p>This method can be called for logical camera devices, which are devices that have + * REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA capability and calls to + * {@link CameraCharacteristics#getPhysicalCameraIds} return a non-empty list of + * physical devices that are backing the logical camera. The camera Id included in the + * 'physicalCameraId' argument selects an individual physical device that will receive + * the customized capture request field.</p> + * + * @throws IllegalArgumentException if the physical camera id is not valid + * + * @param key The metadata field to write. + * @param value The value to set the field to, which must be of a matching + * @param physicalCameraId A valid physical camera Id. The valid camera Ids can be obtained + * via calls to {@link CameraCharacteristics#getPhysicalCameraIds}. + * @return The builder object. + * type to the key. + */ + public <T> Builder setPhysicalCameraKey(@NonNull Key<T> key, T value, + @NonNull String physicalCameraId) { + if (!mRequest.mPhysicalCameraSettings.containsKey(physicalCameraId)) { + throw new IllegalArgumentException("Physical camera id: " + physicalCameraId + + " is not valid!"); + } + + mRequest.mPhysicalCameraSettings.get(physicalCameraId).set(key, value); + + return this; + } + + /** + * Get a capture request field value for a specific physical camera Id. The field + * definitions can be found in {@link CaptureRequest}. + * + *<p>This method can be called for logical camera devices, which are devices that have + * REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA capability and calls to + * {@link CameraCharacteristics#getPhysicalCameraIds} return a non-empty list of + * physical devices that are backing the logical camera. The camera Id included in the + * 'physicalCameraId' argument selects an individual physical device and returns + * its specific capture request field.</p> + * + * @throws IllegalArgumentException if the key or physical camera id were not valid + * + * @param key The metadata field to read. + * @param physicalCameraId A valid physical camera Id. The valid camera Ids can be obtained + * via calls to {@link CameraCharacteristics#getPhysicalCameraIds}. + * @return The value of that key, or {@code null} if the field is not set. + */ + @Nullable + public <T> T getPhysicalCameraKey(Key<T> key,@NonNull String physicalCameraId) { + if (!mRequest.mPhysicalCameraSettings.containsKey(physicalCameraId)) { + throw new IllegalArgumentException("Physical camera id: " + physicalCameraId + + " is not valid!"); + } + + return mRequest.mPhysicalCameraSettings.get(physicalCameraId).get(key); } /** @@ -748,7 +886,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * @hide */ public boolean isEmpty() { - return mRequest.mSettings.isEmpty(); + return mRequest.mLogicalCameraSettings.isEmpty(); } } @@ -2619,6 +2757,29 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> new Key<Integer>("android.statistics.lensShadingMapMode", int.class); /** + * <p>Whether the camera device outputs the OIS data in output + * result metadata.</p> + * <p>When set to ON, + * {@link CaptureResult#STATISTICS_OIS_TIMESTAMPS android.statistics.oisTimestamps}, android.statistics.oisShiftPixelX, + * android.statistics.oisShiftPixelY will provide OIS data in the output result metadata.</p> + * <p><b>Possible values:</b> + * <ul> + * <li>{@link #STATISTICS_OIS_DATA_MODE_OFF OFF}</li> + * <li>{@link #STATISTICS_OIS_DATA_MODE_ON ON}</li> + * </ul></p> + * <p><b>Available values for this device:</b><br> + * android.Statistics.info.availableOisDataModes</p> + * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * + * @see CaptureResult#STATISTICS_OIS_TIMESTAMPS + * @see #STATISTICS_OIS_DATA_MODE_OFF + * @see #STATISTICS_OIS_DATA_MODE_ON + */ + @PublicKey + public static final Key<Integer> STATISTICS_OIS_DATA_MODE = + new Key<Integer>("android.statistics.oisDataMode", int.class); + + /** * <p>Tonemapping / contrast / gamma curve for the blue * channel, to use when {@link CaptureRequest#TONEMAP_MODE android.tonemap.mode} is * CONTRAST_CURVE.</p> diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index c6fc4b4ed56c..d730fa8a31cf 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -3909,6 +3909,76 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { new Key<Integer>("android.statistics.lensShadingMapMode", int.class); /** + * <p>Whether the camera device outputs the OIS data in output + * result metadata.</p> + * <p>When set to ON, + * {@link CaptureResult#STATISTICS_OIS_TIMESTAMPS android.statistics.oisTimestamps}, android.statistics.oisShiftPixelX, + * android.statistics.oisShiftPixelY will provide OIS data in the output result metadata.</p> + * <p><b>Possible values:</b> + * <ul> + * <li>{@link #STATISTICS_OIS_DATA_MODE_OFF OFF}</li> + * <li>{@link #STATISTICS_OIS_DATA_MODE_ON ON}</li> + * </ul></p> + * <p><b>Available values for this device:</b><br> + * android.Statistics.info.availableOisDataModes</p> + * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * + * @see CaptureResult#STATISTICS_OIS_TIMESTAMPS + * @see #STATISTICS_OIS_DATA_MODE_OFF + * @see #STATISTICS_OIS_DATA_MODE_ON + */ + @PublicKey + public static final Key<Integer> STATISTICS_OIS_DATA_MODE = + new Key<Integer>("android.statistics.oisDataMode", int.class); + + /** + * <p>An array of timestamps of OIS samples, in nanoseconds.</p> + * <p>The array contains the timestamps of OIS samples. The timestamps are in the same + * timebase as and comparable to {@link CaptureResult#SENSOR_TIMESTAMP android.sensor.timestamp}.</p> + * <p><b>Units</b>: nanoseconds</p> + * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * + * @see CaptureResult#SENSOR_TIMESTAMP + */ + @PublicKey + public static final Key<long[]> STATISTICS_OIS_TIMESTAMPS = + new Key<long[]>("android.statistics.oisTimestamps", long[].class); + + /** + * <p>An array of shifts of OIS samples, in x direction.</p> + * <p>The array contains the amount of shifts in x direction, in pixels, based on OIS samples. + * A positive value is a shift from left to right in active array coordinate system. For + * example, if the optical center is (1000, 500) in active array coordinates, an shift of + * (3, 0) puts the new optical center at (1003, 500).</p> + * <p>The number of shifts must match the number of timestamps in + * {@link CaptureResult#STATISTICS_OIS_TIMESTAMPS android.statistics.oisTimestamps}.</p> + * <p><b>Units</b>: Pixels in active array.</p> + * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * + * @see CaptureResult#STATISTICS_OIS_TIMESTAMPS + */ + @PublicKey + public static final Key<float[]> STATISTICS_OIS_X_SHIFTS = + new Key<float[]>("android.statistics.oisXShifts", float[].class); + + /** + * <p>An array of shifts of OIS samples, in y direction.</p> + * <p>The array contains the amount of shifts in y direction, in pixels, based on OIS samples. + * A positive value is a shift from top to bottom in active array coordinate system. For + * example, if the optical center is (1000, 500) in active array coordinates, an shift of + * (0, 5) puts the new optical center at (1000, 505).</p> + * <p>The number of shifts must match the number of timestamps in + * {@link CaptureResult#STATISTICS_OIS_TIMESTAMPS android.statistics.oisTimestamps}.</p> + * <p><b>Units</b>: Pixels in active array.</p> + * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * + * @see CaptureResult#STATISTICS_OIS_TIMESTAMPS + */ + @PublicKey + public static final Key<float[]> STATISTICS_OIS_Y_SHIFTS = + new Key<float[]>("android.statistics.oisYShifts", float[].class); + + /** * <p>Tonemapping / contrast / gamma curve for the blue * channel, to use when {@link CaptureRequest#TONEMAP_MODE android.tonemap.mode} is * CONTRAST_CURVE.</p> diff --git a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java index 8c4dbfa58d05..06c2c25ab6bb 100644 --- a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java @@ -94,8 +94,8 @@ public class CameraConstrainedHighSpeedCaptureSessionImpl // Note that after this step, the requestMetadata is mutated (swapped) and can not be used // for next request builder creation. CaptureRequest.Builder singleTargetRequestBuilder = new CaptureRequest.Builder( - requestMetadata, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE); - + requestMetadata, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE, + request.getLogicalCameraId(), /*physicalCameraIdSet*/ null); // Carry over userTag, as native metadata doesn't have this field. singleTargetRequestBuilder.setTag(request.getTag()); @@ -120,7 +120,8 @@ public class CameraConstrainedHighSpeedCaptureSessionImpl // CaptureRequest.Builder creation. requestMetadata = new CameraMetadataNative(request.getNativeCopy()); doubleTargetRequestBuilder = new CaptureRequest.Builder( - requestMetadata, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE); + requestMetadata, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE, + request.getLogicalCameraId(), /*physicalCameraIdSet*/null); doubleTargetRequestBuilder.setTag(request.getTag()); doubleTargetRequestBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT, CaptureRequest.CONTROL_CAPTURE_INTENT_VIDEO_RECORD); diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index f1ffb890eecd..cab9d7046fcd 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -16,27 +16,25 @@ 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.ICameraService; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.CaptureFailure; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; -import android.hardware.camera2.CaptureFailure; import android.hardware.camera2.ICameraDeviceCallbacks; import android.hardware.camera2.ICameraDeviceUser; 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; import android.hardware.camera2.utils.SurfaceUtils; -import android.hardware.ICameraService; import android.os.Build; import android.os.Handler; import android.os.IBinder; @@ -51,16 +49,15 @@ import android.view.Surface; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; -import java.util.Collections; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; -import java.util.List; import java.util.LinkedList; +import java.util.List; +import java.util.Set; import java.util.TreeMap; +import java.util.concurrent.atomic.AtomicBoolean; /** * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate @@ -715,6 +712,38 @@ public class CameraDeviceImpl extends CameraDevice } @Override + public CaptureRequest.Builder createCaptureRequest(int templateType, + Set<String> physicalCameraIdSet) + throws CameraAccessException { + synchronized(mInterfaceLock) { + checkIfCameraClosedOrInError(); + + for (String physicalId : physicalCameraIdSet) { + if (physicalId == getId()) { + throw new IllegalStateException("Physical id matches the logical id!"); + } + } + + CameraMetadataNative templatedRequest = null; + + templatedRequest = mRemoteDevice.createDefaultRequest(templateType); + + // If app target SDK is older than O, or it's not a still capture template, enableZsl + // must be false in the default request. + if (mAppTargetSdkVersion < Build.VERSION_CODES.O || + templateType != TEMPLATE_STILL_CAPTURE) { + overrideEnableZsl(templatedRequest, false); + } + + CaptureRequest.Builder builder = new CaptureRequest.Builder( + templatedRequest, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE, + getId(), physicalCameraIdSet); + + return builder; + } + } + + @Override public CaptureRequest.Builder createCaptureRequest(int templateType) throws CameraAccessException { synchronized(mInterfaceLock) { @@ -732,7 +761,8 @@ public class CameraDeviceImpl extends CameraDevice } CaptureRequest.Builder builder = new CaptureRequest.Builder( - templatedRequest, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE); + templatedRequest, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE, + getId(), /*physicalCameraIdSet*/ null); return builder; } @@ -748,7 +778,7 @@ public class CameraDeviceImpl extends CameraDevice CameraMetadataNative(inputResult.getNativeCopy()); return new CaptureRequest.Builder(resultMetadata, /*reprocess*/true, - inputResult.getSessionId()); + inputResult.getSessionId(), getId(), /*physicalCameraIdSet*/ null); } } @@ -958,7 +988,8 @@ public class CameraDeviceImpl extends CameraDevice // callback is valid handler = checkHandler(handler, callback); - // Make sure that there all requests have at least 1 surface; all surfaces are non-null + // Make sure that there all requests have at least 1 surface; all surfaces are non-null; + // the surface isn't a physical stream surface for reprocessing request for (CaptureRequest request : requestList) { if (request.getTargets().isEmpty()) { throw new IllegalArgumentException( @@ -969,7 +1000,20 @@ public class CameraDeviceImpl extends CameraDevice if (surface == null) { throw new IllegalArgumentException("Null Surface targets are not allowed"); } + + if (!request.isReprocess()) { + continue; + } + for (int i = 0; i < mConfiguredOutputs.size(); i++) { + OutputConfiguration configuration = mConfiguredOutputs.valueAt(i); + if (configuration.isForPhysicalCamera() + && configuration.getSurfaces().contains(surface)) { + throw new IllegalArgumentException( + "Reprocess request on physical stream is not allowed"); + } + } } + } synchronized(mInterfaceLock) { @@ -1798,34 +1842,36 @@ public class CameraDeviceImpl extends CameraDevice 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/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java index a85b5f710696..f47cd665fd9c 100644 --- a/core/java/android/hardware/camera2/params/OutputConfiguration.java +++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java @@ -31,13 +31,12 @@ import android.util.Log; import android.util.Size; import android.view.Surface; -import java.util.Arrays; -import java.util.List; -import java.util.Collections; -import java.util.ArrayList; - import static com.android.internal.util.Preconditions.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + /** * A class for describing camera output, which contains a {@link Surface} and its specific * configuration for creating capture session. @@ -266,6 +265,7 @@ public final class OutputConfiguration implements Parcelable { mConfiguredGenerationId = surface.getGenerationId(); mIsDeferredConfig = false; mIsShared = false; + mPhysicalCameraId = null; } /** @@ -319,6 +319,7 @@ public final class OutputConfiguration implements Parcelable { mConfiguredGenerationId = 0; mIsDeferredConfig = true; mIsShared = false; + mPhysicalCameraId = null; } /** @@ -348,8 +349,9 @@ public final class OutputConfiguration implements Parcelable { * </ol> * * <p>To enable surface sharing, this function must be called before {@link - * CameraDevice#createCaptureSessionByOutputConfigurations}. Calling this function after {@link - * CameraDevice#createCaptureSessionByOutputConfigurations} has no effect.</p> + * CameraDevice#createCaptureSessionByOutputConfigurations} or {@link + * CameraDevice#createReprocessableCaptureSessionByConfigurations}. Calling this function after + * {@link CameraDevice#createCaptureSessionByOutputConfigurations} has no effect.</p> * * <p>Up to {@link #getMaxSharedSurfaceCount} surfaces can be shared for an OutputConfiguration. * The supported surfaces for sharing must be of type SurfaceTexture, SurfaceView, @@ -360,6 +362,44 @@ public final class OutputConfiguration implements Parcelable { } /** + * Set the id of the physical camera for this OutputConfiguration + * + * <p>In the case one logical camera is made up of multiple physical cameras, it could be + * desirable for the camera application to request streams from individual physical cameras. + * This call achieves it by mapping the OutputConfiguration to the physical camera id.</p> + * + * <p>The valid physical camera id can be queried by {@link + * android.hardware.camera2.CameraCharacteristics#getPhysicalCameraIds}. + * </p> + * + * <p>Passing in a null physicalCameraId means that the OutputConfiguration is for a logical + * stream.</p> + * + * <p>This function must be called before {@link + * CameraDevice#createCaptureSessionByOutputConfigurations} or {@link + * CameraDevice#createReprocessableCaptureSessionByConfigurations}. Calling this function + * after {@link CameraDevice#createCaptureSessionByOutputConfigurations} or {@link + * CameraDevice#createReprocessableCaptureSessionByConfigurations} has no effect.</p> + * + * <p>The surface belonging to a physical camera OutputConfiguration must not be used as input + * or output of a reprocessing request. </p> + */ + public void setPhysicalCameraId(@Nullable String physicalCameraId) { + mPhysicalCameraId = physicalCameraId; + } + + /** + * Check if this configuration is for a physical camera. + * + * <p>This returns true if the output configuration was for a physical camera making up a + * logical multi camera via {@link OutputConfiguration#setPhysicalCameraId}.</p> + * @hide + */ + public boolean isForPhysicalCamera() { + return (mPhysicalCameraId != null); + } + + /** * Check if this configuration has deferred configuration. * * <p>This will return true if the output configuration was constructed with surface deferred by @@ -487,6 +527,7 @@ public final class OutputConfiguration implements Parcelable { this.mConfiguredGenerationId = other.mConfiguredGenerationId; this.mIsDeferredConfig = other.mIsDeferredConfig; this.mIsShared = other.mIsShared; + this.mPhysicalCameraId = other.mPhysicalCameraId; } /** @@ -502,6 +543,7 @@ public final class OutputConfiguration implements Parcelable { boolean isShared = source.readInt() == 1; ArrayList<Surface> surfaces = new ArrayList<Surface>(); source.readTypedList(surfaces, Surface.CREATOR); + String physicalCameraId = source.readString(); checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant"); @@ -524,6 +566,7 @@ public final class OutputConfiguration implements Parcelable { StreamConfigurationMap.imageFormatToDataspace(ImageFormat.PRIVATE); mConfiguredGenerationId = 0; } + mPhysicalCameraId = physicalCameraId; } /** @@ -622,6 +665,7 @@ public final class OutputConfiguration implements Parcelable { dest.writeInt(mIsDeferredConfig ? 1 : 0); dest.writeInt(mIsShared ? 1 : 0); dest.writeTypedList(mSurfaces); + dest.writeString(mPhysicalCameraId); } /** @@ -675,13 +719,15 @@ public final class OutputConfiguration implements Parcelable { if (mIsDeferredConfig) { return HashCodeHelpers.hashCode( mRotation, mConfiguredSize.hashCode(), mConfiguredFormat, mConfiguredDataspace, - mSurfaceGroupId, mSurfaceType, mIsShared ? 1 : 0); + mSurfaceGroupId, mSurfaceType, mIsShared ? 1 : 0, + mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode()); } return HashCodeHelpers.hashCode( mRotation, mSurfaces.hashCode(), mConfiguredGenerationId, mConfiguredSize.hashCode(), mConfiguredFormat, - mConfiguredDataspace, mSurfaceGroupId, mIsShared ? 1 : 0); + mConfiguredDataspace, mSurfaceGroupId, mIsShared ? 1 : 0, + mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode()); } private static final String TAG = "OutputConfiguration"; @@ -701,4 +747,6 @@ public final class OutputConfiguration implements Parcelable { private final boolean mIsDeferredConfig; // Flag indicating if this config has shared surfaces private boolean mIsShared; + // The physical camera id that this output configuration is for. + private String mPhysicalCameraId; } diff --git a/core/java/android/hardware/radio/ProgramSelector.java b/core/java/android/hardware/radio/ProgramSelector.java index 3556751f4af4..0cf7605c98a2 100644 --- a/core/java/android/hardware/radio/ProgramSelector.java +++ b/core/java/android/hardware/radio/ProgramSelector.java @@ -59,24 +59,56 @@ import java.util.stream.Stream; */ @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 @@ public final class ProgramSelector implements Parcelable { * * 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 final class ProgramSelector implements Parcelable { 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 @@ public final class ProgramSelector implements Parcelable { * 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 @@ public final class ProgramSelector implements Parcelable { 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 @@ public final class ProgramSelector implements Parcelable { * Type of a radio technology. * * @return program type. + * @deprecated use {@link getPrimaryId} instead */ + @Deprecated public @ProgramType int getProgramType() { return mProgramType; } @@ -273,7 +354,10 @@ public final class ProgramSelector implements Parcelable { * 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 @@ public final class ProgramSelector implements Parcelable { 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 @@ public final class ProgramSelector implements Parcelable { * @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/IIpSecService.aidl b/core/java/android/net/IIpSecService.aidl index 790c80b1d934..eeb30e23d000 100644 --- a/core/java/android/net/IIpSecService.aidl +++ b/core/java/android/net/IIpSecService.aidl @@ -39,9 +39,9 @@ interface IIpSecService void closeUdpEncapsulationSocket(int resourceId); - IpSecTransformResponse createTransportModeTransform(in IpSecConfig c, in IBinder binder); + IpSecTransformResponse createTransform(in IpSecConfig c, in IBinder binder); - void deleteTransportModeTransform(int transformId); + void deleteTransform(int transformId); void applyTransportModeTransform(in ParcelFileDescriptor socket, int direction, int transformId); diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java index 2cda58c99a61..f04f03f6b617 100644 --- a/core/java/android/net/IpSecManager.java +++ b/core/java/android/net/IpSecManager.java @@ -19,6 +19,7 @@ import static com.android.internal.util.Preconditions.checkNotNull; 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 @@ public final class IpSecManager { } /** + * 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 7b9b4830929d..37e2c4fbf04a 100644 --- a/core/java/android/net/IpSecTransform.java +++ b/core/java/android/net/IpSecTransform.java @@ -124,8 +124,7 @@ public final class IpSecTransform implements AutoCloseable { synchronized (this) { try { IIpSecService svc = getIpSecService(); - IpSecTransformResponse result = - svc.createTransportModeTransform(mConfig, new Binder()); + IpSecTransformResponse result = svc.createTransform(mConfig, new Binder()); int status = result.status; checkResultStatus(status); mResourceId = result.resourceId; @@ -170,7 +169,7 @@ public final class IpSecTransform implements AutoCloseable { * still want to clear out the transform. */ IIpSecService svc = getIpSecService(); - svc.deleteTransportModeTransform(mResourceId); + svc.deleteTransform(mResourceId); stopKeepalive(); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); @@ -300,21 +299,6 @@ public final class IpSecTransform implements AutoCloseable { } /** - * 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 +399,7 @@ public final class IpSecTransform implements AutoCloseable { * @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 fd118f340119..ce2de8554dcc 100644 --- a/core/java/android/net/NetworkIdentity.java +++ b/core/java/android/net/NetworkIdentity.java @@ -130,6 +130,7 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { 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/net/NetworkStats.java b/core/java/android/net/NetworkStats.java index a85f80e38dfd..01b2b39213f9 100644 --- a/core/java/android/net/NetworkStats.java +++ b/core/java/android/net/NetworkStats.java @@ -160,13 +160,6 @@ public class NetworkStats implements Parcelable { rxBytes, rxPackets, txBytes, txPackets, operations); } - // TODO: fix the the telephony code to pass DEFAULT_NETWORK_YES and remove this constructor. - public Entry(String iface, int uid, int set, int tag, int metered, int roaming, - long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) { - this(iface, uid, set, tag, metered, roaming, DEFAULT_NETWORK_YES, rxBytes, rxPackets, - txBytes, txPackets, operations); - } - public Entry(String iface, int uid, int set, int tag, int metered, int roaming, int defaultNetwork, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) { diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java index b307c5d6fc53..8efd39a7a785 100644 --- a/core/java/android/net/NetworkTemplate.java +++ b/core/java/android/net/NetworkTemplate.java @@ -24,6 +24,15 @@ import static android.net.ConnectivityManager.TYPE_WIFI; import static android.net.ConnectivityManager.TYPE_WIFI_P2P; import static android.net.ConnectivityManager.TYPE_WIMAX; import static android.net.NetworkIdentity.COMBINE_SUBTYPE_ENABLED; +import static android.net.NetworkStats.DEFAULT_NETWORK_ALL; +import static android.net.NetworkStats.DEFAULT_NETWORK_NO; +import static android.net.NetworkStats.DEFAULT_NETWORK_YES; +import static android.net.NetworkStats.METERED_ALL; +import static android.net.NetworkStats.METERED_NO; +import static android.net.NetworkStats.METERED_YES; +import static android.net.NetworkStats.ROAMING_ALL; +import static android.net.NetworkStats.ROAMING_NO; +import static android.net.NetworkStats.ROAMING_YES; import static android.net.wifi.WifiInfo.removeDoubleQuotes; import static android.telephony.TelephonyManager.NETWORK_CLASS_2_G; import static android.telephony.TelephonyManager.NETWORK_CLASS_3_G; @@ -191,16 +200,30 @@ public class NetworkTemplate implements Parcelable { private final String mNetworkId; + // Matches for the NetworkStats constants METERED_*, ROAMING_* and DEFAULT_NETWORK_*. + private final int mMetered; + private final int mRoaming; + private final int mDefaultNetwork; + public NetworkTemplate(int matchRule, String subscriberId, String networkId) { this(matchRule, subscriberId, new String[] { subscriberId }, networkId); } public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds, String networkId) { + this(matchRule, subscriberId, matchSubscriberIds, networkId, METERED_ALL, ROAMING_ALL, + DEFAULT_NETWORK_ALL); + } + + public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds, + String networkId, int metered, int roaming, int defaultNetwork) { mMatchRule = matchRule; mSubscriberId = subscriberId; mMatchSubscriberIds = matchSubscriberIds; mNetworkId = networkId; + mMetered = metered; + mRoaming = roaming; + mDefaultNetwork = defaultNetwork; if (!isKnownMatchRule(matchRule)) { Log.e(TAG, "Unknown network template rule " + matchRule @@ -213,6 +236,9 @@ public class NetworkTemplate implements Parcelable { mSubscriberId = in.readString(); mMatchSubscriberIds = in.createStringArray(); mNetworkId = in.readString(); + mMetered = in.readInt(); + mRoaming = in.readInt(); + mDefaultNetwork = in.readInt(); } @Override @@ -221,6 +247,9 @@ public class NetworkTemplate implements Parcelable { dest.writeString(mSubscriberId); dest.writeStringArray(mMatchSubscriberIds); dest.writeString(mNetworkId); + dest.writeInt(mMetered); + dest.writeInt(mRoaming); + dest.writeInt(mDefaultNetwork); } @Override @@ -243,12 +272,23 @@ public class NetworkTemplate implements Parcelable { if (mNetworkId != null) { builder.append(", networkId=").append(mNetworkId); } + if (mMetered != METERED_ALL) { + builder.append(", metered=").append(NetworkStats.meteredToString(mMetered)); + } + if (mRoaming != ROAMING_ALL) { + builder.append(", roaming=").append(NetworkStats.roamingToString(mRoaming)); + } + if (mDefaultNetwork != DEFAULT_NETWORK_ALL) { + builder.append(", defaultNetwork=").append(NetworkStats.defaultNetworkToString( + mDefaultNetwork)); + } return builder.toString(); } @Override public int hashCode() { - return Objects.hash(mMatchRule, mSubscriberId, mNetworkId); + return Objects.hash(mMatchRule, mSubscriberId, mNetworkId, mMetered, mRoaming, + mDefaultNetwork); } @Override @@ -257,7 +297,10 @@ public class NetworkTemplate implements Parcelable { final NetworkTemplate other = (NetworkTemplate) obj; return mMatchRule == other.mMatchRule && Objects.equals(mSubscriberId, other.mSubscriberId) - && Objects.equals(mNetworkId, other.mNetworkId); + && Objects.equals(mNetworkId, other.mNetworkId) + && mMetered == other.mMetered + && mRoaming == other.mRoaming + && mDefaultNetwork == other.mDefaultNetwork; } return false; } @@ -300,6 +343,10 @@ public class NetworkTemplate implements Parcelable { * Test if given {@link NetworkIdentity} matches this template. */ public boolean matches(NetworkIdentity ident) { + if (!matchesMetered(ident)) return false; + if (!matchesRoaming(ident)) return false; + if (!matchesDefaultNetwork(ident)) return false; + switch (mMatchRule) { case MATCH_MOBILE_ALL: return matchesMobile(ident); @@ -326,6 +373,24 @@ public class NetworkTemplate implements Parcelable { } } + private boolean matchesMetered(NetworkIdentity ident) { + return (mMetered == METERED_ALL) + || (mMetered == METERED_YES && ident.mMetered) + || (mMetered == METERED_NO && !ident.mMetered); + } + + private boolean matchesRoaming(NetworkIdentity ident) { + return (mRoaming == ROAMING_ALL) + || (mRoaming == ROAMING_YES && ident.mRoaming) + || (mRoaming == ROAMING_NO && !ident.mRoaming); + } + + private boolean matchesDefaultNetwork(NetworkIdentity ident) { + return (mDefaultNetwork == DEFAULT_NETWORK_ALL) + || (mDefaultNetwork == DEFAULT_NETWORK_YES && ident.mDefaultNetwork) + || (mDefaultNetwork == DEFAULT_NETWORK_NO && !ident.mDefaultNetwork); + } + public boolean matchesSubscriberId(String subscriberId) { return ArrayUtils.contains(mMatchSubscriberIds, subscriberId); } diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index dc271d8639d5..03a8dba5a82c 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -35,6 +35,7 @@ import android.util.proto.ProtoOutputStream; 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; @@ -335,6 +336,9 @@ public abstract class BatteryStats implements Parcelable { private final StringBuilder mFormatBuilder = new StringBuilder(32); private final Formatter mFormatter = new Formatter(mFormatBuilder); + private static final String CELLULAR_CONTROLLER_NAME = "Cellular"; + private static final String WIFI_CONTROLLER_NAME = "WiFi"; + /** * Indicates times spent by the uid at each cpu frequency in all process states. * @@ -412,6 +416,13 @@ public abstract class BatteryStats implements Parcelable { /** * @return a non-null {@link LongCounter} representing time spent (milliseconds) in the + * scan state. + */ + public abstract LongCounter getScanTimeCounter(); + + + /** + * @return a non-null {@link LongCounter} representing time spent (milliseconds) in the * receive state. */ public abstract LongCounter getRxTimeCounter(); @@ -683,6 +694,14 @@ public abstract class BatteryStats implements Parcelable { 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 +1519,10 @@ public abstract class BatteryStats implements Parcelable { 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 +2111,23 @@ public abstract class BatteryStats implements Parcelable { */ 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 +2352,9 @@ public abstract class BatteryStats implements Parcelable { 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[] { @@ -2366,6 +2409,14 @@ public abstract class BatteryStats implements Parcelable { public abstract long getWifiOnTime(long elapsedRealtimeUs, int which); /** + * Returns the time in microseconds that wifi has been active while the device was + * running on battery. + * + * {@hide} + */ + public abstract long getWifiActiveTime(long elapsedRealtimeUs, int which); + + /** * Returns the time in microseconds that wifi has been on and the driver has * been in the running state while the device was running on battery. * @@ -3312,6 +3363,20 @@ public abstract class BatteryStats implements Parcelable { final long sleepTimeMs = totalControllerActivityTimeMs - (idleTimeMs + rxTimeMs + totalTxTimeMs); + if (controllerName.equals(WIFI_CONTROLLER_NAME)) { + final long scanTimeMs = counter.getScanTimeCounter().getCountLocked(which); + sb.setLength(0); + sb.append(prefix); + sb.append(" "); + sb.append(controllerName); + sb.append(" Scan time: "); + formatTimeMs(sb, scanTimeMs); + sb.append("("); + sb.append(formatRatioLocked(scanTimeMs, totalControllerActivityTimeMs)); + sb.append(")"); + pw.println(sb.toString()); + } + sb.setLength(0); sb.append(prefix); sb.append(" "); @@ -3353,7 +3418,7 @@ public abstract class BatteryStats implements Parcelable { String [] powerLevel; switch(controllerName) { - case "Cellular": + case CELLULAR_CONTROLLER_NAME: powerLevel = new String[] { " less than 0dBm: ", " 0dBm to 8dBm: ", @@ -4641,7 +4706,7 @@ public abstract class BatteryStats implements Parcelable { if (!didOne) sb.append(" (no activity)"); pw.println(sb.toString()); - printControllerActivity(pw, sb, prefix, "Cellular", + printControllerActivity(pw, sb, prefix, CELLULAR_CONTROLLER_NAME, getModemControllerActivity(), which); pw.print(prefix); @@ -4650,6 +4715,16 @@ public abstract class BatteryStats implements Parcelable { sb.append(" Wifi Statistics:"); pw.println(sb.toString()); + pw.print(prefix); + sb.setLength(0); + sb.append(prefix); + sb.append(" Wifi kernel active time: "); + final long wifiActiveTime = getWifiActiveTime(rawRealtime, which); + formatTimeMs(sb, wifiActiveTime / 1000); + sb.append("("); sb.append(formatRatioLocked(wifiActiveTime, whichBatteryRealtime)); + sb.append(")"); + pw.println(sb.toString()); + pw.print(" Wifi data received: "); pw.println(formatBytesLocked(wifiRxTotalBytes)); pw.print(" Wifi data sent: "); pw.println(formatBytesLocked(wifiTxTotalBytes)); pw.print(" Wifi packets received: "); pw.println(wifiRxTotalPackets); @@ -4727,7 +4802,45 @@ public abstract class BatteryStats implements Parcelable { if (!didOne) sb.append(" (no activity)"); pw.println(sb.toString()); - printControllerActivity(pw, sb, prefix, "WiFi", getWifiControllerActivity(), which); + printControllerActivity(pw, sb, prefix, WIFI_CONTROLLER_NAME, + getWifiControllerActivity(), which); + + 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); @@ -5168,8 +5281,8 @@ public abstract class BatteryStats implements Parcelable { pw.println(sb.toString()); } - printControllerActivityIfInteresting(pw, sb, prefix + " ", "Modem", - u.getModemControllerActivity(), which); + printControllerActivityIfInteresting(pw, sb, prefix + " ", + CELLULAR_CONTROLLER_NAME, u.getModemControllerActivity(), which); if (wifiRxBytes > 0 || wifiTxBytes > 0 || wifiRxPackets > 0 || wifiTxPackets > 0) { pw.print(prefix); pw.print(" Wi-Fi network: "); @@ -5223,7 +5336,7 @@ public abstract class BatteryStats implements Parcelable { pw.println(sb.toString()); } - printControllerActivityIfInteresting(pw, sb, prefix + " ", "WiFi", + printControllerActivityIfInteresting(pw, sb, prefix + " ", WIFI_CONTROLLER_NAME, u.getWifiControllerActivity(), which); if (btRxBytes > 0 || btTxBytes > 0) { diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index 848ab88d3cbc..33e8c3e47b44 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -2352,22 +2352,28 @@ public final class Debug } /** - * Attach a library as a jvmti agent to the current runtime. + * Attach a library as a jvmti agent to the current runtime, with the given classloader + * determining the library search path. + * <p> + * Note: agents may only be attached to debuggable apps. Otherwise, this function will + * throw a SecurityException. * - * @param library library containing the agent - * @param options options passed to the agent + * @param library the library containing the agent. + * @param options the options passed to the agent. + * @param classLoader the classloader determining the library search path. * - * @throws IOException If the agent could not be attached + * @throws IOException if the agent could not be attached. + * @throws SecurityException if the app is not debuggable. */ - public static void attachJvmtiAgent(@NonNull String library, @Nullable String options) - throws IOException { + public static void attachJvmtiAgent(@NonNull String library, @Nullable String options, + @Nullable ClassLoader classLoader) throws IOException { Preconditions.checkNotNull(library); Preconditions.checkArgument(!library.contains("=")); if (options == null) { - VMDebug.attachAgent(library); + VMDebug.attachAgent(library, classLoader); } else { - VMDebug.attachAgent(library + "=" + options); + VMDebug.attachAgent(library + "=" + options, classLoader); } } } diff --git a/core/java/android/os/IPermissionController.aidl b/core/java/android/os/IPermissionController.aidl index 5e8590af11f1..3de953a2dfbe 100644 --- a/core/java/android/os/IPermissionController.aidl +++ b/core/java/android/os/IPermissionController.aidl @@ -22,4 +22,5 @@ interface IPermissionController { 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/ISystemUpdateManager.aidl b/core/java/android/os/ISystemUpdateManager.aidl new file mode 100644 index 000000000000..f7f50791f528 --- /dev/null +++ b/core/java/android/os/ISystemUpdateManager.aidl @@ -0,0 +1,27 @@ +/* //device/java/android/android/os/ISystemUpdateInfo.aidl +** +** Copyright 2018, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.os; + +import android.os.Bundle; +import android.os.PersistableBundle; + +/** @hide */ +interface ISystemUpdateManager { + Bundle retrieveSystemUpdateInfo(); + void updateSystemUpdateInfo(in PersistableBundle data); +} diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index e6cf5e6a6cbf..7654e9b6ee22 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -151,6 +151,12 @@ public class Process { */ 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 673a8ba661b6..57db9d19f42d 100644 --- a/core/java/android/os/RecoverySystem.java +++ b/core/java/android/os/RecoverySystem.java @@ -61,6 +61,7 @@ import java.util.HashSet; 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 @@ public class RecoverySystem { 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 @@ public class RecoverySystem { // 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 @@ public class RecoverySystem { 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 @@ public class RecoverySystem { } else { Log.d(TAG, "Successfully retained euicc data."); } + wipingSucceeded.set(true /* newValue */); } euiccFactoryResetLatch.countDown(); } @@ -808,7 +823,7 @@ public class RecoverySystem { }; 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 @@ public class RecoverySystem { } 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 @@ public class RecoverySystem { } 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/SystemUpdateManager.java b/core/java/android/os/SystemUpdateManager.java new file mode 100644 index 000000000000..ce3e225975f0 --- /dev/null +++ b/core/java/android/os/SystemUpdateManager.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import static com.android.internal.util.Preconditions.checkNotNull; + +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; +import android.annotation.SystemService; +import android.content.Context; + +/** + * Allows querying and posting system update information. + * + * {@hide} + */ +@SystemApi +@SystemService(Context.SYSTEM_UPDATE_SERVICE) +public class SystemUpdateManager { + private static final String TAG = "SystemUpdateManager"; + + /** The status key of the system update info, expecting an int value. */ + @SystemApi + public static final String KEY_STATUS = "status"; + + /** The title of the current update, expecting a String value. */ + @SystemApi + public static final String KEY_TITLE = "title"; + + /** Whether it is a security update, expecting a boolean value. */ + @SystemApi + public static final String KEY_IS_SECURITY_UPDATE = "is_security_update"; + + /** The build fingerprint after installing the current update, expecting a String value. */ + @SystemApi + public static final String KEY_TARGET_BUILD_FINGERPRINT = "target_build_fingerprint"; + + /** The security patch level after installing the current update, expecting a String value. */ + @SystemApi + public static final String KEY_TARGET_SECURITY_PATCH_LEVEL = "target_security_patch_level"; + + /** + * The KEY_STATUS value that indicates there's no update status info available. + */ + @SystemApi + public static final int STATUS_UNKNOWN = 0; + + /** + * The KEY_STATUS value that indicates there's no pending update. + */ + @SystemApi + public static final int STATUS_IDLE = 1; + + /** + * The KEY_STATUS value that indicates an update is available for download, but pending user + * approval to start. + */ + @SystemApi + public static final int STATUS_WAITING_DOWNLOAD = 2; + + /** + * The KEY_STATUS value that indicates an update is in progress (i.e. downloading or installing + * has started). + */ + @SystemApi + public static final int STATUS_IN_PROGRESS = 3; + + /** + * The KEY_STATUS value that indicates an update is available for install. + */ + @SystemApi + public static final int STATUS_WAITING_INSTALL = 4; + + /** + * The KEY_STATUS value that indicates an update will be installed after a reboot. This applies + * to both of A/B and non-A/B OTAs. + */ + @SystemApi + public static final int STATUS_WAITING_REBOOT = 5; + + private final ISystemUpdateManager mService; + + /** @hide */ + public SystemUpdateManager(ISystemUpdateManager service) { + mService = checkNotNull(service, "missing ISystemUpdateManager"); + } + + /** + * Queries the current pending system update info. + * + * <p>Requires the {@link android.Manifest.permission#READ_SYSTEM_UPDATE_INFO} or + * {@link android.Manifest.permission#RECOVERY} permission. + * + * @return A {@code Bundle} that contains the pending system update information in key-value + * pairs. + * + * @throws SecurityException if the caller is not allowed to read the info. + */ + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_SYSTEM_UPDATE_INFO, + android.Manifest.permission.RECOVERY, + }) + public Bundle retrieveSystemUpdateInfo() { + try { + return mService.retrieveSystemUpdateInfo(); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** + * Allows a system updater to publish the pending update info. + * + * <p>The reported info will not persist across reboots. Because only the reporting updater + * understands the criteria to determine a successful/failed update. + * + * <p>Requires the {@link android.Manifest.permission#RECOVERY} permission. + * + * @param infoBundle The {@code PersistableBundle} that contains the system update information, + * such as the current update status. {@link #KEY_STATUS} is required in the bundle. + * + * @throws IllegalArgumentException if @link #KEY_STATUS} does not exist. + * @throws SecurityException if the caller is not allowed to update the info. + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.RECOVERY) + public void updateSystemUpdateInfo(PersistableBundle infoBundle) { + if (infoBundle == null || !infoBundle.containsKey(KEY_STATUS)) { + throw new IllegalArgumentException("Missing status in the bundle"); + } + try { + mService.updateSystemUpdateInfo(infoBundle); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/Instrumentable.java b/core/java/android/os/connectivity/GpsBatteryStats.aidl index dbc61c26e82e..7b96d1a8e062 100644 --- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/Instrumentable.java +++ b/core/java/android/os/connectivity/GpsBatteryStats.aidl @@ -14,15 +14,7 @@ * limitations under the License. */ -package com.android.settingslib.core.instrumentation; +package android.os.connectivity; -public interface Instrumentable { - - int METRICS_CATEGORY_UNKNOWN = 0; - - /** - * Instrumented name for a view as defined in - * {@link com.android.internal.logging.nano.MetricsProto.MetricsEvent}. - */ - int getMetricsCategory(); -} +/** {@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 000000000000..f2ac5ef6d40b --- /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/os/connectivity/WifiBatteryStats.aidl b/core/java/android/os/connectivity/WifiBatteryStats.aidl new file mode 100644 index 000000000000..12ac73828eed --- /dev/null +++ b/core/java/android/os/connectivity/WifiBatteryStats.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 WifiBatteryStats;
\ No newline at end of file diff --git a/core/java/android/os/connectivity/WifiBatteryStats.java b/core/java/android/os/connectivity/WifiBatteryStats.java new file mode 100644 index 000000000000..e5341eeeb17b --- /dev/null +++ b/core/java/android/os/connectivity/WifiBatteryStats.java @@ -0,0 +1,279 @@ +/* + * 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.BatteryStats; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Arrays; + +/** + * API for Wifi power stats + * + * @hide + */ +public final class WifiBatteryStats implements Parcelable { + + private long mLoggingDurationMs; + private long mKernelActiveTimeMs; + private long mNumPacketsTx; + private long mNumBytesTx; + private long mNumPacketsRx; + private long mNumBytesRx; + private long mSleepTimeMs; + private long mScanTimeMs; + private long mIdleTimeMs; + private long mRxTimeMs; + private long mTxTimeMs; + private long mEnergyConsumedMaMs; + private long mNumAppScanRequest; + private long[] mTimeInStateMs; + private long[] mTimeInSupplicantStateMs; + private long[] mTimeInRxSignalStrengthLevelMs; + + public static final Parcelable.Creator<WifiBatteryStats> CREATOR = new + Parcelable.Creator<WifiBatteryStats>() { + public WifiBatteryStats createFromParcel(Parcel in) { + return new WifiBatteryStats(in); + } + + public WifiBatteryStats[] newArray(int size) { + return new WifiBatteryStats[size]; + } + }; + + public WifiBatteryStats() { + initialize(); + } + + public void writeToParcel(Parcel out, int flags) { + out.writeLong(mLoggingDurationMs); + out.writeLong(mKernelActiveTimeMs); + out.writeLong(mNumPacketsTx); + out.writeLong(mNumBytesTx); + out.writeLong(mNumPacketsRx); + out.writeLong(mNumBytesRx); + out.writeLong(mSleepTimeMs); + out.writeLong(mScanTimeMs); + out.writeLong(mIdleTimeMs); + out.writeLong(mRxTimeMs); + out.writeLong(mTxTimeMs); + out.writeLong(mEnergyConsumedMaMs); + out.writeLong(mNumAppScanRequest); + out.writeLongArray(mTimeInStateMs); + out.writeLongArray(mTimeInRxSignalStrengthLevelMs); + out.writeLongArray(mTimeInSupplicantStateMs); + } + + public void readFromParcel(Parcel in) { + mLoggingDurationMs = in.readLong(); + mKernelActiveTimeMs = in.readLong(); + mNumPacketsTx = in.readLong(); + mNumBytesTx = in.readLong(); + mNumPacketsRx = in.readLong(); + mNumBytesRx = in.readLong(); + mSleepTimeMs = in.readLong(); + mScanTimeMs = in.readLong(); + mIdleTimeMs = in.readLong(); + mRxTimeMs = in.readLong(); + mTxTimeMs = in.readLong(); + mEnergyConsumedMaMs = in.readLong(); + mNumAppScanRequest = in.readLong(); + in.readLongArray(mTimeInStateMs); + in.readLongArray(mTimeInRxSignalStrengthLevelMs); + in.readLongArray(mTimeInSupplicantStateMs); + } + + public long getLoggingDurationMs() { + return mLoggingDurationMs; + } + + public long getKernelActiveTimeMs() { + return mKernelActiveTimeMs; + } + + public long getNumPacketsTx() { + return mNumPacketsTx; + } + + public long getNumBytesTx() { + return mNumBytesTx; + } + + public long getNumPacketsRx() { + return mNumPacketsRx; + } + + public long getNumBytesRx() { + return mNumBytesRx; + } + + public long getSleepTimeMs() { + return mSleepTimeMs; + } + + public long getScanTimeMs() { + return mScanTimeMs; + } + + public long getIdleTimeMs() { + return mIdleTimeMs; + } + + public long getRxTimeMs() { + return mRxTimeMs; + } + + public long getTxTimeMs() { + return mTxTimeMs; + } + + public long getEnergyConsumedMaMs() { + return mEnergyConsumedMaMs; + } + + public long getNumAppScanRequest() { + return mNumAppScanRequest; + } + + public long[] getTimeInStateMs() { + return mTimeInStateMs; + } + + public long[] getTimeInRxSignalStrengthLevelMs() { + return mTimeInRxSignalStrengthLevelMs; + } + + public long[] getTimeInSupplicantStateMs() { + return mTimeInSupplicantStateMs; + } + + public void setLoggingDurationMs(long t) { + mLoggingDurationMs = t; + return; + } + + public void setKernelActiveTimeMs(long t) { + mKernelActiveTimeMs = t; + return; + } + + public void setNumPacketsTx(long n) { + mNumPacketsTx = n; + return; + } + + public void setNumBytesTx(long b) { + mNumBytesTx = b; + return; + } + + public void setNumPacketsRx(long n) { + mNumPacketsRx = n; + return; + } + + public void setNumBytesRx(long b) { + mNumBytesRx = b; + return; + } + + public void setSleepTimeMs(long t) { + mSleepTimeMs = t; + return; + } + + public void setScanTimeMs(long t) { + mScanTimeMs = t; + return; + } + + public void setIdleTimeMs(long t) { + mIdleTimeMs = t; + return; + } + + public void setRxTimeMs(long t) { + mRxTimeMs = t; + return; + } + + public void setTxTimeMs(long t) { + mTxTimeMs = t; + return; + } + + public void setEnergyConsumedMaMs(long e) { + mEnergyConsumedMaMs = e; + return; + } + + public void setNumAppScanRequest(long n) { + mNumAppScanRequest = n; + return; + } + + public void setTimeInStateMs(long[] t) { + mTimeInStateMs = Arrays.copyOfRange(t, 0, + Math.min(t.length, BatteryStats.NUM_WIFI_STATES)); + return; + } + + public void setTimeInRxSignalStrengthLevelMs(long[] t) { + mTimeInRxSignalStrengthLevelMs = Arrays.copyOfRange(t, 0, + Math.min(t.length, BatteryStats.NUM_WIFI_SIGNAL_STRENGTH_BINS)); + return; + } + + public void setTimeInSupplicantStateMs(long[] t) { + mTimeInSupplicantStateMs = Arrays.copyOfRange( + t, 0, Math.min(t.length, BatteryStats.NUM_WIFI_SUPPL_STATES)); + return; + } + + public int describeContents() { + return 0; + } + + private WifiBatteryStats(Parcel in) { + initialize(); + readFromParcel(in); + } + + private void initialize() { + mLoggingDurationMs = 0; + mKernelActiveTimeMs = 0; + mNumPacketsTx = 0; + mNumBytesTx = 0; + mNumPacketsRx = 0; + mNumBytesRx = 0; + mSleepTimeMs = 0; + mScanTimeMs = 0; + mIdleTimeMs = 0; + mRxTimeMs = 0; + mTxTimeMs = 0; + mEnergyConsumedMaMs = 0; + mNumAppScanRequest = 0; + mTimeInStateMs = new long[BatteryStats.NUM_WIFI_STATES]; + Arrays.fill(mTimeInStateMs, 0); + mTimeInRxSignalStrengthLevelMs = new long[BatteryStats.NUM_WIFI_SIGNAL_STRENGTH_BINS]; + Arrays.fill(mTimeInRxSignalStrengthLevelMs, 0); + mTimeInSupplicantStateMs = new long[BatteryStats.NUM_WIFI_SUPPL_STATES]; + Arrays.fill(mTimeInSupplicantStateMs, 0); + return; + } +}
\ No newline at end of file diff --git a/core/java/android/privacy/internal/rappor/RapporEncoder.java b/core/java/android/privacy/internal/rappor/RapporEncoder.java index 2eca4c98d235..9ac2b3e1136e 100644 --- a/core/java/android/privacy/internal/rappor/RapporEncoder.java +++ b/core/java/android/privacy/internal/rappor/RapporEncoder.java @@ -33,7 +33,6 @@ import java.util.Random; public class RapporEncoder implements DifferentialPrivacyEncoder { // Hard-coded seed and secret for insecure encoder - private static final long INSECURE_RANDOM_SEED = 0x12345678L; private static final byte[] INSECURE_SECRET = new byte[]{ (byte) 0xD7, (byte) 0x68, (byte) 0x99, (byte) 0x93, (byte) 0x94, (byte) 0x13, (byte) 0x53, (byte) 0x54, @@ -66,8 +65,8 @@ public class RapporEncoder implements DifferentialPrivacyEncoder { // Use SecureRandom as random generator. random = sSecureRandom; } else { - // Hard-coded random generator, to have deterministic result. - random = new Random(INSECURE_RANDOM_SEED); + // To have deterministic result by hard coding encoder id as seed. + random = new Random((long) config.mEncoderId.hashCode()); userSecret = INSECURE_SECRET; } mEncoder = new Encoder(random, null, null, diff --git a/core/java/android/provider/AlarmClock.java b/core/java/android/provider/AlarmClock.java index 21694575631a..7ad9e013c617 100644 --- a/core/java/android/provider/AlarmClock.java +++ b/core/java/android/provider/AlarmClock.java @@ -154,9 +154,12 @@ public final class AlarmClock { public static final String ACTION_SET_TIMER = "android.intent.action.SET_TIMER"; /** - * Activity Action: Dismiss timers. + * Activity Action: Dismiss a timer. * <p> - * Dismiss all currently expired timers. If there are no expired timers, then this is a no-op. + * The timer to dismiss should be specified using the Intent's data URI, which represents a + * deeplink to the timer. + * </p><p> + * If no data URI is provided, dismiss all expired timers. * </p> */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java index 60df467bc20f..c6c8d9d69bfb 100644 --- a/core/java/android/provider/CallLog.java +++ b/core/java/android/provider/CallLog.java @@ -223,14 +223,13 @@ public class CallLog { /** Call was WIFI call. */ public static final int FEATURES_WIFI = 1 << 3; - /** Call was on RTT at some point */ - public static final int FEATURES_RTT = 1 << 4; - /** * Indicates the call underwent Assisted Dialing. - * @hide */ - public static final Integer FEATURES_ASSISTED_DIALING_USED = 0x10; + public static final int FEATURES_ASSISTED_DIALING_USED = 1 << 4; + + /** Call was on RTT at some point */ + public static final int FEATURES_RTT = 1 << 5; /** * The phone number as the user entered it. diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 8c2a8ae9080a..4228fbb7c40e 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 @@ public final class Settings { * 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 @@ public final class Settings { /** * 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 final class Settings { 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 final class Settings { 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 @@ public final class Settings { @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 @@ public final class Settings { @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 @@ public final class Settings { @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 @@ public final class Settings { * 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 final class Settings { */ 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 final class Settings { */ 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 final class Settings { 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 final class Settings { 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 final class Settings { 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 final class Settings { 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 final class Settings { 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 final class Settings { 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 @@ public final class Settings { @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 final class Settings { 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 final class Settings { 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 final class Settings { 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 final class Settings { 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 final class Settings { 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 final class Settings { 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 final class Settings { 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 final class Settings { 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 final class Settings { 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 final class Settings { 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 final class Settings { 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 final class Settings { 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 final class Settings { 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 final class Settings { 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 final class Settings { 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 final class Settings { 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 final class Settings { 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 final class Settings { 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 final class Settings { */ 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 final class Settings { */ 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 final class Settings { 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 final class Settings { 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 final class Settings { 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 final class Settings { 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 final class Settings { */ 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 final class Settings { */ 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 final class Settings { */ 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 final class Settings { 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 final class Settings { */ 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 final class Settings { 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 final class Settings { */ 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 final class Settings { */ 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 final class Settings { */ 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 final class Settings { */ 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 final class Settings { */ 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 final class Settings { */ 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 final class Settings { */ 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 final class Settings { */ 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 final class Settings { */ 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 final class Settings { 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 @@ public final class Settings { @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 final class Settings { 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 @@ public final class Settings { @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 final class Settings { */ 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 final class Settings { */ 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 final class Settings { 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 final class Settings { 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 final class Settings { */ 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 final class Settings { */ 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 final class Settings { 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 final class Settings { 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 final class Settings { */ 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 final class Settings { */ 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 final class Settings { 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 final class Settings { 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 final class Settings { */ 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 final class Settings { */ 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 final class Settings { */ 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 final class Settings { 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 final class Settings { 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 final class Settings { */ 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 final class Settings { 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 @@ public final class Settings { 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 @@ public final class Settings { 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 final class Settings { 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 final class Settings { */ 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. */ @@ -9649,7 +10128,8 @@ public final class Settings { * This is encoded as a key=value list, separated by commas. Ex: * * "battery_tip_enabled=true,summary_enabled=true,high_usage_enabled=true," - * "high_usage_app_count=3,reduced_battery_enabled=false,reduced_battery_percent=50" + * "high_usage_app_count=3,reduced_battery_enabled=false,reduced_battery_percent=50," + * "high_usage_battery_draining=25,high_usage_period_ms=3000" * * The following keys are supported: * @@ -9659,6 +10139,8 @@ public final class Settings { * battery_saver_tip_enabled (boolean) * high_usage_enabled (boolean) * high_usage_app_count (int) + * high_usage_period_ms (long) + * high_usage_battery_draining (int) * app_restriction_enabled (boolean) * reduced_battery_enabled (boolean) * reduced_battery_percent (int) @@ -9869,6 +10351,8 @@ public final class Settings { * The following keys are supported: * <pre> * track_cpu_times_by_proc_state (boolean) + * track_cpu_active_cluster_time (boolean) + * read_binary_cpu_time (boolean) * </pre> * * <p> @@ -9896,6 +10380,15 @@ public final class Settings { public static final String FORCED_APP_STANDBY_ENABLED = "forced_app_standby_enabled"; /** + * Whether or not to enable Forced App Standby on small battery devices. + * Type: int (0 for false, 1 for true) + * Default: 0 + * @hide + */ + public static final String FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED + = "forced_app_standby_for_small_battery_enabled"; + + /** * Whether or not Network Watchlist feature is enabled. * Type: int (0 for false, 1 for true) * Default: 0 @@ -10780,7 +11273,15 @@ public final class Settings { 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 22ef3b6f346f..84c9e8867c44 100644 --- a/core/java/android/provider/SettingsValidators.java +++ b/core/java/android/provider/SettingsValidators.java @@ -21,6 +21,8 @@ import android.net.Uri; 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 class SettingsValidators { } }; + 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 @@ public class SettingsValidators { // 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 @@ public class SettingsValidators { @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 class SettingsValidators { } } } + + 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 f409e5b74d23..72afbb8414f6 100644 --- a/core/java/android/security/keymaster/KeymasterDefs.java +++ b/core/java/android/security/keymaster/KeymasterDefs.java @@ -73,6 +73,7 @@ public final class KeymasterDefs { 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 18f6dab9fc59..df63a91790d8 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 @@ public abstract class AutofillFieldClassificationService extends Service { 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 abstract class AutofillFieldClassificationService extends Service { 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 @@ public abstract class AutofillFieldClassificationService extends Service { 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,11 @@ public abstract class AutofillFieldClassificationService extends Service { 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); + if (scores != null) { + data.putParcelable(EXTRA_SCORES, new Scores(scores)); + } break; default: Log.w(TAG, "Handling unknown message: " + action); @@ -134,27 +130,6 @@ public abstract class AutofillFieldClassificationService extends Service { } /** - * 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,31 +140,22 @@ public abstract class AutofillFieldClassificationService extends Service { * @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"); + Log.e(TAG, "service implementation (" + getClass() + " does not implement onGetScore()"); + return null; } 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 +166,27 @@ public abstract class AutofillFieldClassificationService extends Service { } } - - // 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; - - /* @hide */ - public Scores(String algorithmName, int size1, int size2) { - mAlgorithmName = algorithmName; - mScores = new float[size1][size2]; - } + public final float[][] scores; - 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 +196,18 @@ public abstract class AutofillFieldClassificationService extends Service { @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 +217,6 @@ public abstract class AutofillFieldClassificationService extends Service { 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 536180335a83..cd1efd68df6f 100644 --- a/core/java/android/service/autofill/FieldClassification.java +++ b/core/java/android/service/autofill/FieldClassification.java @@ -105,21 +105,16 @@ public final class FieldClassification { /** * 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 @@ public final class FieldClassification { 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 d8e829d8f67c..398557d5ad2e 100644 --- a/core/java/android/service/autofill/IAutofillFieldClassificationService.aidl +++ b/core/java/android/service/autofill/IAutofillFieldClassificationService.aidl @@ -27,8 +27,6 @@ import java.util.List; * @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 2f9225acc520..9017848055e2 100644 --- a/core/java/android/service/autofill/UserData.java +++ b/core/java/android/service/autofill/UserData.java @@ -155,12 +155,9 @@ public final class UserData implements Parcelable { * <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/AbsoluteSizeSpan.java b/core/java/android/text/style/AbsoluteSizeSpan.java index 908ef55a6ae9..3b4eea76b390 100644 --- a/core/java/android/text/style/AbsoluteSizeSpan.java +++ b/core/java/android/text/style/AbsoluteSizeSpan.java @@ -16,71 +16,105 @@ package android.text.style; +import android.annotation.NonNull; import android.os.Parcel; import android.text.ParcelableSpan; import android.text.TextPaint; import android.text.TextUtils; +/** + * A span that changes the size of the text it's attached to. + * <p> + * For example, the size of the text can be changed to 55dp like this: + * <pre>{@code + * SpannableString string = new SpannableString("Text with absolute size span"); + *string.setSpan(new AbsoluteSizeSpan(55, true), 10, 23, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre> + * <img src="{@docRoot}reference/android/images/text/style/absolutesizespan.png" /> + * <figcaption>Text with text size updated.</figcaption> + */ public class AbsoluteSizeSpan extends MetricAffectingSpan implements ParcelableSpan { private final int mSize; - private boolean mDip; + private final boolean mDip; /** * Set the text size to <code>size</code> physical pixels. */ public AbsoluteSizeSpan(int size) { - mSize = size; + this(size, false); } /** - * Set the text size to <code>size</code> physical pixels, - * or to <code>size</code> device-independent pixels if - * <code>dip</code> is true. + * Set the text size to <code>size</code> physical pixels, or to <code>size</code> + * device-independent pixels if <code>dip</code> is true. */ public AbsoluteSizeSpan(int size, boolean dip) { mSize = size; mDip = dip; } - public AbsoluteSizeSpan(Parcel src) { + /** + * Creates an {@link AbsoluteSizeSpan} from a parcel. + */ + public AbsoluteSizeSpan(@NonNull Parcel src) { mSize = src.readInt(); mDip = src.readInt() != 0; } - + + @Override public int getSpanTypeId() { return getSpanTypeIdInternal(); } /** @hide */ + @Override public int getSpanTypeIdInternal() { return TextUtils.ABSOLUTE_SIZE_SPAN; } - + + @Override public int describeContents() { return 0; } - public void writeToParcel(Parcel dest, int flags) { + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { writeToParcelInternal(dest, flags); } /** @hide */ - public void writeToParcelInternal(Parcel dest, int flags) { + @Override + public void writeToParcelInternal(@NonNull Parcel dest, int flags) { dest.writeInt(mSize); dest.writeInt(mDip ? 1 : 0); } + /** + * Get the text size. This is in physical pixels if {@link #getDip()} returns false or in + * device-independent pixels if {@link #getDip()} returns true. + * + * @return the text size, either in physical pixels or device-independent pixels. + * @see AbsoluteSizeSpan#AbsoluteSizeSpan(int, boolean) + */ public int getSize() { return mSize; } + /** + * Returns whether the size is in device-independent pixels or not, depending on the + * <code>dip</code> flag passed in {@link #AbsoluteSizeSpan(int, boolean)} + * + * @return <code>true</code> if the size is in device-independent pixels, <code>false</code> + * otherwise + * + * @see #AbsoluteSizeSpan(int, boolean) + */ public boolean getDip() { return mDip; } @Override - public void updateDrawState(TextPaint ds) { + public void updateDrawState(@NonNull TextPaint ds) { if (mDip) { ds.setTextSize(mSize * ds.density); } else { @@ -89,7 +123,7 @@ public class AbsoluteSizeSpan extends MetricAffectingSpan implements ParcelableS } @Override - public void updateMeasureState(TextPaint ds) { + public void updateMeasureState(@NonNull TextPaint ds) { if (mDip) { ds.setTextSize(mSize * ds.density); } else { diff --git a/core/java/android/text/style/BackgroundColorSpan.java b/core/java/android/text/style/BackgroundColorSpan.java index de05f50c9860..44e35615ca55 100644 --- a/core/java/android/text/style/BackgroundColorSpan.java +++ b/core/java/android/text/style/BackgroundColorSpan.java @@ -16,52 +16,88 @@ 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.SpannableString} 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(); } - + + @Override public int getSpanTypeId() { return getSpanTypeIdInternal(); } /** @hide */ + @Override public int getSpanTypeIdInternal() { return TextUtils.BACKGROUND_COLOR_SPAN; } - + + @Override public int describeContents() { return 0; } - public void writeToParcel(Parcel dest, int flags) { + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { writeToParcelInternal(dest, flags); } /** @hide */ - public void writeToParcelInternal(Parcel dest, int flags) { + @Override + 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 2bc6d5406ade..f770674503f2 100644 --- a/core/java/android/text/style/ForegroundColorSpan.java +++ b/core/java/android/text/style/ForegroundColorSpan.java @@ -17,53 +17,88 @@ 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.SpannableString} 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(); } - + + @Override public int getSpanTypeId() { return getSpanTypeIdInternal(); } /** @hide */ + @Override public int getSpanTypeIdInternal() { return TextUtils.FOREGROUND_COLOR_SPAN; } + @Override public int describeContents() { return 0; } - public void writeToParcel(Parcel dest, int flags) { + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { writeToParcelInternal(dest, flags); } /** @hide */ - public void writeToParcelInternal(Parcel dest, int flags) { + @Override + 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/text/style/RelativeSizeSpan.java b/core/java/android/text/style/RelativeSizeSpan.java index 95f048a2e240..3094f27ab72d 100644 --- a/core/java/android/text/style/RelativeSizeSpan.java +++ b/core/java/android/text/style/RelativeSizeSpan.java @@ -16,56 +16,85 @@ package android.text.style; +import android.annotation.FloatRange; +import android.annotation.NonNull; import android.os.Parcel; import android.text.ParcelableSpan; import android.text.TextPaint; import android.text.TextUtils; +/** + * Uniformly scales the size of the text to which it's attached by a certain proportion. + * <p> + * For example, a <code>RelativeSizeSpan</code> that increases the text size by 50% can be + * constructed like this: + * <pre>{@code + * SpannableString string = new SpannableString("Text with relative size span"); + *string.setSpan(new RelativeSizeSpan(1.5f), 10, 24, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre> + * <img src="{@docRoot}reference/android/images/text/style/relativesizespan.png" /> + * <figcaption>Text increased by 50% with <code>RelativeSizeSpan</code>.</figcaption> + */ public class RelativeSizeSpan extends MetricAffectingSpan implements ParcelableSpan { private final float mProportion; - public RelativeSizeSpan(float proportion) { + /** + * Creates a {@link RelativeSizeSpan} based on a proportion. + * + * @param proportion the proportion with which the text is scaled. + */ + public RelativeSizeSpan(@FloatRange(from = 0) float proportion) { mProportion = proportion; } - public RelativeSizeSpan(Parcel src) { + /** + * Creates a {@link RelativeSizeSpan} from a parcel. + */ + public RelativeSizeSpan(@NonNull Parcel src) { mProportion = src.readFloat(); } - + + @Override public int getSpanTypeId() { return getSpanTypeIdInternal(); } /** @hide */ + @Override public int getSpanTypeIdInternal() { return TextUtils.RELATIVE_SIZE_SPAN; } - + + @Override public int describeContents() { return 0; } - public void writeToParcel(Parcel dest, int flags) { + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { writeToParcelInternal(dest, flags); } /** @hide */ - public void writeToParcelInternal(Parcel dest, int flags) { + @Override + public void writeToParcelInternal(@NonNull Parcel dest, int flags) { dest.writeFloat(mProportion); } + /** + * @return the proportion with which the text size is changed. + */ public float getSizeChange() { return mProportion; } @Override - public void updateDrawState(TextPaint ds) { + public void updateDrawState(@NonNull TextPaint ds) { ds.setTextSize(ds.getTextSize() * mProportion); } @Override - public void updateMeasureState(TextPaint ds) { + public void updateMeasureState(@NonNull TextPaint ds) { ds.setTextSize(ds.getTextSize() * mProportion); } } diff --git a/core/java/android/text/style/ScaleXSpan.java b/core/java/android/text/style/ScaleXSpan.java index d085018572ec..6ef4ceccced1 100644 --- a/core/java/android/text/style/ScaleXSpan.java +++ b/core/java/android/text/style/ScaleXSpan.java @@ -16,45 +16,79 @@ package android.text.style; +import android.annotation.FloatRange; +import android.annotation.NonNull; import android.os.Parcel; import android.text.ParcelableSpan; import android.text.TextPaint; import android.text.TextUtils; +/** + * Scales horizontally the size of the text to which it's attached by a certain factor. + * <p> + * Values > 1.0 will stretch the text wider. Values < 1.0 will stretch the text narrower. + * <p> + * For example, a <code>ScaleXSpan</code> that stretches the text size by 100% can be + * constructed like this: + * <pre>{@code + * SpannableString string = new SpannableString("Text with ScaleX span"); + *string.setSpan(new ScaleXSpan(2f), 10, 16, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre> + * <img src="{@docRoot}reference/android/images/text/style/scalexspan.png" /> + * <figcaption>Text scaled by 100% with <code>ScaleXSpan</code>.</figcaption> + */ public class ScaleXSpan extends MetricAffectingSpan implements ParcelableSpan { private final float mProportion; - public ScaleXSpan(float proportion) { + /** + * Creates a {@link ScaleXSpan} based on a proportion. Values > 1.0 will stretch the text wider. + * Values < 1.0 will stretch the text narrower. + * + * @param proportion the horizontal scale factor. + */ + public ScaleXSpan(@FloatRange(from = 0) float proportion) { mProportion = proportion; } - public ScaleXSpan(Parcel src) { + /** + * Creates a {@link ScaleXSpan} from a parcel. + */ + public ScaleXSpan(@NonNull Parcel src) { mProportion = src.readFloat(); } - + + @Override public int getSpanTypeId() { return getSpanTypeIdInternal(); } /** @hide */ + @Override public int getSpanTypeIdInternal() { return TextUtils.SCALE_X_SPAN; } - + + @Override public int describeContents() { return 0; } - public void writeToParcel(Parcel dest, int flags) { + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { writeToParcelInternal(dest, flags); } /** @hide */ - public void writeToParcelInternal(Parcel dest, int flags) { + @Override + public void writeToParcelInternal(@NonNull Parcel dest, int flags) { dest.writeFloat(mProportion); } + /** + * Get the horizontal scale factor for the text. + * + * @return the horizontal scale factor. + */ public float getScaleX() { return mProportion; } diff --git a/core/java/android/text/style/StrikethroughSpan.java b/core/java/android/text/style/StrikethroughSpan.java index 1389704f6ad8..a6305050656a 100644 --- a/core/java/android/text/style/StrikethroughSpan.java +++ b/core/java/android/text/style/StrikethroughSpan.java @@ -16,42 +16,65 @@ package android.text.style; +import android.annotation.NonNull; import android.os.Parcel; import android.text.ParcelableSpan; import android.text.TextPaint; import android.text.TextUtils; +/** + * A span that strikes through the text it's attached to. + * <p> + * The span can be used like this: + * <pre>{@code + * SpannableString string = new SpannableString("Text with strikethrough span"); + *string.setSpan(new StrikethroughSpan(), 10, 23, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre> + * <img src="{@docRoot}reference/android/images/text/style/strikethroughspan.png" /> + * <figcaption>Strikethrough text.</figcaption> + */ public class StrikethroughSpan extends CharacterStyle implements UpdateAppearance, ParcelableSpan { + + /** + * Creates a {@link StrikethroughSpan}. + */ public StrikethroughSpan() { } - - public StrikethroughSpan(Parcel src) { + + /** + * Creates a {@link StrikethroughSpan} from a parcel. + */ + public StrikethroughSpan(@NonNull Parcel src) { } - + + @Override public int getSpanTypeId() { return getSpanTypeIdInternal(); } /** @hide */ + @Override public int getSpanTypeIdInternal() { return TextUtils.STRIKETHROUGH_SPAN; } - + + @Override public int describeContents() { return 0; } - public void writeToParcel(Parcel dest, int flags) { + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { writeToParcelInternal(dest, flags); } /** @hide */ - public void writeToParcelInternal(Parcel dest, int flags) { + @Override + public void writeToParcelInternal(@NonNull Parcel dest, int flags) { } @Override - public void updateDrawState(TextPaint ds) { + public void updateDrawState(@NonNull TextPaint ds) { ds.setStrikeThruText(true); } } diff --git a/core/java/android/text/style/SubscriptSpan.java b/core/java/android/text/style/SubscriptSpan.java index f1b0d38cf624..3d15aad662b1 100644 --- a/core/java/android/text/style/SubscriptSpan.java +++ b/core/java/android/text/style/SubscriptSpan.java @@ -16,46 +16,74 @@ package android.text.style; +import android.annotation.NonNull; import android.os.Parcel; import android.text.ParcelableSpan; import android.text.TextPaint; import android.text.TextUtils; +/** + * The span that moves the position of the text baseline lower. + * <p> + * The span can be used like this: + * <pre>{@code + * SpannableString string = new SpannableString("☕- C8H10N4O2\n"); + *string.setSpan(new SubscriptSpan(), 4, 5, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + *string.setSpan(new SubscriptSpan(), 6, 8, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + *string.setSpan(new SubscriptSpan(), 9, 10, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + *string.setSpan(new SubscriptSpan(), 11, 12, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre> + * <img src="{@docRoot}reference/android/images/text/style/subscriptspan.png" /> + * <figcaption>Text with <code>SubscriptSpan</code>.</figcaption> + * Note: Since the span affects the position of the text, if the text is on the last line of a + * TextView, it may appear cut. + */ public class SubscriptSpan extends MetricAffectingSpan implements ParcelableSpan { + + /** + * Creates a {@link SubscriptSpan}. + */ public SubscriptSpan() { } - - public SubscriptSpan(Parcel src) { + + /** + * Creates a {@link SubscriptSpan} from a parcel. + */ + public SubscriptSpan(@NonNull Parcel src) { } - + + @Override public int getSpanTypeId() { return getSpanTypeIdInternal(); } /** @hide */ + @Override public int getSpanTypeIdInternal() { return TextUtils.SUBSCRIPT_SPAN; } - + + @Override public int describeContents() { return 0; } + @Override public void writeToParcel(Parcel dest, int flags) { writeToParcelInternal(dest, flags); } /** @hide */ + @Override public void writeToParcelInternal(Parcel dest, int flags) { } @Override - public void updateDrawState(TextPaint tp) { - tp.baselineShift -= (int) (tp.ascent() / 2); + public void updateDrawState(@NonNull TextPaint textPaint) { + textPaint.baselineShift -= (int) (textPaint.ascent() / 2); } @Override - public void updateMeasureState(TextPaint tp) { - tp.baselineShift -= (int) (tp.ascent() / 2); + public void updateMeasureState(@NonNull TextPaint textPaint) { + textPaint.baselineShift -= (int) (textPaint.ascent() / 2); } } diff --git a/core/java/android/text/style/SuperscriptSpan.java b/core/java/android/text/style/SuperscriptSpan.java index abcf688f10ff..3dc9d3fdfe05 100644 --- a/core/java/android/text/style/SuperscriptSpan.java +++ b/core/java/android/text/style/SuperscriptSpan.java @@ -16,46 +16,71 @@ package android.text.style; +import android.annotation.NonNull; import android.os.Parcel; import android.text.ParcelableSpan; import android.text.TextPaint; import android.text.TextUtils; +/** + * The span that moves the position of the text baseline higher. + * <p> + * The span can be used like this: + * <pre>{@code + * SpannableString string = new SpannableString("1st example"); + *string.setSpan(new SuperscriptSpan(), 1, 3, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre> + * <img src="{@docRoot}reference/android/images/text/style/superscriptspan.png" /> + * <figcaption>Text with <code>SuperscriptSpan</code>.</figcaption> + * Note: Since the span affects the position of the text, if the text is on the first line of a + * TextView, it may appear cut. This can be avoided by decreasing the text size with an {@link + * AbsoluteSizeSpan} + */ public class SuperscriptSpan extends MetricAffectingSpan implements ParcelableSpan { + /** + * Creates a {@link SuperscriptSpan}. + */ public SuperscriptSpan() { } - - public SuperscriptSpan(Parcel src) { + + /** + * Creates a {@link SuperscriptSpan} from a parcel. + */ + public SuperscriptSpan(@NonNull Parcel src) { } - + + @Override public int getSpanTypeId() { return getSpanTypeIdInternal(); } /** @hide */ + @Override public int getSpanTypeIdInternal() { return TextUtils.SUPERSCRIPT_SPAN; } - + + @Override public int describeContents() { return 0; } - public void writeToParcel(Parcel dest, int flags) { + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { writeToParcelInternal(dest, flags); } /** @hide */ - public void writeToParcelInternal(Parcel dest, int flags) { + @Override + public void writeToParcelInternal(@NonNull Parcel dest, int flags) { } @Override - public void updateDrawState(TextPaint tp) { - tp.baselineShift += (int) (tp.ascent() / 2); + public void updateDrawState(@NonNull TextPaint textPaint) { + textPaint.baselineShift += (int) (textPaint.ascent() / 2); } @Override - public void updateMeasureState(TextPaint tp) { - tp.baselineShift += (int) (tp.ascent() / 2); + public void updateMeasureState(@NonNull TextPaint textPaint) { + textPaint.baselineShift += (int) (textPaint.ascent() / 2); } } diff --git a/core/java/android/text/style/UnderlineSpan.java b/core/java/android/text/style/UnderlineSpan.java index 9024dcd39256..800838ef92e9 100644 --- a/core/java/android/text/style/UnderlineSpan.java +++ b/core/java/android/text/style/UnderlineSpan.java @@ -16,42 +16,65 @@ package android.text.style; +import android.annotation.NonNull; import android.os.Parcel; import android.text.ParcelableSpan; import android.text.TextPaint; import android.text.TextUtils; +/** + * A span that underlines the text it's attached to. + * <p> + * The span can be used like this: + * <pre>{@code + * SpannableString string = new SpannableString("Text with underline span"); + *string.setSpan(new UnderlineSpan(), 10, 19, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre> + * <img src="{@docRoot}reference/android/images/text/style/underlinespan.png" /> + * <figcaption>Underlined text.</figcaption> + */ public class UnderlineSpan extends CharacterStyle implements UpdateAppearance, ParcelableSpan { + + /** + * Creates an {@link UnderlineSpan}. + */ public UnderlineSpan() { } - - public UnderlineSpan(Parcel src) { + + /** + * Creates an {@link UnderlineSpan} from a parcel. + */ + public UnderlineSpan(@NonNull Parcel src) { } - + + @Override public int getSpanTypeId() { return getSpanTypeIdInternal(); } /** @hide */ + @Override public int getSpanTypeIdInternal() { return TextUtils.UNDERLINE_SPAN; } - + + @Override public int describeContents() { return 0; } - public void writeToParcel(Parcel dest, int flags) { + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { writeToParcelInternal(dest, flags); } /** @hide */ - public void writeToParcelInternal(Parcel dest, int flags) { + @Override + public void writeToParcelInternal(@NonNull Parcel dest, int flags) { } @Override - public void updateDrawState(TextPaint ds) { + public void updateDrawState(@NonNull TextPaint ds) { ds.setUnderlineText(true); } } diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index e94f91a12905..4e98d9b09318 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -43,7 +43,7 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put("settings_battery_v2", "false"); DEFAULT_FLAGS.put("settings_battery_display_app_list", "false"); DEFAULT_FLAGS.put("settings_security_settings_v2", "true"); - DEFAULT_FLAGS.put("settings_zone_picker_v2", "false"); + DEFAULT_FLAGS.put("settings_zone_picker_v2", "true"); DEFAULT_FLAGS.put("settings_suggestion_ui_v2", "false"); } diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java index ce8998fcb863..5a09dab552e3 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.IOException; 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 class ApkSignatureSchemeV2Verifier { */ 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 class ApkSignatureSchemeV2Verifier { */ 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 @@ public class ApkSignatureSchemeV2Verifier { * 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 @@ public class ApkSignatureSchemeV2Verifier { * @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 @@ public class ApkSignatureSchemeV2Verifier { 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 @@ public class ApkSignatureSchemeV2Verifier { 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 @@ public class ApkSignatureSchemeV2Verifier { 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 c9e67fe1c9ef..1b04eb2f7d44 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.IOException; 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 @@ public class ApkSignatureSchemeV3Verifier { 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 @@ public class ApkSignatureSchemeV3Verifier { 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 class ApkSignatureSchemeV3Verifier { 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 555c4740389a..a2a76169c83a 100644 --- a/core/java/android/util/apk/ApkSignatureVerifier.java +++ b/core/java/android/util/apk/ApkSignatureVerifier.java @@ -36,7 +36,9 @@ import libcore.io.IoUtils; 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 @@ public class ApkSignatureVerifier { // 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 9d53847f8110..4146f6fab011 100644 --- a/core/java/android/util/apk/ApkSigningBlockUtils.java +++ b/core/java/android/util/apk/ApkSigningBlockUtils.java @@ -306,6 +306,26 @@ final class ApkSigningBlockUtils { } /** + * 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 7412ef411fb4..a0d5e4c2dd8e 100644 --- a/core/java/android/util/apk/ApkVerityBuilder.java +++ b/core/java/android/util/apk/ApkVerityBuilder.java @@ -164,11 +164,11 @@ abstract class ApkVerityBuilder { } 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/Surface.java b/core/java/android/view/Surface.java index 1f7f8b9a0c32..8830c90addac 100644 --- a/core/java/android/view/Surface.java +++ b/core/java/android/view/Surface.java @@ -283,6 +283,7 @@ public class Surface implements Parcelable { */ public long getNextFrameNumber() { synchronized (mLock) { + checkNotReleasedLocked(); return nativeGetNextFrameNumber(mNativeObject); } } diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index f54561bfb423..4b24a71c8bfb 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -33,7 +33,6 @@ import android.metrics.LogMaker; 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.Arrays; 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 final class AutofillManager { 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 @@ public final class AutofillManager { * 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 @@ public final class AutofillManager { * 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 @@ public final class AutofillManager { } } } - - 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 41672e7aeb9b..1a11fbba0011 100644 --- a/core/java/android/view/autofill/IAutoFillManager.aidl +++ b/core/java/android/view/autofill/IAutoFillManager.aidl @@ -59,6 +59,6 @@ interface IAutoFillManager { 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 388180dc61e0..d3e807d99990 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -23,6 +23,8 @@ import android.net.wifi.WifiActivityEnergyInfo; import android.os.ParcelFileDescriptor; import android.os.WorkSource; import android.os.connectivity.CellularBatteryStats; +import android.os.connectivity.WifiBatteryStats; +import android.os.connectivity.GpsBatteryStats; import android.os.health.HealthStatsParceler; import android.telephony.DataConnectionRealTimeInfo; import android.telephony.ModemActivityInfo; @@ -91,6 +93,7 @@ interface IBatteryStats { 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 +143,12 @@ interface IBatteryStats { /** {@hide} */ CellularBatteryStats getCellularBatteryStats(); + /** {@hide} */ + WifiBatteryStats getWifiBatteryStats(); + + /** {@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 15dc6f507093..5a59e70865e5 100644 --- a/core/java/com/android/internal/os/BatteryStatsHelper.java +++ b/core/java/com/android/internal/os/BatteryStatsHelper.java @@ -665,14 +665,14 @@ public class BatteryStatsHelper { /** * 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 ac5dbc4e175a..b1c45f729fcb 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -34,6 +34,8 @@ import android.os.BatteryManager; import android.os.BatteryStats; import android.os.Build; import android.os.connectivity.CellularBatteryStats; +import android.os.connectivity.WifiBatteryStats; +import android.os.connectivity.GpsBatteryStats; import android.os.FileUtils; import android.os.Handler; import android.os.IBatteryPropertiesRegistrar; @@ -78,6 +80,7 @@ import android.view.Display; 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; @@ -129,7 +132,7 @@ public class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - private static final int VERSION = 173 + (USE_OLD_HISTORY ? 1000 : 0); + private static final int VERSION = 174 + (USE_OLD_HISTORY ? 1000 : 0); // Maximum number of items we will record in the history. private static final int MAX_HISTORY_ITEMS; @@ -153,11 +156,11 @@ public class BatteryStatsImpl extends BatteryStats { 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 +201,12 @@ public class BatteryStatsImpl extends BatteryStats { 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 +675,10 @@ public class BatteryStatsImpl extends BatteryStats { 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 = @@ -739,6 +752,8 @@ public class BatteryStatsImpl extends BatteryStats { final StopwatchTimer[] mWifiSignalStrengthsTimer = new StopwatchTimer[NUM_WIFI_SIGNAL_STRENGTH_BINS]; + StopwatchTimer mWifiActiveTimer; + int mBluetoothScanNesting; @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) protected StopwatchTimer mBluetoothScanTimer; @@ -2773,12 +2788,14 @@ public class BatteryStatsImpl extends BatteryStats { public static class ControllerActivityCounterImpl extends ControllerActivityCounter implements Parcelable { private final LongSamplingCounter mIdleTimeMillis; + private final LongSamplingCounter mScanTimeMillis; private final LongSamplingCounter mRxTimeMillis; private final LongSamplingCounter[] mTxTimeMillis; private final LongSamplingCounter mPowerDrainMaMs; public ControllerActivityCounterImpl(TimeBase timeBase, int numTxStates) { mIdleTimeMillis = new LongSamplingCounter(timeBase); + mScanTimeMillis = new LongSamplingCounter(timeBase); mRxTimeMillis = new LongSamplingCounter(timeBase); mTxTimeMillis = new LongSamplingCounter[numTxStates]; for (int i = 0; i < numTxStates; i++) { @@ -2789,6 +2806,7 @@ public class BatteryStatsImpl extends BatteryStats { public ControllerActivityCounterImpl(TimeBase timeBase, int numTxStates, Parcel in) { mIdleTimeMillis = new LongSamplingCounter(timeBase, in); + mScanTimeMillis = new LongSamplingCounter(timeBase, in); mRxTimeMillis = new LongSamplingCounter(timeBase, in); final int recordedTxStates = in.readInt(); if (recordedTxStates != numTxStates) { @@ -2804,6 +2822,7 @@ public class BatteryStatsImpl extends BatteryStats { public void readSummaryFromParcel(Parcel in) { mIdleTimeMillis.readSummaryFromParcelLocked(in); + mScanTimeMillis.readSummaryFromParcelLocked(in); mRxTimeMillis.readSummaryFromParcelLocked(in); final int recordedTxStates = in.readInt(); if (recordedTxStates != mTxTimeMillis.length) { @@ -2822,6 +2841,7 @@ public class BatteryStatsImpl extends BatteryStats { public void writeSummaryToParcel(Parcel dest) { mIdleTimeMillis.writeSummaryFromParcelLocked(dest); + mScanTimeMillis.writeSummaryFromParcelLocked(dest); mRxTimeMillis.writeSummaryFromParcelLocked(dest); dest.writeInt(mTxTimeMillis.length); for (LongSamplingCounter counter : mTxTimeMillis) { @@ -2833,6 +2853,7 @@ public class BatteryStatsImpl extends BatteryStats { @Override public void writeToParcel(Parcel dest, int flags) { mIdleTimeMillis.writeToParcel(dest); + mScanTimeMillis.writeToParcel(dest); mRxTimeMillis.writeToParcel(dest); dest.writeInt(mTxTimeMillis.length); for (LongSamplingCounter counter : mTxTimeMillis) { @@ -2843,6 +2864,7 @@ public class BatteryStatsImpl extends BatteryStats { public void reset(boolean detachIfReset) { mIdleTimeMillis.reset(detachIfReset); + mScanTimeMillis.reset(detachIfReset); mRxTimeMillis.reset(detachIfReset); for (LongSamplingCounter counter : mTxTimeMillis) { counter.reset(detachIfReset); @@ -2852,6 +2874,7 @@ public class BatteryStatsImpl extends BatteryStats { public void detach() { mIdleTimeMillis.detach(); + mScanTimeMillis.detach(); mRxTimeMillis.detach(); for (LongSamplingCounter counter : mTxTimeMillis) { counter.detach(); @@ -2869,6 +2892,15 @@ public class BatteryStatsImpl extends BatteryStats { } /** + * @return a LongSamplingCounter, measuring time spent in the scan state in + * milliseconds. + */ + @Override + public LongSamplingCounter getScanTimeCounter() { + return mScanTimeMillis; + } + + /** * @return a LongSamplingCounter, measuring time spent in the receive state in * milliseconds. */ @@ -3880,6 +3912,10 @@ public class BatteryStatsImpl extends BatteryStats { } mKernelUidCpuTimeReader.removeUid(isolatedUid); mKernelUidCpuFreqTimeReader.removeUid(isolatedUid); + if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) { + mKernelUidCpuActiveTimeReader.removeUid(isolatedUid); + mKernelUidCpuClusterTimeReader.removeUid(isolatedUid); + } } public int mapUid(int uid) { @@ -4575,10 +4611,37 @@ public class BatteryStatsImpl extends BatteryStats { 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 +4975,18 @@ public class BatteryStatsImpl extends BatteryStats { 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(); @@ -5565,8 +5640,11 @@ public class BatteryStatsImpl extends BatteryStats { noteWifiRadioApWakeupLocked(elapsedRealtime, uptime, uid); } mHistoryCur.states |= HistoryItem.STATE_WIFI_RADIO_ACTIVE_FLAG; + mWifiActiveTimer.startRunningLocked(elapsedRealtime); } else { mHistoryCur.states &= ~HistoryItem.STATE_WIFI_RADIO_ACTIVE_FLAG; + mWifiActiveTimer.stopRunningLocked( + timestampNs / (1000 * 1000)); } if (DEBUG_HISTORY) Slog.v(TAG, "Wifi network active " + active + " to: " + Integer.toHexString(mHistoryCur.states)); @@ -6123,6 +6201,20 @@ public class BatteryStatsImpl extends BatteryStats { 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); } @@ -6203,6 +6295,10 @@ public class BatteryStatsImpl extends BatteryStats { return mWifiOnTimer.getTotalTimeLocked(elapsedRealtimeUs, which); } + @Override public long getWifiActiveTime(long elapsedRealtimeUs, int which) { + return mWifiActiveTimer.getTotalTimeLocked(elapsedRealtimeUs, which); + } + @Override public long getGlobalWifiRunningTime(long elapsedRealtimeUs, int which) { return mGlobalWifiRunningTimer.getTotalTimeLocked(elapsedRealtimeUs, which); } @@ -6479,9 +6575,11 @@ public class BatteryStatsImpl extends BatteryStats { LongSamplingCounter mUserCpuTime; LongSamplingCounter mSystemCpuTime; LongSamplingCounter[][] mCpuClusterSpeedTimesUs; + LongSamplingCounter mCpuActiveTimeMs; LongSamplingCounterArray mCpuFreqTimeMs; LongSamplingCounterArray mScreenOffCpuFreqTimeMs; + LongSamplingCounterArray mCpuClusterTimesMs; LongSamplingCounterArray[] mProcStateTimeMs; LongSamplingCounterArray[] mProcStateScreenOffTimeMs; @@ -6551,6 +6649,8 @@ public class BatteryStatsImpl extends BatteryStats { 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 +6698,17 @@ public class BatteryStatsImpl extends BatteryStats { } @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 +7771,9 @@ public class BatteryStatsImpl extends BatteryStats { mScreenOffCpuFreqTimeMs.reset(false); } + mCpuActiveTimeMs.reset(false); + mCpuClusterTimesMs.reset(false); + if (mProcStateTimeMs != null) { for (LongSamplingCounterArray counters : mProcStateTimeMs) { if (counters != null) { @@ -7864,6 +7978,8 @@ public class BatteryStatsImpl extends BatteryStats { if (mScreenOffCpuFreqTimeMs != null) { mScreenOffCpuFreqTimeMs.detach(); } + mCpuActiveTimeMs.detach(); + mCpuClusterTimesMs.detach(); if (mProcStateTimeMs != null) { for (LongSamplingCounterArray counters : mProcStateTimeMs) { @@ -8139,6 +8255,10 @@ public class BatteryStatsImpl extends BatteryStats { 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 +8576,9 @@ public class BatteryStatsImpl extends BatteryStats { 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 +9945,11 @@ public class BatteryStatsImpl extends BatteryStats { mWifiSignalStrengthsTimer[i] = new StopwatchTimer(mClocks, null, -800-i, null, mOnBatteryTimeBase); } + mWifiActiveTimer = new StopwatchTimer(mClocks, null, -900, 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,7 +10639,11 @@ public class BatteryStatsImpl extends BatteryStats { mWifiSignalStrengthsTimer[i].reset(false); } mWifiMulticastWakelockTimer.reset(false); + mWifiActiveTimer.reset(false); mWifiActivity.reset(false); + for (int i=0; i< GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) { + mGpsSignalQualityTimer[i].reset(false); + } mBluetoothActivity.reset(false); mModemActivity.reset(false); mNumConnectivityChange = mLoadedNumConnectivityChange = mUnpluggedNumConnectivityChange = 0; @@ -10774,6 +10906,7 @@ public class BatteryStatsImpl extends BatteryStats { // Measured in mAms final long txTimeMs = info.getControllerTxTimeMillis(); final long rxTimeMs = info.getControllerRxTimeMillis(); + final long scanTimeMs = info.getControllerScanTimeMillis(); final long idleTimeMs = info.getControllerIdleTimeMillis(); final long totalTimeMs = txTimeMs + rxTimeMs + idleTimeMs; @@ -10786,6 +10919,7 @@ public class BatteryStatsImpl extends BatteryStats { Slog.d(TAG, " Rx Time: " + rxTimeMs + " ms"); Slog.d(TAG, " Idle Time: " + idleTimeMs + " ms"); Slog.d(TAG, " Total Time: " + totalTimeMs + " ms"); + Slog.d(TAG, " Scan Time: " + scanTimeMs + " ms"); } long totalWifiLockTimeMs = 0; @@ -10919,6 +11053,8 @@ public class BatteryStatsImpl extends BatteryStats { mWifiActivity.getRxTimeCounter().addCountLocked(info.getControllerRxTimeMillis()); mWifiActivity.getTxTimeCounters()[0].addCountLocked( info.getControllerTxTimeMillis()); + mWifiActivity.getScanTimeCounter().addCountLocked( + info.getControllerScanTimeMillis()); mWifiActivity.getIdleTimeCounter().addCountLocked( info.getControllerIdleTimeMillis()); @@ -10962,6 +11098,39 @@ public class BatteryStatsImpl extends BatteryStats { return; } + if (activityInfo != null) { + mHasModemReporting = true; + mModemActivity.getIdleTimeCounter().addCountLocked( + activityInfo.getIdleTimeMillis()); + mModemActivity.getRxTimeCounter().addCountLocked(activityInfo.getRxTimeMillis()); + for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) { + mModemActivity.getTxTimeCounters()[lvl] + .addCountLocked(activityInfo.getTxTimeMillis()[lvl]); + } + + // POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V. + final double opVolt = mPowerProfile.getAveragePower( + PowerProfile.POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE) / 1000.0; + if (opVolt != 0) { + double energyUsed = + activityInfo.getSleepTimeMillis() * + mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_SLEEP) + + activityInfo.getIdleTimeMillis() * + mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_IDLE) + + activityInfo.getRxTimeMillis() * + mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX); + int[] txCurrentMa = activityInfo.getTxTimeMillis(); + for (int i = 0; i < Math.min(txCurrentMa.length, + SignalStrength.NUM_SIGNAL_STRENGTH_BINS); i++) { + energyUsed += txCurrentMa[i] * mPowerProfile.getAveragePower( + PowerProfile.POWER_MODEM_CONTROLLER_TX, i); + } + + // We store the power drain as mAms. + mModemActivity.getPowerCounter().addCountLocked((long) energyUsed); + } + } + final long elapsedRealtimeMs = mClocks.elapsedRealtime(); long radioTime = mMobileRadioActivePerAppTimer.getTimeSinceMarkLocked( elapsedRealtimeMs * 1000); @@ -11060,26 +11229,6 @@ public class BatteryStatsImpl extends BatteryStats { mNetworkStatsPool.release(delta); delta = null; } - - if (activityInfo != null) { - mHasModemReporting = true; - mModemActivity.getIdleTimeCounter().addCountLocked( - activityInfo.getIdleTimeMillis()); - mModemActivity.getRxTimeCounter().addCountLocked(activityInfo.getRxTimeMillis()); - for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) { - mModemActivity.getTxTimeCounters()[lvl] - .addCountLocked(activityInfo.getTxTimeMillis()[lvl]); - } - - // POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V. - final double opVolt = mPowerProfile.getAveragePower( - PowerProfile.POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE) / 1000.0; - if (opVolt != 0) { - // We store the power drain as mAms. - mModemActivity.getPowerCounter().addCountLocked( - (long) (activityInfo.getEnergyUsed() / opVolt)); - } - } } } @@ -11437,6 +11586,10 @@ public class BatteryStatsImpl extends BatteryStats { if (!mOnBatteryInternal) { mKernelUidCpuTimeReader.readDelta(null); mKernelUidCpuFreqTimeReader.readDelta(null); + if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) { + mKernelUidCpuActiveTimeReader.readDelta(null); + mKernelUidCpuClusterTimeReader.readDelta(null); + } for (int cluster = mKernelCpuSpeedReaders.length - 1; cluster >= 0; --cluster) { mKernelCpuSpeedReaders[cluster].readDelta(); } @@ -11453,6 +11606,10 @@ public class BatteryStatsImpl extends BatteryStats { updateClusterSpeedTimes(updatedUids); } readKernelUidCpuFreqTimesLocked(partialTimersToConsider); + if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) { + readKernelUidCpuActiveTimesLocked(); + readKernelUidCpuClusterTimesLocked(); + } } /** @@ -11764,6 +11921,64 @@ public class BatteryStatsImpl extends BatteryStats { } } + /** + * 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 +12582,71 @@ public class BatteryStatsImpl extends BatteryStats { return s; } + /*@hide */ + public WifiBatteryStats getWifiBatteryStats() { + WifiBatteryStats s = new WifiBatteryStats(); + final int which = STATS_SINCE_CHARGED; + final long rawRealTime = SystemClock.elapsedRealtime() * 1000; + final ControllerActivityCounter counter = getWifiControllerActivity(); + final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(which); + final long scanTimeMs = counter.getScanTimeCounter().getCountLocked(which); + final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(which); + final long txTimeMs = counter.getTxTimeCounters()[0].getCountLocked(which); + final long totalControllerActivityTimeMs + = computeBatteryRealtime(SystemClock.elapsedRealtime() * 1000, which) / 1000; + final long sleepTimeMs + = totalControllerActivityTimeMs - (idleTimeMs + rxTimeMs + txTimeMs); + final long energyConsumedMaMs = counter.getPowerCounter().getCountLocked(which); + long numAppScanRequest = 0; + for (int i = 0; i < mUidStats.size(); i++) { + numAppScanRequest += mUidStats.valueAt(i).mWifiScanTimer.getCountLocked(which); + } + long[] timeInStateMs = new long[NUM_WIFI_STATES]; + for (int i=0; i<NUM_WIFI_STATES; i++) { + timeInStateMs[i] = getWifiStateTime(i, rawRealTime, which) / 1000; + } + long[] timeInSupplStateMs = new long[NUM_WIFI_SUPPL_STATES]; + for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) { + timeInSupplStateMs[i] = getWifiSupplStateTime(i, rawRealTime, which) / 1000; + } + long[] timeSignalStrengthTimeMs = new long[NUM_WIFI_SIGNAL_STRENGTH_BINS]; + for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) { + timeSignalStrengthTimeMs[i] = getWifiSignalStrengthTime(i, rawRealTime, which) / 1000; + } + s.setLoggingDurationMs(computeBatteryRealtime(rawRealTime, which) / 1000); + s.setKernelActiveTimeMs(getWifiActiveTime(rawRealTime, which) / 1000); + s.setNumPacketsTx(getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which)); + s.setNumBytesTx(getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which)); + s.setNumPacketsRx(getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which)); + s.setNumBytesRx(getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which)); + s.setSleepTimeMs(sleepTimeMs); + s.setIdleTimeMs(idleTimeMs); + s.setRxTimeMs(rxTimeMs); + s.setTxTimeMs(txTimeMs); + s.setScanTimeMs(scanTimeMs); + s.setEnergyConsumedMaMs(energyConsumedMaMs); + s.setNumAppScanRequest(numAppScanRequest); + s.setTimeInStateMs(timeInStateMs); + s.setTimeInSupplicantStateMs(timeInSupplStateMs); + s.setTimeInRxSignalStrengthLevelMs(timeSignalStrengthTimeMs); + 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; @@ -12626,10 +12906,19 @@ public class BatteryStatsImpl extends BatteryStats { public final class Constants extends ContentObserver { public static final String KEY_TRACK_CPU_TIMES_BY_PROC_STATE = "track_cpu_times_by_proc_state"; + public static final String KEY_TRACK_CPU_ACTIVE_CLUSTER_TIME + = "track_cpu_active_cluster_time"; + public static final String KEY_READ_BINARY_CPU_TIME + = "read_binary_cpu_time"; private static final boolean DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE = true; + private static final boolean DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME = true; + private static final boolean DEFAULT_READ_BINARY_CPU_TIME = false; public boolean TRACK_CPU_TIMES_BY_PROC_STATE = DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE; + public boolean TRACK_CPU_ACTIVE_CLUSTER_TIME = DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME; + // Not used right now. + public boolean READ_BINARY_CPU_TIME = DEFAULT_READ_BINARY_CPU_TIME; private ContentResolver mResolver; private final KeyValueListParser mParser = new KeyValueListParser(','); @@ -12665,6 +12954,11 @@ public class BatteryStatsImpl extends BatteryStats { updateTrackCpuTimesByProcStateLocked(TRACK_CPU_TIMES_BY_PROC_STATE, mParser.getBoolean(KEY_TRACK_CPU_TIMES_BY_PROC_STATE, DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE)); + TRACK_CPU_ACTIVE_CLUSTER_TIME = mParser.getBoolean( + KEY_TRACK_CPU_ACTIVE_CLUSTER_TIME, DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME); + READ_BINARY_CPU_TIME = mParser.getBoolean( + KEY_READ_BINARY_CPU_TIME, DEFAULT_READ_BINARY_CPU_TIME); + } } @@ -12679,6 +12973,10 @@ public class BatteryStatsImpl extends BatteryStats { public void dumpLocked(PrintWriter pw) { pw.print(KEY_TRACK_CPU_TIMES_BY_PROC_STATE); pw.print("="); pw.println(TRACK_CPU_TIMES_BY_PROC_STATE); + pw.print(KEY_TRACK_CPU_ACTIVE_CLUSTER_TIME); pw.print("="); + pw.println(TRACK_CPU_ACTIVE_CLUSTER_TIME); + pw.print(KEY_READ_BINARY_CPU_TIME); pw.print("="); + pw.println(READ_BINARY_CPU_TIME); } } @@ -13044,7 +13342,11 @@ public class BatteryStatsImpl extends BatteryStats { for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) { mWifiSignalStrengthsTimer[i].readSummaryFromParcelLocked(in); } + mWifiActiveTimer.readSummaryFromParcelLocked(in); mWifiActivity.readSummaryFromParcel(in); + for (int i=0; i<GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) { + mGpsSignalQualityTimer[i].readSummaryFromParcelLocked(in); + } mBluetoothActivity.readSummaryFromParcel(in); mModemActivity.readSummaryFromParcel(in); mHasWifiReporting = in.readInt() != 0; @@ -13249,6 +13551,10 @@ public class BatteryStatsImpl extends BatteryStats { 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,7 +13788,11 @@ public class BatteryStatsImpl extends BatteryStats { for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) { mWifiSignalStrengthsTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS); } + mWifiActiveTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS); mWifiActivity.writeSummaryToParcel(out); + for (int i=0; i< GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) { + mGpsSignalQualityTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS); + } mBluetoothActivity.writeSummaryToParcel(out); mModemActivity.writeSummaryToParcel(out); out.writeInt(mHasWifiReporting ? 1 : 0); @@ -13725,6 +14035,9 @@ public class BatteryStatsImpl extends BatteryStats { 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,9 +14267,14 @@ public class BatteryStatsImpl extends BatteryStats { mWifiSignalStrengthsTimer[i] = new StopwatchTimer(mClocks, null, -800-i, null, mOnBatteryTimeBase, in); } - + mWifiActiveTimer = new StopwatchTimer(mClocks, null, -900, null, + mOnBatteryTimeBase, in); mWifiActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase, NUM_WIFI_TX_LEVELS, in); + for (int i=0; i<GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) { + mGpsSignalQualityTimer[i] = new StopwatchTimer(mClocks, null, -1000-i, + null, mOnBatteryTimeBase, in); + } mBluetoothActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase, NUM_BT_TX_LEVELS, in); mModemActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase, @@ -14154,7 +14472,11 @@ public class BatteryStatsImpl extends BatteryStats { for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) { mWifiSignalStrengthsTimer[i].writeToParcel(out, uSecRealtime); } + mWifiActiveTimer.writeToParcel(out, uSecRealtime); mWifiActivity.writeToParcel(out, 0); + for (int i=0; i< GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) { + mGpsSignalQualityTimer[i].writeToParcel(out, uSecRealtime); + } mBluetoothActivity.writeToParcel(out, 0); mModemActivity.writeToParcel(out, 0); out.writeInt(mHasWifiReporting ? 1 : 0); @@ -14348,6 +14670,10 @@ public class BatteryStatsImpl extends BatteryStats { 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 bb743c157d1f..a34e7f50c9c9 100644 --- a/core/java/com/android/internal/os/CpuPowerCalculator.java +++ b/core/java/com/android/internal/os/CpuPowerCalculator.java @@ -31,8 +31,7 @@ public class CpuPowerCalculator extends PowerCalculator { @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 @@ public class CpuPowerCalculator extends PowerCalculator { 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 @@ public class CpuPowerCalculator extends PowerCalculator { 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 000000000000..cb96c5cdfb60 --- /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 000000000000..85153bc45d07 --- /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 872b465a9ca5..240fc513055c 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.Context; 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 class PowerProfile { 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"; /** @@ -94,6 +97,7 @@ public class PowerProfile { public static final String POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE = "bluetooth.controller.voltage"; + public static final String POWER_MODEM_CONTROLLER_SLEEP = "modem.controller.sleep"; public static final String POWER_MODEM_CONTROLLER_IDLE = "modem.controller.idle"; public static final String POWER_MODEM_CONTROLLER_RX = "modem.controller.rx"; public static final String POWER_MODEM_CONTROLLER_TX = "modem.controller.tx"; @@ -182,9 +186,6 @@ public class PowerProfile { */ 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 +198,15 @@ public class PowerProfile { */ 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 +216,32 @@ public class PowerProfile { 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 +255,7 @@ public class PowerProfile { 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 +273,7 @@ public class PowerProfile { } catch (NumberFormatException nfe) { } if (element.equals(TAG_ITEM)) { - sPowerMap.put(name, value); + sPowerItemMap.put(name, value); } else if (parsingArray) { array.add(value); } @@ -263,7 +281,7 @@ public class PowerProfile { } } 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); @@ -279,10 +297,6 @@ public class PowerProfile { com.android.internal.R.integer.config_bluetooth_rx_cur_ma, com.android.internal.R.integer.config_bluetooth_tx_cur_ma, com.android.internal.R.integer.config_bluetooth_operating_voltage_mv, - com.android.internal.R.integer.config_wifi_idle_receive_cur_ma, - com.android.internal.R.integer.config_wifi_active_rx_cur_ma, - com.android.internal.R.integer.config_wifi_tx_cur_ma, - com.android.internal.R.integer.config_wifi_operating_voltage_mv, }; String[] configResIdKeys = new String[]{ @@ -290,62 +304,62 @@ public class PowerProfile { POWER_BLUETOOTH_CONTROLLER_RX, POWER_BLUETOOTH_CONTROLLER_TX, POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE, - POWER_WIFI_CONTROLLER_IDLE, - POWER_WIFI_CONTROLLER_RX, - POWER_WIFI_CONTROLLER_TX, - POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE, }; for (int i = 0; i < configResIds.length; i++) { 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 +368,30 @@ public class PowerProfile { 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 +402,10 @@ public class PowerProfile { * @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 +418,10 @@ public class PowerProfile { * @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 +445,16 @@ public class PowerProfile { * @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 c7897b2bbc3b..486b5842400c 100644 --- a/core/java/com/android/internal/os/WakelockPowerCalculator.java +++ b/core/java/com/android/internal/os/WakelockPowerCalculator.java @@ -26,7 +26,7 @@ public class WakelockPowerCalculator extends PowerCalculator { 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 85655a5dfc6b..e097362a3fe8 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -115,7 +115,7 @@ oneway interface IStatusBar /** * 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 e35f9f45acfe..e08eb587ab97 100644 --- a/core/java/com/android/internal/util/ConcurrentUtils.java +++ b/core/java/com/android/internal/util/ConcurrentUtils.java @@ -18,11 +18,13 @@ package com.android.internal.util; 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 @@ public class ConcurrentUtils { } } + /** + * 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 66b777e8e8e6..2b5103377ecb 100644 --- a/core/java/com/android/internal/util/DumpUtils.java +++ b/core/java/com/android/internal/util/DumpUtils.java @@ -102,6 +102,7 @@ public final class DumpUtils { 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 f7ea7875c8df..7fd94c6859fb 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 @@ public class ScreenshotHelper { 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 7f4b384235e5..dcb81fa623f4 100644 --- a/core/jni/android/graphics/GraphicsJNI.h +++ b/core/jni/android/graphics/GraphicsJNI.h @@ -66,10 +66,6 @@ public: 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 f3aeb32f3f86..888db32fdfac 100644 --- a/core/jni/android/opengl/util.cpp +++ b/core/jni/android/opengl/util.cpp @@ -622,7 +622,7 @@ void util_multiplyMV(JNIEnv *env, jclass clazz, // --------------------------------------------------------------------------- -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 @@ static int checkFormat(SkColorType colorType, int format, int type) 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 @@ static jint util_texImage2D(JNIEnv *env, jclass clazz, 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 @@ static jint util_texImage2D(JNIEnv *env, jclass clazz, 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 @@ static jint util_texSubImage2D(JNIEnv *env, jclass clazz, 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_database_SQLiteCommon.cpp b/core/jni/android_database_SQLiteCommon.cpp index eefcb74e0f70..34544d37a6bf 100644 --- a/core/jni/android_database_SQLiteCommon.cpp +++ b/core/jni/android_database_SQLiteCommon.cpp @@ -18,8 +18,108 @@ #include <utils/String8.h> +#include <map> + namespace android { +static const std::map<int, std::string> sErrorCodesMap = { + // Primary Result Code List + {4, "SQLITE_ABORT"}, + {23, "SQLITE_AUTH"}, + {5, "SQLITE_BUSY"}, + {14, "SQLITE_CANTOPEN"}, + {19, "SQLITE_CONSTRAINT"}, + {11, "SQLITE_CORRUPT"}, + {101, "SQLITE_DONE"}, + {16, "SQLITE_EMPTY"}, + {1, "SQLITE_ERROR"}, + {24, "SQLITE_FORMAT"}, + {13, "SQLITE_FULL"}, + {2, "SQLITE_INTERNAL"}, + {9, "SQLITE_INTERRUPT"}, + {10, "SQLITE_IOERR"}, + {6, "SQLITE_LOCKED"}, + {20, "SQLITE_MISMATCH"}, + {21, "SQLITE_MISUSE"}, + {22, "SQLITE_NOLFS"}, + {7, "SQLITE_NOMEM"}, + {26, "SQLITE_NOTADB"}, + {12, "SQLITE_NOTFOUND"}, + {27, "SQLITE_NOTICE"}, + {0, "SQLITE_OK"}, + {3, "SQLITE_PERM"}, + {15, "SQLITE_PROTOCOL"}, + {25, "SQLITE_RANGE"}, + {8, "SQLITE_READONLY"}, + {100, "SQLITE_ROW"}, + {17, "SQLITE_SCHEMA"}, + {18, "SQLITE_TOOBIG"}, + {28, "SQLITE_WARNING"}, + // Extended Result Code List + {516, "SQLITE_ABORT_ROLLBACK"}, + {261, "SQLITE_BUSY_RECOVERY"}, + {517, "SQLITE_BUSY_SNAPSHOT"}, + {1038, "SQLITE_CANTOPEN_CONVPATH"}, + {782, "SQLITE_CANTOPEN_FULLPATH"}, + {526, "SQLITE_CANTOPEN_ISDIR"}, + {270, "SQLITE_CANTOPEN_NOTEMPDIR"}, + {275, "SQLITE_CONSTRAINT_CHECK"}, + {531, "SQLITE_CONSTRAINT_COMMITHOOK"}, + {787, "SQLITE_CONSTRAINT_FOREIGNKEY"}, + {1043, "SQLITE_CONSTRAINT_FUNCTION"}, + {1299, "SQLITE_CONSTRAINT_NOTNULL"}, + {1555, "SQLITE_CONSTRAINT_PRIMARYKEY"}, + {2579, "SQLITE_CONSTRAINT_ROWID"}, + {1811, "SQLITE_CONSTRAINT_TRIGGER"}, + {2067, "SQLITE_CONSTRAINT_UNIQUE"}, + {2323, "SQLITE_CONSTRAINT_VTAB"}, + {267, "SQLITE_CORRUPT_VTAB"}, + {3338, "SQLITE_IOERR_ACCESS"}, + {2826, "SQLITE_IOERR_BLOCKED"}, + {3594, "SQLITE_IOERR_CHECKRESERVEDLOCK"}, + {4106, "SQLITE_IOERR_CLOSE"}, + {6666, "SQLITE_IOERR_CONVPATH"}, + {2570, "SQLITE_IOERR_DELETE"}, + {5898, "SQLITE_IOERR_DELETE_NOENT"}, + {4362, "SQLITE_IOERR_DIR_CLOSE"}, + {1290, "SQLITE_IOERR_DIR_FSYNC"}, + {1802, "SQLITE_IOERR_FSTAT"}, + {1034, "SQLITE_IOERR_FSYNC"}, + {6410, "SQLITE_IOERR_GETTEMPPATH"}, + {3850, "SQLITE_IOERR_LOCK"}, + {6154, "SQLITE_IOERR_MMAP"}, + {3082, "SQLITE_IOERR_NOMEM"}, + {2314, "SQLITE_IOERR_RDLOCK"}, + {266, "SQLITE_IOERR_READ"}, + {5642, "SQLITE_IOERR_SEEK"}, + {5130, "SQLITE_IOERR_SHMLOCK"}, + {5386, "SQLITE_IOERR_SHMMAP"}, + {4618, "SQLITE_IOERR_SHMOPEN"}, + {4874, "SQLITE_IOERR_SHMSIZE"}, + {522, "SQLITE_IOERR_SHORT_READ"}, + {1546, "SQLITE_IOERR_TRUNCATE"}, + {2058, "SQLITE_IOERR_UNLOCK"}, + {778, "SQLITE_IOERR_WRITE"}, + {262, "SQLITE_LOCKED_SHAREDCACHE"}, + {539, "SQLITE_NOTICE_RECOVER_ROLLBACK"}, + {283, "SQLITE_NOTICE_RECOVER_WAL"}, + {256, "SQLITE_OK_LOAD_PERMANENTLY"}, + {520, "SQLITE_READONLY_CANTLOCK"}, + {1032, "SQLITE_READONLY_DBMOVED"}, + {264, "SQLITE_READONLY_RECOVERY"}, + {776, "SQLITE_READONLY_ROLLBACK"}, + {284, "SQLITE_WARNING_AUTOINDEX"}, +}; + +static std::string sqlite3_error_code_to_msg(int errcode) { + auto it = sErrorCodesMap.find(errcode); + if (it != sErrorCodesMap.end()) { + return std::to_string(errcode) + " " + it->second; + } else { + return std::to_string(errcode); + } +} + /* throw a SQLiteException with a message appropriate for the error in handle */ void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle) { throw_sqlite3_exception(env, handle, NULL); @@ -123,7 +223,8 @@ void throw_sqlite3_exception(JNIEnv* env, int errcode, if (sqlite3Message) { String8 fullMessage; fullMessage.append(sqlite3Message); - fullMessage.appendFormat(" (code %d)", errcode); // print extended error code + const char* errcode_msg = sqlite3_error_code_to_msg(errcode).c_str(); + fullMessage.appendFormat(" (code %s)", errcode_msg); // print extended error code if (message) { fullMessage.append(": "); fullMessage.append(message); diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h index 092aaf62bbc0..c79f5bd96e99 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 @@ static inline audio_format_t audioFormatToNative(int audioFormat) 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 @@ static inline int audioFormatFromNative(audio_format_t nativeFormat) 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 @@ static inline int audioFormatFromNative(audio_format_t nativeFormat) } } +// 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 7ec68edddf52..2be947158fc5 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -1770,6 +1770,24 @@ android_media_AudioSystem_getStreamVolumeDB(JNIEnv *env, jobject thiz, (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 @@ static const JNINativeMethod gMethods[] = { (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 556ac27dfe9e..11011b1d7c16 100644 --- a/core/jni/android_media_AudioTrack.cpp +++ b/core/jni/android_media_AudioTrack.cpp @@ -73,6 +73,7 @@ struct audiotrack_callback_cookie { jobject audioTrack_ref; bool busy; Condition cond; + bool isOffload; }; // keep these values in sync with AudioTrack.java @@ -90,6 +91,7 @@ class AudioTrackJniStorage { AudioTrackJniStorage() { mCallbackData.audioTrack_class = 0; mCallbackData.audioTrack_ref = 0; + mCallbackData.isOffload = false; } ~AudioTrackJniStorage() { @@ -132,27 +134,34 @@ static void audioCallback(int event, void* user, void *info) { } 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 @@ sp<AudioTrack> android_media_AudioTrack_getAudioTrack(JNIEnv* env, jobject audio 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 @@ android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, job 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 @@ android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, job 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 @@ static const JNINativeMethod gMethods[] = { {"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 2752a7e07ff4..e5efb90c922e 100644 --- a/core/proto/android/os/incident.proto +++ b/core/proto/android/os/incident.proto @@ -21,7 +21,6 @@ option java_outer_classname = "IncidentProtoMetadata"; 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/print.proto"; 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/server/forceappstandbytracker.proto b/core/proto/android/server/forceappstandbytracker.proto index 8753bf768321..c9f7d52ae83f 100644 --- a/core/proto/android/server/forceappstandbytracker.proto +++ b/core/proto/android/server/forceappstandbytracker.proto @@ -41,4 +41,13 @@ message ForceAppStandbyTrackerProto { // Packages that are disallowed OP_RUN_ANY_IN_BACKGROUND. repeated RunAnyInBackgroundRestrictedPackages run_any_in_background_restricted_packages = 5; + + // Whether device is a small battery device + optional bool is_small_battery_device = 6; + + // Whether force app standby for small battery device setting is enabled + optional bool force_all_apps_standby_for_small_battery = 7; + + // Whether device is charging + optional bool is_charging = 8; } diff --git a/core/proto/android/service/netstats.proto b/core/proto/android/service/netstats.proto index 23613fdabf76..ad9191cac2e4 100644 --- a/core/proto/android/service/netstats.proto +++ b/core/proto/android/service/netstats.proto @@ -63,6 +63,8 @@ message NetworkIdentityProto { 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 547e83c144a4..93d852ca1326 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 @@ -2307,6 +2313,11 @@ <permission android:name="android.permission.RECOVERY" android:protectionLevel="signature|privileged" /> + <!-- @SystemApi Allows an application to read system update info. + @hide --> + <permission android:name="android.permission.READ_SYSTEM_UPDATE_INFO" + android:protectionLevel="signature" /> + <!-- Allows the system to bind to an application's task services @hide --> <permission android:name="android.permission.BIND_JOB_SERVICE" @@ -2837,6 +2848,14 @@ <permission android:name="android.permission.INSTALL_SELF_UPDATES" android:protectionLevel="signature|privileged" /> + <!-- @SystemApi Allows an application to install updates. This is a limited version + of {@link android.Manifest.permission#INSTALL_PACKAGES}. + <p>Not for use by third-party applications. + @hide + --> + <permission android:name="android.permission.INSTALL_PACKAGE_UPDATES" + android:protectionLevel="signature|privileged" /> + <!-- @SystemApi Allows an application to clear user data. <p>Not for use by third-party applications @hide @@ -3186,10 +3205,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 +3715,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/drawable/ic_screenshot.xml b/core/res/res/drawable/ic_screenshot.xml index 3074b28497cf..24dd4d86edaf 100644 --- a/core/res/res/drawable/ic_screenshot.xml +++ b/core/res/res/drawable/ic_screenshot.xml @@ -17,10 +17,8 @@ Copyright (C) 2018 The Android Open Source Project android:width="24.0dp" android:height="24.0dp" android:viewportWidth="24.0" - android:viewportHeight="24.0"> - <path - android:pathData="M0,0h24v24H0V0z" - android:fillColor="#00000000"/> + android:viewportHeight="24.0" + android:tint="?attr/colorControlNormal"> <path android:fillColor="#FF000000" android:pathData="M17.0,1.0L7.0,1.0C5.9,1.0 5.0,1.9 5.0,3.0l0.0,18.0c0.0,1.1 0.9,2.0 2.0,2.0l10.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L19.0,3.0C19.0,1.9 18.1,1.0 17.0,1.0zM17.0,20.0L7.0,20.0L7.0,4.0l10.0,0.0L17.0,20.0z"/> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index f084159b3f58..c623c9aa0fb7 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,18 +640,12 @@ <!-- Wifi driver supports batched scan --> <bool translatable="false" name="config_wifi_batched_scan_supported">false</bool> - <!-- Idle Receive current for wifi radio. 0 by default--> - <integer translatable="false" name="config_wifi_idle_receive_cur_ma">0</integer> - - <!-- Rx current for wifi radio. 0 by default--> - <integer translatable="false" name="config_wifi_active_rx_cur_ma">0</integer> - - <!-- Tx current for wifi radio. 0 by default--> - <integer translatable="false" name="config_wifi_tx_cur_ma">0</integer> - - <!-- Operating volatage for wifi radio. 0 by default--> - <integer translatable="false" name="config_wifi_operating_voltage_mv">0</integer> + <!-- 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> + <!-- Flag indicating whether the we should enable the automatic brightness in Settings. Software implementation will be used if config_hardware_auto_brightness_available is not set --> <bool name="config_automatic_brightness_available">false</bool> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 2cfe919fb8f5..170ba4264e04 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 1993ab4b5924..4ef0a6c93d9e 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" /> @@ -382,10 +384,6 @@ <java-symbol type="integer" name="config_wifi_framework_current_network_boost" /> <java-symbol type="string" name="config_wifi_random_mac_oui" /> <java-symbol type="integer" name="config_wifi_network_switching_blacklist_time" /> - <java-symbol type="integer" name="config_wifi_idle_receive_cur_ma" /> - <java-symbol type="integer" name="config_wifi_active_rx_cur_ma" /> - <java-symbol type="integer" name="config_wifi_tx_cur_ma" /> - <java-symbol type="integer" name="config_wifi_operating_voltage_mv" /> <java-symbol type="string" name="config_wifi_framework_sap_2G_channel_list" /> <java-symbol type="integer" name="config_wifi_framework_max_tx_rate_for_full_scan" /> <java-symbol type="integer" name="config_wifi_framework_max_rx_rate_for_full_scan" /> @@ -1508,6 +1506,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 +3223,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.xml b/core/res/res/xml/power_profile.xml index 6e31cd289463..bc4b10f827ce 100644 --- a/core/res/res/xml/power_profile.xml +++ b/core/res/res/xml/power_profile.xml @@ -51,15 +51,6 @@ <value>0.1</value> <!-- ~1mA --> </array> - - <!-- Radio related values. For modems WITH energy reporting support in firmware, use - modem.controller.idle, modem.controller.tx, modem.controller.rx, modem.controller.voltage. - --> - <item name="modem.controller.idle">0</item> - <item name="modem.controller.rx">0</item> - <item name="modem.controller.tx">0</item> - <item name="modem.controller.voltage">0</item> - <!-- A list of heterogeneous CPU clusters, where the value for each cluster represents the number of CPU cores for that cluster. @@ -123,4 +114,17 @@ <value>2</value> <!-- 4097-/hr --> </array> + <!-- Cellular modem related values. Default is 0.--> + <item name="modem.controller.sleep">0</item> + <item name="modem.controller.idle">0</item> + <item name="modem.controller.rx">0</item> + <array name="modem.controller.tx"> <!-- Strength 0 to 4 --> + <value>0</value> + <value>0</value> + <value>0</value> + <value>0</value> + <value>0</value> + </array> + <item name="modem.controller.voltage">0</item> + </device> 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 000000000000..cdb71343e5e5 --- /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 37b318065918..fa0ea5c3aa85 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -213,6 +213,7 @@ public class SettingsBackupTest { Settings.Global.FANCY_IME_ANIMATIONS, Settings.Global.FORCE_ALLOW_ON_EXTERNAL, Settings.Global.FORCED_APP_STANDBY_ENABLED, + Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED, Settings.Global.FSTRIM_MANDATORY_INTERVAL, Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST, Settings.Global.GLOBAL_HTTP_PROXY_HOST, @@ -534,7 +535,9 @@ public class SettingsBackupTest { 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 00732b09f821..4c4aeaf49855 100644 --- a/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java +++ b/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java @@ -36,20 +36,28 @@ public class SettingsValidatorsTest { @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 @@ public class SettingsValidatorsTest { } 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 b5a7bec75197..32053e30e886 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 @@ import java.util.Arrays; * * 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 @@ import java.util.Arrays; @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 @@ public class BatteryStatsCpuTimesTest { mBatteryStatsImpl = new MockBatteryStatsImpl(mClocks) .setKernelUidCpuTimeReader(mKernelUidCpuTimeReader) .setKernelUidCpuFreqTimeReader(mKernelUidCpuFreqTimeReader) + .setKernelUidCpuActiveTimeReader(mKernelUidCpuActiveTimeReader) + .setKernelUidCpuClusterTimeReader(mKernelUidCpuClusterTimeReader) .setUserInfoProvider(mUserInfoProvider); } @@ -134,6 +144,10 @@ public class BatteryStatsCpuTimesTest { 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 @@ public class BatteryStatsCpuTimesTest { 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 @@ public class BatteryStatsCpuTimesTest { 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 @@ public class BatteryStatsCpuTimesTest { 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 @@ public class BatteryStatsCpuTimesTest { 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 @@ public class BatteryStatsCpuTimesTest { 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 @@ public class BatteryStatsCpuTimesTest { 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 @@ public class BatteryStatsCpuTimesTest { 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 @@ public class BatteryStatsCpuTimesTest { } } 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 @@ public class BatteryStatsCpuTimesTest { 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 @@ public class BatteryStatsCpuTimesTest { 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 @@ public class BatteryStatsCpuTimesTest { 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 @@ public class BatteryStatsCpuTimesTest { 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 @@ public class BatteryStatsCpuTimesTest { 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 e8f24567599e..702f4b8c0dc5 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 @@ import org.junit.runners.Suite; 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 000000000000..1ac82bd1dc96 --- /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 000000000000..0d1f85277ebb --- /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 6c5a2aac159b..660c744f050d 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 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl { 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 000000000000..eb7da9ca4ffc --- /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/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java b/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java index 91664381efe0..6fe19a263e8b 100644 --- a/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java +++ b/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java @@ -72,13 +72,13 @@ public class LongitudinalReportingEncoderTest { final LongitudinalReportingEncoder encoder = LongitudinalReportingEncoder.createInsecureEncoderForTest( config); - assertEquals(1, encoder.encodeBoolean(true)[0]); + assertEquals(0, encoder.encodeBoolean(true)[0]); assertEquals(0, encoder.encodeBoolean(true)[0]); assertEquals(1, encoder.encodeBoolean(true)[0]); + assertEquals(0, encoder.encodeBoolean(true)[0]); assertEquals(1, encoder.encodeBoolean(true)[0]); assertEquals(1, encoder.encodeBoolean(true)[0]); assertEquals(1, encoder.encodeBoolean(true)[0]); - assertEquals(0, encoder.encodeBoolean(true)[0]); assertEquals(1, encoder.encodeBoolean(true)[0]); assertEquals(1, encoder.encodeBoolean(true)[0]); assertEquals(1, encoder.encodeBoolean(true)[0]); @@ -86,12 +86,12 @@ public class LongitudinalReportingEncoderTest { assertEquals(0, encoder.encodeBoolean(false)[0]); assertEquals(1, encoder.encodeBoolean(false)[0]); assertEquals(1, encoder.encodeBoolean(false)[0]); - assertEquals(0, encoder.encodeBoolean(false)[0]); + assertEquals(1, encoder.encodeBoolean(false)[0]); assertEquals(0, encoder.encodeBoolean(false)[0]); assertEquals(0, encoder.encodeBoolean(false)[0]); assertEquals(1, encoder.encodeBoolean(false)[0]); assertEquals(0, encoder.encodeBoolean(false)[0]); - assertEquals(0, encoder.encodeBoolean(false)[0]); + assertEquals(1, encoder.encodeBoolean(false)[0]); assertEquals(1, encoder.encodeBoolean(false)[0]); // Test if IRR returns original result when f = 0 diff --git a/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java b/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java index dad98b8e4a35..fa0343df88b4 100644 --- a/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java +++ b/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java @@ -80,7 +80,7 @@ public class RapporEncoderTest { int numBits = 8; final long inputValue = 254L; final long prrValue = 250L; - final long prrAndIrrValue = 184L; + final long prrAndIrrValue = 244L; final RapporConfig config1 = new RapporConfig( "Foo", // encoderId diff --git a/data/etc/platform.xml b/data/etc/platform.xml index 993bae1eea00..4be6408dca16 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/absolutesizespan.png b/docs/html/reference/images/text/style/absolutesizespan.png Binary files differnew file mode 100644 index 000000000000..40d5a79ae37e --- /dev/null +++ b/docs/html/reference/images/text/style/absolutesizespan.png diff --git a/docs/html/reference/images/text/style/backgroundcolorspan.png b/docs/html/reference/images/text/style/backgroundcolorspan.png Binary files differnew file mode 100644 index 000000000000..e7e72714c5bb --- /dev/null +++ b/docs/html/reference/images/text/style/backgroundcolorspan.png diff --git a/docs/html/reference/images/text/style/foregroundcolorspan.png b/docs/html/reference/images/text/style/foregroundcolorspan.png Binary files differnew file mode 100644 index 000000000000..f60db6c55254 --- /dev/null +++ b/docs/html/reference/images/text/style/foregroundcolorspan.png diff --git a/docs/html/reference/images/text/style/relativesizespan.png b/docs/html/reference/images/text/style/relativesizespan.png Binary files differnew file mode 100644 index 000000000000..eaca5ad0e93f --- /dev/null +++ b/docs/html/reference/images/text/style/relativesizespan.png diff --git a/docs/html/reference/images/text/style/scalexspan.png b/docs/html/reference/images/text/style/scalexspan.png Binary files differnew file mode 100644 index 000000000000..a5ca26f571c6 --- /dev/null +++ b/docs/html/reference/images/text/style/scalexspan.png diff --git a/docs/html/reference/images/text/style/strikethroughspan.png b/docs/html/reference/images/text/style/strikethroughspan.png Binary files differnew file mode 100644 index 000000000000..a49ecadea063 --- /dev/null +++ b/docs/html/reference/images/text/style/strikethroughspan.png diff --git a/docs/html/reference/images/text/style/subscriptspan.png b/docs/html/reference/images/text/style/subscriptspan.png Binary files differnew file mode 100644 index 000000000000..aac7092a3322 --- /dev/null +++ b/docs/html/reference/images/text/style/subscriptspan.png diff --git a/docs/html/reference/images/text/style/superscriptspan.png b/docs/html/reference/images/text/style/superscriptspan.png Binary files differnew file mode 100644 index 000000000000..996f59d55dde --- /dev/null +++ b/docs/html/reference/images/text/style/superscriptspan.png diff --git a/docs/html/reference/images/text/style/underlinespan.png b/docs/html/reference/images/text/style/underlinespan.png Binary files differnew file mode 100644 index 000000000000..dbcd0d9f1498 --- /dev/null +++ b/docs/html/reference/images/text/style/underlinespan.png diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java index 05dadc97a5ba..4d715d1cb9ea 100644 --- a/graphics/java/android/graphics/ImageDecoder.java +++ b/graphics/java/android/graphics/ImageDecoder.java @@ -33,6 +33,8 @@ import android.graphics.drawable.NinePatchDrawable; 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 @@ public final class ImageDecoder implements AutoCloseable { 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 @@ public final class ImageDecoder implements AutoCloseable { 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 @@ public final class ImageDecoder implements AutoCloseable { } /** + * 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 @@ public final class ImageDecoder implements AutoCloseable { 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 @@ public final class ImageDecoder implements AutoCloseable { "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 @@ public final class ImageDecoder implements AutoCloseable { 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 @@ public final class ImageDecoder implements AutoCloseable { } 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,8 +887,46 @@ public final class ImageDecoder implements AutoCloseable { } } - 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() { diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java index ce3bd9af73b6..da170c0fae24 100644 --- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java @@ -20,12 +20,14 @@ import android.annotation.IntRange; 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 @@ public class AnimatedImageDrawable extends Drawable implements Animatable { * 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 e3740e3cf284..f74c39d84bce 100644 --- a/graphics/java/android/graphics/drawable/BitmapDrawable.java +++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java @@ -27,6 +27,7 @@ import android.graphics.BitmapFactory; 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 com.android.internal.R; 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 @@ public class BitmapDrawable extends Drawable { */ @Deprecated public BitmapDrawable() { - mBitmapState = new BitmapState((Bitmap) null); + init(new BitmapState((Bitmap) null), null); } /** @@ -124,8 +126,7 @@ public class BitmapDrawable extends Drawable { @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 @@ public class BitmapDrawable extends Drawable { */ @Deprecated public BitmapDrawable(Bitmap bitmap) { - this(new BitmapState(bitmap), null); + init(new BitmapState(bitmap), null); } /** @@ -143,8 +144,7 @@ public class BitmapDrawable extends Drawable { * 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 @@ public class BitmapDrawable extends Drawable { */ @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 @@ public class BitmapDrawable extends Drawable { */ @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 @@ public class BitmapDrawable extends Drawable { */ @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 @@ public class BitmapDrawable extends Drawable { */ @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 @@ public class BitmapDrawable extends Drawable { } } + 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 @@ public class BitmapDrawable extends Drawable { } } + 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 f17cd768c386..291b0a0be4f4 100644 --- a/graphics/java/android/graphics/drawable/Drawable.java +++ b/graphics/java/android/graphics/drawable/Drawable.java @@ -37,6 +37,7 @@ import android.graphics.BitmapFactory; 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.graphics.Xfermode; 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 @@ public abstract class Drawable { 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 @@ public abstract class Drawable { 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 @@ public abstract class Drawable { } 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/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp index e2f02df62b30..77925fd87fc7 100644 --- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp +++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp @@ -142,6 +142,7 @@ void RenderNodeDrawable::forceDraw(SkCanvas* canvas) { static bool layerNeedsPaint(const LayerProperties& properties, float alphaMultiplier, SkPaint* paint) { + paint->setFilterQuality(kLow_SkFilterQuality); if (alphaMultiplier < 1.0f || properties.alpha() < 255 || properties.xferMode() != SkBlendMode::kSrcOver || properties.colorFilter() != nullptr) { paint->setAlpha(properties.alpha() * alphaMultiplier); @@ -200,18 +201,15 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const { // composing a hardware layer if (renderNode->getLayerSurface() && mComposeLayer) { SkASSERT(properties.effectiveLayerType() == LayerType::RenderLayer); - SkPaint* paint = nullptr; - SkPaint tmpPaint; - if (layerNeedsPaint(layerProperties, alphaMultiplier, &tmpPaint)) { - paint = &tmpPaint; - } + SkPaint paint; + layerNeedsPaint(layerProperties, alphaMultiplier, &paint); // surfaces for layers are created on LAYER_SIZE boundaries (which are >= layer size) so // we need to restrict the portion of the surface drawn to the size of the renderNode. SkASSERT(renderNode->getLayerSurface()->width() >= bounds.width()); SkASSERT(renderNode->getLayerSurface()->height() >= bounds.height()); canvas->drawImageRect(renderNode->getLayerSurface()->makeImageSnapshot().get(), - bounds, bounds, paint); + bounds, bounds, &paint); if (!renderNode->getSkiaLayer()->hasRenderedSinceRepaint) { renderNode->getSkiaLayer()->hasRenderedSinceRepaint = true; diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp index c7f57fee1d5a..2953ea8b21e9 100644 --- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp +++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp @@ -1046,6 +1046,40 @@ TEST(RenderNodeDrawable, renderNode) { EXPECT_EQ(2, canvas.mDrawCounter); } +// Verify that layers are composed with kLow_SkFilterQuality filter quality. +RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, layerComposeQuality) { + static const int CANVAS_WIDTH = 1; + static const int CANVAS_HEIGHT = 1; + static const int LAYER_WIDTH = 1; + static const int LAYER_HEIGHT = 1; + class FrameTestCanvas : public TestCanvasBase { + public: + FrameTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {} + void onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst, + const SkPaint* paint, SrcRectConstraint constraint) override { + mDrawCounter++; + EXPECT_EQ(kLow_SkFilterQuality, paint->getFilterQuality()); + } + }; + + auto layerNode = TestUtils::createSkiaNode( + 0, 0, LAYER_WIDTH, LAYER_HEIGHT, + [](RenderProperties& properties, SkiaRecordingCanvas& canvas) { + canvas.drawPaint(SkPaint()); + }); + + layerNode->animatorProperties().mutateLayerProperties().setType(LayerType::RenderLayer); + layerNode->setLayerSurface(SkSurface::MakeRasterN32Premul(LAYER_WIDTH, LAYER_HEIGHT)); + + FrameTestCanvas canvas; + RenderNodeDrawable drawable(layerNode.get(), &canvas, true); + canvas.drawDrawable(&drawable); + EXPECT_EQ(1, canvas.mDrawCounter); //make sure the layer was composed + + // clean up layer pointer, so we can safely destruct RenderNode + layerNode->setLayerSurface(nullptr); +} + TEST(ReorderBarrierDrawable, testShadowMatrix) { static const int CANVAS_WIDTH = 100; static const int CANVAS_HEIGHT = 100; diff --git a/libs/incident/Android.mk b/libs/incident/Android.mk index 5f3e4071afef..b63400f14609 100644 --- a/libs/incident/Android.mk +++ b/libs/incident/Android.mk @@ -32,6 +32,7 @@ LOCAL_C_INCLUDES := \ 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 2849d580d88b..c56f689b7419 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 @@ public: 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/core/proto/android/os/incidentheader.proto b/libs/incident/proto/android/os/header.proto index f0c736a866a6..a84dc48b8b34 100644 --- a/core/proto/android/os/incidentheader.proto +++ b/libs/incident/proto/android/os/header.proto @@ -19,31 +19,22 @@ 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. +// 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 name of the anomaly, usually human readable. - optional string incident_name = 1; + // 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 clicking the bug-report button. + // It's optional and may directly come from a user input 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. + // 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; - optional string name = 2; + 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 = 6; + optional StatsdConfigKey config_key = 3; } diff --git a/libs/incident/src/IncidentReportArgs.cpp b/libs/incident/src/IncidentReportArgs.cpp index bd9c8eeb1d74..fbc21e558806 100644 --- a/libs/incident/src/IncidentReportArgs.cpp +++ b/libs/incident/src/IncidentReportArgs.cpp @@ -161,8 +161,14 @@ IncidentReportArgs::addSection(int section) } 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 833376cfa057..603926f4fe4d 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 @@ package com.android.internal.location.gnssmetrics; 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 @@ import com.android.internal.location.nano.GnssLogsProto.GnssLog; */ 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 class GnssMetrics { * */ 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 @@ public class GnssMetrics { 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 93fc3da54550..b07d04220046 100644 --- a/media/java/android/media/AudioFormat.java +++ b/media/java/android/media/AudioFormat.java @@ -238,22 +238,15 @@ public final class AudioFormat implements Parcelable { 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 @@ public final class AudioFormat implements Parcelable { /** 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 @@ public final class AudioFormat implements Parcelable { 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 @@ public final class AudioFormat implements Parcelable { 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 @@ public final class AudioFormat implements Parcelable { 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 @@ public final class AudioFormat implements Parcelable { 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 @@ public final class AudioFormat implements Parcelable { 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 @@ public final class AudioFormat implements Parcelable { /** * 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 @@ public final class AudioFormat implements Parcelable { 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 @@ public final class AudioFormat implements Parcelable { } /** @hide */ - @IntDef({ + @IntDef(flag = false, prefix = "ENCODING", value = { ENCODING_DEFAULT, ENCODING_PCM_8BIT, ENCODING_PCM_16BIT, @@ -1025,8 +1046,14 @@ public final class AudioFormat implements Parcelable { 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 913b5e841112..2ac4063d1b08 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -1329,6 +1329,19 @@ public class AudioManager { } //==================================================================== + // 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 @@ public class AudioManager { } /** + * 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 e56944dff782..b4316ba9b1df 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 class AudioSystem 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 @@ public class AudioSystem (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 e535fdf53d01..5928d03dc4a1 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -24,7 +24,9 @@ import java.nio.ByteBuffer; 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 @@ public class AudioTrack extends PlayerBase * 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 class AudioTrack extends PlayerBase 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 @@ public class AudioTrack extends PlayerBase // 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 @@ public class AudioTrack extends PlayerBase 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 @@ public class AudioTrack extends PlayerBase * <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 @@ public class AudioTrack extends PlayerBase 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 @@ public class AudioTrack extends PlayerBase } /** + * 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 @@ public class AudioTrack extends PlayerBase .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 @@ public class AudioTrack extends PlayerBase * 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 @@ public class AudioTrack extends PlayerBase 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 @@ public class AudioTrack extends PlayerBase 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 @@ public class AudioTrack extends PlayerBase 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 @@ public class AudioTrack extends PlayerBase 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 bb6ae9863d31..6c6522328e8d 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -203,5 +203,8 @@ interface IAudioService { 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/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index 3c49b80b4b5e..78477f757e2a 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -1380,7 +1380,8 @@ public class MediaRecorder implements AudioRouting if (listener != null && !mRoutingChangeListeners.containsKey(listener)) { enableNativeRoutingCallbacksLocked(true); mRoutingChangeListeners.put( - listener, new NativeRoutingEventHandlerDelegate(this, listener, handler)); + listener, new NativeRoutingEventHandlerDelegate(this, listener, + handler != null ? handler : mEventHandler)); } } } @@ -1401,36 +1402,6 @@ public class MediaRecorder implements AudioRouting } } - /** - * Helper class to handle the forwarding of native events to the appropriate listener - * (potentially) handled in a different thread - */ - private class NativeRoutingEventHandlerDelegate { - private MediaRecorder mMediaRecorder; - private AudioRouting.OnRoutingChangedListener mOnRoutingChangedListener; - private Handler mHandler; - - NativeRoutingEventHandlerDelegate(final MediaRecorder mediaRecorder, - final AudioRouting.OnRoutingChangedListener listener, Handler handler) { - mMediaRecorder = mediaRecorder; - mOnRoutingChangedListener = listener; - mHandler = handler != null ? handler : mEventHandler; - } - - void notifyClient() { - if (mHandler != null) { - mHandler.post(new Runnable() { - @Override - public void run() { - if (mOnRoutingChangedListener != null) { - mOnRoutingChangedListener.onRoutingChanged(mMediaRecorder); - } - } - }); - } - } - } - private native final boolean native_setInputDevice(int deviceId); private native final int native_getRoutedDeviceId(); private native final void native_enableDeviceCallback(boolean enabled); diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java index fadb76d80fe5..4c96d890a751 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java @@ -197,7 +197,7 @@ public class CameraDeviceBinderTest extends AndroidTestCase { assertFalse(metadata.isEmpty()); CaptureRequest.Builder request = new CaptureRequest.Builder(metadata, /*reprocess*/false, - CameraCaptureSession.SESSION_ID_NONE); + CameraCaptureSession.SESSION_ID_NONE, mCameraId, /*physicalCameraIdSet*/null); assertFalse(request.isEmpty()); assertFalse(metadata.isEmpty()); if (needStream) { diff --git a/packages/ExtServices/AndroidManifest.xml b/packages/ExtServices/AndroidManifest.xml index 63d3623c468a..45e557c00333 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 a2e65bc8f9fd..72647ab8ae3f 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 ea516a1db8b8..4709d35018c2 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 android.view.autofill.AutofillValue; 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 @@ public class AutofillFieldClassificationServiceImpl extends AutofillFieldClassif 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/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index b13de2ec5ce1..f6541bb92fb4 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -1015,4 +1015,10 @@ <!-- About phone, status item value if the actual value is not available. --> <string name="status_unavailable">Unavailable</string> + <!-- Summary to show how many devices are connected in wifi hotspot [CHAR LIMIT=NONE] --> + <plurals name="wifi_tether_connected_summary"> + <item quantity="one">%1$d device connected</item> + <item quantity="other">%1$d devices connected</item> + </plurals> + </resources> diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java index 764c5922cc64..9b69304be308 100755 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java @@ -128,14 +128,18 @@ public class A2dpProfile implements LocalBluetoothProfile { public boolean connect(BluetoothDevice device) { if (mService == null) return false; - List<BluetoothDevice> sinks = getConnectedDevices(); - if (sinks != null) { - for (BluetoothDevice sink : sinks) { - if (sink.equals(device)) { - Log.w(TAG, "Connecting to device " + device + " : disconnect skipped"); - continue; + int max_connected_devices = mLocalAdapter.getMaxConnectedAudioDevices(); + if (max_connected_devices == 1) { + // Original behavior: disconnect currently connected device + List<BluetoothDevice> sinks = getConnectedDevices(); + if (sinks != null) { + for (BluetoothDevice sink : sinks) { + if (sink.equals(device)) { + Log.w(TAG, "Connecting to device " + device + " : disconnect skipped"); + continue; + } + mService.disconnect(sink); } - mService.disconnect(sink); } } return mService.connect(device); @@ -157,6 +161,16 @@ public class A2dpProfile implements LocalBluetoothProfile { return mService.getConnectionState(device); } + public boolean setActiveDevice(BluetoothDevice device) { + if (mService == null) return false; + return mService.setActiveDevice(device); + } + + public BluetoothDevice getActiveDevice() { + if (mService == null) return null; + return mService.getActiveDevice(); + } + public boolean isPreferred(BluetoothDevice device) { if (mService == null) return false; return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF; @@ -180,8 +194,8 @@ public class A2dpProfile implements LocalBluetoothProfile { boolean isA2dpPlaying() { if (mService == null) return false; List<BluetoothDevice> sinks = mService.getConnectedDevices(); - if (!sinks.isEmpty()) { - if (mService.isA2dpPlaying(sinks.get(0))) { + for (BluetoothDevice device : sinks) { + if (mService.isA2dpPlaying(device)) { return true; } } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java index 4c41b490d403..ac3599cae05b 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java @@ -28,4 +28,5 @@ public interface BluetoothCallback { void onDeviceDeleted(CachedBluetoothDevice cachedDevice); void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState); void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state); + void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile); } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java index f57d02bb92fa..3cda9c9e3789 100755 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java @@ -16,9 +16,12 @@ package com.android.settingslib.bluetooth; +import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothHeadset; +import android.bluetooth.BluetoothProfile; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -31,6 +34,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.Set; /** @@ -106,6 +110,12 @@ public class BluetoothEventManager { // Dock event broadcasts addHandler(Intent.ACTION_DOCK_EVENT, new DockEventHandler()); + // Active device broadcasts + addHandler(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED, + new ActiveDeviceChangedHandler()); + addHandler(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED, + new ActiveDeviceChangedHandler()); + mContext.registerReceiver(mBroadcastReceiver, mAdapterIntentFilter, null, mReceiverHandler); mContext.registerReceiver(mProfileBroadcastReceiver, mProfileIntentFilter, null, mReceiverHandler); } @@ -409,4 +419,35 @@ public class BluetoothEventManager { return deviceAdded; } + + private class ActiveDeviceChangedHandler implements Handler { + @Override + public void onReceive(Context context, Intent intent, BluetoothDevice device) { + String action = intent.getAction(); + if (action == null) { + Log.w(TAG, "ActiveDeviceChangedHandler: action is null"); + return; + } + CachedBluetoothDevice activeDevice = mDeviceManager.findDevice(device); + int bluetoothProfile = 0; + if (Objects.equals(action, BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED)) { + bluetoothProfile = BluetoothProfile.A2DP; + } else if (Objects.equals(action, BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) { + bluetoothProfile = BluetoothProfile.HEADSET; + } else { + Log.w(TAG, "ActiveDeviceChangedHandler: unknown action " + action); + return; + } + dispatchActiveDeviceChanged(activeDevice, bluetoothProfile); + } + } + + private void dispatchActiveDeviceChanged(CachedBluetoothDevice activeDevice, + int bluetoothProfile) { + synchronized (mCallbacks) { + for (BluetoothCallback callback : mCallbacks) { + callback.onActiveDeviceChanged(activeDevice, bluetoothProfile); + } + } + } } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index 9caff100cb9d..fb0f75b522b3 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -105,6 +105,10 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> private static final long MAX_UUID_DELAY_FOR_AUTO_CONNECT = 5000; private static final long MAX_HOGP_DELAY_FOR_AUTO_CONNECT = 30000; + // Active device state + private boolean mIsActiveDeviceA2dp = false; + private boolean mIsActiveDeviceHeadset = false; + /** * Describes the current device and profile for logging. * @@ -156,6 +160,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> mRemovedProfiles.add(profile); mLocalNapRoleConnected = false; } + fetchActiveDevices(); } CachedBluetoothDevice(Context context, @@ -359,6 +364,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> fetchName(); fetchBtClass(); updateProfiles(); + fetchActiveDevices(); migratePhonebookPermissionChoice(); migrateMessagePermissionChoice(); fetchMessageRejectionCount(); @@ -454,6 +460,33 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> return mDevice.getBondState(); } + /** + * Set the device status as active or non-active per Bluetooth profile. + * + * @param isActive true if the device is active + * @param bluetoothProfile the Bluetooth profile + */ + public void setActiveDevice(boolean isActive, int bluetoothProfile) { + boolean changed = false; + switch (bluetoothProfile) { + case BluetoothProfile.A2DP: + changed = (mIsActiveDeviceA2dp != isActive); + mIsActiveDeviceA2dp = isActive; + break; + case BluetoothProfile.HEADSET: + changed = (mIsActiveDeviceHeadset != isActive); + mIsActiveDeviceHeadset = isActive; + break; + default: + Log.w(TAG, "setActiveDevice: unknown profile " + bluetoothProfile + + " isActive " + isActive); + break; + } + if (changed) { + dispatchAttributesChanged(); + } + } + void setRssi(short rssi) { if (mRssi != rssi) { mRssi = rssi; @@ -529,6 +562,17 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> return true; } + private void fetchActiveDevices() { + A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile(); + if (a2dpProfile != null) { + mIsActiveDeviceA2dp = mDevice.equals(a2dpProfile.getActiveDevice()); + } + HeadsetProfile headsetProfile = mProfileManager.getHeadsetProfile(); + if (headsetProfile != null) { + mIsActiveDeviceHeadset = mDevice.equals(headsetProfile.getActiveDevice()); + } + } + /** * Refreshes the UI for the BT class, including fetching the latest value * for the class. @@ -896,37 +940,60 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> com.android.settingslib.Utils.formatPercentage(batteryLevel); } + // TODO: A temporary workaround solution using string description the device is active. + // Issue tracked by b/72317067 . + // An alternative solution would be visual indication. + // Intentionally not adding the strings to strings.xml for now: + // 1) If this is just a short-term solution, no need to waste translation effort + // 2) The number of strings with all possible combinations becomes enormously large. + // If string description becomes part of the final solution, we MUST NOT + // concatenate the strings here: this does not translate well. + String activeString = null; + if (mIsActiveDeviceA2dp && mIsActiveDeviceHeadset) { + activeString = ", active"; + } else { + if (mIsActiveDeviceA2dp) { + activeString = ", active(media)"; + } + if (mIsActiveDeviceHeadset) { + activeString = ", active(phone)"; + } + } + if (activeString == null) activeString = ""; + if (profileConnected) { if (a2dpNotConnected && hfpNotConnected) { if (batteryLevelPercentageString != null) { return mContext.getString( R.string.bluetooth_connected_no_headset_no_a2dp_battery_level, - batteryLevelPercentageString); + batteryLevelPercentageString) + activeString; } else { - return mContext.getString(R.string.bluetooth_connected_no_headset_no_a2dp); + return mContext.getString(R.string.bluetooth_connected_no_headset_no_a2dp) + + activeString; } } else if (a2dpNotConnected) { if (batteryLevelPercentageString != null) { return mContext.getString(R.string.bluetooth_connected_no_a2dp_battery_level, - batteryLevelPercentageString); + batteryLevelPercentageString) + activeString; } else { - return mContext.getString(R.string.bluetooth_connected_no_a2dp); + return mContext.getString(R.string.bluetooth_connected_no_a2dp) + activeString; } } else if (hfpNotConnected) { if (batteryLevelPercentageString != null) { return mContext.getString(R.string.bluetooth_connected_no_headset_battery_level, - batteryLevelPercentageString); + batteryLevelPercentageString) + activeString; } else { - return mContext.getString(R.string.bluetooth_connected_no_headset); + return mContext.getString(R.string.bluetooth_connected_no_headset) + + activeString; } } else { if (batteryLevelPercentageString != null) { return mContext.getString(R.string.bluetooth_connected_battery_level, - batteryLevelPercentageString); + batteryLevelPercentageString) + activeString; } else { - return mContext.getString(R.string.bluetooth_connected); + return mContext.getString(R.string.bluetooth_connected) + activeString; } } } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java index d45fe1a38a22..ee1219126fe3 100755 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java @@ -153,6 +153,16 @@ public class HeadsetProfile implements LocalBluetoothProfile { return BluetoothProfile.STATE_DISCONNECTED; } + public boolean setActiveDevice(BluetoothDevice device) { + if (mService == null) return false; + return mService.setActiveDevice(device); + } + + public BluetoothDevice getActiveDevice() { + if (mService == null) return null; + return mService.getActiveDevice(); + } + public boolean isPreferred(BluetoothDevice device) { if (mService == null) return false; return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF; diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java index 22674cb6de9b..cda4e454fe74 100755 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java @@ -239,4 +239,8 @@ public class LocalBluetoothAdapter { public BluetoothDevice getRemoteDevice(String address) { return mAdapter.getRemoteDevice(address); } + + public int getMaxConnectedAudioDevices() { + return mAdapter.getMaxConnectedAudioDevices(); + } } diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/EventLogWriter.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/EventLogWriter.java deleted file mode 100644 index 72273046ef29..000000000000 --- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/EventLogWriter.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * 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 com.android.settingslib.core.instrumentation; - -import android.content.Context; -import android.metrics.LogMaker; -import android.util.Log; -import android.util.Pair; - -import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.nano.MetricsProto; - -/** - * {@link LogWriter} that writes data to eventlog. - */ -public class EventLogWriter implements LogWriter { - - private final MetricsLogger mMetricsLogger = new MetricsLogger(); - - public void visible(Context context, int source, int category) { - final LogMaker logMaker = new LogMaker(category) - .setType(MetricsProto.MetricsEvent.TYPE_OPEN) - .addTaggedData(MetricsProto.MetricsEvent.FIELD_CONTEXT, source); - MetricsLogger.action(logMaker); - } - - public void hidden(Context context, int category) { - MetricsLogger.hidden(context, category); - } - - public void action(int category, int value, Pair<Integer, Object>... taggedData) { - if (taggedData == null || taggedData.length == 0) { - mMetricsLogger.action(category, value); - } else { - final LogMaker logMaker = new LogMaker(category) - .setType(MetricsProto.MetricsEvent.TYPE_ACTION) - .setSubtype(value); - for (Pair<Integer, Object> pair : taggedData) { - logMaker.addTaggedData(pair.first, pair.second); - } - mMetricsLogger.write(logMaker); - } - } - - public void action(int category, boolean value, Pair<Integer, Object>... taggedData) { - action(category, value ? 1 : 0, taggedData); - } - - public void action(Context context, int category, Pair<Integer, Object>... taggedData) { - action(context, category, "", taggedData); - } - - public void actionWithSource(Context context, int source, int category) { - final LogMaker logMaker = new LogMaker(category) - .setType(MetricsProto.MetricsEvent.TYPE_ACTION); - if (source != MetricsProto.MetricsEvent.VIEW_UNKNOWN) { - logMaker.addTaggedData(MetricsProto.MetricsEvent.FIELD_CONTEXT, source); - } - MetricsLogger.action(logMaker); - } - - /** @deprecated use {@link #action(int, int, Pair[])} */ - @Deprecated - public void action(Context context, int category, int value) { - MetricsLogger.action(context, category, value); - } - - /** @deprecated use {@link #action(int, boolean, Pair[])} */ - @Deprecated - public void action(Context context, int category, boolean value) { - MetricsLogger.action(context, category, value); - } - - public void action(Context context, int category, String pkg, - Pair<Integer, Object>... taggedData) { - if (taggedData == null || taggedData.length == 0) { - MetricsLogger.action(context, category, pkg); - } else { - final LogMaker logMaker = new LogMaker(category) - .setType(MetricsProto.MetricsEvent.TYPE_ACTION) - .setPackageName(pkg); - for (Pair<Integer, Object> pair : taggedData) { - logMaker.addTaggedData(pair.first, pair.second); - } - MetricsLogger.action(logMaker); - } - } - - public void count(Context context, String name, int value) { - MetricsLogger.count(context, name, value); - } - - public void histogram(Context context, String name, int bucket) { - MetricsLogger.histogram(context, name, bucket); - } -} diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java deleted file mode 100644 index 4b9f5727208d..000000000000 --- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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 com.android.settingslib.core.instrumentation; - -import android.content.Context; -import android.util.Pair; - -/** - * Generic log writer interface. - */ -public interface LogWriter { - - /** - * Logs a visibility event when view becomes visible. - */ - void visible(Context context, int source, int category); - - /** - * Logs a visibility event when view becomes hidden. - */ - void hidden(Context context, int category); - - /** - * Logs a user action. - */ - void action(int category, int value, Pair<Integer, Object>... taggedData); - - /** - * Logs a user action. - */ - void action(int category, boolean value, Pair<Integer, Object>... taggedData); - - /** - * Logs an user action. - */ - void action(Context context, int category, Pair<Integer, Object>... taggedData); - - /** - * Logs an user action. - */ - void actionWithSource(Context context, int source, int category); - - /** - * Logs an user action. - * @deprecated use {@link #action(int, int, Pair[])} - */ - @Deprecated - void action(Context context, int category, int value); - - /** - * Logs an user action. - * @deprecated use {@link #action(int, boolean, Pair[])} - */ - @Deprecated - void action(Context context, int category, boolean value); - - /** - * Logs an user action. - */ - void action(Context context, int category, String pkg, Pair<Integer, Object>... taggedData); - - /** - * Logs a count. - */ - void count(Context context, String name, int value); - - /** - * Logs a histogram event. - */ - void histogram(Context context, String name, int bucket); -} diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java deleted file mode 100644 index 1e5b378e931c..000000000000 --- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * 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 com.android.settingslib.core.instrumentation; - -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.text.TextUtils; -import android.util.Pair; - -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; - -import java.util.ArrayList; -import java.util.List; - -/** - * FeatureProvider for metrics. - */ -public class MetricsFeatureProvider { - private List<LogWriter> mLoggerWriters; - - public MetricsFeatureProvider() { - mLoggerWriters = new ArrayList<>(); - installLogWriters(); - } - - protected void installLogWriters() { - mLoggerWriters.add(new EventLogWriter()); - } - - public void visible(Context context, int source, int category) { - for (LogWriter writer : mLoggerWriters) { - writer.visible(context, source, category); - } - } - - public void hidden(Context context, int category) { - for (LogWriter writer : mLoggerWriters) { - writer.hidden(context, category); - } - } - - public void actionWithSource(Context context, int source, int category) { - for (LogWriter writer : mLoggerWriters) { - writer.actionWithSource(context, source, category); - } - } - - /** - * Logs a user action. Includes the elapsed time since the containing - * fragment has been visible. - */ - public void action(VisibilityLoggerMixin visibilityLogger, int category, int value) { - for (LogWriter writer : mLoggerWriters) { - writer.action(category, value, - sinceVisibleTaggedData(visibilityLogger.elapsedTimeSinceVisible())); - } - } - - /** - * Logs a user action. Includes the elapsed time since the containing - * fragment has been visible. - */ - public void action(VisibilityLoggerMixin visibilityLogger, int category, boolean value) { - for (LogWriter writer : mLoggerWriters) { - writer.action(category, value, - sinceVisibleTaggedData(visibilityLogger.elapsedTimeSinceVisible())); - } - } - - public void action(Context context, int category, Pair<Integer, Object>... taggedData) { - for (LogWriter writer : mLoggerWriters) { - writer.action(context, category, taggedData); - } - } - - /** @deprecated use {@link #action(VisibilityLoggerMixin, int, int)} */ - @Deprecated - public void action(Context context, int category, int value) { - for (LogWriter writer : mLoggerWriters) { - writer.action(context, category, value); - } - } - - /** @deprecated use {@link #action(VisibilityLoggerMixin, int, boolean)} */ - @Deprecated - public void action(Context context, int category, boolean value) { - for (LogWriter writer : mLoggerWriters) { - writer.action(context, category, value); - } - } - - public void action(Context context, int category, String pkg, - Pair<Integer, Object>... taggedData) { - for (LogWriter writer : mLoggerWriters) { - writer.action(context, category, pkg, taggedData); - } - } - - public void count(Context context, String name, int value) { - for (LogWriter writer : mLoggerWriters) { - writer.count(context, name, value); - } - } - - public void histogram(Context context, String name, int bucket) { - for (LogWriter writer : mLoggerWriters) { - writer.histogram(context, name, bucket); - } - } - - public int getMetricsCategory(Object object) { - if (object == null || !(object instanceof Instrumentable)) { - return MetricsEvent.VIEW_UNKNOWN; - } - return ((Instrumentable) object).getMetricsCategory(); - } - - public void logDashboardStartIntent(Context context, Intent intent, - int sourceMetricsCategory) { - if (intent == null) { - return; - } - final ComponentName cn = intent.getComponent(); - if (cn == null) { - final String action = intent.getAction(); - if (TextUtils.isEmpty(action)) { - // Not loggable - return; - } - action(context, MetricsEvent.ACTION_SETTINGS_TILE_CLICK, action, - Pair.create(MetricsEvent.FIELD_CONTEXT, sourceMetricsCategory)); - return; - } else if (TextUtils.equals(cn.getPackageName(), context.getPackageName())) { - // Going to a Setting internal page, skip click logging in favor of page's own - // visibility logging. - return; - } - action(context, MetricsEvent.ACTION_SETTINGS_TILE_CLICK, cn.flattenToString(), - Pair.create(MetricsEvent.FIELD_CONTEXT, sourceMetricsCategory)); - } - - private Pair<Integer, Object> sinceVisibleTaggedData(long timestamp) { - return Pair.create(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS, timestamp); - } -} diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SharedPreferencesLogger.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SharedPreferencesLogger.java deleted file mode 100644 index facce4e0bcbb..000000000000 --- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SharedPreferencesLogger.java +++ /dev/null @@ -1,259 +0,0 @@ -/* - * 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 com.android.settingslib.core.instrumentation; - -import android.annotation.Nullable; -import android.content.ComponentName; -import android.content.Context; -import android.content.SharedPreferences; -import android.content.pm.PackageManager; -import android.os.AsyncTask; -import android.support.annotation.VisibleForTesting; -import android.text.TextUtils; -import android.util.Log; -import android.util.Pair; - -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; - -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentSkipListSet; - -public class SharedPreferencesLogger implements SharedPreferences { - - private static final String LOG_TAG = "SharedPreferencesLogger"; - - private final String mTag; - private final Context mContext; - private final MetricsFeatureProvider mMetricsFeature; - private final Set<String> mPreferenceKeySet; - - public SharedPreferencesLogger(Context context, String tag, - MetricsFeatureProvider metricsFeature) { - mContext = context; - mTag = tag; - mMetricsFeature = metricsFeature; - mPreferenceKeySet = new ConcurrentSkipListSet<>(); - } - - @Override - public Map<String, ?> getAll() { - return null; - } - - @Override - public String getString(String key, @Nullable String defValue) { - return defValue; - } - - @Override - public Set<String> getStringSet(String key, @Nullable Set<String> defValues) { - return defValues; - } - - @Override - public int getInt(String key, int defValue) { - return defValue; - } - - @Override - public long getLong(String key, long defValue) { - return defValue; - } - - @Override - public float getFloat(String key, float defValue) { - return defValue; - } - - @Override - public boolean getBoolean(String key, boolean defValue) { - return defValue; - } - - @Override - public boolean contains(String key) { - return false; - } - - @Override - public Editor edit() { - return new EditorLogger(); - } - - @Override - public void registerOnSharedPreferenceChangeListener( - OnSharedPreferenceChangeListener listener) { - } - - @Override - public void unregisterOnSharedPreferenceChangeListener( - OnSharedPreferenceChangeListener listener) { - } - - private void logValue(String key, Object value) { - logValue(key, value, false /* forceLog */); - } - - private void logValue(String key, Object value, boolean forceLog) { - final String prefKey = buildPrefKey(mTag, key); - if (!forceLog && !mPreferenceKeySet.contains(prefKey)) { - // Pref key doesn't exist in set, this is initial display so we skip metrics but - // keeps track of this key. - mPreferenceKeySet.add(prefKey); - return; - } - // TODO: Remove count logging to save some resource. - mMetricsFeature.count(mContext, buildCountName(prefKey, value), 1); - - final Pair<Integer, Object> valueData; - if (value instanceof Long) { - final Long longVal = (Long) value; - final int intVal; - if (longVal > Integer.MAX_VALUE) { - intVal = Integer.MAX_VALUE; - } else if (longVal < Integer.MIN_VALUE) { - intVal = Integer.MIN_VALUE; - } else { - intVal = longVal.intValue(); - } - valueData = Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, - intVal); - } else if (value instanceof Integer) { - valueData = Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, - value); - } else if (value instanceof Boolean) { - valueData = Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, - (Boolean) value ? 1 : 0); - } else if (value instanceof Float) { - valueData = Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_FLOAT_VALUE, - value); - } else if (value instanceof String) { - Log.d(LOG_TAG, "Tried to log string preference " + prefKey + " = " + value); - valueData = null; - } else { - Log.w(LOG_TAG, "Tried to log unloggable object" + value); - valueData = null; - } - if (valueData != null) { - // Pref key exists in set, log it's change in metrics. - mMetricsFeature.action(mContext, MetricsEvent.ACTION_SETTINGS_PREFERENCE_CHANGE, - Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_NAME, prefKey), - valueData); - } - } - - @VisibleForTesting - void logPackageName(String key, String value) { - final String prefKey = mTag + "/" + key; - mMetricsFeature.action(mContext, MetricsEvent.ACTION_SETTINGS_PREFERENCE_CHANGE, value, - Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_NAME, prefKey)); - } - - private void safeLogValue(String key, String value) { - new AsyncPackageCheck().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, key, value); - } - - public static String buildCountName(String prefKey, Object value) { - return prefKey + "|" + value; - } - - public static String buildPrefKey(String tag, String key) { - return tag + "/" + key; - } - - private class AsyncPackageCheck extends AsyncTask<String, Void, Void> { - @Override - protected Void doInBackground(String... params) { - String key = params[0]; - String value = params[1]; - PackageManager pm = mContext.getPackageManager(); - try { - // Check if this might be a component. - ComponentName name = ComponentName.unflattenFromString(value); - if (value != null) { - value = name.getPackageName(); - } - } catch (Exception e) { - } - try { - pm.getPackageInfo(value, PackageManager.MATCH_ANY_USER); - logPackageName(key, value); - } catch (PackageManager.NameNotFoundException e) { - // Clearly not a package, and it's unlikely this preference is in prefSet, so - // lets force log it. - logValue(key, value, true /* forceLog */); - } - return null; - } - } - - public class EditorLogger implements Editor { - @Override - public Editor putString(String key, @Nullable String value) { - safeLogValue(key, value); - return this; - } - - @Override - public Editor putStringSet(String key, @Nullable Set<String> values) { - safeLogValue(key, TextUtils.join(",", values)); - return this; - } - - @Override - public Editor putInt(String key, int value) { - logValue(key, value); - return this; - } - - @Override - public Editor putLong(String key, long value) { - logValue(key, value); - return this; - } - - @Override - public Editor putFloat(String key, float value) { - logValue(key, value); - return this; - } - - @Override - public Editor putBoolean(String key, boolean value) { - logValue(key, value); - return this; - } - - @Override - public Editor remove(String key) { - return this; - } - - @Override - public Editor clear() { - return this; - } - - @Override - public boolean commit() { - return true; - } - - @Override - public void apply() { - } - } -} diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java deleted file mode 100644 index 79838962ef1e..000000000000 --- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * 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 com.android.settingslib.core.instrumentation; - -import android.app.Activity; -import android.content.Context; -import android.content.Intent; - -import android.os.SystemClock; -import com.android.internal.logging.nano.MetricsProto; -import com.android.settingslib.core.lifecycle.LifecycleObserver; -import com.android.settingslib.core.lifecycle.events.OnPause; -import com.android.settingslib.core.lifecycle.events.OnResume; - -import static com.android.settingslib.core.instrumentation.Instrumentable.METRICS_CATEGORY_UNKNOWN; - -/** - * Logs visibility change of a fragment. - */ -public class VisibilityLoggerMixin implements LifecycleObserver, OnResume, OnPause { - - private static final String TAG = "VisibilityLoggerMixin"; - - private final int mMetricsCategory; - - private MetricsFeatureProvider mMetricsFeature; - private int mSourceMetricsCategory = MetricsProto.MetricsEvent.VIEW_UNKNOWN; - private long mVisibleTimestamp; - - /** - * The metrics category constant for logging source when a setting fragment is opened. - */ - public static final String EXTRA_SOURCE_METRICS_CATEGORY = ":settings:source_metrics"; - - private VisibilityLoggerMixin() { - mMetricsCategory = METRICS_CATEGORY_UNKNOWN; - } - - public VisibilityLoggerMixin(int metricsCategory, MetricsFeatureProvider metricsFeature) { - mMetricsCategory = metricsCategory; - mMetricsFeature = metricsFeature; - } - - @Override - public void onResume() { - mVisibleTimestamp = SystemClock.elapsedRealtime(); - if (mMetricsFeature != null && mMetricsCategory != METRICS_CATEGORY_UNKNOWN) { - mMetricsFeature.visible(null /* context */, mSourceMetricsCategory, mMetricsCategory); - } - } - - @Override - public void onPause() { - mVisibleTimestamp = 0; - if (mMetricsFeature != null && mMetricsCategory != METRICS_CATEGORY_UNKNOWN) { - mMetricsFeature.hidden(null /* context */, mMetricsCategory); - } - } - - /** - * Sets source metrics category for this logger. Source is the caller that opened this UI. - */ - public void setSourceMetricsCategory(Activity activity) { - if (mSourceMetricsCategory != MetricsProto.MetricsEvent.VIEW_UNKNOWN || activity == null) { - return; - } - final Intent intent = activity.getIntent(); - if (intent == null) { - return; - } - mSourceMetricsCategory = intent.getIntExtra(EXTRA_SOURCE_METRICS_CATEGORY, - MetricsProto.MetricsEvent.VIEW_UNKNOWN); - } - - /** Returns elapsed time since onResume() */ - public long elapsedTimeSinceVisible() { - if (mVisibleTimestamp == 0) { - return 0; - } - return SystemClock.elapsedRealtime() - mVisibleTimestamp; - } -} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java deleted file mode 100644 index 8bea51d1696d..000000000000 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * 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 com.android.settingslib.core.instrumentation; - -import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.util.Pair; - -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.settingslib.TestConfig; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.robolectric.RuntimeEnvironment; -import org.robolectric.annotation.Config; -import org.robolectric.util.ReflectionHelpers; - -import java.util.ArrayList; -import java.util.List; - -@RunWith(SettingsLibRobolectricTestRunner.class) -@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) -public class MetricsFeatureProviderTest { - private static int CATEGORY = 10; - private static boolean SUBTYPE_BOOLEAN = true; - private static int SUBTYPE_INTEGER = 1; - private static long ELAPSED_TIME = 1000; - - @Mock private LogWriter mockLogWriter; - @Mock private VisibilityLoggerMixin mockVisibilityLogger; - - private Context mContext; - private MetricsFeatureProvider mProvider; - - @Captor - private ArgumentCaptor<Pair> mPairCaptor; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mContext = RuntimeEnvironment.application; - mProvider = new MetricsFeatureProvider(); - List<LogWriter> writers = new ArrayList<>(); - writers.add(mockLogWriter); - ReflectionHelpers.setField(mProvider, "mLoggerWriters", writers); - - when(mockVisibilityLogger.elapsedTimeSinceVisible()).thenReturn(ELAPSED_TIME); - } - - @Test - public void logDashboardStartIntent_intentEmpty_shouldNotLog() { - mProvider.logDashboardStartIntent(mContext, null /* intent */, - MetricsEvent.SETTINGS_GESTURES); - - verifyNoMoreInteractions(mockLogWriter); - } - - @Test - public void logDashboardStartIntent_intentHasNoComponent_shouldLog() { - final Intent intent = new Intent(Intent.ACTION_ASSIST); - - mProvider.logDashboardStartIntent(mContext, intent, MetricsEvent.SETTINGS_GESTURES); - - verify(mockLogWriter).action( - eq(mContext), - eq(MetricsEvent.ACTION_SETTINGS_TILE_CLICK), - anyString(), - eq(Pair.create(MetricsEvent.FIELD_CONTEXT, MetricsEvent.SETTINGS_GESTURES))); - } - - @Test - public void logDashboardStartIntent_intentIsExternal_shouldLog() { - final Intent intent = new Intent().setComponent(new ComponentName("pkg", "cls")); - - mProvider.logDashboardStartIntent(mContext, intent, MetricsEvent.SETTINGS_GESTURES); - - verify(mockLogWriter).action( - eq(mContext), - eq(MetricsEvent.ACTION_SETTINGS_TILE_CLICK), - anyString(), - eq(Pair.create(MetricsEvent.FIELD_CONTEXT, MetricsEvent.SETTINGS_GESTURES))); - } - - @Test - public void action_BooleanLogsElapsedTime() { - mProvider.action(mockVisibilityLogger, CATEGORY, SUBTYPE_BOOLEAN); - verify(mockLogWriter).action(eq(CATEGORY), eq(SUBTYPE_BOOLEAN), mPairCaptor.capture()); - - Pair value = mPairCaptor.getValue(); - assertThat(value.first instanceof Integer).isTrue(); - assertThat((int) value.first).isEqualTo(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS); - assertThat(value.second).isEqualTo(ELAPSED_TIME); - } - - @Test - public void action_IntegerLogsElapsedTime() { - mProvider.action(mockVisibilityLogger, CATEGORY, SUBTYPE_INTEGER); - verify(mockLogWriter).action(eq(CATEGORY), eq(SUBTYPE_INTEGER), mPairCaptor.capture()); - - Pair value = mPairCaptor.getValue(); - assertThat(value.first instanceof Integer).isTrue(); - assertThat((int) value.first).isEqualTo(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS); - assertThat(value.second).isEqualTo(ELAPSED_TIME); - } -} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java deleted file mode 100644 index d558a645aeb7..000000000000 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * 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 com.android.settingslib.core.instrumentation; - -import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_SETTINGS_PREFERENCE_CHANGE; -import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_FLOAT_VALUE; -import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE; -import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_NAME; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.argThat; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -import android.content.Context; -import android.content.SharedPreferences; -import android.util.Pair; - -import com.android.settingslib.TestConfig; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - -import com.google.common.truth.Platform; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Answers; -import org.mockito.ArgumentMatcher; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.robolectric.annotation.Config; - -@RunWith(SettingsLibRobolectricTestRunner.class) -@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) -public class SharedPreferenceLoggerTest { - - private static final String TEST_TAG = "tag"; - private static final String TEST_KEY = "key"; - - @Mock(answer = Answers.RETURNS_DEEP_STUBS) - private Context mContext; - - private ArgumentMatcher<Pair<Integer, Object>> mNamePairMatcher; - @Mock - private MetricsFeatureProvider mMetricsFeature; - private SharedPreferencesLogger mSharedPrefLogger; - - @Before - public void init() { - MockitoAnnotations.initMocks(this); - mSharedPrefLogger = new SharedPreferencesLogger(mContext, TEST_TAG, mMetricsFeature); - mNamePairMatcher = pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_NAME, String.class); - } - - @Test - public void putInt_shouldNotLogInitialPut() { - final SharedPreferences.Editor editor = mSharedPrefLogger.edit(); - editor.putInt(TEST_KEY, 1); - editor.putInt(TEST_KEY, 1); - editor.putInt(TEST_KEY, 1); - editor.putInt(TEST_KEY, 2); - editor.putInt(TEST_KEY, 2); - editor.putInt(TEST_KEY, 2); - editor.putInt(TEST_KEY, 2); - - verify(mMetricsFeature, times(6)).action(any(Context.class), anyInt(), - argThat(mNamePairMatcher), - argThat(pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, Integer.class))); - } - - @Test - public void putBoolean_shouldNotLogInitialPut() { - final SharedPreferences.Editor editor = mSharedPrefLogger.edit(); - editor.putBoolean(TEST_KEY, true); - editor.putBoolean(TEST_KEY, true); - editor.putBoolean(TEST_KEY, false); - editor.putBoolean(TEST_KEY, false); - editor.putBoolean(TEST_KEY, false); - - - verify(mMetricsFeature).action(any(Context.class), anyInt(), - argThat(mNamePairMatcher), - argThat(pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, true))); - verify(mMetricsFeature, times(3)).action(any(Context.class), anyInt(), - argThat(mNamePairMatcher), - argThat(pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, false))); - } - - @Test - public void putLong_shouldNotLogInitialPut() { - final SharedPreferences.Editor editor = mSharedPrefLogger.edit(); - editor.putLong(TEST_KEY, 1); - editor.putLong(TEST_KEY, 1); - editor.putLong(TEST_KEY, 1); - editor.putLong(TEST_KEY, 1); - editor.putLong(TEST_KEY, 2); - - verify(mMetricsFeature, times(4)).action(any(Context.class), anyInt(), - argThat(mNamePairMatcher), - argThat(pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, Integer.class))); - } - - @Test - public void putLong_biggerThanIntMax_shouldLogIntMax() { - final SharedPreferences.Editor editor = mSharedPrefLogger.edit(); - final long veryBigNumber = 500L + Integer.MAX_VALUE; - editor.putLong(TEST_KEY, 1); - editor.putLong(TEST_KEY, veryBigNumber); - - verify(mMetricsFeature).action(any(Context.class), anyInt(), - argThat(mNamePairMatcher), - argThat(pairMatches( - FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, Integer.MAX_VALUE))); - } - - @Test - public void putLong_smallerThanIntMin_shouldLogIntMin() { - final SharedPreferences.Editor editor = mSharedPrefLogger.edit(); - final long veryNegativeNumber = -500L + Integer.MIN_VALUE; - editor.putLong(TEST_KEY, 1); - editor.putLong(TEST_KEY, veryNegativeNumber); - - verify(mMetricsFeature).action(any(Context.class), anyInt(), - argThat(mNamePairMatcher), - argThat(pairMatches( - FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, Integer.MIN_VALUE))); - } - - @Test - public void putFloat_shouldNotLogInitialPut() { - final SharedPreferences.Editor editor = mSharedPrefLogger.edit(); - editor.putFloat(TEST_KEY, 1); - editor.putFloat(TEST_KEY, 1); - editor.putFloat(TEST_KEY, 1); - editor.putFloat(TEST_KEY, 1); - editor.putFloat(TEST_KEY, 2); - - verify(mMetricsFeature, times(4)).action(any(Context.class), anyInt(), - argThat(mNamePairMatcher), - argThat(pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_FLOAT_VALUE, Float.class))); - } - - @Test - public void logPackage_shouldUseLogPackageApi() { - mSharedPrefLogger.logPackageName("key", "com.android.settings"); - verify(mMetricsFeature).action(any(Context.class), - eq(ACTION_SETTINGS_PREFERENCE_CHANGE), - eq("com.android.settings"), - any(Pair.class)); - } - - private ArgumentMatcher<Pair<Integer, Object>> pairMatches(int tag, Class clazz) { - return pair -> pair.first == tag && Platform.isInstanceOfType(pair.second, clazz); - } - - private ArgumentMatcher<Pair<Integer, Object>> pairMatches(int tag, boolean bool) { - return pair -> pair.first == tag - && Platform.isInstanceOfType(pair.second, Integer.class) - && pair.second.equals((bool ? 1 : 0)); - } - - private ArgumentMatcher<Pair<Integer, Object>> pairMatches(int tag, int val) { - return pair -> pair.first == tag - && Platform.isInstanceOfType(pair.second, Integer.class) - && pair.second.equals(val); - } -} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java deleted file mode 100644 index a2648861d1d8..000000000000 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * 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 com.android.settingslib.core.instrumentation; - -import static com.android.settingslib.core.instrumentation.Instrumentable.METRICS_CATEGORY_UNKNOWN; - -import static org.mockito.ArgumentMatchers.nullable; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.eq; -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.app.Activity; -import android.content.Context; -import android.content.Intent; - -import com.android.internal.logging.nano.MetricsProto; -import com.android.settingslib.SettingsLibRobolectricTestRunner; -import com.android.settingslib.TestConfig; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.robolectric.annotation.Config; - - -@RunWith(SettingsLibRobolectricTestRunner.class) -@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) -public class VisibilityLoggerMixinTest { - - @Mock - private MetricsFeatureProvider mMetricsFeature; - - private VisibilityLoggerMixin mMixin; - - @Before - public void init() { - MockitoAnnotations.initMocks(this); - mMixin = new VisibilityLoggerMixin(TestInstrumentable.TEST_METRIC, mMetricsFeature); - } - - @Test - public void shouldLogVisibleOnResume() { - mMixin.onResume(); - - verify(mMetricsFeature, times(1)) - .visible(nullable(Context.class), eq(MetricsProto.MetricsEvent.VIEW_UNKNOWN), - eq(TestInstrumentable.TEST_METRIC)); - } - - @Test - public void shouldLogVisibleWithSource() { - final Intent sourceIntent = new Intent() - .putExtra(VisibilityLoggerMixin.EXTRA_SOURCE_METRICS_CATEGORY, - MetricsProto.MetricsEvent.SETTINGS_GESTURES); - final Activity activity = mock(Activity.class); - when(activity.getIntent()).thenReturn(sourceIntent); - mMixin.setSourceMetricsCategory(activity); - mMixin.onResume(); - - verify(mMetricsFeature, times(1)) - .visible(nullable(Context.class), eq(MetricsProto.MetricsEvent.SETTINGS_GESTURES), - eq(TestInstrumentable.TEST_METRIC)); - } - - @Test - public void shouldLogHideOnPause() { - mMixin.onPause(); - - verify(mMetricsFeature, times(1)) - .hidden(nullable(Context.class), eq(TestInstrumentable.TEST_METRIC)); - } - - @Test - public void shouldNotLogIfMetricsFeatureIsNull() { - mMixin = new VisibilityLoggerMixin(TestInstrumentable.TEST_METRIC, null); - mMixin.onResume(); - mMixin.onPause(); - - verify(mMetricsFeature, never()) - .hidden(nullable(Context.class), anyInt()); - } - - @Test - public void shouldNotLogIfMetricsCategoryIsUnknown() { - mMixin = new VisibilityLoggerMixin(METRICS_CATEGORY_UNKNOWN, mMetricsFeature); - - mMixin.onResume(); - mMixin.onPause(); - - verify(mMetricsFeature, never()) - .hidden(nullable(Context.class), anyInt()); - } - - private final class TestInstrumentable implements Instrumentable { - - public static final int TEST_METRIC = 12345; - - @Override - public int getMetricsCategory() { - return TEST_METRIC; - } - } -} diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 9ee205f9cde7..2a697b8d5034 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 @@ public class SettingsProvider extends ContentProvider { // 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 @@ public class SettingsProvider extends ContentProvider { } 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 @@ public class SettingsProvider extends ContentProvider { 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 0f43db052622..64b2ae6e23d3 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 aa2cdbb7730f..80ac82576d13 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 c19fa0803245..24afebde046b 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 000000000000..d90129260fcc --- /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 000000000000..a1ad52841eb8 --- /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 59c0957d98cb..e52aa14abfa9 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 3d0ab3599bf1..b96f44750a0c 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 ed7df4b7f58a..c3ddbbe83dd7 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 9ab8ac6346d3..b3b6a0c43a98 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 1fd239b15b30..0b9a7b226105 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 3d09b743ca88..9f6a946dea2e 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 5541f3de8e7c..5bcb7fd2685e 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 000000000000..e0f0ed994166 --- /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 e8b418cd902b..dacc3f96220b 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 739a2554e481..2c69501106a6 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" @@ -40,6 +41,17 @@ systemui:showDark="false" /> + <com.android.systemui.statusbar.policy.DateView + android:id="@+id/date" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:padding="4dp" + android:singleLine="true" + android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date" + android:textSize="@dimen/qs_time_collapsed_size" + android:gravity="center_vertical" + systemui:datePattern="@string/abbrev_wday_month_day_no_year_alarm" /> + <android.widget.Space android:id="@+id/space" android:layout_width="0dp" diff --git a/packages/SystemUI/res/layout/rotate_suggestion.xml b/packages/SystemUI/res/layout/rotate_suggestion.xml index 7762950b5a23..5074682f3808 100644 --- a/packages/SystemUI/res/layout/rotate_suggestion.xml +++ b/packages/SystemUI/res/layout/rotate_suggestion.xml @@ -14,19 +14,13 @@ limitations under the License. --> -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:systemui="http://schemas.android.com/apk/res-auto" - android:layout_width="@dimen/navigation_side_padding" +<com.android.systemui.statusbar.policy.KeyButtonView + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/rotate_suggestion" + android:layout_width="@dimen/navigation_extra_key_width" android:layout_height="match_parent" - android:layout_weight="0" - > - <com.android.systemui.statusbar.policy.KeyButtonView - android:id="@+id/rotate_suggestion" - android:layout_width="@dimen/navigation_extra_key_width" - android:layout_height="match_parent" - android:layout_marginEnd="2dp" - android:visibility="invisible" - android:scaleType="centerInside" - /> - <!-- TODO android:contentDescription --> -</FrameLayout> + android:layout_marginEnd="2dp" + android:visibility="invisible" + android:scaleType="centerInside" + android:contentDescription="@string/accessibility_rotate_button" +/> 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 000000000000..cdb2a91a73d2 --- /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 17b38cbce824..8c0b9bab35a0 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" /> - <!-- 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" + </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:layout_weight="1" - android:orientation="horizontal" /> + android:gravity="center_horizontal|center_vertical" + /> <com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area" - android:layout_width="wrap_content" + android:layout_width="0dp" android:layout_height="match_parent" + android:layout_weight="1" 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 c5e5ee1f4af0..bef0830d5802 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 bfa92ad89a1e..1fafb2fc72d6 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 0134086ae42e..4e17c71b0c93 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 fed97c57ce09..edda613f2fbc 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 6437903bd12a..ab83bcf23ba5 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -228,6 +228,8 @@ <string name="accessibility_menu">Menu</string> <!-- Content description of the accessibility button in the navigation bar (not shown on the screen). [CHAR LIMIT=NONE] --> <string name="accessibility_accessibility_button">Accessibility</string> + <!-- Content description of the rotate button in the navigation bar (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_rotate_button">Rotate screen</string> <!-- Content description of the recents button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> <string name="accessibility_recent">Overview</string> <!-- Content description of the search button for accessibility. [CHAR LIMIT=NONE] --> @@ -1702,12 +1704,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 +2069,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/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 4837fefd04b2..bcce6d1d2ee8 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -173,7 +173,7 @@ <style name="TextAppearance.StatusBar.Expanded.Date"> <item name="android:textSize">@dimen/qs_time_expanded_size</item> <item name="android:textStyle">normal</item> - <item name="android:textColor">?android:attr/textColorPrimary</item> + <item name="android:textColor">#ffffffff</item> <item name="android:fontFamily">sans-serif</item> </style> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java index 13f30b2c27b9..7d159b745254 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java @@ -20,6 +20,7 @@ import android.animation.Animator; import android.animation.AnimatorSet; import android.animation.RectEvaluator; import android.annotation.FloatRange; +import android.annotation.Nullable; import android.app.Activity; import android.content.Context; import android.content.res.Configuration; @@ -39,6 +40,7 @@ import android.view.Surface; import android.view.View; import android.view.ViewGroup; import android.view.ViewParent; +import android.view.ViewRootImpl; import android.view.ViewStub; import java.util.ArrayList; @@ -294,17 +296,25 @@ public class Utilities { } /** - * @return The next frame name for the specified surface. + * @return The next frame name for the specified surface or -1 if the surface is no longer + * valid. */ public static long getNextFrameNumber(Surface s) { - return s.getNextFrameNumber(); + return s != null && s.isValid() + ? s.getNextFrameNumber() + : -1; + } /** * @return The surface for the specified view. */ - public static Surface getSurface(View v) { - return v.getViewRootImpl().mSurface; + public static @Nullable Surface getSurface(View v) { + ViewRootImpl viewRoot = v.getViewRootImpl(); + if (viewRoot == null) { + return null; + } + return viewRoot.mSurface; } /** diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index e58ad0589c8a..19afcf5e33a8 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -991,6 +991,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { 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 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { 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 2fe66a14a41d..8666b0c873e7 100644 --- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java @@ -224,7 +224,6 @@ public class BatteryMeterView extends LinearLayout implements 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 000000000000..302face14c1a --- /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 931a99415615..69e347c9476d 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 class DataCollector implements SensorEventListener { 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 e4b405f580d4..ed659e2d16d5 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 @@ public class FalsingManager implements SensorEventListener { 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/keyboard/KeyboardUI.java b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java index 4b775a5a61e4..b8411e2963bd 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java +++ b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java @@ -608,6 +608,9 @@ public class KeyboardUI extends SystemUI implements InputManager.OnTabletModeCha public void onScanningStateChanged(boolean started) { } @Override public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) { } + @Override + public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, + int bluetoothProfile) { } } private final class BluetoothErrorListener implements Utils.ErrorListener { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java index 7f0acc254a7c..7320b861dbd5 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 @@ public class QSContainerImpl extends FrameLayout { 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 @@ public class QSContainerImpl extends FrameLayout { 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 @@ public class QSContainerImpl extends FrameLayout { 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 @@ public class QSContainerImpl extends FrameLayout { 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 927a49cb60f3..92475da697bd 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 @@ import com.android.systemui.tuner.TunerService; 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 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, 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 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, 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 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, if (mSettingsAlpha != null) { mSettingsAlpha.setPosition(headerExpansionFraction); } - - mExpandIndicator.setExpanded(headerExpansionFraction > EXPAND_INDICATOR_THRESHOLD); } @Override @@ -237,8 +235,6 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, 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 398592ad1699..a97b35cba048 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -18,10 +18,12 @@ import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS; import static android.app.StatusBarManager.DISABLE_NONE; import android.content.Context; +import android.content.Intent; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Color; import android.graphics.Rect; +import android.provider.AlarmClock; import android.support.annotation.VisibleForTesting; import android.util.AttributeSet; import android.view.View; @@ -38,9 +40,11 @@ import com.android.systemui.plugins.ActivityStarter; 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 { +public class QuickStatusBarHeader extends RelativeLayout + implements CommandQueue.Callbacks, View.OnClickListener { private ActivityStarter mActivityStarter; @@ -53,6 +57,8 @@ public class QuickStatusBarHeader extends RelativeLayout implements CommandQueue protected QuickQSPanel mHeaderQsPanel; protected QSTileHost mHost; + private View mDate; + public QuickStatusBarHeader(Context context, AttributeSet attrs) { super(context, attrs); } @@ -63,21 +69,21 @@ public class QuickStatusBarHeader extends RelativeLayout implements CommandQueue Resources res = getResources(); mHeaderQsPanel = findViewById(R.id.quick_qs_panel); + mDate = findViewById(R.id.date); + mDate.setOnClickListener(this); // RenderThread is doing more harm than good when touching the header (to expand quick // settings), so disable it for this view 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); @@ -146,6 +152,14 @@ public class QuickStatusBarHeader extends RelativeLayout implements CommandQueue super.onDetachedFromWindow(); } + @Override + public void onClick(View v) { + if (v == mDate) { + Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(new Intent( + AlarmClock.ACTION_SHOW_ALARMS), 0); + } + } + public void setListening(boolean listening) { if (listening == mListening) { return; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java index 9e265e2295c3..4ceace34befe 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java @@ -33,7 +33,6 @@ import android.provider.Settings.Global; import android.service.notification.ZenModeConfig; import android.service.notification.ZenModeConfig.ZenRule; import android.service.quicksettings.Tile; -import android.util.Log; import android.util.Slog; import android.view.LayoutInflater; import android.view.View; @@ -55,7 +54,6 @@ import com.android.systemui.plugins.qs.QSTile.BooleanState; import com.android.systemui.qs.QSHost; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.statusbar.policy.ZenModeController; -import com.android.systemui.statusbar.policy.ZenModeController.Callback; import com.android.systemui.volume.ZenModePanel; /** Quick settings tile: Do not disturb **/ @@ -134,8 +132,7 @@ public class DndTile extends QSTileImpl<BooleanState> { if (mState.value) { mController.setZen(ZEN_MODE_OFF, null, TAG); } else { - int zen = Prefs.getInt(mContext, Prefs.Key.DND_FAVORITE_ZEN, Global.ZEN_MODE_ALARMS); - mController.setZen(zen, null, TAG); + mController.setZen(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, TAG); } } @@ -159,9 +156,7 @@ public class DndTile extends QSTileImpl<BooleanState> { showDetail(true); } }); - int zen = Prefs.getInt(mContext, Prefs.Key.DND_FAVORITE_ZEN, - Global.ZEN_MODE_ALARMS); - mController.setZen(zen, null, TAG); + mController.setZen(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, TAG); } else { showDetail(true); } @@ -313,9 +308,7 @@ public class DndTile extends QSTileImpl<BooleanState> { mController.setZen(ZEN_MODE_OFF, null, TAG); mAuto = false; } else { - int zen = Prefs.getInt(mContext, Prefs.Key.DND_FAVORITE_ZEN, - ZEN_MODE_ALARMS); - mController.setZen(zen, null, TAG); + mController.setZen(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, TAG); } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java index 6db46b5917b6..675aa8f73a09 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 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { 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 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { 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 @@ class GlobalScreenshot { } /** - * 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 @@ class GlobalScreenshot { } /** - * 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 657b9534f3a8..c6abcf272722 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 @@ public class CommandQueue extends IStatusBar.Stub { 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 @@ public class CommandQueue extends IStatusBar.Stub { } @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 @@ public class CommandQueue extends IStatusBar.Stub { 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 43047ed6a5c5..0a12be4eac7f 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.statusbar.policy.UserInfoController; 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 @@ public class KeyguardIndicationController { 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 @@ public class KeyguardIndicationController { 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 5b06874219e6..7360486ac7e9 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 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. && 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 c4024a57ceed..52776d7e8430 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.content.res.TypedArray; 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 class NotificationGuts extends FrameLayout { } } + 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 @@ public class NotificationGuts extends FrameLayout { } } + 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 class NotificationGuts extends FrameLayout { } } - 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 441c18431894..9d8892da3c74 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.ArraySet; 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 @@ public class NotificationGutsManager implements Dumpable { 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 @@ public class NotificationGutsManager implements Dumpable { } /** - * 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 @@ public class NotificationGutsManager implements Dumpable { 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/ButtonDispatcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java index 9b123cbea8d7..7284ee8b39bc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java @@ -34,6 +34,7 @@ public class ButtonDispatcher { private View.OnClickListener mClickListener; private View.OnTouchListener mTouchListener; private View.OnLongClickListener mLongClickListener; + private View.OnHoverListener mOnHoverListener; private Boolean mLongClickable; private Integer mAlpha; private Float mDarkIntensity; @@ -56,6 +57,7 @@ public class ButtonDispatcher { view.setOnClickListener(mClickListener); view.setOnTouchListener(mTouchListener); view.setOnLongClickListener(mLongClickListener); + view.setOnHoverListener(mOnHoverListener); if (mLongClickable != null) { view.setLongClickable(mLongClickable); } @@ -174,6 +176,14 @@ public class ButtonDispatcher { } } + public void setOnHoverListener(View.OnHoverListener hoverListener) { + mOnHoverListener = hoverListener; + final int N = mViews.size(); + for (int i = 0; i < N; i++) { + mViews.get(i).setOnHoverListener(mOnHoverListener); + } + } + public void setClickable(boolean clickable) { abortCurrentGesture(); final int N = mViews.size(); 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 70ec45e4cdb4..368b36bfd8e1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java @@ -26,7 +26,6 @@ import static com.android.systemui.statusbar.phone.StatusBar.dumpBarTransitions; import android.accessibilityservice.AccessibilityServiceInfo; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.annotation.Nullable; import android.app.ActivityManager; @@ -111,7 +110,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks { /** Allow some time inbetween the long press for back and recents. */ private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200; - private static final int ROTATE_SUGGESTION_TIMEOUT_MS = 4000; + private static final int BUTTON_FADE_IN_OUT_DURATION_MS = 100; protected NavigationBarView mNavigationBarView = null; protected AssistManager mAssistManager; @@ -120,6 +119,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks { private int mNavigationIconHints = 0; private int mNavigationBarMode; + private boolean mAccessibilityFeedbackEnabled; private AccessibilityManager mAccessibilityManager; private MagnificationContentObserver mMagnificationObserver; private ContentResolver mContentResolver; @@ -143,6 +143,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks { public boolean mHomeBlockedThisTouch; private int mLastRotationSuggestion; + private boolean mHoveringRotationSuggestion; private RotationLockController mRotationLockController; private TaskStackListenerImpl mTaskStackListener; @@ -335,43 +336,77 @@ public class NavigationBarFragment extends Fragment implements Callbacks { } @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 - Handler h = getView().getHandler(); + 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; + } + if (rotation == mWindowManager.getDefaultDisplay().getRotation()) { // Use this as a signal to remove any current suggestions - h.removeCallbacks(mRemoveRotationProposal); + getView().getHandler().removeCallbacks(mRemoveRotationProposal); setRotateSuggestionButtonState(false); } else { mLastRotationSuggestion = rotation; // Remember rotation for click setRotateSuggestionButtonState(true); - h.removeCallbacks(mRemoveRotationProposal); // Stop any pending removal - h.postDelayed(mRemoveRotationProposal, - ROTATE_SUGGESTION_TIMEOUT_MS); // Schedule timeout + rescheduleRotationTimeout(false); } } + private void rescheduleRotationTimeout(final boolean reasonHover) { + // May be called due to a new rotation proposal or a change in hover state + if (reasonHover) { + // Don't reschedule if a hide animator is running + if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) { + return; + } + // Don't reschedule if not visible + if (mNavigationBarView.getRotateSuggestionButton().getVisibility() != View.VISIBLE) { + return; + } + } + + Handler h = getView().getHandler(); + h.removeCallbacks(mRemoveRotationProposal); // Stop any pending removal + h.postDelayed(mRemoveRotationProposal, + computeRotationProposalTimeout()); // Schedule timeout + } + + private int computeRotationProposalTimeout() { + if (mAccessibilityFeedbackEnabled) return 20000; + if (mHoveringRotationSuggestion) return 16000; + return 6000; + } + public void setRotateSuggestionButtonState(final boolean visible) { setRotateSuggestionButtonState(visible, false); } public void setRotateSuggestionButtonState(final boolean visible, final boolean skipAnim) { ButtonDispatcher rotBtn = mNavigationBarView.getRotateSuggestionButton(); - boolean currentlyVisible = rotBtn.getVisibility() == View.VISIBLE; + final boolean currentlyVisible = rotBtn.getVisibility() == View.VISIBLE; // Rerun a show animation to indicate change but don't rerun a hide animation if (!visible && !currentlyVisible) return; - View currentView = mNavigationBarView.getRotateSuggestionButton().getCurrentView(); + View currentView = rotBtn.getCurrentView(); if (currentView == null) return; - KeyButtonDrawable kbd = mNavigationBarView.getRotateSuggestionButton().getImageDrawable(); + KeyButtonDrawable kbd = rotBtn.getImageDrawable(); if (kbd == null) return; - AnimatedVectorDrawable animIcon = (AnimatedVectorDrawable) kbd.getDrawable(0); + AnimatedVectorDrawable animIcon = null; + if (kbd.getDrawable(0) instanceof AnimatedVectorDrawable) { + animIcon = (AnimatedVectorDrawable) kbd.getDrawable(0); + } + if (visible) { // Appear and change rotBtn.setVisibility(View.VISIBLE); + mNavigationBarView.notifyAccessibilitySubtreeChanged(); if (skipAnim) { currentView.setAlpha(1f); @@ -384,18 +419,22 @@ public class NavigationBarFragment extends Fragment implements Callbacks { ObjectAnimator appearFade = ObjectAnimator.ofFloat(currentView, "alpha", 0f, 1f); - appearFade.setDuration(100); + appearFade.setDuration(BUTTON_FADE_IN_OUT_DURATION_MS); appearFade.setInterpolator(Interpolators.LINEAR); mRotateShowAnimator = appearFade; appearFade.start(); - // Run the rotate icon's animation - animIcon.reset(); - animIcon.start(); + // Run the rotate icon's animation if it has one + if (animIcon != null) { + animIcon.reset(); + animIcon.start(); + } + } else { // Hide if (skipAnim) { rotBtn.setVisibility(View.INVISIBLE); + mNavigationBarView.notifyAccessibilitySubtreeChanged(); return; } @@ -406,12 +445,13 @@ public class NavigationBarFragment extends Fragment implements Callbacks { ObjectAnimator fadeOut = ObjectAnimator.ofFloat(currentView, "alpha", 0f); - fadeOut.setDuration(100); + fadeOut.setDuration(BUTTON_FADE_IN_OUT_DURATION_MS); fadeOut.setInterpolator(Interpolators.LINEAR); fadeOut.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { rotBtn.setVisibility(View.INVISIBLE); + mNavigationBarView.notifyAccessibilitySubtreeChanged(); } }); @@ -525,6 +565,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks { ButtonDispatcher rotateSuggestionButton = mNavigationBarView.getRotateSuggestionButton(); rotateSuggestionButton.setOnClickListener(this::onRotateSuggestionClick); + rotateSuggestionButton.setOnHoverListener(this::onRotateSuggestionHover); } private boolean onHomeTouch(View v, MotionEvent event) { @@ -700,6 +741,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks { } catch (Settings.SettingNotFoundException e) { } + boolean feedbackEnabled = false; // AccessibilityManagerService resolves services for the current user since the local // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission final List<AccessibilityServiceInfo> services = @@ -710,8 +752,15 @@ public class NavigationBarFragment extends Fragment implements Callbacks { if ((info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0) { requestingServices++; } + + if (info.feedbackType != 0 && info.feedbackType != + AccessibilityServiceInfo.FEEDBACK_GENERIC) { + feedbackEnabled = true; + } } + mAccessibilityFeedbackEnabled = feedbackEnabled; + final boolean showAccessibilityButton = requestingServices >= 1; final boolean targetSelection = requestingServices >= 2; mNavigationBarView.setAccessibilityButtonState(showAccessibilityButton, targetSelection); @@ -721,6 +770,14 @@ public class NavigationBarFragment extends Fragment implements Callbacks { mRotationLockController.setRotationLockedAtAngle(true, mLastRotationSuggestion); } + private boolean onRotateSuggestionHover(View v, MotionEvent event) { + final int action = event.getActionMasked(); + mHoveringRotationSuggestion = (action == MotionEvent.ACTION_HOVER_ENTER) + || (action == MotionEvent.ACTION_HOVER_MOVE); + rescheduleRotationTimeout(true); + return false; // Must return false so a11y hover events are dispatched correctly. + } + // ----- Methods that StatusBar talks to (should be minimized) ----- public void setLightBarController(LightBarController lightBarController) { @@ -768,18 +825,18 @@ public class NavigationBarFragment extends Fragment implements Callbacks { private final Stub mRotationWatcher = new Stub() { @Override - public void onRotationChanged(int rotation) throws RemoteException { - // If the screen rotation changes while locked, update lock rotation to flow with - // new screen rotation and hide any showing suggestions. - if (mRotationLockController.isRotationLocked()) { - mRotationLockController.setRotationLockedAtAngle(true, rotation); - setRotateSuggestionButtonState(false, true); - } - + public void onRotationChanged(final int rotation) throws RemoteException { // We need this to be scheduled as early as possible to beat the redrawing of // window in response to the orientation change. Handler h = getView().getHandler(); Message msg = Message.obtain(h, () -> { + // If the screen rotation changes while locked, update lock rotation to flow with + // new screen rotation and hide any showing suggestions. + if (mRotationLockController.isRotationLocked()) { + mRotationLockController.setRotationLockedAtAngle(true, rotation); + setRotateSuggestionButtonState(false, true); + } + if (mNavigationBarView != null && mNavigationBarView.needsReorient(rotation)) { repositionNavigationBar(); 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 970d1de251d5..b18121255353 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,11 @@ public class PhoneStatusBarView extends PanelBar { } }; private DarkReceiver mBattery; + private int mLastOrientation; + @Nullable + private View mCutoutSpace; + @Nullable + private DisplayCutout mDisplayCutout; public PhoneStatusBarView(Context context, AttributeSet attrs) { super(context, attrs); @@ -76,6 +89,7 @@ public class PhoneStatusBarView extends PanelBar { public void onFinishInflate() { mBarTransitions.init(); mBattery = findViewById(R.id.battery); + mCutoutSpace = findViewById(R.id.cutout_space_view); } @Override @@ -83,12 +97,51 @@ public class PhoneStatusBarView extends PanelBar { 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 +267,80 @@ public class PhoneStatusBarView extends PanelBar { 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() { + // Not all layouts have a cutout (e.g., Car) + if (mCutoutSpace == null) { + return; + } + + 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 14329b564800..3b394ddd63cf 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 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, @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/SettingsButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java index 6220fcbdf13b..1130b6de2544 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java @@ -32,6 +32,8 @@ import com.android.systemui.Interpolators; public class SettingsButton extends AlphaOptimizedImageButton { + private static final boolean TUNER_ENABLE_AVAILABLE = false; + private static final long LONG_PRESS_LENGTH = 1000; private static final long ACCEL_LENGTH = 750; private static final long FULL_SPEED_LENGTH = 375; @@ -59,7 +61,7 @@ public class SettingsButton extends AlphaOptimizedImageButton { public boolean onTouchEvent(MotionEvent event) { switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: - postDelayed(mLongPressCallback, LONG_PRESS_LENGTH); + if (TUNER_ENABLE_AVAILABLE) postDelayed(mLongPressCallback, LONG_PRESS_LENGTH); break; case MotionEvent.ACTION_UP: if (mUpToSpeed) { 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 5e08ec3575fc..c30fb22630da 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 @@ public class StatusBar extends SystemUI implements DemoMode, 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 @@ public class StatusBar extends SystemUI implements DemoMode, mFingerprintUnlockController.dump(pw); } + if (mKeyguardIndicationController != null) { + mKeyguardIndicationController.dump(fd, pw, args); + } + if (mScrimController != null) { mScrimController.dump(fd, pw, args); } @@ -4506,6 +4510,7 @@ public class StatusBar extends SystemUI implements DemoMode, ((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 bcda60ebc62c..07610ceff7b0 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 @@ public interface StatusBarIconController { } /** - * 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 000000000000..1897171b5e54 --- /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/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java index 3b15c2b8253f..fcf084b29327 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java @@ -276,6 +276,9 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED); } + @Override + public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {} + private ActuallyCachedState getCachedState(CachedBluetoothDevice device) { ActuallyCachedState state = mCachedState.get(device); if (state == null) { 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 55050995d5f6..4ca33cd3f601 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 @@ public class NotificationChildrenContainer extends ViewGroup { } } 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/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java index 8e584bc362eb..5a4478f072e0 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java @@ -58,7 +58,7 @@ public class TunerServiceImpl extends TunerService { private static final String TUNER_VERSION = "sysui_tuner_version"; - private static final int CURRENT_TUNER_VERSION = 1; + private static final int CURRENT_TUNER_VERSION = 2; private final Observer mObserver = new Observer(); // Map of Uris we listen on to their settings keys. @@ -116,6 +116,9 @@ public class TunerServiceImpl extends TunerService { TextUtils.join(",", iconBlacklist), mCurrentUser); } } + if (oldVersion < 2) { + setTunerEnabled(mContext, false); + } setValue(TUNER_VERSION, newVersion); } 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 000000000000..3423452c2a27 --- /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 f8843a997d59..e0af9baf0bb0 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.os.SystemClock; 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.IOException; 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 @@ public class OutputChooserDialog extends SystemUIDialog 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 @@ public class OutputChooserDialog extends SystemUIDialog 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 @@ public class OutputChooserDialog extends SystemUIDialog 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 class OutputChooserDialog extends SystemUIDialog 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 @@ public class OutputChooserDialog extends SystemUIDialog 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 @@ public class OutputChooserDialog extends SystemUIDialog 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 @@ public class OutputChooserDialog extends SystemUIDialog 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 @@ public class OutputChooserDialog extends SystemUIDialog } 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 @@ public class OutputChooserDialog extends SystemUIDialog 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 @@ public class OutputChooserDialog extends SystemUIDialog 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 class OutputChooserDialog extends SystemUIDialog 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 22ced60006b0..d4c6f897846e 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.LayoutInflater; 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 @@ import com.android.systemui.qs.AutoSizingList; /** * 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 @@ public class OutputChooserLayout extends FrameLayout { private AutoSizingList mItemList; private View mEmpty; private TextView mEmptyText; + private TextView mTitle; private Item[] mItems; @@ -62,8 +62,6 @@ public class OutputChooserLayout extends FrameLayout { super(context, attrs); mContext = context; mTag = TAG; - mQsDetailIconOverlaySize = (int) getResources().getDimension( - R.dimen.qs_detail_icon_overlay_size); } @Override @@ -74,7 +72,8 @@ public class OutputChooserLayout extends FrameLayout { 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 @@ public class OutputChooserLayout extends FrameLayout { 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 @@ public class OutputChooserLayout extends FrameLayout { } 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 9f7c5a7fde22..e76bf572b1a1 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.Message; 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 @@ public class VolumeDialogImpl implements VolumeDialog { 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 e74736ab2fcc..859dc2f18dc6 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 fbcbd2096db7..4c7c1d1608fd 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 @@ public abstract class SysuiTestCase { 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 000000000000..8f38c2ca948d --- /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 66524cc2b193..2edcd01ed445 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 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { 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 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { 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 000000000000..537d606365c4 --- /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 f99ac5c37a66..9b67f8f32704 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -5144,6 +5144,11 @@ message MetricsEvent { // 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 3b8d4bca0598..672518cc17ed 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.app.StatusBarManager; 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.InputDevice; 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 @@ public class GlobalActionPerformer { case AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN: { return lockScreen(); } + case AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT: { + return takeScreenshot(); + } } return false; } finally { @@ -167,4 +186,12 @@ public class GlobalActionPerformer { 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 6d845f9a9d3a..978ed25378e1 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.UserHandle; 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 @@ public final class AutofillManagerService extends SystemService { } @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 a5bd59a9e77d..6bcfc4bf5a5e 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 @@ final class AutofillManagerServiceImpl { 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 44560879f028..4d69ef952f72 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 @@ public final class AutofillManagerServiceShellCommand extends ShellCommand { 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 7228f1d2a8ea..da5220104e3c 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.ServiceConnection; 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 com.android.internal.annotations.GuardedBy; 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 @@ final class FieldClassificationStrategy { mUserId = userId; } - private ComponentName getServiceComponentName() { + @Nullable + private ServiceInfo getServiceInfo() { final String packageName = mContext.getPackageManager().getServicesSystemSharedLibraryPackageName(); if (packageName == null) { @@ -96,9 +99,15 @@ final class FieldClassificationStrategy { 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 @@ final class FieldClassificationStrategy { } } - 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 @@ final class FieldClassificationStrategy { } 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 a0e23a152224..63f83849db34 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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState 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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState 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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState 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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } 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 518891006b37..465bb09927a5 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 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter 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 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter 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 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter } else if ("agents".startsWith(arg)) { dumpAgents(pw); return; + } else if ("transportclients".equals(arg.toLowerCase())) { + mTransportManager.dump(pw); + return; } } } @@ -3576,6 +3589,8 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter } } + 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 6d9231dcddfa..7e179e5d24f8 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.TransportConnectionListener; 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 @@ public class TransportManager { !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/internal/PerformBackupTask.java b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java index cc3af8c8c933..289dd140ee7f 100644 --- a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java +++ b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java @@ -114,14 +114,14 @@ public class PerformBackupTask implements BackupRestoreTask { private RefactoredBackupManagerService backupManagerService; private final Object mCancelLock = new Object(); - ArrayList<BackupRequest> mQueue; - ArrayList<BackupRequest> mOriginalQueue; - File mStateDir; - @Nullable DataChangedJournal mJournal; - BackupState mCurrentState; - List<String> mPendingFullBackups; - IBackupObserver mObserver; - IBackupManagerMonitor mMonitor; + private ArrayList<BackupRequest> mQueue; + private ArrayList<BackupRequest> mOriginalQueue; + private File mStateDir; + @Nullable private DataChangedJournal mJournal; + private BackupState mCurrentState; + private List<String> mPendingFullBackups; + private IBackupObserver mObserver; + private IBackupManagerMonitor mMonitor; private final TransportClient mTransportClient; private final OnTaskFinishedListener mListener; @@ -130,18 +130,18 @@ public class PerformBackupTask implements BackupRestoreTask { private volatile int mEphemeralOpToken; // carried information about the current in-flight operation - IBackupAgent mAgentBinder; - PackageInfo mCurrentPackage; - File mSavedStateName; - File mBackupDataName; - File mNewStateName; - ParcelFileDescriptor mSavedState; - ParcelFileDescriptor mBackupData; - ParcelFileDescriptor mNewState; - int mStatus; - boolean mFinished; - final boolean mUserInitiated; - final boolean mNonIncremental; + private IBackupAgent mAgentBinder; + private PackageInfo mCurrentPackage; + private File mSavedStateName; + private File mBackupDataName; + private File mNewStateName; + private ParcelFileDescriptor mSavedState; + private ParcelFileDescriptor mBackupData; + private ParcelFileDescriptor mNewState; + private int mStatus; + private boolean mFinished; + private final boolean mUserInitiated; + private final boolean mNonIncremental; private volatile boolean mCancelAll; @@ -241,7 +241,7 @@ public class PerformBackupTask implements BackupRestoreTask { // We're starting a backup pass. Initialize the transport and send // the PM metadata blob if we haven't already. - void beginBackup() { + private void beginBackup() { if (DEBUG_BACKUP_TRACE) { backupManagerService.clearBackupTrace(); StringBuilder b = new StringBuilder(256); @@ -369,7 +369,7 @@ public class PerformBackupTask implements BackupRestoreTask { // Transport has been initialized and the PM metadata submitted successfully // if that was warranted. Now we process the single next thing in the queue. - void invokeNextAgent() { + private void invokeNextAgent() { mStatus = BackupTransport.TRANSPORT_OK; backupManagerService.addBackupTrace("invoke q=" + mQueue.size()); @@ -511,7 +511,7 @@ public class PerformBackupTask implements BackupRestoreTask { } } - void finalizeBackup() { + private void finalizeBackup() { backupManagerService.addBackupTrace("finishing"); // Mark packages that we didn't backup (because backup was cancelled, etc.) as needing @@ -617,14 +617,14 @@ public class PerformBackupTask implements BackupRestoreTask { } // Remove the PM metadata state. This will generate an init on the next pass. - void clearMetadata() { + private void clearMetadata() { final File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL); if (pmState.exists()) pmState.delete(); } // Invoke an agent's doBackup() and start a timeout message spinning on the main // handler in case it doesn't get back to us. - int invokeAgentForBackup(String packageName, IBackupAgent agent) { + private int invokeAgentForBackup(String packageName, IBackupAgent agent) { if (DEBUG) { Slog.d(TAG, "invokeAgentForBackup on " + packageName); } @@ -711,7 +711,7 @@ public class PerformBackupTask implements BackupRestoreTask { return BackupTransport.TRANSPORT_OK; } - public void failAgent(IBackupAgent agent, String message) { + private void failAgent(IBackupAgent agent, String message) { try { agent.fail(message); } catch (Exception e) { @@ -1059,7 +1059,7 @@ public class PerformBackupTask implements BackupRestoreTask { } } - void revertAndEndBackup() { + private void revertAndEndBackup() { if (MORE_DEBUG) { Slog.i(TAG, "Reverting backup queue - restaging everything"); } @@ -1085,14 +1085,14 @@ public class PerformBackupTask implements BackupRestoreTask { } - void errorCleanup() { + private void errorCleanup() { mBackupDataName.delete(); mNewStateName.delete(); clearAgentState(); } // Cleanup common to both success and failure cases - void clearAgentState() { + private void clearAgentState() { try { if (mSavedState != null) mSavedState.close(); } catch (IOException e) { @@ -1123,7 +1123,7 @@ public class PerformBackupTask implements BackupRestoreTask { } } - void executeNextState(BackupState nextState) { + private void executeNextState(BackupState nextState) { if (MORE_DEBUG) { Slog.i(TAG, " => executing next step on " + this + " nextState=" + nextState); 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 399f338d26b2..7b2e3df67942 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.Handler; 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 com.android.server.backup.TransportManager; 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 @@ import java.util.concurrent.ExecutionException; */ 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 @@ public class TransportClient { 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 @@ public class TransportClient { // For logging String classNameForLog = mTransportComponent.getShortClassName().replaceFirst(".*\\.", ""); - mPrefixForLog = classNameForLog + "#" + mIdentifier + ": "; + mPrefixForLog = classNameForLog + "#" + mIdentifier + ":"; } public ComponentName getTransportComponent() { @@ -229,7 +240,7 @@ public class TransportClient { 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 @@ public class TransportClient { 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 @@ public class TransportClient { } 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 @@ public class TransportClient { } 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 1cbe74716b03..1132bce612b7 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 @@ public class TransportClientManager { 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 @@ public class TransportClientManager { 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 @@ public class TransportClientManager { * 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 92bba9bf06f0..56b2d44ec420 100644 --- a/services/backup/java/com/android/server/backup/transport/TransportUtils.java +++ b/services/backup/java/com/android/server/backup/transport/TransportUtils.java @@ -41,21 +41,20 @@ public class TransportUtils { } 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); + if (Log.isLoggable(tag, priority)) { + Slog.println(priority, tag, 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); + 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 4ffa5f1f38f9..f4675fd3316d 100644 --- a/services/core/java/com/android/server/AppOpsService.java +++ b/services/core/java/com/android/server/AppOpsService.java @@ -1316,14 +1316,8 @@ public class AppOpsService extends IAppOpsService.Stub { 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 @@ public class AppOpsService extends IAppOpsService.Stub { 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 @@ public class AppOpsService extends IAppOpsService.Stub { } 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 @@ public class AppOpsService extends IAppOpsService.Stub { 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 @@ public class AppOpsService extends IAppOpsService.Stub { } 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 a75a3675f7f9..de113a6a53e0 100644 --- a/services/core/java/com/android/server/ForceAppStandbyTracker.java +++ b/services/core/java/com/android/server/ForceAppStandbyTracker.java @@ -26,6 +26,8 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.database.ContentObserver; +import android.net.Uri; +import android.os.BatteryManager; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -89,6 +91,9 @@ public class ForceAppStandbyTracker { private final MyHandler mHandler; + @VisibleForTesting + FeatureFlagsObserver mFlagsObserver; + /** * Pair of (uid (not user-id), packageName) with OP_RUN_ANY_IN_BACKGROUND *not* allowed. */ @@ -113,14 +118,36 @@ public class ForceAppStandbyTracker { @GuardedBy("mLock") boolean mStarted; + /** + * Only used for small battery use-case. + */ + @GuardedBy("mLock") + boolean mIsPluggedIn; + @GuardedBy("mLock") - boolean mForceAllAppsStandby; // True if device is in extreme battery saver mode + boolean mBatterySaverEnabled; + /** + * True if the forced app standby is currently enabled + */ @GuardedBy("mLock") - boolean mForcedAppStandbyEnabled; // True if the forced app standby feature is enabled + boolean mForceAllAppsStandby; - private class FeatureFlagObserver extends ContentObserver { - FeatureFlagObserver() { + /** + * True if the forced app standby for small battery devices feature is enabled in settings + */ + @GuardedBy("mLock") + boolean mForceAllAppStandbyForSmallBattery; + + /** + * True if the forced app standby feature is enabled in settings + */ + @GuardedBy("mLock") + boolean mForcedAppStandbyEnabled; + + @VisibleForTesting + class FeatureFlagsObserver extends ContentObserver { + FeatureFlagsObserver() { super(null); } @@ -128,6 +155,9 @@ public class ForceAppStandbyTracker { mContext.getContentResolver().registerContentObserver( Settings.Global.getUriFor(Settings.Global.FORCED_APP_STANDBY_ENABLED), false, this); + + mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor( + Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED), false, this); } boolean isForcedAppStandbyEnabled() { @@ -135,20 +165,43 @@ public class ForceAppStandbyTracker { Settings.Global.FORCED_APP_STANDBY_ENABLED, 1) == 1; } + boolean isForcedAppStandbyForSmallBatteryEnabled() { + return Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED, 0) == 1; + } + @Override - public void onChange(boolean selfChange) { - final boolean enabled = isForcedAppStandbyEnabled(); - synchronized (mLock) { - if (mForcedAppStandbyEnabled == enabled) { - return; + public void onChange(boolean selfChange, Uri uri) { + if (Settings.Global.getUriFor(Settings.Global.FORCED_APP_STANDBY_ENABLED).equals(uri)) { + final boolean enabled = isForcedAppStandbyEnabled(); + synchronized (mLock) { + if (mForcedAppStandbyEnabled == enabled) { + return; + } + mForcedAppStandbyEnabled = enabled; + if (DEBUG) { + Slog.d(TAG,"Forced app standby feature flag changed: " + + mForcedAppStandbyEnabled); + } } - mForcedAppStandbyEnabled = enabled; - if (DEBUG) { - Slog.d(TAG, - "Forced app standby feature flag changed: " + mForcedAppStandbyEnabled); + mHandler.notifyForcedAppStandbyFeatureFlagChanged(); + } else if (Settings.Global.getUriFor( + Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED).equals(uri)) { + final boolean enabled = isForcedAppStandbyForSmallBatteryEnabled(); + synchronized (mLock) { + if (mForceAllAppStandbyForSmallBattery == enabled) { + return; + } + mForceAllAppStandbyForSmallBattery = enabled; + if (DEBUG) { + Slog.d(TAG, "Forced app standby for small battery feature flag changed: " + + mForceAllAppStandbyForSmallBattery); + } + updateForceAllAppStandbyState(); } + } else { + Slog.w(TAG, "Unexpected feature flag uri encountered: " + uri); } - mHandler.notifyFeatureFlagChanged(); } } @@ -289,9 +342,11 @@ public class ForceAppStandbyTracker { mAppOpsManager = Preconditions.checkNotNull(injectAppOpsManager()); mAppOpsService = Preconditions.checkNotNull(injectIAppOpsService()); mPowerManagerInternal = Preconditions.checkNotNull(injectPowerManagerInternal()); - final FeatureFlagObserver flagObserver = new FeatureFlagObserver(); - flagObserver.register(); - mForcedAppStandbyEnabled = flagObserver.isForcedAppStandbyEnabled(); + mFlagsObserver = new FeatureFlagsObserver(); + mFlagsObserver.register(); + mForcedAppStandbyEnabled = mFlagsObserver.isForcedAppStandbyEnabled(); + mForceAllAppStandbyForSmallBattery = + mFlagsObserver.isForcedAppStandbyForSmallBatteryEnabled(); try { mIActivityManager.registerUidObserver(new UidObserver(), @@ -306,16 +361,24 @@ public class ForceAppStandbyTracker { IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_USER_REMOVED); + filter.addAction(Intent.ACTION_BATTERY_CHANGED); mContext.registerReceiver(new MyReceiver(), filter); refreshForcedAppStandbyUidPackagesLocked(); mPowerManagerInternal.registerLowPowerModeObserver( ServiceType.FORCE_ALL_APPS_STANDBY, - (state) -> updateForceAllAppsStandby(state.batterySaverEnabled)); + (state) -> { + synchronized (mLock) { + mBatterySaverEnabled = state.batterySaverEnabled; + updateForceAllAppStandbyState(); + } + }); - updateForceAllAppsStandby(mPowerManagerInternal.getLowPowerState( - ServiceType.FORCE_ALL_APPS_STANDBY).batterySaverEnabled); + mBatterySaverEnabled = mPowerManagerInternal.getLowPowerState( + ServiceType.FORCE_ALL_APPS_STANDBY).batterySaverEnabled; + + updateForceAllAppStandbyState(); } } @@ -340,6 +403,11 @@ public class ForceAppStandbyTracker { return LocalServices.getService(PowerManagerInternal.class); } + @VisibleForTesting + boolean isSmallBatteryDevice() { + return ActivityManager.isSmallBatteryDevice(); + } + /** * Update {@link #mRunAnyRestrictedPackages} with the current app ops state. */ @@ -369,18 +437,26 @@ public class ForceAppStandbyTracker { } } - /** - * Update {@link #mForceAllAppsStandby} and notifies the listeners. - */ - void updateForceAllAppsStandby(boolean enable) { + private void updateForceAllAppStandbyState() { synchronized (mLock) { - if (enable == mForceAllAppsStandby) { - return; + if (mForceAllAppStandbyForSmallBattery && isSmallBatteryDevice()) { + toggleForceAllAppsStandbyLocked(!mIsPluggedIn); + } else { + toggleForceAllAppsStandbyLocked(mBatterySaverEnabled); } - mForceAllAppsStandby = enable; + } + } - mHandler.notifyForceAllAppsStandbyChanged(); + /** + * Update {@link #mForceAllAppsStandby} and notifies the listeners. + */ + private void toggleForceAllAppsStandbyLocked(boolean enable) { + if (enable == mForceAllAppsStandby) { + return; } + mForceAllAppsStandby = enable; + + mHandler.notifyForceAllAppsStandbyChanged(); } private int findForcedAppStandbyUidPackageIndexLocked(int uid, @NonNull String packageName) { @@ -515,6 +591,11 @@ public class ForceAppStandbyTracker { if (userId > 0) { mHandler.doUserRemoved(userId); } + } else if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) { + synchronized (mLock) { + mIsPluggedIn = (intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0); + } + updateForceAllAppStandbyState(); } } } @@ -533,7 +614,7 @@ public class ForceAppStandbyTracker { private static final int MSG_TEMP_WHITELIST_CHANGED = 5; private static final int MSG_FORCE_ALL_CHANGED = 6; private static final int MSG_USER_REMOVED = 7; - private static final int MSG_FEATURE_FLAG_CHANGED = 8; + private static final int MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED = 8; public MyHandler(Looper looper) { super(looper); @@ -563,8 +644,8 @@ public class ForceAppStandbyTracker { obtainMessage(MSG_FORCE_ALL_CHANGED).sendToTarget(); } - public void notifyFeatureFlagChanged() { - obtainMessage(MSG_FEATURE_FLAG_CHANGED).sendToTarget(); + public void notifyForcedAppStandbyFeatureFlagChanged() { + obtainMessage(MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED).sendToTarget(); } public void doUserRemoved(int userId) { @@ -618,7 +699,7 @@ public class ForceAppStandbyTracker { l.onForceAllAppsStandbyChanged(sender); } return; - case MSG_FEATURE_FLAG_CHANGED: + case MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED: // Feature flag for forced app standby changed. final boolean unblockAlarms; synchronized (mLock) { @@ -737,21 +818,23 @@ public class ForceAppStandbyTracker { * @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; } @@ -765,12 +848,13 @@ public class ForceAppStandbyTracker { 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; } } @@ -842,6 +926,18 @@ public class ForceAppStandbyTracker { pw.println(isForceAllAppsStandbyEnabled()); pw.print(indent); + pw.print("Small Battery Device: "); + pw.println(isSmallBatteryDevice()); + + pw.print(indent); + pw.print("Force all apps standby for small battery device: "); + pw.println(mForceAllAppStandbyForSmallBattery); + + pw.print(indent); + pw.print("Plugged In: "); + pw.println(mIsPluggedIn); + + pw.print(indent); pw.print("Foreground uids: ["); String sep = ""; @@ -880,6 +976,11 @@ public class ForceAppStandbyTracker { final long token = proto.start(fieldId); proto.write(ForceAppStandbyTrackerProto.FORCE_ALL_APPS_STANDBY, mForceAllAppsStandby); + proto.write(ForceAppStandbyTrackerProto.IS_SMALL_BATTERY_DEVICE, + isSmallBatteryDevice()); + proto.write(ForceAppStandbyTrackerProto.FORCE_ALL_APPS_STANDBY_FOR_SMALL_BATTERY, + mForceAllAppStandbyForSmallBattery); + proto.write(ForceAppStandbyTrackerProto.IS_CHARGING, mIsPluggedIn); for (int i = 0; i < mForegroundUids.size(); i++) { if (mForegroundUids.valueAt(i)) { diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java index 46a35ec800ba..ef6bc437cb6c 100644 --- a/services/core/java/com/android/server/IpSecService.java +++ b/services/core/java/com/android/server/IpSecService.java @@ -148,7 +148,7 @@ public class IpSecService extends IIpSecService.Stub { * resources. * * <p>References to the IResource object may be held by other RefcountedResource objects, - * and as such, the kernel resources and quota may not be cleaned up. + * and as such, the underlying resources and quota may not be cleaned up. */ void invalidate() throws RemoteException; @@ -298,7 +298,12 @@ public class IpSecService extends IIpSecService.Stub { } } - /* Very simple counting class that looks much like a counting semaphore */ + /** + * Very simple counting class that looks much like a counting semaphore + * + * <p>This class is not thread-safe, and expects that that users of this class will ensure + * synchronization and thread safety by holding the IpSecService.this instance lock. + */ @VisibleForTesting static class ResourceTracker { private final int mMax; @@ -341,26 +346,38 @@ public class IpSecService extends IIpSecService.Stub { @VisibleForTesting static final class UserRecord { - /* Type names */ - public static final String TYPENAME_SPI = "SecurityParameterIndex"; - public static final String TYPENAME_TRANSFORM = "IpSecTransform"; - public static final String TYPENAME_ENCAP_SOCKET = "UdpEncapSocket"; - /* Maximum number of each type of resource that a single UID may possess */ public static final int MAX_NUM_ENCAP_SOCKETS = 2; public static final int MAX_NUM_TRANSFORMS = 4; public static final int MAX_NUM_SPIS = 8; + /** + * Store each of the OwnedResource types in an (thinly wrapped) sparse array for indexing + * and explicit (user) reference management. + * + * <p>These are stored in separate arrays to improve debuggability and dump output clarity. + * + * <p>Resources are removed from this array when the user releases their explicit reference + * by calling one of the releaseResource() methods. + */ final RefcountedResourceArray<SpiRecord> mSpiRecords = - new RefcountedResourceArray<>(TYPENAME_SPI); - final ResourceTracker mSpiQuotaTracker = new ResourceTracker(MAX_NUM_SPIS); - + new RefcountedResourceArray<>(SpiRecord.class.getSimpleName()); final RefcountedResourceArray<TransformRecord> mTransformRecords = - new RefcountedResourceArray<>(TYPENAME_TRANSFORM); - final ResourceTracker mTransformQuotaTracker = new ResourceTracker(MAX_NUM_TRANSFORMS); - + new RefcountedResourceArray<>(TransformRecord.class.getSimpleName()); final RefcountedResourceArray<EncapSocketRecord> mEncapSocketRecords = - new RefcountedResourceArray<>(TYPENAME_ENCAP_SOCKET); + new RefcountedResourceArray<>(EncapSocketRecord.class.getSimpleName()); + + /** + * Trackers for quotas for each of the OwnedResource types. + * + * <p>These trackers are separate from the resource arrays, since they are incremented and + * decremented at different points in time. Specifically, quota is only returned upon final + * resource deallocation (after all explicit and implicit references are released). Note + * that it is possible that calls to releaseResource() will not return the used quota if + * there are other resources that depend on (are parents of) the resource being released. + */ + final ResourceTracker mSpiQuotaTracker = new ResourceTracker(MAX_NUM_SPIS); + final ResourceTracker mTransformQuotaTracker = new ResourceTracker(MAX_NUM_TRANSFORMS); final ResourceTracker mSocketQuotaTracker = new ResourceTracker(MAX_NUM_ENCAP_SOCKETS); void removeSpiRecord(int resourceId) { @@ -395,11 +412,15 @@ public class IpSecService extends IIpSecService.Stub { } } + /** + * This class is not thread-safe, and expects that that users of this class will ensure + * synchronization and thread safety by holding the IpSecService.this instance lock. + */ @VisibleForTesting static final class UserResourceTracker { private final SparseArray<UserRecord> mUserRecords = new SparseArray<>(); - /** Never-fail getter that populates the list of UIDs as-needed */ + /** Lazy-initialization/getter that populates or retrieves the UserRecord as needed */ public UserRecord getUserRecord(int uid) { checkCallerUid(uid); @@ -428,18 +449,20 @@ public class IpSecService extends IIpSecService.Stub { @VisibleForTesting final UserResourceTracker mUserResourceTracker = new UserResourceTracker(); /** - * The KernelResourceRecord class provides a facility to cleanly and reliably track system + * The OwnedResourceRecord class provides a facility to cleanly and reliably track system * resources. It relies on a provided resourceId that should uniquely identify the kernel * resource. To use this class, the user should implement the invalidate() and * freeUnderlyingResources() methods that are responsible for cleaning up IpSecService resource - * tracking arrays and kernel resources, respectively + * tracking arrays and kernel resources, respectively. + * + * <p>This class associates kernel resources with the UID that owns and controls them. */ - private abstract class KernelResourceRecord implements IResource { + private abstract class OwnedResourceRecord implements IResource { final int pid; final int uid; protected final int mResourceId; - KernelResourceRecord(int resourceId) { + OwnedResourceRecord(int resourceId) { super(); if (resourceId == INVALID_RESOURCE_ID) { throw new IllegalArgumentException("Resource ID must not be INVALID_RESOURCE_ID"); @@ -479,8 +502,6 @@ public class IpSecService extends IIpSecService.Stub { } }; - // TODO: Move this to right after RefcountedResource. With this here, Gerrit was showing many - // more things as changed. /** * Thin wrapper over SparseArray to ensure resources exist, and simplify generic typing. * @@ -534,7 +555,12 @@ public class IpSecService extends IIpSecService.Stub { } } - private final class TransformRecord extends KernelResourceRecord { + /** + * Tracks an SA in the kernel, and manages cleanup paths. Once a TransformRecord is + * created, the SpiRecord that originally tracked the SAs will reliquish the + * responsibility of freeing the underlying SA to this class via the mOwnedByTransform flag. + */ + private final class TransformRecord extends OwnedResourceRecord { private final IpSecConfig mConfig; private final SpiRecord mSpi; private final EncapSocketRecord mSocket; @@ -603,7 +629,12 @@ public class IpSecService extends IIpSecService.Stub { } } - private final class SpiRecord extends KernelResourceRecord { + /** + * Tracks a single SA in the kernel, and manages cleanup paths. Once used in a Transform, the + * responsibility for cleaning up underlying resources will be passed to the TransformRecord + * object + */ + private final class SpiRecord extends OwnedResourceRecord { private final String mSourceAddress; private final String mDestinationAddress; private int mSpi; @@ -692,7 +723,14 @@ public class IpSecService extends IIpSecService.Stub { } } - private final class EncapSocketRecord extends KernelResourceRecord { + /** + * Tracks a UDP encap socket, and manages cleanup paths + * + * <p>While this class does not manage non-kernel resources, race conditions around socket + * binding require that the service creates the encap socket, binds it and applies the socket + * policy before handing it to a user. + */ + private final class EncapSocketRecord extends OwnedResourceRecord { private FileDescriptor mSocket; private final int mPort; @@ -1105,16 +1143,14 @@ public class IpSecService extends IIpSecService.Stub { * receive data. */ @Override - public synchronized IpSecTransformResponse createTransportModeTransform( - IpSecConfig c, IBinder binder) throws RemoteException { + public synchronized IpSecTransformResponse createTransform(IpSecConfig c, IBinder binder) + throws RemoteException { checkIpSecConfig(c); - checkNotNull(binder, "Null Binder passed to createTransportModeTransform"); + checkNotNull(binder, "Null Binder passed to createTransform"); final int resourceId = mNextResourceId++; UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid()); - - // Avoid resizing by creating a dependency array of min-size 2 (1 UDP encap + 1 SPI) - List<RefcountedResource> dependencies = new ArrayList<>(2); + List<RefcountedResource> dependencies = new ArrayList<>(); if (!userRecord.mTransformQuotaTracker.isAvailable()) { return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE); @@ -1186,7 +1222,7 @@ public class IpSecService extends IIpSecService.Stub { * other reasons. */ @Override - public synchronized void deleteTransportModeTransform(int resourceId) throws RemoteException { + public synchronized void deleteTransform(int resourceId) throws RemoteException { UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid()); releaseResource(userRecord.mTransformRecords, resourceId); } diff --git a/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java b/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java index 80f8e519beb7..833def3e87b1 100644 --- a/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java +++ b/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java @@ -26,4 +26,7 @@ public interface PersistentDataBlockManagerInternal { /** Retrieves handle to a lockscreen credential to be used for Factory Reset Protection. */ byte[] getFrpCredentialHandle(); + + /** Update the OEM unlock enabled bit, bypassing user restriction checks. */ + void forceOemUnlockEnabled(boolean enabled); } diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java index c32a2d10b0bc..4298140d31a0 100644 --- a/services/core/java/com/android/server/PersistentDataBlockService.java +++ b/services/core/java/com/android/server/PersistentDataBlockService.java @@ -668,5 +668,13 @@ public class PersistentDataBlockService extends SystemService { IoUtils.closeQuietly(inputStream); } } + + @Override + public void forceOemUnlockEnabled(boolean enabled) { + synchronized (mLock) { + doSetOemUnlockEnabledLocked(enabled); + computeAndWriteDigestLocked(); + } + } }; } diff --git a/services/core/java/com/android/server/SystemUpdateManagerService.java b/services/core/java/com/android/server/SystemUpdateManagerService.java new file mode 100644 index 000000000000..6c1ffdd32d18 --- /dev/null +++ b/services/core/java/com/android/server/SystemUpdateManagerService.java @@ -0,0 +1,255 @@ +/* + * 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.server; + +import static android.os.SystemUpdateManager.KEY_STATUS; +import static android.os.SystemUpdateManager.STATUS_IDLE; +import static android.os.SystemUpdateManager.STATUS_UNKNOWN; + +import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; +import static org.xmlpull.v1.XmlPullParser.END_TAG; +import static org.xmlpull.v1.XmlPullParser.START_TAG; + +import android.Manifest; +import android.annotation.Nullable; +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Binder; +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; +import android.os.ISystemUpdateManager; +import android.os.PersistableBundle; +import android.os.SystemUpdateManager; +import android.provider.Settings; +import android.util.AtomicFile; +import android.util.Slog; +import android.util.Xml; + +import com.android.internal.util.FastXmlSerializer; +import com.android.internal.util.XmlUtils; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +public class SystemUpdateManagerService extends ISystemUpdateManager.Stub { + + private static final String TAG = "SystemUpdateManagerService"; + + private static final int UID_UNKNOWN = -1; + + private static final String INFO_FILE = "system-update-info.xml"; + private static final int INFO_FILE_VERSION = 0; + private static final String TAG_INFO = "info"; + private static final String KEY_VERSION = "version"; + private static final String KEY_UID = "uid"; + private static final String KEY_BOOT_COUNT = "boot-count"; + private static final String KEY_INFO_BUNDLE = "info-bundle"; + + private final Context mContext; + private final AtomicFile mFile; + private final Object mLock = new Object(); + private int mLastUid = UID_UNKNOWN; + private int mLastStatus = STATUS_UNKNOWN; + + public SystemUpdateManagerService(Context context) { + mContext = context; + mFile = new AtomicFile(new File(Environment.getDataSystemDirectory(), INFO_FILE)); + + // Populate mLastUid and mLastStatus. + synchronized (mLock) { + loadSystemUpdateInfoLocked(); + } + } + + @Override + public void updateSystemUpdateInfo(PersistableBundle infoBundle) { + mContext.enforceCallingOrSelfPermission(Manifest.permission.RECOVERY, TAG); + + int status = infoBundle.getInt(KEY_STATUS, STATUS_UNKNOWN); + if (status == STATUS_UNKNOWN) { + Slog.w(TAG, "Invalid status info. Ignored"); + return; + } + + // There could be multiple updater apps running on a device. But only one at most should + // be active (i.e. with a pending update), with the rest reporting idle status. We will + // only accept the reported status if any of the following conditions holds: + // a) none has been reported before; + // b) the current on-file status was last reported by the same caller; + // c) an active update is being reported. + int uid = Binder.getCallingUid(); + if (mLastUid == UID_UNKNOWN || mLastUid == uid || status != STATUS_IDLE) { + synchronized (mLock) { + saveSystemUpdateInfoLocked(infoBundle, uid); + } + } else { + Slog.i(TAG, "Inactive updater reporting IDLE status. Ignored"); + } + } + + @Override + public Bundle retrieveSystemUpdateInfo() { + if (mContext.checkCallingOrSelfPermission(Manifest.permission.READ_SYSTEM_UPDATE_INFO) + == PackageManager.PERMISSION_DENIED + && mContext.checkCallingOrSelfPermission(Manifest.permission.RECOVERY) + == PackageManager.PERMISSION_DENIED) { + throw new SecurityException("Can't read system update info. Requiring " + + "READ_SYSTEM_UPDATE_INFO or RECOVERY permission."); + } + + synchronized (mLock) { + return loadSystemUpdateInfoLocked(); + } + } + + // Reads and validates the info file. Returns the loaded info bundle on success; or a default + // info bundle with UNKNOWN status. + private Bundle loadSystemUpdateInfoLocked() { + PersistableBundle loadedBundle = null; + try (FileInputStream fis = mFile.openRead()) { + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(fis, StandardCharsets.UTF_8.name()); + loadedBundle = readInfoFileLocked(parser); + } catch (FileNotFoundException e) { + Slog.i(TAG, "No existing info file " + mFile.getBaseFile()); + } catch (XmlPullParserException e) { + Slog.e(TAG, "Failed to parse the info file:", e); + } catch (IOException e) { + Slog.e(TAG, "Failed to read the info file:", e); + } + + // Validate the loaded bundle. + if (loadedBundle == null) { + return removeInfoFileAndGetDefaultInfoBundleLocked(); + } + + int version = loadedBundle.getInt(KEY_VERSION, -1); + if (version == -1) { + Slog.w(TAG, "Invalid info file (invalid version). Ignored"); + return removeInfoFileAndGetDefaultInfoBundleLocked(); + } + + int lastUid = loadedBundle.getInt(KEY_UID, -1); + if (lastUid == -1) { + Slog.w(TAG, "Invalid info file (invalid UID). Ignored"); + return removeInfoFileAndGetDefaultInfoBundleLocked(); + } + + int lastBootCount = loadedBundle.getInt(KEY_BOOT_COUNT, -1); + if (lastBootCount == -1 || lastBootCount != getBootCount()) { + Slog.w(TAG, "Outdated info file. Ignored"); + return removeInfoFileAndGetDefaultInfoBundleLocked(); + } + + PersistableBundle infoBundle = loadedBundle.getPersistableBundle(KEY_INFO_BUNDLE); + if (infoBundle == null) { + Slog.w(TAG, "Invalid info file (missing info). Ignored"); + return removeInfoFileAndGetDefaultInfoBundleLocked(); + } + + int lastStatus = infoBundle.getInt(KEY_STATUS, STATUS_UNKNOWN); + if (lastStatus == STATUS_UNKNOWN) { + Slog.w(TAG, "Invalid info file (invalid status). Ignored"); + return removeInfoFileAndGetDefaultInfoBundleLocked(); + } + + // Everything looks good upon reaching this point. + mLastStatus = lastStatus; + mLastUid = lastUid; + return new Bundle(infoBundle); + } + + private void saveSystemUpdateInfoLocked(PersistableBundle infoBundle, int uid) { + // Wrap the incoming bundle with extra info (e.g. version, uid, boot count). We use nested + // PersistableBundle to avoid manually parsing XML attributes when loading the info back. + PersistableBundle outBundle = new PersistableBundle(); + outBundle.putPersistableBundle(KEY_INFO_BUNDLE, infoBundle); + outBundle.putInt(KEY_VERSION, INFO_FILE_VERSION); + outBundle.putInt(KEY_UID, uid); + outBundle.putInt(KEY_BOOT_COUNT, getBootCount()); + + // Only update the info on success. + if (writeInfoFileLocked(outBundle)) { + mLastUid = uid; + mLastStatus = infoBundle.getInt(KEY_STATUS); + } + } + + // Performs I/O work only, without validating the loaded info. + @Nullable + private PersistableBundle readInfoFileLocked(XmlPullParser parser) + throws XmlPullParserException, IOException { + int type; + while ((type = parser.next()) != END_DOCUMENT) { + if (type == START_TAG && TAG_INFO.equals(parser.getName())) { + return PersistableBundle.restoreFromXml(parser); + } + } + return null; + } + + private boolean writeInfoFileLocked(PersistableBundle outBundle) { + FileOutputStream fos = null; + try { + fos = mFile.startWrite(); + + XmlSerializer out = new FastXmlSerializer(); + out.setOutput(fos, StandardCharsets.UTF_8.name()); + out.startDocument(null, true); + + out.startTag(null, TAG_INFO); + outBundle.saveToXml(out); + out.endTag(null, TAG_INFO); + + out.endDocument(); + mFile.finishWrite(fos); + return true; + } catch (IOException | XmlPullParserException e) { + Slog.e(TAG, "Failed to save the info file:", e); + if (fos != null) { + mFile.failWrite(fos); + } + } + return false; + } + + private Bundle removeInfoFileAndGetDefaultInfoBundleLocked() { + if (mFile.exists()) { + Slog.i(TAG, "Removing info file"); + mFile.delete(); + } + + mLastStatus = STATUS_UNKNOWN; + mLastUid = UID_UNKNOWN; + Bundle infoBundle = new Bundle(); + infoBundle.putInt(KEY_STATUS, STATUS_UNKNOWN); + return infoBundle; + } + + private int getBootCount() { + return Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.BOOT_COUNT, 0); + } +} diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java index e38148c7bd42..db21ef1f9b5c 100644 --- a/services/core/java/com/android/server/am/ActivityDisplay.java +++ b/services/core/java/com/android/server/am/ActivityDisplay.java @@ -676,6 +676,15 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> { } } + public void dumpStacks(PrintWriter pw) { + for (int i = mStacks.size() - 1; i >= 0; --i) { + pw.print(mStacks.get(i).mStackId); + if (i > 0) { + pw.print(","); + } + } + } + public void writeToProto(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 29d33ce777f5..254d49b0bf27 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -27,9 +27,9 @@ import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS; import static android.Manifest.permission.READ_FRAME_BUFFER; import static android.Manifest.permission.REMOVE_TASKS; import static android.Manifest.permission.START_TASKS_FROM_RECENTS; -import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT; import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; import static android.app.ActivityManager.RESIZE_MODE_PRESERVE_WINDOW; +import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT; import static android.app.ActivityManager.StackId.INVALID_STACK_ID; import static android.app.ActivityManagerInternal.ASSIST_KEY_CONTENT; import static android.app.ActivityManagerInternal.ASSIST_KEY_DATA; @@ -120,6 +120,7 @@ import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_APPLICAT import static android.text.format.DateUtils.DAY_IN_MILLIS; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; + import static com.android.internal.util.XmlUtils.readBooleanAttribute; import static com.android.internal.util.XmlUtils.readIntAttribute; import static com.android.internal.util.XmlUtils.readLongAttribute; @@ -198,6 +199,7 @@ import static android.view.WindowManager.TRANSIT_NONE; import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE; import static android.view.WindowManager.TRANSIT_TASK_OPEN; import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT; + import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; import static org.xmlpull.v1.XmlPullParser.START_TAG; @@ -277,8 +279,8 @@ import android.content.pm.IPackageManager; import android.content.pm.InstrumentationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; -import android.content.pm.PackageManagerInternal; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.PackageManagerInternal; import android.content.pm.ParceledListSlice; import android.content.pm.PathPermission; import android.content.pm.PermissionInfo; @@ -356,18 +358,18 @@ import android.text.style.SuggestionSpan; import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; -import android.util.LongSparseArray; -import android.util.StatsLog; -import android.util.TimingsTraceLog; import android.util.DebugUtils; import android.util.EventLog; import android.util.Log; +import android.util.LongSparseArray; import android.util.Pair; import android.util.PrintWriterPrinter; import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; +import android.util.StatsLog; import android.util.TimeUtils; +import android.util.TimingsTraceLog; import android.util.Xml; import android.util.proto.ProtoOutputStream; import android.view.Gravity; @@ -440,6 +442,12 @@ import com.android.server.utils.PriorityDump; import com.android.server.vr.VrManagerInternal; import com.android.server.wm.PinnedStackWindowController; import com.android.server.wm.WindowManagerService; + +import dalvik.system.VMRuntime; + +import libcore.io.IoUtils; +import libcore.util.EmptyArray; + import com.google.android.collect.Lists; import com.google.android.collect.Maps; @@ -479,11 +487,6 @@ import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; -import dalvik.system.VMRuntime; - -import libcore.io.IoUtils; -import libcore.util.EmptyArray; - public class ActivityManagerService extends IActivityManager.Stub implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback { @@ -8601,6 +8604,16 @@ public class ActivityManagerService extends IActivityManager.Stub } 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 { @@ -10404,10 +10417,9 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public Bitmap getTaskDescriptionIcon(String filePath, int userId) { - if (userId != UserHandle.getCallingUserId()) { - enforceCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, - "getTaskDescriptionIcon"); - } + userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), + userId, false, ALLOW_FULL_ONLY, "getTaskDescriptionIcon", null); + final File passedIconFile = new File(filePath); final File legitIconFile = new File(TaskPersister.getUserImagesDir(userId), passedIconFile.getName()); @@ -21470,6 +21482,17 @@ public class ActivityManagerService extends IActivityManager.Stub private void resizeStackWithBoundsFromWindowManager(int stackId, boolean deferResume) { final Rect newStackBounds = new Rect(); final ActivityStack stack = mStackSupervisor.getStack(stackId); + + // TODO(b/71548119): Revert CL introducing below once cause of mismatch is found. + if (stack == null) { + final StringWriter writer = new StringWriter(); + final PrintWriter printWriter = new PrintWriter(writer); + mStackSupervisor.dumpDisplays(printWriter); + printWriter.flush(); + + Log.wtf(TAG, "stack not found:" + stackId + " displays:" + writer); + } + stack.getBoundsForNewConfiguration(newStackBounds); mStackSupervisor.resizeStackLocked( stack, !newStackBounds.isEmpty() ? newStackBounds : null /* bounds */, @@ -25149,6 +25172,10 @@ public class ActivityManagerService extends IActivityManager.Stub public int getMaxRunningUsers() { return mUserController.mMaxRunningUsers; } + + public boolean isCallerRecents(int callingUid) { + return getRecentTasks().isCallerRecents(callingUid); + } } /** diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 8168cba213f5..3d8863ee8c01 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -3752,6 +3752,15 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } } + public void dumpDisplays(PrintWriter pw) { + for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { + final ActivityDisplay display = mActivityDisplays.valueAt(i); + pw.print("[id:" + display.mDisplayId + " stacks:"); + display.dumpStacks(pw); + pw.print("]"); + } + } + public void dump(PrintWriter pw, String prefix) { pw.print(prefix); pw.print("mFocusedStack=" + mFocusedStack); pw.print(" mLastFocusedStack="); pw.println(mLastFocusedStack); diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java index 89102748796a..26d65bc7414c 100644 --- a/services/core/java/com/android/server/am/AppErrors.java +++ b/services/core/java/com/android/server/am/AppErrors.java @@ -745,7 +745,7 @@ class AppErrors { mContext.getContentResolver(), Settings.Secure.SHOW_FIRST_CRASH_DIALOG_DEV_OPTION, 0, - UserHandle.USER_CURRENT) != 0; + mService.mUserController.getCurrentUserId()) != 0; final boolean crashSilenced = mAppsNotReportingCrashes != null && mAppsNotReportingCrashes.contains(proc.info.packageName); if ((mService.canShowErrorDialogs() || showBackground) && !crashSilenced diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java index 1fcaeef72dba..927b72ceb37e 100644 --- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java +++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java @@ -99,7 +99,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { // Keep the last WiFi stats so we can compute a delta. @GuardedBy("mWorkerLock") private WifiActivityEnergyInfo mLastInfo = - new WifiActivityEnergyInfo(0, 0, 0, new long[]{0}, 0, 0, 0); + new WifiActivityEnergyInfo(0, 0, 0, new long[]{0}, 0, 0, 0, 0); BatteryExternalStatsWorker(Context context, BatteryStatsImpl stats) { mContext = context; @@ -374,6 +374,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { private WifiActivityEnergyInfo extractDeltaLocked(WifiActivityEnergyInfo latest) { final long timePeriodMs = latest.mTimestamp - mLastInfo.mTimestamp; + final long lastScanMs = mLastInfo.mControllerScanTimeMs; final long lastIdleMs = mLastInfo.mControllerIdleTimeMs; final long lastTxMs = mLastInfo.mControllerTxTimeMs; final long lastRxMs = mLastInfo.mControllerRxTimeMs; @@ -388,14 +389,16 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { final long txTimeMs = latest.mControllerTxTimeMs - lastTxMs; final long rxTimeMs = latest.mControllerRxTimeMs - lastRxMs; final long idleTimeMs = latest.mControllerIdleTimeMs - lastIdleMs; + final long scanTimeMs = latest.mControllerScanTimeMs - lastScanMs; - if (txTimeMs < 0 || rxTimeMs < 0) { + if (txTimeMs < 0 || rxTimeMs < 0 || scanTimeMs < 0) { // The stats were reset by the WiFi system (which is why our delta is negative). // Returns the unaltered stats. delta.mControllerEnergyUsed = latest.mControllerEnergyUsed; delta.mControllerRxTimeMs = latest.mControllerRxTimeMs; delta.mControllerTxTimeMs = latest.mControllerTxTimeMs; delta.mControllerIdleTimeMs = latest.mControllerIdleTimeMs; + delta.mControllerScanTimeMs = latest.mControllerScanTimeMs; Slog.v(TAG, "WiFi energy data was reset, new WiFi energy data is " + delta); } else { final long totalActiveTimeMs = txTimeMs + rxTimeMs; @@ -433,6 +436,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { // These times seem to be the most reliable. delta.mControllerTxTimeMs = txTimeMs; delta.mControllerRxTimeMs = rxTimeMs; + delta.mControllerScanTimeMs = scanTimeMs; // WiFi calculates the idle time as a difference from the on time and the various // Rx + Tx times. There seems to be some missing time there because this sometimes // becomes negative. Just cap it at 0 and ensure that it is less than the expected idle diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 81e8eb0d4136..04b49ba3db33 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -40,6 +40,8 @@ import android.os.UserManagerInternal; import android.os.WorkSource; import android.os.WorkSource.WorkChain; import android.os.connectivity.CellularBatteryStats; +import android.os.connectivity.WifiBatteryStats; +import android.os.connectivity.GpsBatteryStats; import android.os.health.HealthStatsParceler; import android.os.health.HealthStatsWriter; import android.os.health.UidHealthStats; @@ -594,6 +596,12 @@ public final class BatteryStatsService extends IBatteryStats.Stub } } + 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 +1456,26 @@ public final class BatteryStatsService extends IBatteryStats.Stub } /** + * Gets a snapshot of Wifi stats + * @hide + */ + public WifiBatteryStats getWifiBatteryStats() { + synchronized (mStats) { + return mStats.getWifiBatteryStats(); + } + } + + /** + * 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/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 1a47aa5cd777..5ada484e9e32 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -1431,7 +1431,13 @@ class UserController implements Handler.Callback { if (callingUid != 0 && callingUid != SYSTEM_UID) { final boolean allow; - if (mInjector.checkComponentPermission(INTERACT_ACROSS_USERS_FULL, callingPid, + if (mInjector.isCallerRecents(callingUid) + && callingUserId == getCurrentUserId() + && isSameProfileGroup(callingUserId, targetUserId)) { + // If the caller is Recents and it is running in the current user, we then allow it + // to access its profiles. + allow = true; + } else if (mInjector.checkComponentPermission(INTERACT_ACROSS_USERS_FULL, callingPid, callingUid, -1, true) == PackageManager.PERMISSION_GRANTED) { // If the caller has this permission, they always pass go. And collect $200. allow = true; @@ -2149,5 +2155,9 @@ class UserController implements Handler.Callback { mService.mLockTaskController.clearLockedTasks(reason); } } + + protected boolean isCallerRecents(int callingUid) { + return mService.getRecentTasks().isCallerRecents(callingUid); + } } } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 799f2a92bc33..a7147206bda2 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 @@ public class AudioService extends IAudioService.Stub 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 @@ public class AudioService extends IAudioService.Stub 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 @@ public class AudioService extends IAudioService.Stub + " 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 class AudioService extends IAudioService.Stub 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 @@ public class AudioService extends IAudioService.Stub 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); - - setIndex(index, device, caller); + 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); + } } } } + @GuardedBy("mSettingsLock") public void setAllIndexesToMax() { synchronized (VolumeStreamState.class) { for (int i = 0; i < mIndexMap.size(); i++) { @@ -5397,7 +5431,7 @@ public class AudioService extends IAudioService.Stub // 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 e093c9df7c4b..1ae7d20fb3fe 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 @@ class TunerSession extends ITuner.Stub { 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/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index bd1dbf9c46e8..fa5fdf587b8d 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -63,6 +63,7 @@ import android.util.KeyValueListParser; import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; +import android.util.StatsLog; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; @@ -823,6 +824,8 @@ public final class JobSchedulerService extends com.android.server.SystemService jobStatus.enqueueWorkLocked(ActivityManager.getService(), work); } startTrackingJobLocked(jobStatus, toCancel); + StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED, + uId, null, jobStatus.getBatteryName(), 2); // If the job is immediately ready to run, then we can just immediately // put it in the pending list and try to schedule it. This is especially diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index 6dc5403acc99..48d275cc483f 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 @@ public class GnssLocationProvider implements LocationProviderInterface { 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 e458f485c9ab..971ac8b922ef 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 @@ public abstract class NetworkPolicyManagerInternal { * 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 96b06c2c7f66..e406d51ac823 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.messages.nano.SystemMessageProto.SystemMessage; 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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { 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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { 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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mSystemReady = true; + waitForAdminData(); + // read policy from disk readPolicyAL(); @@ -4590,6 +4600,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { return mSubscriptionOpportunisticQuota.get(getSubIdLocked(network)); } } + + @Override + public void onAdminDataAvailable() { + mAdminDataAvailableLatch.countDown(); + } } private int parseSubId(NetworkState state) { @@ -4617,6 +4632,16 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { 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 fbf11fce08eb..7387ad4f90ff 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 @@ class WatchlistConfig { 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 @@ class WatchlistConfig { 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 @@ class WatchlistConfig { 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/oemlock/OemLockService.java b/services/core/java/com/android/server/oemlock/OemLockService.java index 2a2ff06b7a5a..a6200bf433e4 100644 --- a/services/core/java/com/android/server/oemlock/OemLockService.java +++ b/services/core/java/com/android/server/oemlock/OemLockService.java @@ -31,10 +31,10 @@ import android.os.UserManager; import android.os.UserManagerInternal; import android.os.UserManagerInternal.UserRestrictionsListener; import android.service.oemlock.IOemLockService; -import android.service.persistentdata.PersistentDataBlockManager; import android.util.Slog; import com.android.server.LocalServices; +import com.android.server.PersistentDataBlockManagerInternal; import com.android.server.SystemService; import com.android.server.pm.UserRestrictionsUtils; @@ -217,13 +217,12 @@ public class OemLockService extends SystemService { * is used to erase FRP information on a unlockable device. */ private void setPersistentDataBlockOemUnlockAllowedBit(boolean allowed) { - final PersistentDataBlockManager pdbm = (PersistentDataBlockManager) - mContext.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE); + final PersistentDataBlockManagerInternal pdbmi + = LocalServices.getService(PersistentDataBlockManagerInternal.class); // if mOemLock is PersistentDataBlockLock, then the bit should have already been set - if (pdbm != null && !(mOemLock instanceof PersistentDataBlockLock) - && pdbm.getOemUnlockEnabled() != allowed) { + if (pdbmi != null && !(mOemLock instanceof PersistentDataBlockLock)) { Slog.i(TAG, "Update OEM Unlock bit in pst partition to " + allowed); - pdbm.setOemUnlockEnabled(allowed); + pdbmi.forceOemUnlockEnabled(allowed); } } diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index cc448daa2619..a6ff4f7e5f70 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -344,9 +344,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { final boolean isSelfUpdatePermissionGranted = (mPm.checkUidPermission(android.Manifest.permission.INSTALL_SELF_UPDATES, mInstallerUid) == PackageManager.PERMISSION_GRANTED); + final boolean isUpdatePermissionGranted = + (mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGE_UPDATES, + mInstallerUid) == PackageManager.PERMISSION_GRANTED); + final int targetPackageUid = mPm.getPackageUid(mPackageName, 0, userId); final boolean isPermissionGranted = isInstallPermissionGranted - || (isSelfUpdatePermissionGranted - && mPm.getPackageUid(mPackageName, 0, userId) == mInstallerUid); + || (isUpdatePermissionGranted && targetPackageUid != -1) + || (isSelfUpdatePermissionGranted && targetPackageUid == mInstallerUid); final boolean isInstallerRoot = (mInstallerUid == Process.ROOT_UID); final boolean isInstallerSystem = (mInstallerUid == Process.SYSTEM_UID); final boolean forcePermissionPrompt = diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 2585cf3d7fa5..faf6114237cd 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_INTERNAL_ERROR; 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; @@ -119,6 +118,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; +import android.app.ActivityManagerInternal; import android.app.AppOpsManager; import android.app.IActivityManager; import android.app.ResourcesManager; @@ -993,6 +993,7 @@ public class PackageManagerService extends IPackageManager.Stub private List<String> mKeepUninstalledPackages; private UserManagerInternal mUserManagerInternal; + private ActivityManagerInternal mActivityManagerInternal; private DeviceIdleController.LocalService mDeviceIdleController; @@ -4576,6 +4577,14 @@ Slog.e("TODD", return mUserManagerInternal; } + private ActivityManagerInternal getActivityManagerInternal() { + if (mActivityManagerInternal == null) { + mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); + } + return mActivityManagerInternal; + } + + private DeviceIdleController.LocalService getDeviceIdleController() { if (mDeviceIdleController == null) { mDeviceIdleController = @@ -4736,8 +4745,12 @@ Slog.e("TODD", int filterCallingUid, int userId) { if (!sUserManager.exists(userId)) return null; flags = updateFlagsForComponent(flags, userId, component); - mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, - false /* requireFullPermission */, false /* checkShell */, "get activity info"); + + if (!isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId)) { + mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, + false /* requireFullPermission */, false /* checkShell */, "get activity info"); + } + synchronized (mPackages) { PackageParser.Activity a = mActivities.mActivities.get(component); @@ -4759,6 +4772,22 @@ Slog.e("TODD", return null; } + private boolean isRecentsAccessingChildProfiles(int callingUid, int targetUserId) { + if (!getActivityManagerInternal().isCallerRecents(callingUid)) { + return false; + } + final long token = Binder.clearCallingIdentity(); + try { + final int callingUserId = UserHandle.getUserId(callingUid); + if (ActivityManager.getCurrentUser() != callingUserId) { + return false; + } + return sUserManager.isSameProfileGroup(callingUserId, targetUserId); + } finally { + Binder.restoreCallingIdentity(token); + } + } + @Override public boolean activitySupportsIntent(ComponentName component, Intent intent, String resolvedType) { @@ -16495,16 +16524,6 @@ Slog.e("TODD", 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/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index a33f0716c4cb..47cd81326932 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -236,6 +236,8 @@ class PackageManagerShellCommand extends ShellCommand { return runHasFeature(); case "set-harmful-app-warning": return runSetHarmfulAppWarning(); + case "get-harmful-app-warning": + return runGetHarmfulAppWarning(); default: { String nextArg = getNextArg(); if (nextArg == null) { @@ -2125,6 +2127,31 @@ class PackageManagerShellCommand extends ShellCommand { return 0; } + private int runGetHarmfulAppWarning() throws RemoteException { + int userId = UserHandle.USER_CURRENT; + + String opt; + while ((opt = getNextOption()) != null) { + if (opt.equals("--user")) { + userId = UserHandle.parseUserArg(getNextArgRequired()); + } else { + getErrPrintWriter().println("Error: Unknown option: " + opt); + return -1; + } + } + + userId = translateUserId(userId, false /*allowAll*/, "runGetHarmfulAppWarning"); + + final String packageName = getNextArgRequired(); + final CharSequence warning = mInterface.getHarmfulAppWarning(packageName, userId); + if (!TextUtils.isEmpty(warning)) { + getOutPrintWriter().println(warning); + return 0; + } else { + return 1; + } + } + private static String checkAbiArgument(String abi) { if (TextUtils.isEmpty(abi)) { throw new IllegalArgumentException("Missing ABI argument"); @@ -2684,6 +2711,9 @@ class PackageManagerShellCommand extends ShellCommand { pw.println(""); pw.println(" set-harmful-app-warning [--user <USER_ID>] <PACKAGE> [<WARNING>]"); pw.println(" Mark the app as harmful with the given warning message."); + pw.println(""); + pw.println(" get-harmful-app-warning [--user <USER_ID>] <PACKAGE>"); + pw.println(" Return the harmful app warning message for the given app, if present"); pw.println(); Intent.printIntentArgsHelp(pw , ""); } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 63449976587e..0f394a4eed65 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_SUB_WINDOW; 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.StatusBarManager; 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.IBinder; 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 class PhoneWindowManager implements WindowManagerPolicy { 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); } @@ -4355,6 +4352,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { DisplayFrames displayFrames, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, DisplayCutout.ParcelableWrapper outDisplayCutout) { final int fl = PolicyControl.getWindowFlags(null, attrs); + final int pfl = attrs.privateFlags; final int sysuiVis = PolicyControl.getSystemUiVisibility(null, attrs); final int systemUiVisibility = (sysuiVis | attrs.subtreeSystemUiVisibility); final int displayRotation = displayFrames.mRotation; @@ -4377,8 +4375,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - if ((fl & (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) - == (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) { + final boolean layoutInScreenAndInsetDecor = + (fl & (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) + == (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR); + final boolean screenDecor = (pfl & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0; + + if (layoutInScreenAndInsetDecor && !screenDecor) { Rect frame; int availRight, availBottom; if (canHideNavigationBar() && @@ -4498,7 +4500,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 +6195,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** * 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 +7144,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 +7184,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 cf930f5cc6b7..5811714c73c4 100644 --- a/services/core/java/com/android/server/slice/PinnedSliceState.java +++ b/services/core/java/com/android/server/slice/PinnedSliceState.java @@ -21,7 +21,10 @@ import android.app.slice.SliceSpec; import android.content.ContentProviderClient; import android.net.Uri; import android.os.Bundle; +import android.os.IBinder; +import android.os.IBinder.DeathRecipient; import android.os.RemoteException; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; @@ -48,9 +51,13 @@ public class PinnedSliceState { @GuardedBy("mLock") private final ArraySet<String> mPinnedPkgs = new ArraySet<>(); @GuardedBy("mLock") - private final ArraySet<ISliceListener> mListeners = new ArraySet<>(); + private final ArrayMap<IBinder, ISliceListener> mListeners = new ArrayMap<>(); @GuardedBy("mLock") private SliceSpec[] mSupportedSpecs = null; + @GuardedBy("mLock") + private final ArrayMap<IBinder, String> mPkgMap = new ArrayMap<>(); + + private final DeathRecipient mDeathRecipient = this::handleRecheckListeners; public PinnedSliceState(SliceManagerService service, Uri uri) { mService = service; @@ -102,20 +109,29 @@ public class PinnedSliceState { 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) { + if (mListeners.size() == 0) { mService.listen(mUri); } + try { + listener.asBinder().linkToDeath(mDeathRecipient, 0); + } catch (RemoteException e) { + } + mListeners.put(listener.asBinder(), listener); + mPkgMap.put(listener.asBinder(), pkg); mergeSpecs(specs); } } public boolean removeSliceListener(ISliceListener listener) { synchronized (mLock) { - if (mListeners.remove(listener) && mListeners.size() == 0) { + listener.asBinder().unlinkToDeath(mDeathRecipient, 0); + mPkgMap.remove(listener.asBinder()); + if (mListeners.containsKey(listener.asBinder()) && mListeners.size() == 1) { mService.unlisten(mUri); } + mListeners.remove(listener.asBinder()); } return !isPinned(); } @@ -154,38 +170,68 @@ public class PinnedSliceState { return client; } + private void handleRecheckListeners() { + if (!isPinned()) return; + synchronized (mLock) { + for (int i = mListeners.size() - 1; i >= 0; i--) { + ISliceListener l = mListeners.valueAt(i); + if (!l.asBinder().isBinderAlive()) { + mListeners.removeAt(i); + } + } + if (!isPinned()) { + // All the listeners died, remove from pinned state. + mService.removePinnedSlice(mUri); + } + } + } + private void handleBind() { - Slice s; + Slice cachedSlice = doBind(null); + synchronized (mLock) { + if (!isPinned()) return; + for (int i = mListeners.size() - 1; i >= 0; i--) { + ISliceListener l = mListeners.valueAt(i); + Slice s = cachedSlice; + if (s == null || s.hasHint(Slice.HINT_CALLER_NEEDED)) { + s = doBind(mPkgMap.get(l)); + } + if (s == null) { + mListeners.removeAt(i); + continue; + } + try { + l.onSliceUpdated(s); + } catch (RemoteException e) { + Log.e(TAG, "Unable to notify slice " + mUri, e); + mListeners.removeAt(i); + continue; + } + } + if (!isPinned()) { + // All the listeners died, remove from pinned state. + mService.removePinnedSlice(mUri); + } + } + } + + 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; + return null; } - if (res == null) return; + if (res == null) return null; Bundle.setDefusable(res, true); - s = res.getParcelable(SliceProvider.EXTRA_SLICE); - } - synchronized (mLock) { - mListeners.removeIf(l -> { - try { - l.onSliceUpdated(s); - return false; - } catch (RemoteException e) { - Log.e(TAG, "Unable to notify slice " + mUri, e); - return true; - } - }); - if (!isPinned()) { - // All the listeners died, remove from pinned state. - mService.removePinnedSlice(mUri); - } + return res.getParcelable(SliceProvider.EXTRA_SLICE); } } diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java index 2d9e772a6b0c..c1915801b61b 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 @@ public class SliceManagerService extends ISliceManager.Stub { @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; @@ -81,9 +91,9 @@ public class SliceManagerService extends ISliceManager.Stub { mObserver = new ContentObserver(mHandler) { @Override - public void onChange(boolean selfChange, Uri uri) { + public void onChange(boolean selfChange, Uri uri, int userId) { try { - getPinnedSlice(uri).onChange(); + getPinnedSlice(maybeAddUserId(uri, userId)).onChange(); } catch (IllegalStateException e) { Log.e(TAG, "Received change for unpinned slice " + uri, e); } @@ -111,7 +121,7 @@ public class SliceManagerService extends ISliceManager.Stub { verifyCaller(pkg); uri = maybeAddUserId(uri, Binder.getCallingUserHandle().getIdentifier()); enforceAccess(pkg, uri); - getOrCreatePinnedSlice(uri).addSliceListener(listener, specs); + getOrCreatePinnedSlice(uri).addSliceListener(listener, pkg, specs); } @Override @@ -156,8 +166,45 @@ public class SliceManagerService extends ISliceManager.Stub { 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) { + protected void removePinnedSlice(Uri uri) { synchronized (mLock) { mPinnedSlicesByUri.remove(uri).destroy(); } @@ -186,7 +233,7 @@ public class SliceManagerService extends ISliceManager.Stub { } @VisibleForTesting - PinnedSliceState createPinnedSlice(Uri uri) { + protected PinnedSliceState createPinnedSlice(Uri uri) { return new PinnedSliceState(this, uri); } @@ -202,12 +249,45 @@ public class SliceManagerService extends ISliceManager.Stub { 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 @@ public class SliceManagerService extends ISliceManager.Stub { } 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,13 +345,14 @@ public class SliceManagerService extends ISliceManager.Stub { 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. // TODO: Unify if possible @VisibleForTesting - String getDefaultHome(int userId) { + protected String getDefaultHome(int userId) { final long token = Binder.clearCallingIdentity(); try { final List<ResolveInfo> allHomeCandidates = new ArrayList<>(); @@ -301,7 +388,7 @@ public class SliceManagerService extends ISliceManager.Stub { lastPriority = ri.priority; } } - return detected.getPackageName(); + return detected != null ? detected.getPackageName() : null; } finally { Binder.restoreCallingIdentity(token); } @@ -349,4 +436,26 @@ public class SliceManagerService extends ISliceManager.Stub { 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 2f5e2f8665f9..4cb5e089bd83 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.content.pm.UserInfo; 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 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } 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 b5d0c60efae8..95006ffb2ed6 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 @@ public interface StatusBarManagerInternal { * * @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 79d3dbbe8c83..c58c208dea81 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 @@ public class StatusBarManagerService extends IStatusBarService.Stub { } @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 b474c62ba427..ce3f512deccf 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 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree 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 fba404ed6f0e..6dc384a8831e 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.PRIVATE_FLAG_KEYGUARD; 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 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo */ 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 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo @Override void assignChildLayers(SurfaceControl.Transaction t) { - 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++); + final int HOME_STACK_STATE = 0; + final int NORMAL_STACK_STATE = 1; + final int ALWAYS_ON_TOP_STATE = 2; + + int layer = 0; + 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++); + } } - } - for (int i = 0; i < mChildren.size(); i++) { - final TaskStack s = mChildren.get(i); - if (s.needsZBoost() && !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.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 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return mAppAnimationLayer; } + SurfaceControl getSplitScreenDividerAnchor() { + return mSplitScreenDividerAnchor; + } + @Override void onParentSet() { super.onParentSet(); @@ -3541,11 +3563,18 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo 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 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo && 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/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java index 688b4ffcee4b..8515dcb69970 100644 --- a/services/core/java/com/android/server/wm/RemoteAnimationController.java +++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java @@ -99,11 +99,15 @@ class RemoteAnimationController { } private RemoteAnimationTarget[] createAnimations() { - final RemoteAnimationTarget[] result = new RemoteAnimationTarget[mPendingAnimations.size()]; + final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>(); for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { - result[i] = mPendingAnimations.get(i).createRemoteAppAnimation(); + final RemoteAnimationTarget target = + mPendingAnimations.get(i).createRemoteAppAnimation(); + if (target != null) { + targets.add(target); + } } - return result; + return targets.toArray(new RemoteAnimationTarget[targets.size()]); } private void onAnimationFinished() { @@ -145,9 +149,17 @@ class RemoteAnimationController { } RemoteAnimationTarget createRemoteAppAnimation() { - return new RemoteAnimationTarget(mAppWindowToken.getTask().mTaskId, getMode(), + final Task task = mAppWindowToken.getTask(); + final WindowState mainWindow = mAppWindowToken.findMainWindow(); + if (task == null) { + return null; + } + if (mainWindow == null) { + return null; + } + return new RemoteAnimationTarget(task.mTaskId, getMode(), mCapturedLeash, !mAppWindowToken.fillsParent(), - mAppWindowToken.findMainWindow().mWinAnimator.mLastClipRect, + mainWindow.mWinAnimator.mLastClipRect, mAppWindowToken.getPrefixOrderIndex(), mPosition, mStackBounds); } diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index b18c1a0ed504..0bc58e024f8a 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -55,6 +55,17 @@ cc_library_static { "frameworks/native/services", "system/gatekeeper/include", ], + + product_variables: { + arc: { + cflags: [ + "-DUSE_ARC", + ], + srcs: [ + "com_android_server_ArcVideoService.cpp", + ], + } + } } cc_defaults { @@ -119,4 +130,17 @@ cc_defaults { "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 000000000000..7df8276085ee --- /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 071b6b808ff0..07ddb0561f92 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -52,6 +52,9 @@ int register_android_server_SyntheticPasswordManager(JNIEnv* env); 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 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) 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 99275e88a242..7a0b1bf46fcf 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 @@ import java.util.List; */ 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 77b87b6394b4..6bee9d67f215 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.Preconditions; 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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void onStart() { publishBinderService(Context.DEVICE_POLICY_SERVICE, mService); - mService.handleStart(); } @Override @@ -1743,6 +1744,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return LocalServices.getService(UsageStatsManagerInternal.class); } + NetworkPolicyManagerInternal getNetworkPolicyManagerInternal() { + return LocalServices.getService(NetworkPolicyManagerInternal.class); + } + NotificationManager getNotificationManager() { return mContext.getSystemService(NotificationManager.class); } @@ -1996,6 +2001,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { 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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { 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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override - void handleStart() { - pushActiveAdminPackages(); - } - - @Override void handleStartUser(int userId) { updateScreenCaptureDisabledInWindowManager(userId, getScreenCaptureDisabled(null, userId)); @@ -3493,6 +3498,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } + 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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { 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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { 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/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index e660c50fcbc1..94a356e65878 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -1172,6 +1172,15 @@ public final class SystemServer { } traceEnd(); + traceBeginAndSlog("StartSystemUpdateManagerService"); + try { + ServiceManager.addService(Context.SYSTEM_UPDATE_SERVICE, + new SystemUpdateManagerService(context)); + } catch (Throwable e) { + reportWtf("starting SystemUpdateManagerService", e); + } + traceEnd(); + traceBeginAndSlog("StartUpdateLockService"); try { ServiceManager.addService(Context.UPDATE_LOCK_SERVICE, diff --git a/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java b/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java index 429dd8fd1d3d..de54e52d4d0a 100644 --- a/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java +++ b/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java @@ -42,6 +42,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.os.BatteryManager; import android.os.Handler; import android.os.Looper; import android.os.PowerManager.ServiceType; @@ -51,6 +52,7 @@ import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.provider.Settings; +import android.provider.Settings.Global; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.test.mock.MockContentResolver; @@ -105,6 +107,9 @@ public class ForceAppStandbyTrackerTest { PowerManagerInternal injectPowerManagerInternal() { return mMockPowerManagerInternal; } + + @Override + boolean isSmallBatteryDevice() { return mIsSmallBatteryDevice; }; } private static final int UID_1 = Process.FIRST_APPLICATION_UID + 1; @@ -144,6 +149,7 @@ public class ForceAppStandbyTrackerTest { private FakeSettingsProvider mFakeSettingsProvider; private boolean mPowerSaveMode; + private boolean mIsSmallBatteryDevice; private final ArraySet<Pair<Integer, String>> mRestrictedPackages = new ArraySet(); @@ -232,6 +238,7 @@ public class ForceAppStandbyTrackerTest { assertNotNull(mAppOpsCallback); assertNotNull(mPowerSaveObserver); assertNotNull(mReceiver); + assertNotNull(instance.mFlagsObserver); } private void setAppOps(int uid, String packageName, boolean restrict) throws RemoteException { @@ -852,6 +859,56 @@ public class ForceAppStandbyTrackerTest { assertTrue(instance.isRunAnyInBackgroundAppOpsAllowed(UID_10_2, PACKAGE_2)); } + @Test + public void testSmallBatteryAndPluggedIn() throws Exception { + // This is a small battery device + mIsSmallBatteryDevice = true; + + final ForceAppStandbyTrackerTestable instance = newInstance(); + callStart(instance); + assertFalse(instance.isForceAllAppsStandbyEnabled()); + + // Setting/experiment for all app standby for small battery is enabled + Global.putInt(mMockContentResolver, Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED, 1); + instance.mFlagsObserver.onChange(true, + Global.getUriFor(Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED)); + assertTrue(instance.isForceAllAppsStandbyEnabled()); + + // When battery is plugged in, force app standby is disabled + Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED); + intent.putExtra(BatteryManager.EXTRA_PLUGGED, BatteryManager.BATTERY_PLUGGED_USB); + mReceiver.onReceive(mMockContext, intent); + assertFalse(instance.isForceAllAppsStandbyEnabled()); + + // When battery stops plugged in, force app standby is enabled + mReceiver.onReceive(mMockContext, new Intent(Intent.ACTION_BATTERY_CHANGED)); + assertTrue(instance.isForceAllAppsStandbyEnabled()); + } + + @Test + public void testNotSmallBatteryAndPluggedIn() throws Exception { + // Not a small battery device, so plugged in status should not affect forced app standby + mIsSmallBatteryDevice = false; + + final ForceAppStandbyTrackerTestable instance = newInstance(); + callStart(instance); + assertFalse(instance.isForceAllAppsStandbyEnabled()); + + mPowerSaveMode = true; + mPowerSaveObserver.accept(getPowerSaveState()); + assertTrue(instance.isForceAllAppsStandbyEnabled()); + + // When battery is plugged in, force app standby is unaffected + Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED); + intent.putExtra(BatteryManager.EXTRA_PLUGGED, BatteryManager.BATTERY_PLUGGED_USB); + mReceiver.onReceive(mMockContext, intent); + assertTrue(instance.isForceAllAppsStandbyEnabled()); + + // When battery stops plugged in, force app standby is unaffected + mReceiver.onReceive(mMockContext, new Intent(Intent.ACTION_BATTERY_CHANGED)); + assertTrue(instance.isForceAllAppsStandbyEnabled()); + } + static int[] array(int... appIds) { Arrays.sort(appIds); return appIds; 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 1213e814b3fa..e72e4601bbe8 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 @@ public class GlobalActionPerformerTest { @Mock Context mMockContext; @Mock WindowManagerInternal mMockWindowManagerInternal; @Mock StatusBarManager mMockStatusBarManager; + @Mock ScreenshotHelper mMockScreenshotHelper; @Before public void setup() { @@ -48,7 +54,8 @@ public class GlobalActionPerformerTest { .thenReturn(mMockStatusBarManager); mGlobalActionPerformer = - new GlobalActionPerformer(mMockContext, mMockWindowManagerInternal); + new GlobalActionPerformer(mMockContext, mMockWindowManagerInternal, + () -> mMockScreenshotHelper); } @Test @@ -70,4 +77,13 @@ public class GlobalActionPerformerTest { 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 5134f523a8ff..06f138ba898b 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 android.view.IWindowManager; 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 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi } @Override + NetworkPolicyManagerInternal getNetworkPolicyManagerInternal() { + return services.networkPolicyManagerInternal; + } + + @Override PackageManagerInternal getPackageManagerInternal() { return services.packageManagerInternal; } @@ -438,5 +444,10 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi 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 1df0ff23f847..725ede81754a 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 @@ public class DevicePolicyManagerTest extends DpmTestBase { 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 @@ public class DevicePolicyManagerTest extends DpmTestBase { 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 @@ public class DevicePolicyManagerTest extends DpmTestBase { 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 @@ public class DevicePolicyManagerTest extends DpmTestBase { 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 @@ public class DevicePolicyManagerTest extends DpmTestBase { 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 @@ public class DevicePolicyManagerTest extends DpmTestBase { 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 268d424734e2..0343a52e05da 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 android.view.IWindowManager; 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 class MockSystemServices { 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 @@ public class MockSystemServices { 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/PrivacyUtilsTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/PrivacyUtilsTests.java index a31b46ce5534..999dce51bd9c 100644 --- a/services/tests/servicestests/src/com/android/server/net/watchlist/PrivacyUtilsTests.java +++ b/services/tests/servicestests/src/com/android/server/net/watchlist/PrivacyUtilsTests.java @@ -77,9 +77,9 @@ public class PrivacyUtilsTests { assertEquals(6, result.size()); assertTrue(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB48")); assertTrue(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB49")); - assertFalse(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB47")); - assertTrue(result.get("E86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB45")); - assertFalse(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB44")); + assertTrue(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB47")); + assertFalse(result.get("E86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB45")); + assertTrue(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB44")); assertTrue(result.get("B86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB43")); } @@ -87,7 +87,7 @@ public class PrivacyUtilsTests { public void testPrivacyUtils_createInsecureDPEncoderForTest() throws Exception { DifferentialPrivacyEncoder encoder = PrivacyUtils.createInsecureDPEncoderForTest("foo"); assertEquals( - "EncoderId: watchlist_encoder:foo, ProbabilityF: 0.400, ProbabilityP: 0.250, " + "EncoderId: watchlist_encoder:foo, ProbabilityF: 0.469, ProbabilityP: 0.280, " + "ProbabilityQ: 1.000", encoder.getConfig().toString()); assertTrue(encoder.isInsecureEncoderForTest()); @@ -97,7 +97,7 @@ public class PrivacyUtilsTests { public void testPrivacyUtils_createSecureDPEncoderTest() throws Exception { DifferentialPrivacyEncoder encoder = PrivacyUtils.createSecureDPEncoder(TEST_SECRET, "foo"); assertEquals( - "EncoderId: watchlist_encoder:foo, ProbabilityF: 0.400, ProbabilityP: 0.250, " + "EncoderId: watchlist_encoder:foo, ProbabilityF: 0.469, ProbabilityP: 0.280, " + "ProbabilityQ: 1.000", encoder.getConfig().toString()); assertFalse(encoder.isInsecureEncoderForTest()); 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 851d2c62e87e..654acc20328d 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.Test; 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 @@ public class WatchlistConfigTests { 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/policy/PhoneWindowManagerLayoutTest.java b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java index b6c370eb6b08..293f9afeea22 100644 --- a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java +++ b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java @@ -27,14 +27,19 @@ import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; +import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; import android.graphics.PixelFormat; +import android.graphics.Rect; import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; +import android.view.DisplayCutout; import android.view.WindowManager; import org.junit.Before; @@ -262,4 +267,23 @@ public class PhoneWindowManagerLayoutTest extends PhoneWindowManagerTestBase { assertInsetBy(mAppWindow.decorFrame, 0, 0, 0, 0); } + @Test + public void insetHint_screenDecorWindow() { + addDisplayCutout(); + mAppWindow.attrs.privateFlags |= PRIVATE_FLAG_IS_SCREEN_DECOR; + + mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + + final Rect content = new Rect(); + final Rect stable = new Rect(); + final Rect outsets = new Rect(); + final DisplayCutout.ParcelableWrapper cutout = new DisplayCutout.ParcelableWrapper(); + mPolicy.getInsetHintLw(mAppWindow.attrs, null /* taskBounds */, mFrames, content, + stable, outsets, cutout); + + assertThat(content, equalTo(new Rect())); + assertThat(stable, equalTo(new Rect())); + assertThat(outsets, equalTo(new Rect())); + assertThat(cutout.get(), equalTo(DisplayCutout.NO_CUTOUT)); + } }
\ No newline at end of file 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 6468763440a5..5f44fb675330 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 @@ public class ZOrderingTests extends WindowTestsBase { 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 @@ public class ZOrderingTests extends WindowTestsBase { 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 @@ public class ZOrderingTests extends WindowTestsBase { } } - 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 @@ public class ZOrderingTests extends WindowTestsBase { // 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 @@ public class ZOrderingTests extends WindowTestsBase { // 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 @@ public class ZOrderingTests extends WindowTestsBase { // 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 @@ public class ZOrderingTests extends WindowTestsBase { 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 @@ public class ZOrderingTests extends WindowTestsBase { 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 @@ public class ZOrderingTests extends WindowTestsBase { // 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 @@ public class ZOrderingTests extends WindowTestsBase { // 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 @@ public class ZOrderingTests extends WindowTestsBase { 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 f022dcf376a6..347557291427 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 ce328c29f01c..d3bb80485dfb 100644 --- a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java +++ b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java @@ -6,6 +6,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; @@ -21,8 +22,11 @@ import android.app.slice.SliceSpec; import android.content.ContentProvider; import android.content.IContentProvider; import android.net.Uri; +import android.os.Binder; import android.os.Bundle; import android.os.Handler; +import android.os.IBinder; +import android.os.IBinder.DeathRecipient; import android.os.RemoteException; import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; @@ -34,6 +38,7 @@ import com.android.server.UiServiceTestCase; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; @SmallTest @RunWith(AndroidTestingRunner.class) @@ -147,9 +152,10 @@ public class PinnedSliceStateTest extends UiServiceTestCase { @Test public void testListenerPin() { ISliceListener listener = mock(ISliceListener.class); + when(listener.asBinder()).thenReturn(new Binder()); assertFalse(mPinnedSliceManager.isPinned()); - mPinnedSliceManager.addSliceListener(listener, FIRST_SPECS); + mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS); assertTrue(mPinnedSliceManager.isPinned()); assertTrue(mPinnedSliceManager.removeSliceListener(listener)); @@ -159,12 +165,16 @@ public class PinnedSliceStateTest extends UiServiceTestCase { @Test public void testMultiListenerPin() { ISliceListener listener = mock(ISliceListener.class); + Binder value = new Binder(); + when(listener.asBinder()).thenReturn(value); ISliceListener listener2 = mock(ISliceListener.class); + Binder value2 = new Binder(); + when(listener2.asBinder()).thenReturn(value2); 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)); @@ -172,11 +182,33 @@ public class PinnedSliceStateTest extends UiServiceTestCase { } @Test + public void testListenerDeath() throws RemoteException { + ISliceListener listener = mock(ISliceListener.class); + IBinder binder = mock(IBinder.class); + when(binder.isBinderAlive()).thenReturn(true); + when(listener.asBinder()).thenReturn(binder); + assertFalse(mPinnedSliceManager.isPinned()); + + mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS); + assertTrue(mPinnedSliceManager.isPinned()); + + ArgumentCaptor<DeathRecipient> arg = ArgumentCaptor.forClass(DeathRecipient.class); + verify(binder).linkToDeath(arg.capture(), anyInt()); + + when(binder.isBinderAlive()).thenReturn(false); + arg.getValue().binderDied(); + + verify(mSliceService).removePinnedSlice(eq(TEST_URI)); + assertFalse(mPinnedSliceManager.isPinned()); + } + + @Test public void testPkgListenerPin() { ISliceListener listener = mock(ISliceListener.class); + when(listener.asBinder()).thenReturn(new Binder()); assertFalse(mPinnedSliceManager.isPinned()); - mPinnedSliceManager.addSliceListener(listener, FIRST_SPECS); + mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS); assertTrue(mPinnedSliceManager.isPinned()); mPinnedSliceManager.pin("pkg", FIRST_SPECS); @@ -191,6 +223,7 @@ public class PinnedSliceStateTest extends UiServiceTestCase { clearInvocations(mIContentProvider); ISliceListener listener = mock(ISliceListener.class); + when(listener.asBinder()).thenReturn(new Binder()); Slice s = new Slice.Builder(TEST_URI).build(); Bundle b = new Bundle(); b.putParcelable(SliceProvider.EXTRA_SLICE, s); @@ -199,7 +232,7 @@ public class PinnedSliceStateTest extends UiServiceTestCase { 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 ff3d58652aed..6782188c0f34 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.annotations.VisibleForTesting; 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.Arrays; 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 @@ public class AppStandbyController { // 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 @@ public class AppStandbyController { @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 class AppStandbyController { } } + 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 @@ public class AppStandbyController { 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 78cc81f2bab6..979feaa5a165 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 class UsageStatsService extends SystemService implements public void setActiveAdminApps(Set<String> packageNames, int userId) { mAppStandby.setActiveAdminApps(packageNames, userId); } + + @Override + public void onAdminDataAvailable() { + mAppStandby.onAdminDataAvailable(); + } } } diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java index 8c7d6b30ecdc..d17bdc85eb22 100644 --- a/telecomm/java/android/telecom/Call.java +++ b/telecomm/java/android/telecom/Call.java @@ -419,7 +419,6 @@ public final class Call { /** * Indicates the call used Assisted Dialing. * See also {@link Connection#PROPERTY_ASSISTED_DIALING_USED} - * @hide */ public static final int PROPERTY_ASSISTED_DIALING_USED = 0x00000200; diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index aaef8d3d3856..75224434bc1c 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -35,6 +35,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.ParcelFileDescriptor; +import android.os.Parcelable; import android.os.RemoteException; import android.os.SystemClock; import android.util.ArraySet; @@ -401,7 +402,6 @@ public abstract class Connection extends Conferenceable { /** * Set by the framework to indicate that a connection is using assisted dialing. - * @hide */ public static final int PROPERTY_ASSISTED_DIALING_USED = 1 << 9; @@ -2538,6 +2538,19 @@ public abstract class Connection extends Conferenceable { } /** + * Adds a parcelable extra to this {@code Connection}. + * + * @param key The extra key. + * @param value The value. + * @hide + */ + public final void putExtra(@NonNull String key, @NonNull Parcelable value) { + Bundle newExtras = new Bundle(); + newExtras.putParcelable(key, value); + putExtras(newExtras); + } + + /** * Removes extras from this {@code Connection}. * * @param keys The keys of the extras to remove. diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index d292db3efc82..91d5da30935b 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -602,12 +602,17 @@ public class TelecomManager { /** * The boolean indicated by this extra controls whether or not a call is eligible to undergo * assisted dialing. This extra is stored under {@link #EXTRA_OUTGOING_CALL_EXTRAS}. - * @hide */ public static final String EXTRA_USE_ASSISTED_DIALING = "android.telecom.extra.USE_ASSISTED_DIALING"; /** + * The bundle indicated by this extra store information related to the assisted dialing action. + */ + public static final String EXTRA_ASSISTED_DIALING_TRANSFORMATION_INFO = + "android.telecom.extra.ASSISTED_DIALING_TRANSFORMATION_INFO"; + + /** * The following 4 constants define how properties such as phone numbers and names are * displayed to the user. */ diff --git a/telecomm/java/android/telecom/TransformationInfo.java b/telecomm/java/android/telecom/TransformationInfo.java new file mode 100755 index 000000000000..3e848c6f2f80 --- /dev/null +++ b/telecomm/java/android/telecom/TransformationInfo.java @@ -0,0 +1,127 @@ +/* + * 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.telecom; + +import android.os.Parcelable; +import android.os.Parcel; + +/** + * A container class to hold information related to the Assisted Dialing operation. All member + * variables must be set when constructing a new instance of this class. + */ +public final class TransformationInfo implements Parcelable { + private String mOriginalNumber; + private String mTransformedNumber; + private String mUserHomeCountryCode; + private String mUserRoamingCountryCode; + private int mTransformedNumberCountryCallingCode; + + public TransformationInfo(String originalNumber, + String transformedNumber, + String userHomeCountryCode, + String userRoamingCountryCode, + int transformedNumberCountryCallingCode) { + String missing = ""; + if (originalNumber == null) { + missing += " mOriginalNumber"; + } + if (transformedNumber == null) { + missing += " mTransformedNumber"; + } + if (userHomeCountryCode == null) { + missing += " mUserHomeCountryCode"; + } + if (userRoamingCountryCode == null) { + missing += " mUserRoamingCountryCode"; + } + + if (!missing.isEmpty()) { + throw new IllegalStateException("Missing required properties:" + missing); + } + this.mOriginalNumber = originalNumber; + this.mTransformedNumber = transformedNumber; + this.mUserHomeCountryCode = userHomeCountryCode; + this.mUserRoamingCountryCode = userRoamingCountryCode; + this.mTransformedNumberCountryCallingCode = transformedNumberCountryCallingCode; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel out, int flags) { + out.writeString(mOriginalNumber); + out.writeString(mTransformedNumber); + out.writeString(mUserHomeCountryCode); + out.writeString(mUserRoamingCountryCode); + out.writeInt(mTransformedNumberCountryCallingCode); + } + + public static final Parcelable.Creator<TransformationInfo> CREATOR + = new Parcelable.Creator<TransformationInfo>() { + public TransformationInfo createFromParcel(Parcel in) { + return new TransformationInfo(in); + } + + public TransformationInfo[] newArray(int size) { + return new TransformationInfo[size]; + } + }; + + private TransformationInfo(Parcel in) { + mOriginalNumber = in.readString(); + mTransformedNumber = in.readString(); + mUserHomeCountryCode = in.readString(); + mUserRoamingCountryCode = in.readString(); + mTransformedNumberCountryCallingCode = in.readInt(); + } + + /** + * The original number that underwent Assisted Dialing. + */ + public String getOriginalNumber() { + return mOriginalNumber; + } + + /** + * The number after it underwent Assisted Dialing. + */ + public String getTransformedNumber() { + return mTransformedNumber; + } + + /** + * The user's home country code that was used when attempting to transform the number. + */ + public String getUserHomeCountryCode() { + return mUserHomeCountryCode; + } + + /** + * The users's roaming country code that was used when attempting to transform the number. + */ + public String getUserRoamingCountryCode() { + return mUserRoamingCountryCode; + } + + /** + * The country calling code that was used in the transformation. + */ + public int getTransformedNumberCountryCallingCode() { + return mTransformedNumberCountryCallingCode; + } +} diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index ce0b551311b0..649d4783cc7a 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -1644,6 +1644,13 @@ public class CarrierConfigManager { "roaming_operator_string_array"; /** + * Controls whether Assisted Dialing is enabled and the preference is shown. This feature + * transforms numbers when the user is roaming. + */ + public static final String KEY_ASSISTED_DIALING_ENABLED_BOOL = + "assisted_dialing_enabled_bool"; + + /** * URL from which the proto containing the public key of the Carrier used for * IMSI encryption will be downloaded. * @hide @@ -2040,6 +2047,7 @@ public class CarrierConfigManager { false); sDefaults.putStringArray(KEY_NON_ROAMING_OPERATOR_STRING_ARRAY, null); sDefaults.putStringArray(KEY_ROAMING_OPERATOR_STRING_ARRAY, null); + sDefaults.putBoolean(KEY_ASSISTED_DIALING_ENABLED_BOOL, true); sDefaults.putBoolean(KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, false); sDefaults.putBoolean(KEY_RTT_SUPPORTED_BOOL, false); sDefaults.putBoolean(KEY_DISABLE_CHARGE_INDICATION_BOOL, false); diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java index 4e1c15fa1251..38408fe3d0fd 100644 --- a/telephony/java/android/telephony/SubscriptionInfo.java +++ b/telephony/java/android/telephony/SubscriptionInfo.java @@ -126,14 +126,31 @@ public class SubscriptionInfo implements Parcelable { 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 class SubscriptionInfo implements Parcelable { 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 @@ public class SubscriptionInfo implements Parcelable { this.mCountryIso = countryIso; this.mIsEmbedded = isEmbedded; this.mAccessRules = accessRules; + this.mCardId = cardId; } /** @@ -387,6 +405,14 @@ public class SubscriptionInfo implements Parcelable { 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 @@ public class SubscriptionInfo implements Parcelable { 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 @@ public class SubscriptionInfo implements Parcelable { mIconBitmap.writeToParcel(dest, flags); dest.writeBoolean(mIsEmbedded); dest.writeTypedArray(mAccessRules, flags); + dest.writeString(mCardId); } @Override @@ -459,11 +487,13 @@ public class SubscriptionInfo implements Parcelable { @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 14060935f4ff..57f4cf28d90d 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -284,6 +284,14 @@ public class SubscriptionManager { 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/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index de9e691eadcf..17f809d3863a 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -1024,8 +1024,8 @@ public class TelephonyManager { /** * An int extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} which indicates - * the updated carrier id {@link TelephonyManager#getSubscriptionCarrierId()} of the current - * subscription. + * the updated carrier id {@link TelephonyManager#getAndroidCarrierIdForSubscription()} of + * the current subscription. * <p>Will be {@link TelephonyManager#UNKNOWN_CARRIER_ID} if the subscription is unavailable or * the carrier cannot be identified. */ @@ -6900,14 +6900,19 @@ public class TelephonyManager { /** * Returns carrier id of the current subscription. - * <p>To recognize a carrier (including MVNO) as a first class identity, assign each carrier - * with a canonical integer a.k.a carrier id. + * <p>To recognize a carrier (including MVNO) as a first-class identity, Android assigns each + * carrier with a canonical integer a.k.a. android carrier id. The Android carrier ID is an + * Android platform-wide identifier for a carrier. AOSP maintains carrier ID assignments in + * <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/carrier_list.textpb">here</a> + * + * <p>Apps which have carrier-specific configurations or business logic can use the carrier id + * as an Android platform-wide identifier for carriers. * * @return Carrier id of the current subscription. Return {@link #UNKNOWN_CARRIER_ID} if the * subscription is unavailable or the carrier cannot be identified. * @throws IllegalStateException if telephony service is unavailable. */ - public int getSubscriptionCarrierId() { + public int getAndroidCarrierIdForSubscription() { try { ITelephony service = getITelephony(); return service.getSubscriptionCarrierId(getSubId()); @@ -6923,17 +6928,18 @@ public class TelephonyManager { /** * Returns carrier name of the current subscription. - * <p>Carrier name is a user-facing name of carrier id {@link #getSubscriptionCarrierId()}, - * usually the brand name of the subsidiary (e.g. T-Mobile). Each carrier could configure - * multiple {@link #getSimOperatorName() SPN} but should have a single carrier name. - * Carrier name is not a canonical identity, use {@link #getSubscriptionCarrierId()} instead. + * <p>Carrier name is a user-facing name of carrier id + * {@link #getAndroidCarrierIdForSubscription()}, usually the brand name of the subsidiary + * (e.g. T-Mobile). Each carrier could configure multiple {@link #getSimOperatorName() SPN} but + * should have a single carrier name. Carrier name is not a canonical identity, + * use {@link #getAndroidCarrierIdForSubscription()} instead. * <p>The returned carrier name is unlocalized. * * @return Carrier name of the current subscription. Return {@code null} if the subscription is * unavailable or the carrier cannot be identified. * @throws IllegalStateException if telephony service is unavailable. */ - public String getSubscriptionCarrierName() { + public CharSequence getAndroidCarrierNameForSubscription() { try { ITelephony service = getITelephony(); return service.getSubscriptionCarrierName(getSubId()); diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java index 2ab8d4fb900e..73a05af8e56e 100644 --- a/telephony/java/android/telephony/data/ApnSetting.java +++ b/telephony/java/android/telephony/data/ApnSetting.java @@ -21,22 +21,23 @@ import android.annotation.StringDef; import android.content.ContentValues; import android.database.Cursor; import android.hardware.radio.V1_0.ApnTypes; -import android.net.NetworkUtils; import android.os.Parcel; import android.os.Parcelable; import android.provider.Telephony; import android.telephony.Rlog; +import android.telephony.ServiceState; +import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.net.InetAddress; import java.net.MalformedURLException; -import java.net.UnknownHostException; import java.net.URL; -import java.net.InetAddress; -import java.util.Arrays; +import java.net.UnknownHostException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -67,8 +68,8 @@ public class ApnSetting implements Parcelable { private final int mMtu; private final boolean mCarrierEnabled; - private final int mBearer; - private final int mBearerBitmask; + + private final int mNetworkTypeBitmask; private final int mProfileId; @@ -103,34 +104,6 @@ public class ApnSetting implements Parcelable { } /** - * Radio Access Technology info. - * To check what values can hold, refer to ServiceState.java. - * This should be spread to other technologies, - * but currently only used for LTE(14) and EHRPD(13). - * - * @return the bearer info of the APN - * @hide - */ - public int getBearer() { - return mBearer; - } - - /** - * Returns the radio access technology bitmask for this APN. - * - * To check what values can hold, refer to ServiceState.java. This is a bitmask of radio - * technologies in ServiceState. - * This should be spread to other technologies, - * but currently only used for LTE(14) and EHRPD(13). - * - * @return the radio access technology bitmask - * @hide - */ - public int getBearerBitmask() { - return mBearerBitmask; - } - - /** * Returns the profile id to which the APN saved in modem. * * @return the profile id of the APN @@ -411,6 +384,20 @@ public class ApnSetting implements Parcelable { return mCarrierEnabled; } + /** + * Returns a bitmask describing the Radio Technologies(Network Types) which this APN may use. + * + * NetworkType bitmask is calculated from NETWORK_TYPE defined in {@link TelephonyManager}. + * + * Examples of Network Types include {@link TelephonyManager#NETWORK_TYPE_UNKNOWN}, + * {@link TelephonyManager#NETWORK_TYPE_GPRS}, {@link TelephonyManager#NETWORK_TYPE_EDGE}. + * + * @return a bitmask describing the Radio Technologies(Network Types) + */ + public int getNetworkTypeBitmask() { + return mNetworkTypeBitmask; + } + /** @hide */ @StringDef({ MVNO_TYPE_SPN, @@ -452,8 +439,7 @@ public class ApnSetting implements Parcelable { this.mRoamingProtocol = builder.mRoamingProtocol; this.mMtu = builder.mMtu; this.mCarrierEnabled = builder.mCarrierEnabled; - this.mBearer = builder.mBearer; - this.mBearerBitmask = builder.mBearerBitmask; + this.mNetworkTypeBitmask = builder.mNetworkTypeBitmask; this.mProfileId = builder.mProfileId; this.mModemCognitive = builder.mModemCognitive; this.mMaxConns = builder.mMaxConns; @@ -467,8 +453,8 @@ public class ApnSetting implements Parcelable { public static ApnSetting makeApnSetting(int id, String operatorNumeric, String entryName, String apnName, InetAddress proxy, int port, URL mmsc, InetAddress mmsProxy, int mmsPort, String user, String password, int authType, List<String> types, - String protocol, String roamingProtocol, boolean carrierEnabled, int bearer, - int bearerBitmask, int profileId, boolean modemCognitive, int maxConns, + String protocol, String roamingProtocol, boolean carrierEnabled, + int networkTypeBitmask, int profileId, boolean modemCognitive, int maxConns, int waitTime, int maxConnsTime, int mtu, String mvnoType, String mvnoMatchData) { return new Builder() .setId(id) @@ -487,8 +473,7 @@ public class ApnSetting implements Parcelable { .setProtocol(protocol) .setRoamingProtocol(roamingProtocol) .setCarrierEnabled(carrierEnabled) - .setBearer(bearer) - .setBearerBitmask(bearerBitmask) + .setNetworkTypeBitmask(networkTypeBitmask) .setProfileId(profileId) .setModemCognitive(modemCognitive) .setMaxConns(maxConns) @@ -504,6 +489,14 @@ public class ApnSetting implements Parcelable { public static ApnSetting makeApnSetting(Cursor cursor) { String[] types = parseTypes( cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE))); + int networkTypeBitmask = cursor.getInt( + cursor.getColumnIndexOrThrow(Telephony.Carriers.NETWORK_TYPE_BITMASK)); + if (networkTypeBitmask == 0) { + final int bearerBitmask = cursor.getInt(cursor.getColumnIndexOrThrow( + Telephony.Carriers.BEARER_BITMASK)); + networkTypeBitmask = + ServiceState.convertBearerBitmaskToNetworkTypeBitmask(bearerBitmask); + } return makeApnSetting( cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)), @@ -529,9 +522,7 @@ public class ApnSetting implements Parcelable { Telephony.Carriers.ROAMING_PROTOCOL)), cursor.getInt(cursor.getColumnIndexOrThrow( Telephony.Carriers.CARRIER_ENABLED)) == 1, - cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.BEARER)), - cursor.getInt(cursor.getColumnIndexOrThrow( - Telephony.Carriers.BEARER_BITMASK)), + networkTypeBitmask, cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROFILE_ID)), cursor.getInt(cursor.getColumnIndexOrThrow( Telephony.Carriers.MODEM_COGNITIVE)) == 1, @@ -551,7 +542,7 @@ public class ApnSetting implements Parcelable { return makeApnSetting(apn.mId, apn.mOperatorNumeric, apn.mEntryName, apn.mApnName, apn.mProxy, apn.mPort, apn.mMmsc, apn.mMmsProxy, apn.mMmsPort, apn.mUser, apn.mPassword, apn.mAuthType, apn.mTypes, apn.mProtocol, apn.mRoamingProtocol, - apn.mCarrierEnabled, apn.mBearer, apn.mBearerBitmask, apn.mProfileId, + apn.mCarrierEnabled, apn.mNetworkTypeBitmask, apn.mProfileId, apn.mModemCognitive, apn.mMaxConns, apn.mWaitTime, apn.mMaxConnsTime, apn.mMtu, apn.mMvnoType, apn.mMvnoMatchData); } @@ -559,7 +550,7 @@ public class ApnSetting implements Parcelable { /** @hide */ public String toString() { StringBuilder sb = new StringBuilder(); - sb.append("[ApnSettingV3] ") + sb.append("[ApnSettingV4] ") .append(mEntryName) .append(", ").append(mId) .append(", ").append(mOperatorNumeric) @@ -579,8 +570,6 @@ public class ApnSetting implements Parcelable { sb.append(", ").append(mProtocol); sb.append(", ").append(mRoamingProtocol); sb.append(", ").append(mCarrierEnabled); - sb.append(", ").append(mBearer); - sb.append(", ").append(mBearerBitmask); sb.append(", ").append(mProfileId); sb.append(", ").append(mModemCognitive); sb.append(", ").append(mMaxConns); @@ -590,6 +579,7 @@ public class ApnSetting implements Parcelable { sb.append(", ").append(mMvnoType); sb.append(", ").append(mMvnoMatchData); sb.append(", ").append(mPermanentFailed); + sb.append(", ").append(mNetworkTypeBitmask); return sb.toString(); } @@ -678,8 +668,6 @@ public class ApnSetting implements Parcelable { && Objects.equals(mProtocol, other.mProtocol) && Objects.equals(mRoamingProtocol, other.mRoamingProtocol) && Objects.equals(mCarrierEnabled, other.mCarrierEnabled) - && Objects.equals(mBearer, other.mBearer) - && Objects.equals(mBearerBitmask, other.mBearerBitmask) && Objects.equals(mProfileId, other.mProfileId) && Objects.equals(mModemCognitive, other.mModemCognitive) && Objects.equals(mMaxConns, other.mMaxConns) @@ -687,13 +675,14 @@ public class ApnSetting implements Parcelable { && Objects.equals(mMaxConnsTime, other.mMaxConnsTime) && Objects.equals(mMtu, other.mMtu) && Objects.equals(mMvnoType, other.mMvnoType) - && Objects.equals(mMvnoMatchData, other.mMvnoMatchData); + && Objects.equals(mMvnoMatchData, other.mMvnoMatchData) + && Objects.equals(mNetworkTypeBitmask, other.mNetworkTypeBitmask); } /** * Compare two APN settings * - * Note: This method does not compare 'id', 'bearer', 'bearerBitmask'. We only use this for + * Note: This method does not compare 'mId', 'mNetworkTypeBitmask'. We only use this for * determining if tearing a data call is needed when conditions change. See * cleanUpConnectionsOnUpdatedApns in DcTracker. * @@ -752,13 +741,13 @@ public class ApnSetting implements Parcelable { && xorEquals(this.mProtocol, other.mProtocol) && xorEquals(this.mRoamingProtocol, other.mRoamingProtocol) && Objects.equals(this.mCarrierEnabled, other.mCarrierEnabled) - && Objects.equals(this.mBearerBitmask, other.mBearerBitmask) && Objects.equals(this.mProfileId, other.mProfileId) && Objects.equals(this.mMvnoType, other.mMvnoType) && Objects.equals(this.mMvnoMatchData, other.mMvnoMatchData) && xorEqualsURL(this.mMmsc, other.mMmsc) && xorEqualsInetAddress(this.mMmsProxy, other.mMmsProxy) - && xorEqualsPort(this.mMmsPort, other.mMmsPort)); + && xorEqualsPort(this.mMmsPort, other.mMmsPort)) + && Objects.equals(this.mNetworkTypeBitmask, other.mNetworkTypeBitmask); } // Equal or one is not specified. @@ -808,53 +797,33 @@ public class ApnSetting implements Parcelable { return TextUtils.join(",", types); } + private String nullToEmpty(String stringValue) { + return stringValue == null ? "" : stringValue; + } + /** @hide */ // Called by DPM. public ContentValues toContentValues() { ContentValues apnValue = new ContentValues(); - if (mOperatorNumeric != null) { - apnValue.put(Telephony.Carriers.NUMERIC, mOperatorNumeric); - } - if (mEntryName != null) { - apnValue.put(Telephony.Carriers.NAME, mEntryName); - } - if (mApnName != null) { - apnValue.put(Telephony.Carriers.APN, mApnName); - } - if (mProxy != null) { - apnValue.put(Telephony.Carriers.PROXY, inetAddressToString(mProxy)); - } + apnValue.put(Telephony.Carriers.NUMERIC, nullToEmpty(mOperatorNumeric)); + apnValue.put(Telephony.Carriers.NAME, nullToEmpty(mEntryName)); + apnValue.put(Telephony.Carriers.APN, nullToEmpty(mApnName)); + apnValue.put(Telephony.Carriers.PROXY, mProxy == null ? "" : inetAddressToString(mProxy)); apnValue.put(Telephony.Carriers.PORT, portToString(mPort)); - if (mMmsc != null) { - apnValue.put(Telephony.Carriers.MMSC, URLToString(mMmsc)); - } + apnValue.put(Telephony.Carriers.MMSC, mMmsc == null ? "" : URLToString(mMmsc)); apnValue.put(Telephony.Carriers.MMSPORT, portToString(mMmsPort)); - if (mMmsProxy != null) { - apnValue.put(Telephony.Carriers.MMSPROXY, inetAddressToString(mMmsProxy)); - } - if (mUser != null) { - apnValue.put(Telephony.Carriers.USER, mUser); - } - if (mPassword != null) { - apnValue.put(Telephony.Carriers.PASSWORD, mPassword); - } + apnValue.put(Telephony.Carriers.MMSPROXY, mMmsProxy == null + ? "" : inetAddressToString(mMmsProxy)); + apnValue.put(Telephony.Carriers.USER, nullToEmpty(mUser)); + apnValue.put(Telephony.Carriers.PASSWORD, nullToEmpty(mPassword)); apnValue.put(Telephony.Carriers.AUTH_TYPE, mAuthType); String apnType = deParseTypes(mTypes); - if (apnType != null) { - apnValue.put(Telephony.Carriers.TYPE, apnType); - } - if (mProtocol != null) { - apnValue.put(Telephony.Carriers.PROTOCOL, mProtocol); - } - if (mRoamingProtocol != null) { - apnValue.put(Telephony.Carriers.ROAMING_PROTOCOL, mRoamingProtocol); - } + apnValue.put(Telephony.Carriers.TYPE, nullToEmpty(apnType)); + apnValue.put(Telephony.Carriers.PROTOCOL, nullToEmpty(mProtocol)); + apnValue.put(Telephony.Carriers.ROAMING_PROTOCOL, nullToEmpty(mRoamingProtocol)); apnValue.put(Telephony.Carriers.CARRIER_ENABLED, mCarrierEnabled); - // networkTypeBit. - apnValue.put(Telephony.Carriers.BEARER_BITMASK, mBearerBitmask); - if (mMvnoType != null) { - apnValue.put(Telephony.Carriers.MVNO_TYPE, mMvnoType); - } + apnValue.put(Telephony.Carriers.MVNO_TYPE, nullToEmpty(mMvnoType)); + apnValue.put(Telephony.Carriers.NETWORK_TYPE_BITMASK, mNetworkTypeBitmask); return apnValue; } @@ -905,8 +874,16 @@ public class ApnSetting implements Parcelable { if (inetAddress == null) { return null; } - return TextUtils.isEmpty(inetAddress.getHostName()) - ? inetAddress.getHostAddress() : inetAddress.getHostName(); + final String inetAddressString = inetAddress.toString(); + if (TextUtils.isEmpty(inetAddressString)) { + return null; + } + final String hostName = inetAddressString.substring(0, inetAddressString.indexOf("/")); + final String address = inetAddressString.substring(inetAddressString.indexOf("/") + 1); + if (TextUtils.isEmpty(hostName) && TextUtils.isEmpty(address)) { + return null; + } + return TextUtils.isEmpty(hostName) ? address : hostName; } private static int portFromString(String strPort) { @@ -952,16 +929,33 @@ public class ApnSetting implements Parcelable { dest.writeString(mRoamingProtocol); dest.writeInt(mCarrierEnabled ? 1: 0); dest.writeString(mMvnoType); + dest.writeInt(mNetworkTypeBitmask); } private static ApnSetting readFromParcel(Parcel in) { - return makeApnSetting(in.readInt(), in.readString(), in.readString(), in.readString(), - (InetAddress)in.readValue(InetAddress.class.getClassLoader()), - in.readInt(), (URL)in.readValue(URL.class.getClassLoader()), - (InetAddress)in.readValue(InetAddress.class.getClassLoader()), - in.readInt(), in.readString(), in.readString(), in.readInt(), - Arrays.asList(in.readStringArray()), in.readString(), in.readString(), - in.readInt() > 0, 0, 0, 0, false, 0, 0, 0, 0, in.readString(), null); + final int id = in.readInt(); + final String operatorNumeric = in.readString(); + final String entryName = in.readString(); + final String apnName = in.readString(); + final InetAddress proxy = (InetAddress)in.readValue(InetAddress.class.getClassLoader()); + final int port = in.readInt(); + final URL mmsc = (URL)in.readValue(URL.class.getClassLoader()); + final InetAddress mmsProxy = (InetAddress)in.readValue(InetAddress.class.getClassLoader()); + final int mmsPort = in.readInt(); + final String user = in.readString(); + final String password = in.readString(); + final int authType = in.readInt(); + final List<String> types = Arrays.asList(in.readStringArray()); + final String protocol = in.readString(); + final String roamingProtocol = in.readString(); + final boolean carrierEnabled = in.readInt() > 0; + final String mvnoType = in.readString(); + final int networkTypeBitmask = in.readInt(); + + return makeApnSetting(id, operatorNumeric, entryName, apnName, + proxy, port, mmsc, mmsProxy, mmsPort, user, password, authType, types, protocol, + roamingProtocol, carrierEnabled, networkTypeBitmask, 0, false, + 0, 0, 0, 0, mvnoType, null); } public static final Parcelable.Creator<ApnSetting> CREATOR = @@ -1061,9 +1055,8 @@ public class ApnSetting implements Parcelable { private String mProtocol; private String mRoamingProtocol; private int mMtu; + private int mNetworkTypeBitmask; private boolean mCarrierEnabled; - private int mBearer; - private int mBearerBitmask; private int mProfileId; private boolean mModemCognitive; private int mMaxConns; @@ -1078,35 +1071,23 @@ public class ApnSetting implements Parcelable { public Builder() {} /** - * Set the MTU size of the mobile interface to which the APN connected. - * - * @param mtu the MTU size to set for the APN - * @hide - */ - public Builder setMtu(int mtu) { - this.mMtu = mtu; - return this; - } - - /** - * Sets bearer info. + * Sets the unique database id for this entry. * - * @param bearer the bearer info to set for the APN - * @hide + * @param id the unique database id to set for this entry */ - public Builder setBearer(int bearer) { - this.mBearer = bearer; + private Builder setId(int id) { + this.mId = id; return this; } /** - * Sets the radio access technology bitmask for this APN. + * Set the MTU size of the mobile interface to which the APN connected. * - * @param bearerBitmask the radio access technology bitmask to set for this APN + * @param mtu the MTU size to set for the APN * @hide */ - public Builder setBearerBitmask(int bearerBitmask) { - this.mBearerBitmask = bearerBitmask; + public Builder setMtu(int mtu) { + this.mMtu = mtu; return this; } @@ -1298,16 +1279,6 @@ public class ApnSetting implements Parcelable { } /** - * Sets the unique database id for this entry. - * - * @param id the unique database id to set for this entry - */ - public Builder setId(int id) { - this.mId = id; - return this; - } - - /** * Set the numeric operator ID for the APN. * * @param operatorNumeric the numeric operator ID to set for this entry @@ -1341,7 +1312,7 @@ public class ApnSetting implements Parcelable { } /** - * Sets the current status of APN. + * Sets the current status for this APN. * * @param carrierEnabled the current status to set for this APN */ @@ -1351,6 +1322,16 @@ public class ApnSetting implements Parcelable { } /** + * Sets Radio Technology (Network Type) info for this APN. + * + * @param networkTypeBitmask the Radio Technology (Network Type) info + */ + public Builder setNetworkTypeBitmask(int networkTypeBitmask) { + this.mNetworkTypeBitmask = networkTypeBitmask; + return this; + } + + /** * Sets the MVNO match type for this APN. * * Example of possible values: {@link #MVNO_TYPE_SPN}, {@link #MVNO_TYPE_IMSI}. diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java index 253432755179..d14670725e8e 100644 --- a/telephony/java/android/telephony/euicc/EuiccManager.java +++ b/telephony/java/android/telephony/euicc/EuiccManager.java @@ -71,8 +71,18 @@ public class EuiccManager { * TODO(b/35851809): Make this a SystemApi. */ @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_OTA_STATUS_CHANGED - = "android.telephony.euicc.action.OTA_STATUS_CHANGED"; + public static final String ACTION_OTA_STATUS_CHANGED = + "android.telephony.euicc.action.OTA_STATUS_CHANGED"; + + /** + * Broadcast Action: The action sent to carrier app so it knows the carrier setup is not + * completed. + * + * TODO(b/35851809): Make this a public API. + */ + @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_NOTIFY_CARRIER_SETUP = + "android.telephony.euicc.action.NOTIFY_CARRIER_SETUP"; /** * Intent action to provision an embedded subscription. diff --git a/tests/net/Android.mk b/tests/net/Android.mk index 1bd1af5e3c22..994f3ccd7560 100644 --- a/tests/net/Android.mk +++ b/tests/net/Android.mk @@ -31,32 +31,34 @@ LOCAL_COMPATIBILITY_SUITE := device-tests LOCAL_CERTIFICATE := platform # These are not normally accessible from apps so they must be explicitly included. -LOCAL_JNI_SHARED_LIBRARIES := libframeworksnettestsjni \ +LOCAL_JNI_SHARED_LIBRARIES := \ + android.hidl.token@1.0 \ libbacktrace \ libbase \ libbinder \ libc++ \ + libcrypto \ libcutils \ + libframeworksnettestsjni \ + libhidl-gen-utils \ + libhidlbase \ + libhidltransport \ + libhwbinder \ liblog \ liblzma \ libnativehelper \ libnetdaidl \ + libpackagelistparser \ + libpcre2 \ + libselinux \ libui \ libunwind \ libutils \ + libvintf \ libvndksupport \ - libcrypto \ - libhidl-gen-utils \ - libhidlbase \ - libhidltransport \ - libpackagelistparser \ - libpcre2 \ - libselinux \ libtinyxml2 \ - libvintf \ - libhwbinder \ libunwindstack \ - android.hidl.token@1.0 + libutilscallstack LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java index 4fbb228e6e53..d9d4eeba900f 100644 --- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java +++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java @@ -204,13 +204,13 @@ public class IpSecServiceParameterizedTest { } @Test - public void testCreateTransportModeTransform() throws Exception { + public void testCreateTransform() throws Exception { IpSecConfig ipSecConfig = new IpSecConfig(); addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig); addAuthAndCryptToIpSecConfig(ipSecConfig); IpSecTransformResponse createTransformResp = - mIpSecService.createTransportModeTransform(ipSecConfig, new Binder()); + mIpSecService.createTransform(ipSecConfig, new Binder()); assertEquals(IpSecManager.Status.OK, createTransformResp.status); verify(mMockNetd) @@ -236,14 +236,14 @@ public class IpSecServiceParameterizedTest { } @Test - public void testCreateTransportModeTransformAead() throws Exception { + public void testCreateTransformAead() throws Exception { IpSecConfig ipSecConfig = new IpSecConfig(); addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig); ipSecConfig.setAuthenticatedEncryption(AEAD_ALGO); IpSecTransformResponse createTransformResp = - mIpSecService.createTransportModeTransform(ipSecConfig, new Binder()); + mIpSecService.createTransform(ipSecConfig, new Binder()); assertEquals(IpSecManager.Status.OK, createTransformResp.status); verify(mMockNetd) @@ -269,14 +269,14 @@ public class IpSecServiceParameterizedTest { } @Test - public void testDeleteTransportModeTransform() throws Exception { + public void testDeleteTransform() throws Exception { IpSecConfig ipSecConfig = new IpSecConfig(); addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig); addAuthAndCryptToIpSecConfig(ipSecConfig); IpSecTransformResponse createTransformResp = - mIpSecService.createTransportModeTransform(ipSecConfig, new Binder()); - mIpSecService.deleteTransportModeTransform(createTransformResp.resourceId); + mIpSecService.createTransform(ipSecConfig, new Binder()); + mIpSecService.deleteTransform(createTransformResp.resourceId); verify(mMockNetd) .ipSecDeleteSecurityAssociation( @@ -302,7 +302,7 @@ public class IpSecServiceParameterizedTest { addAuthAndCryptToIpSecConfig(ipSecConfig); IpSecTransformResponse createTransformResp = - mIpSecService.createTransportModeTransform(ipSecConfig, new Binder()); + mIpSecService.createTransform(ipSecConfig, new Binder()); IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid()); @@ -334,7 +334,7 @@ public class IpSecServiceParameterizedTest { addAuthAndCryptToIpSecConfig(ipSecConfig); IpSecTransformResponse createTransformResp = - mIpSecService.createTransportModeTransform(ipSecConfig, new Binder()); + mIpSecService.createTransform(ipSecConfig, new Binder()); ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket()); int resourceId = createTransformResp.resourceId; diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java index 3eba881df427..a375b600ca60 100644 --- a/tests/net/java/com/android/server/IpSecServiceTest.java +++ b/tests/net/java/com/android/server/IpSecServiceTest.java @@ -166,6 +166,7 @@ public class IpSecServiceTest { mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId); udpEncapResp.fileDescriptor.close(); + // Verify quota and RefcountedResource objects cleaned up IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid()); assertEquals(0, userRecord.mSocketQuotaTracker.mCurrent); @@ -179,10 +180,8 @@ public class IpSecServiceTest { @Test public void testUdpEncapsulationSocketBinderDeath() throws Exception { - int localport = findUnusedPort(); - IpSecUdpEncapResponse udpEncapResp = - mIpSecService.openUdpEncapsulationSocket(localport, new Binder()); + mIpSecService.openUdpEncapsulationSocket(0, new Binder()); IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid()); @@ -192,6 +191,7 @@ public class IpSecServiceTest { refcountedRecord.binderDied(); + // Verify quota and RefcountedResource objects cleaned up assertEquals(0, userRecord.mSocketQuotaTracker.mCurrent); try { userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(udpEncapResp.resourceId); @@ -412,9 +412,9 @@ public class IpSecServiceTest { } @Test - public void testDeleteInvalidTransportModeTransform() throws Exception { + public void testDeleteInvalidTransform() throws Exception { try { - mIpSecService.deleteTransportModeTransform(1); + mIpSecService.deleteTransform(1); fail("IllegalArgumentException not thrown"); } catch (IllegalArgumentException e) { } diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java index 10d6deba61df..9f2cb921ea8e 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.ArgumentCaptor; 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 @@ public class IpConnectivityMetricsTest { } @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 @@ public class IpConnectivityMetricsTest { } @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 4098b989a16b..0504c79c55bd 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 class ServiceManagerPermissionTests extends TestCase { 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/WifiActivityEnergyInfo.java b/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java index 29bf02cac8fe..03c9fbeec918 100644 --- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java +++ b/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java @@ -56,6 +56,11 @@ public final class WifiActivityEnergyInfo implements Parcelable { /** * @hide */ + public long mControllerScanTimeMs; + + /** + * @hide + */ public long mControllerIdleTimeMs; /** @@ -69,13 +74,14 @@ public final class WifiActivityEnergyInfo implements Parcelable { public static final int STACK_STATE_STATE_IDLE = 3; public WifiActivityEnergyInfo(long timestamp, int stackState, - long txTime, long[] txTimePerLevel, long rxTime, long idleTime, - long energyUsed) { + long txTime, long[] txTimePerLevel, long rxTime, long scanTime, + long idleTime, long energyUsed) { mTimestamp = timestamp; mStackState = stackState; mControllerTxTimeMs = txTime; mControllerTxTimePerLevelMs = txTimePerLevel; mControllerRxTimeMs = rxTime; + mControllerScanTimeMs = scanTime; mControllerIdleTimeMs = idleTime; mControllerEnergyUsed = energyUsed; } @@ -88,6 +94,7 @@ public final class WifiActivityEnergyInfo implements Parcelable { + " mControllerTxTimeMs=" + mControllerTxTimeMs + " mControllerTxTimePerLevelMs=" + Arrays.toString(mControllerTxTimePerLevelMs) + " mControllerRxTimeMs=" + mControllerRxTimeMs + + " mControllerScanTimeMs=" + mControllerScanTimeMs + " mControllerIdleTimeMs=" + mControllerIdleTimeMs + " mControllerEnergyUsed=" + mControllerEnergyUsed + " }"; @@ -101,10 +108,11 @@ public final class WifiActivityEnergyInfo implements Parcelable { long txTime = in.readLong(); long[] txTimePerLevel = in.createLongArray(); long rxTime = in.readLong(); + long scanTime = in.readLong(); long idleTime = in.readLong(); long energyUsed = in.readLong(); return new WifiActivityEnergyInfo(timestamp, stackState, - txTime, txTimePerLevel, rxTime, idleTime, energyUsed); + txTime, txTimePerLevel, rxTime, scanTime, idleTime, energyUsed); } public WifiActivityEnergyInfo[] newArray(int size) { return new WifiActivityEnergyInfo[size]; @@ -117,6 +125,7 @@ public final class WifiActivityEnergyInfo implements Parcelable { out.writeLong(mControllerTxTimeMs); out.writeLongArray(mControllerTxTimePerLevelMs); out.writeLong(mControllerRxTimeMs); + out.writeLong(mControllerScanTimeMs); out.writeLong(mControllerIdleTimeMs); out.writeLong(mControllerEnergyUsed); } @@ -157,6 +166,13 @@ public final class WifiActivityEnergyInfo implements Parcelable { } /** + * @return scan time in ms + */ + public long getControllerScanTimeMillis() { + return mControllerScanTimeMs; + } + + /** * @return idle time in ms */ public long getControllerIdleTimeMillis() { @@ -183,6 +199,7 @@ public final class WifiActivityEnergyInfo implements Parcelable { public boolean isValid() { return ((mControllerTxTimeMs >=0) && (mControllerRxTimeMs >=0) && + (mControllerScanTimeMs >=0) && (mControllerIdleTimeMs >=0)); } -} +}
\ No newline at end of file diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index 6438631cb8ed..2c6011823828 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -267,8 +267,15 @@ public class WifiConfiguration implements Parcelable { 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 */ diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 99080d6cc2c0..e4b510db68f4 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -1625,6 +1625,7 @@ public class WifiManager { * * @return hex-string encoded configuration token or null if there is no current network * @hide + * @deprecated This API is deprecated */ public String getCurrentNetworkWpsNfcConfigurationToken() { try { @@ -2210,20 +2211,34 @@ public class WifiManager { /** @hide */ public static final int SAVE_NETWORK_SUCCEEDED = BASE + 9; - /** @hide */ + /** @hide + * @deprecated This is deprecated + */ public static final int START_WPS = BASE + 10; - /** @hide */ + /** @hide + * @deprecated This is deprecated + */ public static final int START_WPS_SUCCEEDED = BASE + 11; - /** @hide */ + /** @hide + * @deprecated This is deprecated + */ public static final int WPS_FAILED = BASE + 12; - /** @hide */ + /** @hide + * @deprecated This is deprecated + */ public static final int WPS_COMPLETED = BASE + 13; - /** @hide */ + /** @hide + * @deprecated This is deprecated + */ public static final int CANCEL_WPS = BASE + 14; - /** @hide */ + /** @hide + * @deprecated This is deprecated + */ public static final int CANCEL_WPS_FAILED = BASE + 15; - /** @hide */ + /** @hide + * @deprecated This is deprecated + */ public static final int CANCEL_WPS_SUCCEDED = BASE + 16; /** @hide */ @@ -2263,15 +2278,25 @@ public class WifiManager { public static final int BUSY = 2; /* WPS specific errors */ - /** WPS overlap detected */ + /** WPS overlap detected + * @deprecated This is deprecated + */ public static final int WPS_OVERLAP_ERROR = 3; - /** WEP on WPS is prohibited */ + /** WEP on WPS is prohibited + * @deprecated This is deprecated + */ public static final int WPS_WEP_PROHIBITED = 4; - /** TKIP only prohibited */ + /** TKIP only prohibited + * @deprecated This is deprecated + */ public static final int WPS_TKIP_ONLY_PROHIBITED = 5; - /** Authentication failure on WPS */ + /** Authentication failure on WPS + * @deprecated This is deprecated + */ public static final int WPS_AUTH_FAILURE = 6; - /** WPS timed out */ + /** WPS timed out + * @deprecated This is deprecated + */ public static final int WPS_TIMED_OUT = 7; /** @@ -2312,12 +2337,19 @@ public class WifiManager { public void onFailure(int reason); } - /** Interface for callback invocation on a start WPS action */ + /** Interface for callback invocation on a start WPS action + * @deprecated This is deprecated + */ public static abstract class WpsCallback { - /** WPS start succeeded */ + + /** WPS start succeeded + * @deprecated This API is deprecated + */ public abstract void onStarted(String pin); - /** WPS operation completed successfully */ + /** WPS operation completed successfully + * @deprecated This API is deprecated + */ public abstract void onSucceeded(); /** @@ -2326,6 +2358,7 @@ public class WifiManager { * {@link #WPS_TKIP_ONLY_PROHIBITED}, {@link #WPS_OVERLAP_ERROR}, * {@link #WPS_WEP_PROHIBITED}, {@link #WPS_TIMED_OUT} or {@link #WPS_AUTH_FAILURE} * and some generic errors. + * @deprecated This API is deprecated */ public abstract void onFailed(int reason); } @@ -3032,6 +3065,7 @@ public class WifiManager { * @param listener for callbacks on success or failure. Can be null. * @throws IllegalStateException if the WifiManager instance needs to be * initialized again + * @deprecated This API is deprecated */ public void startWps(WpsInfo config, WpsCallback listener) { if (config == null) throw new IllegalArgumentException("config cannot be null"); @@ -3044,6 +3078,7 @@ public class WifiManager { * @param listener for callbacks on success or failure. Can be null. * @throws IllegalStateException if the WifiManager instance needs to be * initialized again + * @deprecated This API is deprecated */ public void cancelWps(WpsCallback listener) { getChannel().sendMessage(CANCEL_WPS, 0, putListener(listener)); diff --git a/wifi/java/android/net/wifi/rtt/LocationCivic.java b/wifi/java/android/net/wifi/rtt/LocationCivic.java new file mode 100644 index 000000000000..610edb6399b4 --- /dev/null +++ b/wifi/java/android/net/wifi/rtt/LocationCivic.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi.rtt; + +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Arrays; +import java.util.Objects; + +/** + * Location Civic Report (LCR). + * <p> + * The information matches the IEEE 802.11-2016 LCR report. + * <p> + * Note: depending on the mechanism by which this information is returned (i.e. the API which + * returns an instance of this class) it is possibly Self Reported (by the peer). In such a case + * the information is NOT validated - use with caution. Consider validating it with other sources + * of information before using it. + */ +public final class LocationCivic implements Parcelable { + private final byte[] mData; + + /** + * Parse the raw LCR information element (byte array) and extract the LocationCivic structure. + * + * Note: any parsing errors or invalid/unexpected errors will result in a null being returned. + * + * @hide + */ + @Nullable + public static LocationCivic parseInformationElement(byte id, byte[] data) { + // TODO + return null; + } + + /** @hide */ + public LocationCivic(byte[] data) { + mData = data; + } + + /** + * Return the Location Civic data reported by the peer. + * + * @return An arbitrary location information. + */ + public byte[] getData() { + return mData; + } + + /** @hide */ + @Override + public int describeContents() { + return 0; + } + + /** @hide */ + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeByteArray(mData); + } + + public static final Parcelable.Creator<LocationCivic> CREATOR = + new Parcelable.Creator<LocationCivic>() { + @Override + public LocationCivic[] newArray(int size) { + return new LocationCivic[size]; + } + + @Override + public LocationCivic createFromParcel(Parcel in) { + byte[] data = in.createByteArray(); + + return new LocationCivic(data); + } + }; + + /** @hide */ + @Override + public String toString() { + return new StringBuilder("LCR: data=").append(Arrays.toString(mData)).toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof LocationCivic)) { + return false; + } + + LocationCivic lhs = (LocationCivic) o; + + return Arrays.equals(mData, lhs.mData); + } + + @Override + public int hashCode() { + return Objects.hash(mData); + } +} diff --git a/wifi/java/android/net/wifi/rtt/LocationConfigurationInformation.java b/wifi/java/android/net/wifi/rtt/LocationConfigurationInformation.java new file mode 100644 index 000000000000..8aba56aa0ee7 --- /dev/null +++ b/wifi/java/android/net/wifi/rtt/LocationConfigurationInformation.java @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi.rtt; + +import android.annotation.IntDef; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; + +/** + * The Device Location Configuration Information (LCI) specifies the location information of a peer + * device (e.g. an Access Point). + * <p> + * The information matches the IEEE 802.11-2016 LCI report (Location configuration information + * report). + * <p> + * Note: depending on the mechanism by which this information is returned (i.e. the API which + * returns an instance of this class) it is possibly Self Reported (by the peer). In such a case + * the information is NOT validated - use with caution. Consider validating it with other sources + * of information before using it. + */ +public final class LocationConfigurationInformation implements Parcelable { + /** @hide */ + @IntDef({ + ALTITUDE_UNKNOWN, ALTITUDE_IN_METERS, ALTITUDE_IN_FLOORS }) + @Retention(RetentionPolicy.SOURCE) + public @interface AltitudeTypes { + } + + /** + * Define an Altitude Type returned by {@link #getAltitudeType()}. Indicates that the location + * does not specify an altitude or altitude uncertainty. The corresponding methods, + * {@link #getAltitude()} and {@link #getAltitudeUncertainty()} are not valid and will throw + * an exception. + */ + public static final int ALTITUDE_UNKNOWN = 0; + + /** + * Define an Altitude Type returned by {@link #getAltitudeType()}. Indicates that the location + * specifies the altitude and altitude uncertainty in meters. The corresponding methods, + * {@link #getAltitude()} and {@link #getAltitudeUncertainty()} return a valid value in meters. + */ + public static final int ALTITUDE_IN_METERS = 1; + + /** + * Define an Altitude Type returned by {@link #getAltitudeType()}. Indicates that the + * location specifies the altitude in floors, and does not specify an altitude uncertainty. + * The {@link #getAltitude()} method returns valid value in floors, and the + * {@link #getAltitudeUncertainty()} method is not valid and will throw an exception. + */ + public static final int ALTITUDE_IN_FLOORS = 2; + + private final double mLatitude; + private final double mLatitudeUncertainty; + private final double mLongitude; + private final double mLongitudeUncertainty; + private final int mAltitudeType; + private final double mAltitude; + private final double mAltitudeUncertainty; + + /** + * Parse the raw LCI information element (byte array) and extract the + * LocationConfigurationInformation structure. + * + * Note: any parsing errors or invalid/unexpected errors will result in a null being returned. + * + * @hide + */ + @Nullable + public static LocationConfigurationInformation parseInformationElement(byte id, byte[] data) { + // TODO + return null; + } + + /** @hide */ + public LocationConfigurationInformation(double latitude, double latitudeUncertainty, + double longitude, double longitudeUncertainty, @AltitudeTypes int altitudeType, + double altitude, double altitudeUncertainty) { + mLatitude = latitude; + mLatitudeUncertainty = latitudeUncertainty; + mLongitude = longitude; + mLongitudeUncertainty = longitudeUncertainty; + mAltitudeType = altitudeType; + mAltitude = altitude; + mAltitudeUncertainty = altitudeUncertainty; + } + + /** + * Get latitude in degrees. Values are per WGS 84 reference system. Valid values are between + * -90 and 90. + * + * @return Latitude in degrees. + */ + public double getLatitude() { + return mLatitude; + } + + /** + * Get the uncertainty of the latitude {@link #getLatitude()} in degrees. A value of 0 indicates + * an unknown uncertainty. + * + * @return Uncertainty of the latitude in degrees. + */ + public double getLatitudeUncertainty() { + return mLatitudeUncertainty; + } + + /** + * Get longitude in degrees. Values are per WGS 84 reference system. Valid values are between + * -180 and 180. + * + * @return Longitude in degrees. + */ + public double getLongitude() { + return mLongitude; + } + + /** + * Get the uncertainty of the longitude {@link #getLongitude()} ()} in degrees. A value of 0 + * indicates an unknown uncertainty. + * + * @return Uncertainty of the longitude in degrees. + */ + public double getLongitudeUncertainty() { + return mLongitudeUncertainty; + } + + /** + * Specifies the type of the altitude measurement returned by {@link #getAltitude()} and + * {@link #getAltitudeUncertainty()}. The possible values are: + * <li>{@link #ALTITUDE_UNKNOWN}: The altitude and altitude uncertainty are not provided. + * <li>{@link #ALTITUDE_IN_METERS}: The altitude and altitude uncertainty are provided in + * meters. Values are per WGS 84 reference system. + * <li>{@link #ALTITUDE_IN_FLOORS}: The altitude is provided in floors, the altitude uncertainty + * is not provided. + * + * @return The type of the altitude and altitude uncertainty. + */ + public @AltitudeTypes int getAltitudeType() { + return mAltitudeType; + } + + /** + * The altitude is interpreted according to the {@link #getAltitudeType()}. The possible values + * are: + * <li>{@link #ALTITUDE_UNKNOWN}: The altitude is not provided - this method will throw an + * exception. + * <li>{@link #ALTITUDE_IN_METERS}: The altitude is provided in meters. Values are per WGS 84 + * reference system. + * <li>{@link #ALTITUDE_IN_FLOORS}: The altitude is provided in floors. + * + * @return Altitude value whose meaning is specified by {@link #getAltitudeType()}. + */ + public double getAltitude() { + if (mAltitudeType == ALTITUDE_UNKNOWN) { + throw new IllegalStateException( + "getAltitude(): invoked on an invalid type: getAltitudeType()==UNKNOWN"); + } + return mAltitude; + } + + /** + * Only valid if the the {@link #getAltitudeType()} is equal to {@link #ALTITUDE_IN_METERS} - + * otherwise this method will throw an exception. + * <p> + * Get the uncertainty of the altitude {@link #getAltitude()} in meters. A value of 0 + * indicates an unknown uncertainty. + * + * @return Uncertainty of the altitude in meters. + */ + public double getAltitudeUncertainty() { + if (mAltitudeType != ALTITUDE_IN_METERS) { + throw new IllegalStateException( + "getAltitude(): invoked on an invalid type: getAltitudeType()!=IN_METERS"); + } + return mAltitudeUncertainty; + } + + /** @hide */ + @Override + public int describeContents() { + return 0; + } + + /** @hide */ + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeDouble(mLatitude); + dest.writeDouble(mLatitudeUncertainty); + dest.writeDouble(mLongitude); + dest.writeDouble(mLongitudeUncertainty); + dest.writeInt(mAltitudeType); + dest.writeDouble(mAltitude); + dest.writeDouble(mAltitudeUncertainty); + } + + public static final Creator<LocationConfigurationInformation> CREATOR = + new Creator<LocationConfigurationInformation>() { + @Override + public LocationConfigurationInformation[] newArray(int size) { + return new LocationConfigurationInformation[size]; + } + + @Override + public LocationConfigurationInformation createFromParcel(Parcel in) { + double latitude = in.readDouble(); + double latitudeUnc = in.readDouble(); + double longitude = in.readDouble(); + double longitudeUnc = in.readDouble(); + int altitudeType = in.readInt(); + double altitude = in.readDouble(); + double altitudeUnc = in.readDouble(); + + return new LocationConfigurationInformation(latitude, latitudeUnc, longitude, + longitudeUnc, altitudeType, altitude, altitudeUnc); + } + }; + + /** @hide */ + @Override + public String toString() { + return new StringBuilder("LCI: latitude=").append(mLatitude).append( + ", latitudeUncertainty=").append(mLatitudeUncertainty).append( + ", longitude=").append(mLongitude).append(", longitudeUncertainty=").append( + mLongitudeUncertainty).append(", altitudeType=").append(mAltitudeType).append( + ", altitude=").append(mAltitude).append(", altitudeUncertainty=").append( + mAltitudeUncertainty).toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof LocationConfigurationInformation)) { + return false; + } + + LocationConfigurationInformation lhs = (LocationConfigurationInformation) o; + + return mLatitude == lhs.mLatitude && mLatitudeUncertainty == lhs.mLatitudeUncertainty + && mLongitude == lhs.mLongitude + && mLongitudeUncertainty == lhs.mLongitudeUncertainty + && mAltitudeType == lhs.mAltitudeType && mAltitude == lhs.mAltitude + && mAltitudeUncertainty == lhs.mAltitudeUncertainty; + } + + @Override + public int hashCode() { + return Objects.hash(mLatitude, mLatitudeUncertainty, mLongitude, mLongitudeUncertainty, + mAltitudeType, mAltitude, mAltitudeUncertainty); + } +} diff --git a/wifi/java/android/net/wifi/rtt/RangingResult.java b/wifi/java/android/net/wifi/rtt/RangingResult.java index d5ca8f7f9fb0..201833bff431 100644 --- a/wifi/java/android/net/wifi/rtt/RangingResult.java +++ b/wifi/java/android/net/wifi/rtt/RangingResult.java @@ -65,29 +65,37 @@ public final class RangingResult implements Parcelable { private final int mDistanceMm; private final int mDistanceStdDevMm; private final int mRssi; + private final LocationConfigurationInformation mLci; + private final LocationCivic mLcr; private final long mTimestamp; /** @hide */ public RangingResult(@RangeResultStatus int status, @NonNull MacAddress mac, int distanceMm, - int distanceStdDevMm, int rssi, long timestamp) { + int distanceStdDevMm, int rssi, LocationConfigurationInformation lci, LocationCivic lcr, + long timestamp) { mStatus = status; mMac = mac; mPeerHandle = null; mDistanceMm = distanceMm; mDistanceStdDevMm = distanceStdDevMm; mRssi = rssi; + mLci = lci; + mLcr = lcr; mTimestamp = timestamp; } /** @hide */ public RangingResult(@RangeResultStatus int status, PeerHandle peerHandle, int distanceMm, - int distanceStdDevMm, int rssi, long timestamp) { + int distanceStdDevMm, int rssi, LocationConfigurationInformation lci, LocationCivic lcr, + long timestamp) { mStatus = status; mMac = null; mPeerHandle = peerHandle; mDistanceMm = distanceMm; mDistanceStdDevMm = distanceStdDevMm; mRssi = rssi; + mLci = lci; + mLcr = lcr; mTimestamp = timestamp; } @@ -169,6 +177,38 @@ public final class RangingResult implements Parcelable { } /** + * @return The Location Configuration Information (LCI) as self-reported by the peer. + * <p> + * Note: the information is NOT validated - use with caution. Consider validating it with + * other sources of information before using it. + */ + @Nullable + public LocationConfigurationInformation getReportedLocationConfigurationInformation() { + if (mStatus != STATUS_SUCCESS) { + throw new IllegalStateException( + "getReportedLocationConfigurationInformation(): invoked on an invalid result: " + + "getStatus()=" + mStatus); + } + return mLci; + } + + /** + * @return The Location Civic report (LCR) as self-reported by the peer. + * <p> + * Note: the information is NOT validated - use with caution. Consider validating it with + * other sources of information before using it. + */ + @Nullable + public LocationCivic getReportedLocationCivic() { + if (mStatus != STATUS_SUCCESS) { + throw new IllegalStateException( + "getReportedLocationCivic(): invoked on an invalid result: getStatus()=" + + mStatus); + } + return mLcr; + } + + /** * @return The timestamp, in us since boot, at which the ranging operation was performed. * <p> * Only valid if {@link #getStatus()} returns {@link #STATUS_SUCCESS}, otherwise will throw an @@ -205,6 +245,18 @@ public final class RangingResult implements Parcelable { dest.writeInt(mDistanceMm); dest.writeInt(mDistanceStdDevMm); dest.writeInt(mRssi); + if (mLci == null) { + dest.writeBoolean(false); + } else { + dest.writeBoolean(true); + mLci.writeToParcel(dest, flags); + } + if (mLcr == null) { + dest.writeBoolean(false); + } else { + dest.writeBoolean(true); + mLcr.writeToParcel(dest, flags); + } dest.writeLong(mTimestamp); } @@ -230,13 +282,23 @@ public final class RangingResult implements Parcelable { int distanceMm = in.readInt(); int distanceStdDevMm = in.readInt(); int rssi = in.readInt(); + boolean lciPresent = in.readBoolean(); + LocationConfigurationInformation lci = null; + if (lciPresent) { + lci = LocationConfigurationInformation.CREATOR.createFromParcel(in); + } + boolean lcrPresent = in.readBoolean(); + LocationCivic lcr = null; + if (lcrPresent) { + lcr = LocationCivic.CREATOR.createFromParcel(in); + } long timestamp = in.readLong(); if (peerHandlePresent) { return new RangingResult(status, peerHandle, distanceMm, distanceStdDevMm, rssi, - timestamp); + lci, lcr, timestamp); } else { return new RangingResult(status, mac, distanceMm, distanceStdDevMm, rssi, - timestamp); + lci, lcr, timestamp); } } }; @@ -248,8 +310,8 @@ public final class RangingResult implements Parcelable { mMac).append(", peerHandle=").append( mPeerHandle == null ? "<null>" : mPeerHandle.peerId).append(", distanceMm=").append( mDistanceMm).append(", distanceStdDevMm=").append(mDistanceStdDevMm).append( - ", rssi=").append(mRssi).append(", timestamp=").append(mTimestamp).append( - "]").toString(); + ", rssi=").append(mRssi).append(", lci=").append(mLci).append(", lcr=").append( + mLcr).append(", timestamp=").append(mTimestamp).append("]").toString(); } @Override @@ -267,12 +329,13 @@ public final class RangingResult implements Parcelable { return mStatus == lhs.mStatus && Objects.equals(mMac, lhs.mMac) && Objects.equals( mPeerHandle, lhs.mPeerHandle) && mDistanceMm == lhs.mDistanceMm && mDistanceStdDevMm == lhs.mDistanceStdDevMm && mRssi == lhs.mRssi + && Objects.equals(mLci, lhs.mLci) && Objects.equals(mLcr, lhs.mLcr) && mTimestamp == lhs.mTimestamp; } @Override public int hashCode() { return Objects.hash(mStatus, mMac, mPeerHandle, mDistanceMm, mDistanceStdDevMm, mRssi, - mTimestamp); + mLci, mLcr, mTimestamp); } } diff --git a/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java b/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java index 72e95b93e741..41c7f8644e2e 100644 --- a/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java +++ b/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java @@ -17,6 +17,7 @@ package android.net.wifi.rtt; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; @@ -32,7 +33,6 @@ import android.os.Handler; import android.os.IBinder; import android.os.Parcel; import android.os.test.TestLooper; -import android.test.suitebuilder.annotation.SmallTest; import org.junit.Before; import org.junit.Test; @@ -46,7 +46,6 @@ import java.util.List; /** * Unit test harness for WifiRttManager class. */ -@SmallTest public class WifiRttManagerTest { private WifiRttManager mDut; private TestLooper mMockLooper; @@ -80,7 +79,7 @@ public class WifiRttManagerTest { List<RangingResult> results = new ArrayList<>(); results.add( new RangingResult(RangingResult.STATUS_SUCCESS, MacAddress.BROADCAST_ADDRESS, 15, 5, - 10, 666)); + 10, null, null, 666)); RangingResultCallback callbackMock = mock(RangingResultCallback.class); ArgumentCaptor<IRttCallback> callbackCaptor = ArgumentCaptor.forClass(IRttCallback.class); @@ -236,10 +235,23 @@ public class WifiRttManagerTest { int distanceStdDevCm = 10; int rssi = 5; long timestamp = System.currentTimeMillis(); + double latitude = 5.5; + double latitudeUncertainty = 6.5; + double longitude = 7.5; + double longitudeUncertainty = 8.5; + int altitudeType = LocationConfigurationInformation.ALTITUDE_IN_METERS; + double altitude = 9.5; + double altitudeUncertainty = 55.5; + byte[] lcrData = { 0x1, 0x2, 0x3, 0xA, 0xB, 0xC }; + + LocationConfigurationInformation lci = new LocationConfigurationInformation(latitude, + latitudeUncertainty, longitude, longitudeUncertainty, altitudeType, altitude, + altitudeUncertainty); + LocationCivic lcr = new LocationCivic(lcrData); // RangingResults constructed with a MAC address RangingResult result = new RangingResult(status, mac, distanceCm, distanceStdDevCm, rssi, - timestamp); + lci, lcr, timestamp); Parcel parcelW = Parcel.obtain(); result.writeToParcel(parcelW, 0); @@ -255,7 +267,7 @@ public class WifiRttManagerTest { // RangingResults constructed with a PeerHandle result = new RangingResult(status, peerHandle, distanceCm, distanceStdDevCm, rssi, - timestamp); + null, null, timestamp); parcelW = Parcel.obtain(); result.writeToParcel(parcelW, 0); @@ -269,4 +281,83 @@ public class WifiRttManagerTest { assertEquals(result, rereadResult); } + + /** + * Validate that LocationConfigurationInformation parcel works (produces same object on + * write/read). + */ + @Test + public void testLciParcel() { + double latitude = 1.5; + double latitudeUncertainty = 2.5; + double longitude = 3.5; + double longitudeUncertainty = 4.5; + int altitudeType = LocationConfigurationInformation.ALTITUDE_IN_FLOORS; + double altitude = 5.5; + double altitudeUncertainty = 6.5; + + LocationConfigurationInformation lci = new LocationConfigurationInformation(latitude, + latitudeUncertainty, longitude, longitudeUncertainty, altitudeType, altitude, + altitudeUncertainty); + + Parcel parcelW = Parcel.obtain(); + lci.writeToParcel(parcelW, 0); + byte[] bytes = parcelW.marshall(); + parcelW.recycle(); + + Parcel parcelR = Parcel.obtain(); + parcelR.unmarshall(bytes, 0, bytes.length); + parcelR.setDataPosition(0); + LocationConfigurationInformation rereadLci = + LocationConfigurationInformation.CREATOR.createFromParcel(parcelR); + + assertEquals(lci, rereadLci); + } + + /** + * Validate that the LCI throws an exception when accessing invalid fields an certain altitude + * types. + */ + @Test + public void testLciInvalidAltitudeFieldAccess() { + boolean exceptionThrown; + LocationConfigurationInformation lci = new LocationConfigurationInformation(0, 0, 0, 0, + LocationConfigurationInformation.ALTITUDE_UNKNOWN, 0, 0); + + // UNKNOWN - invalid altitude & altitude uncertainty + exceptionThrown = false; + try { + lci.getAltitude(); + } catch (IllegalStateException e) { + exceptionThrown = true; + } + assertTrue("UNKNOWN / getAltitude()", exceptionThrown); + + exceptionThrown = false; + try { + lci.getAltitudeUncertainty(); + } catch (IllegalStateException e) { + exceptionThrown = true; + } + assertTrue("UNKNOWN / getAltitudeUncertainty()", exceptionThrown); + + lci = new LocationConfigurationInformation(0, 0, 0, 0, + LocationConfigurationInformation.ALTITUDE_IN_FLOORS, 0, 0); + + // FLOORS - invalid altitude uncertainty + exceptionThrown = false; + try { + lci.getAltitudeUncertainty(); + } catch (IllegalStateException e) { + exceptionThrown = true; + } + assertTrue("FLOORS / getAltitudeUncertainty()", exceptionThrown); + + // and good accesses just in case + lci.getAltitude(); + lci = new LocationConfigurationInformation(0, 0, 0, 0, + LocationConfigurationInformation.ALTITUDE_IN_METERS, 0, 0); + lci.getAltitude(); + lci.getAltitudeUncertainty(); + } } |