Merge "Remove all the implementation for keep eSIM profile during FDR"
diff --git a/Android.bp b/Android.bp
index 641ce5f..d1332bb 100644
--- a/Android.bp
+++ b/Android.bp
@@ -230,6 +230,7 @@
"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",
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
index 6975609..682885b 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.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 @@
.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 3d8c5a6..8d26717 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6474,6 +6474,7 @@
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);
@@ -6543,6 +6544,7 @@
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);
@@ -15573,8 +15575,10 @@
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;
@@ -15613,6 +15617,7 @@
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;
@@ -15653,6 +15658,7 @@
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;
@@ -15673,6 +15679,7 @@
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;
@@ -15843,6 +15850,7 @@
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
@@ -15858,6 +15866,8 @@
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
@@ -15867,6 +15877,7 @@
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
@@ -15916,6 +15927,8 @@
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
@@ -15998,6 +16011,7 @@
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;
@@ -16008,8 +16022,10 @@
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);
}
@@ -16097,6 +16113,10 @@
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;
@@ -16205,6 +16225,7 @@
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
@@ -27376,7 +27397,7 @@
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);
@@ -27409,7 +27430,7 @@
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";
@@ -27439,11 +27460,11 @@
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 {
@@ -27477,11 +27498,11 @@
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 {
@@ -27923,6 +27944,29 @@
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();
@@ -27946,6 +27990,8 @@
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);
@@ -31547,7 +31593,7 @@
}
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[]);
@@ -34024,9 +34070,10 @@
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";
@@ -39874,6 +39921,7 @@
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
@@ -40094,6 +40142,7 @@
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
@@ -40516,6 +40565,7 @@
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";
@@ -40531,6 +40581,7 @@
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";
@@ -40543,6 +40594,18 @@
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);
@@ -40706,6 +40769,7 @@
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";
@@ -41451,6 +41515,8 @@
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();
@@ -41488,8 +41554,6 @@
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();
@@ -41707,6 +41771,7 @@
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();
@@ -41750,11 +41815,11 @@
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 66b6d99..ba9f2ac 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -75,6 +75,7 @@
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 @@
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 @@
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 @@
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";
@@ -3502,6 +3506,22 @@
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[]);
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 9d6d8a1..7a7a2f6 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -127,6 +127,10 @@
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/atoms.proto b/cmds/statsd/src/atoms.proto
index ef99c9f..a07bd2f 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -327,8 +327,9 @@
optional string name = 2;
enum State {
- OFF = 0;
- ON = 1;
+ FINISHED = 0;
+ STARTED = 1;
+ SCHEDULED = 2;
}
optional State state = 3;
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 6782f3f..34fa3c4 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -36,13 +36,13 @@
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/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index ccb54f9..da9f728 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -339,4 +339,9 @@
* 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/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 33277ea..fb8d101 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -112,6 +112,7 @@
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.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 @@
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 d465e0d..7fccda8 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -9210,10 +9210,13 @@
/**
* 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 {
@@ -9224,10 +9227,12 @@
}
/**
- * 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 {
@@ -9242,9 +9247,9 @@
*
* 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/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index 8ffacf5..c4316a0 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -299,7 +299,7 @@
@Override
public Bundle call(String method, String arg, Bundle extras) {
if (method.equals(METHOD_SLICE)) {
- Uri uri = extras.getParcelable(EXTRA_BIND_URI);
+ Uri uri = getUriWithoutUserId(extras.getParcelable(EXTRA_BIND_URI));
List<SliceSpec> supportedSpecs = extras.getParcelableArrayList(EXTRA_SUPPORTED_SPECS);
String callingPackage = getCallingPackage();
@@ -327,19 +327,19 @@
}
return b;
} else if (method.equals(METHOD_PIN)) {
- Uri uri = extras.getParcelable(EXTRA_BIND_URI);
+ 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);
+ 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)));
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 265f7c7..f69aab01 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3024,7 +3024,8 @@
//@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 @@
/**
* 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/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 1201ef4..96d043c 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.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 @@
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 @@
}
/**
+ * <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 @@
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 @@
* <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 @@
* @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 @@
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 @@
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 @@
* <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 @@
* @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 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 639795a..40ee834 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -31,6 +31,7 @@
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 @@
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 90bf896..a2bc91e 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -996,7 +996,12 @@
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 2294ec5..e7c8961 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -845,6 +845,53 @@
*/
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 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 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 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 ce75fa52..481b764 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -33,9 +33,11 @@
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 @@
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 @@
* 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 @@
*/
@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 @@
* 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 @@
*/
@Nullable
public <T> T get(Key<T> key) {
- return mSettings.get(key);
+ return mLogicalCameraSettings.get(key);
}
/**
@@ -319,7 +348,7 @@
@SuppressWarnings("unchecked")
@Override
protected <T> T getProtected(Key<?> key) {
- return (T) mSettings.get(key);
+ return (T) mLogicalCameraSettings.get(key);
}
/**
@@ -403,7 +432,7 @@
* @hide
*/
public CameraMetadataNative getNativeCopy() {
- return new CameraMetadataNative(mSettings);
+ return new CameraMetadataNative(mLogicalCameraSettings);
}
/**
@@ -444,14 +473,16 @@
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 @@
* @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 @@
@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 @@
}
/**
+ * Retrieves the logical camera id.
+ * @hide
+ */
+ public String getLogicalCameraId() {
+ return mLogicalCameraId;
+ }
+
+ /**
* @hide
*/
public void convertSurfaceToStreamId(
@@ -633,14 +701,20 @@
* 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 @@
* 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 @@
*/
@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 @@
* @hide
*/
public boolean isEmpty() {
- return mRequest.mSettings.isEmpty();
+ return mRequest.mLogicalCameraSettings.isEmpty();
}
}
@@ -2619,6 +2757,29 @@
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 237a92d..d730fa8 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2203,8 +2203,6 @@
* significant illumination change, this value will be set to DETECTED for a single capture
* result. Otherwise the value will be NOT_DETECTED. The threshold for detection is similar
* to what would trigger a new passive focus scan to begin in CONTINUOUS autofocus modes.</p>
- * <p>afSceneChange may be DETECTED only if afMode is AF_MODE_CONTINUOUS_VIDEO or
- * AF_MODE_CONTINUOUS_PICTURE. In other AF modes, afSceneChange must be NOT_DETECTED.</p>
* <p>This key will be available if the camera device advertises this key via {@link android.hardware.camera2.CameraCharacteristics#getAvailableCaptureResultKeys }.</p>
* <p><b>Possible values:</b>
* <ul>
@@ -3911,6 +3909,76 @@
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 8c4dbfa..06c2c25 100644
--- a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
@@ -94,8 +94,8 @@
// 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 @@
// 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 4455d45..cab9d70 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -18,13 +18,14 @@
import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
+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;
@@ -34,7 +35,6 @@
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;
@@ -49,16 +49,15 @@
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
@@ -713,6 +712,38 @@
}
@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) {
@@ -730,7 +761,8 @@
}
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;
}
@@ -746,7 +778,7 @@
CameraMetadataNative(inputResult.getNativeCopy());
return new CaptureRequest.Builder(resultMetadata, /*reprocess*/true,
- inputResult.getSessionId());
+ inputResult.getSessionId(), getId(), /*physicalCameraIdSet*/ null);
}
}
@@ -956,7 +988,8 @@
// 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(
@@ -967,7 +1000,20 @@
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) {
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index a85b5f7..f47cd66 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.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 @@
mConfiguredGenerationId = surface.getGenerationId();
mIsDeferredConfig = false;
mIsShared = false;
+ mPhysicalCameraId = null;
}
/**
@@ -319,6 +319,7 @@
mConfiguredGenerationId = 0;
mIsDeferredConfig = true;
mIsShared = false;
+ mPhysicalCameraId = null;
}
/**
@@ -348,8 +349,9 @@
* </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 @@
}
/**
+ * 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 @@
this.mConfiguredGenerationId = other.mConfiguredGenerationId;
this.mIsDeferredConfig = other.mIsDeferredConfig;
this.mIsShared = other.mIsShared;
+ this.mPhysicalCameraId = other.mPhysicalCameraId;
}
/**
@@ -502,6 +543,7 @@
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 @@
StreamConfigurationMap.imageFormatToDataspace(ImageFormat.PRIVATE);
mConfiguredGenerationId = 0;
}
+ mPhysicalCameraId = physicalCameraId;
}
/**
@@ -622,6 +665,7 @@
dest.writeInt(mIsDeferredConfig ? 1 : 0);
dest.writeInt(mIsShared ? 1 : 0);
dest.writeTypedList(mSurfaces);
+ dest.writeString(mPhysicalCameraId);
}
/**
@@ -675,13 +719,15 @@
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 @@
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/net/IIpSecService.aidl b/core/java/android/net/IIpSecService.aidl
index 790c80b..eeb30e2 100644
--- a/core/java/android/net/IIpSecService.aidl
+++ b/core/java/android/net/IIpSecService.aidl
@@ -39,9 +39,9 @@
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/IpSecTransform.java b/core/java/android/net/IpSecTransform.java
index be6026f..37e2c4f 100644
--- a/core/java/android/net/IpSecTransform.java
+++ b/core/java/android/net/IpSecTransform.java
@@ -124,8 +124,7 @@
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 @@
* still want to clear out the transform.
*/
IIpSecService svc = getIpSecService();
- svc.deleteTransportModeTransform(mResourceId);
+ svc.deleteTransform(mResourceId);
stopKeepalive();
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index a85f80e..01b2b39 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -160,13 +160,6 @@
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 b307c5d..8efd39a 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_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 @@
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 @@
mSubscriberId = in.readString();
mMatchSubscriberIds = in.createStringArray();
mNetworkId = in.readString();
+ mMetered = in.readInt();
+ mRoaming = in.readInt();
+ mDefaultNetwork = in.readInt();
}
@Override
@@ -221,6 +247,9 @@
dest.writeString(mSubscriberId);
dest.writeStringArray(mMatchSubscriberIds);
dest.writeString(mNetworkId);
+ dest.writeInt(mMetered);
+ dest.writeInt(mRoaming);
+ dest.writeInt(mDefaultNetwork);
}
@Override
@@ -243,12 +272,23 @@
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 @@
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 @@
* 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 @@
}
}
+ 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 49879a8..03a8dba 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -336,6 +336,9 @@
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.
*
@@ -413,6 +416,13 @@
/**
* @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();
@@ -2399,6 +2409,14 @@
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.
*
@@ -3345,6 +3363,20 @@
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(" ");
@@ -3386,7 +3418,7 @@
String [] powerLevel;
switch(controllerName) {
- case "Cellular":
+ case CELLULAR_CONTROLLER_NAME:
powerLevel = new String[] {
" less than 0dBm: ",
" 0dBm to 8dBm: ",
@@ -4674,7 +4706,7 @@
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);
@@ -4683,6 +4715,16 @@
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);
@@ -4760,7 +4802,8 @@
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);
@@ -5238,8 +5281,8 @@
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: ");
@@ -5293,7 +5336,7 @@
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 848ab88..33e8c3e 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -2352,22 +2352,28 @@
}
/**
- * 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/ISystemUpdateManager.aidl b/core/java/android/os/ISystemUpdateManager.aidl
new file mode 100644
index 0000000..f7f5079
--- /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/SystemUpdateManager.java b/core/java/android/os/SystemUpdateManager.java
new file mode 100644
index 0000000..ce3e2259
--- /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/WifiBatteryStats.aidl
similarity index 66%
rename from packages/SettingsLib/src/com/android/settingslib/core/instrumentation/Instrumentable.java
rename to core/java/android/os/connectivity/WifiBatteryStats.aidl
index dbc61c2..12ac738 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/Instrumentable.java
+++ b/core/java/android/os/connectivity/WifiBatteryStats.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 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 0000000..e5341ee
--- /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 2eca4c98..9ac2b3e 100644
--- a/core/java/android/privacy/internal/rappor/RapporEncoder.java
+++ b/core/java/android/privacy/internal/rappor/RapporEncoder.java
@@ -33,7 +33,6 @@
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 @@
// 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 2169457..7ad9e01 100644
--- a/core/java/android/provider/AlarmClock.java
+++ b/core/java/android/provider/AlarmClock.java
@@ -154,9 +154,12 @@
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 60df467..c6c8d9d 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -223,14 +223,13 @@
/** 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 b2cc18b..4228fbb 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10128,7 +10128,8 @@
* 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:
*
@@ -10138,6 +10139,8 @@
* 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)
@@ -10348,6 +10351,8 @@
* 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>
@@ -10375,6 +10380,15 @@
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
diff --git a/core/java/android/service/autofill/AutofillFieldClassificationService.java b/core/java/android/service/autofill/AutofillFieldClassificationService.java
index 4e4caf0..df63a91 100644
--- a/core/java/android/service/autofill/AutofillFieldClassificationService.java
+++ b/core/java/android/service/autofill/AutofillFieldClassificationService.java
@@ -99,7 +99,9 @@
final String[] userDataValues = (String[]) args.arg5;
final float[][] scores = onGetScores(algorithmName, algorithmArgs, actualValues,
Arrays.asList(userDataValues));
- data.putParcelable(EXTRA_SCORES, new Scores(scores));
+ if (scores != null) {
+ data.putParcelable(EXTRA_SCORES, new Scores(scores));
+ }
break;
default:
Log.w(TAG, "Handling unknown message: " + action);
@@ -148,7 +150,8 @@
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
@@ -182,7 +185,7 @@
}
}
- private Scores(float[][] scores) {
+ private Scores(float[][] scores) {
this.scores = scores;
}
diff --git a/core/java/android/text/style/AbsoluteSizeSpan.java b/core/java/android/text/style/AbsoluteSizeSpan.java
index 908ef55..3b4eea7 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 @@
}
@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 4f471a8..44e35615 100644
--- a/core/java/android/text/style/BackgroundColorSpan.java
+++ b/core/java/android/text/style/BackgroundColorSpan.java
@@ -27,11 +27,10 @@
* Changes the background color of the text to which the span is attached.
* <p>
* For example, to set a green background color for a text you would create a {@link
- * android.text.SpannableStringBuilder} based on the text and set the span.
+ * 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>
+ *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>
*/
@@ -58,30 +57,29 @@
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;
}
- /**
- * Flatten this object into a Parcel.
- *
- * @param dest The Parcel in which the object should be written.
- * @param flags Additional flags about how the object should be written.
- */
+ @Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
writeToParcelInternal(dest, flags);
}
/** @hide */
+ @Override
public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
dest.writeInt(mColor);
}
diff --git a/core/java/android/text/style/ForegroundColorSpan.java b/core/java/android/text/style/ForegroundColorSpan.java
index 08ab2a1..f7706745 100644
--- a/core/java/android/text/style/ForegroundColorSpan.java
+++ b/core/java/android/text/style/ForegroundColorSpan.java
@@ -27,11 +27,10 @@
* Changes the color of the text to which the span is attached.
* <p>
* For example, to set a green text color you would create a {@link
- * android.text.SpannableStringBuilder} based on the text and set the span.
+ * 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>
+ *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>
*/
@@ -59,30 +58,29 @@
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;
}
- /**
- * Flatten this object into a Parcel.
- *
- * @param dest The Parcel in which the object should be written.
- * @param flags Additional flags about how the object should be written.
- */
+ @Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
writeToParcelInternal(dest, flags);
}
/** @hide */
+ @Override
public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
dest.writeInt(mColor);
}
diff --git a/core/java/android/text/style/RelativeSizeSpan.java b/core/java/android/text/style/RelativeSizeSpan.java
index 95f048a..3094f27 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 d0850185..6ef4cec 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 1389704..a630505 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 f1b0d38..3d15aad 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 abcf688..3dc9d3f 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 9024dcd..800838e 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 e94f91a..4e98d9b 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -43,7 +43,7 @@
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/view/Surface.java b/core/java/android/view/Surface.java
index 1f7f8b9..8830c90 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -283,6 +283,7 @@
*/
public long getNextFrameNumber() {
synchronized (mLock) {
+ checkNotReleasedLocked();
return nativeGetNextFrameNumber(mNativeObject);
}
}
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index e2d1ad5..d3e807d 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -23,6 +23,7 @@
import android.os.ParcelFileDescriptor;
import android.os.WorkSource;
import android.os.connectivity.CellularBatteryStats;
+import android.os.connectivity.WifiBatteryStats;
import android.os.connectivity.GpsBatteryStats;
import android.os.health.HealthStatsParceler;
import android.telephony.DataConnectionRealTimeInfo;
@@ -143,6 +144,9 @@
CellularBatteryStats getCellularBatteryStats();
/** {@hide} */
+ WifiBatteryStats getWifiBatteryStats();
+
+ /** {@hide} */
GpsBatteryStats getGpsBatteryStats();
HealthStatsParceler takeUidSnapshot(int uid);
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 799e3e8..b1c45f7 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -34,6 +34,7 @@
import android.os.BatteryStats;
import android.os.Build;
import android.os.connectivity.CellularBatteryStats;
+import android.os.connectivity.WifiBatteryStats;
import android.os.connectivity.GpsBatteryStats;
import android.os.FileUtils;
import android.os.Handler;
@@ -131,7 +132,7 @@
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;
@@ -751,6 +752,8 @@
final StopwatchTimer[] mWifiSignalStrengthsTimer =
new StopwatchTimer[NUM_WIFI_SIGNAL_STRENGTH_BINS];
+ StopwatchTimer mWifiActiveTimer;
+
int mBluetoothScanNesting;
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
protected StopwatchTimer mBluetoothScanTimer;
@@ -2785,12 +2788,14 @@
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++) {
@@ -2801,6 +2806,7 @@
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) {
@@ -2816,6 +2822,7 @@
public void readSummaryFromParcel(Parcel in) {
mIdleTimeMillis.readSummaryFromParcelLocked(in);
+ mScanTimeMillis.readSummaryFromParcelLocked(in);
mRxTimeMillis.readSummaryFromParcelLocked(in);
final int recordedTxStates = in.readInt();
if (recordedTxStates != mTxTimeMillis.length) {
@@ -2834,6 +2841,7 @@
public void writeSummaryToParcel(Parcel dest) {
mIdleTimeMillis.writeSummaryFromParcelLocked(dest);
+ mScanTimeMillis.writeSummaryFromParcelLocked(dest);
mRxTimeMillis.writeSummaryFromParcelLocked(dest);
dest.writeInt(mTxTimeMillis.length);
for (LongSamplingCounter counter : mTxTimeMillis) {
@@ -2845,6 +2853,7 @@
@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) {
@@ -2855,6 +2864,7 @@
public void reset(boolean detachIfReset) {
mIdleTimeMillis.reset(detachIfReset);
+ mScanTimeMillis.reset(detachIfReset);
mRxTimeMillis.reset(detachIfReset);
for (LongSamplingCounter counter : mTxTimeMillis) {
counter.reset(detachIfReset);
@@ -2864,6 +2874,7 @@
public void detach() {
mIdleTimeMillis.detach();
+ mScanTimeMillis.detach();
mRxTimeMillis.detach();
for (LongSamplingCounter counter : mTxTimeMillis) {
counter.detach();
@@ -2881,6 +2892,15 @@
}
/**
+ * @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.
*/
@@ -3892,8 +3912,10 @@
}
mKernelUidCpuTimeReader.removeUid(isolatedUid);
mKernelUidCpuFreqTimeReader.removeUid(isolatedUid);
- mKernelUidCpuActiveTimeReader.removeUid(isolatedUid);
- mKernelUidCpuClusterTimeReader.removeUid(isolatedUid);
+ if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
+ mKernelUidCpuActiveTimeReader.removeUid(isolatedUid);
+ mKernelUidCpuClusterTimeReader.removeUid(isolatedUid);
+ }
}
public int mapUid(int uid) {
@@ -5618,8 +5640,11 @@
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));
@@ -6270,6 +6295,10 @@
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);
}
@@ -9916,6 +9945,7 @@
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);
@@ -10609,10 +10639,11 @@
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);
}
- mWifiActivity.reset(false);
mBluetoothActivity.reset(false);
mModemActivity.reset(false);
mNumConnectivityChange = mLoadedNumConnectivityChange = mUnpluggedNumConnectivityChange = 0;
@@ -10875,6 +10906,7 @@
// 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;
@@ -10887,6 +10919,7 @@
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;
@@ -11020,6 +11053,8 @@
mWifiActivity.getRxTimeCounter().addCountLocked(info.getControllerRxTimeMillis());
mWifiActivity.getTxTimeCounters()[0].addCountLocked(
info.getControllerTxTimeMillis());
+ mWifiActivity.getScanTimeCounter().addCountLocked(
+ info.getControllerScanTimeMillis());
mWifiActivity.getIdleTimeCounter().addCountLocked(
info.getControllerIdleTimeMillis());
@@ -11063,6 +11098,39 @@
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);
@@ -11161,26 +11229,6 @@
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));
- }
- }
}
}
@@ -11538,8 +11586,10 @@
if (!mOnBatteryInternal) {
mKernelUidCpuTimeReader.readDelta(null);
mKernelUidCpuFreqTimeReader.readDelta(null);
- mKernelUidCpuActiveTimeReader.readDelta(null);
- mKernelUidCpuClusterTimeReader.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();
}
@@ -11556,8 +11606,10 @@
updateClusterSpeedTimes(updatedUids);
}
readKernelUidCpuFreqTimesLocked(partialTimersToConsider);
- readKernelUidCpuActiveTimesLocked();
- readKernelUidCpuClusterTimesLocked();
+ if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
+ readKernelUidCpuActiveTimesLocked();
+ readKernelUidCpuClusterTimesLocked();
+ }
}
/**
@@ -12530,6 +12582,56 @@
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();
@@ -12804,10 +12906,19 @@
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(',');
@@ -12843,6 +12954,11 @@
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);
+
}
}
@@ -12857,6 +12973,10 @@
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);
}
}
@@ -13222,10 +13342,11 @@
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);
}
- mWifiActivity.readSummaryFromParcel(in);
mBluetoothActivity.readSummaryFromParcel(in);
mModemActivity.readSummaryFromParcel(in);
mHasWifiReporting = in.readInt() != 0;
@@ -13667,10 +13788,11 @@
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);
}
- mWifiActivity.writeSummaryToParcel(out);
mBluetoothActivity.writeSummaryToParcel(out);
mModemActivity.writeSummaryToParcel(out);
out.writeInt(mHasWifiReporting ? 1 : 0);
@@ -14145,12 +14267,14 @@
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);
}
- mWifiActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
- NUM_WIFI_TX_LEVELS, in);
mBluetoothActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
NUM_BT_TX_LEVELS, in);
mModemActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
@@ -14348,10 +14472,11 @@
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);
}
- mWifiActivity.writeToParcel(out, 0);
mBluetoothActivity.writeToParcel(out, 0);
mModemActivity.writeToParcel(out, 0);
out.writeInt(mHasWifiReporting ? 1 : 0);
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index fcbbcd0..240fc51 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -97,6 +97,7 @@
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";
@@ -296,10 +297,6 @@
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[]{
@@ -307,10 +304,6 @@
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++) {
diff --git a/core/jni/android_database_SQLiteCommon.cpp b/core/jni/android_database_SQLiteCommon.cpp
index eefcb74..34544d3 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 @@
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/proto/android/server/forceappstandbytracker.proto b/core/proto/android/server/forceappstandbytracker.proto
index 8753bf7..c9f7d52 100644
--- a/core/proto/android/server/forceappstandbytracker.proto
+++ b/core/proto/android/server/forceappstandbytracker.proto
@@ -41,4 +41,13 @@
// 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/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ba30981..93d852c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2313,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"
@@ -2843,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
diff --git a/core/res/res/drawable/ic_screenshot.xml b/core/res/res/drawable/ic_screenshot.xml
index 3074b28..24dd4d8 100644
--- a/core/res/res/drawable/ic_screenshot.xml
+++ b/core/res/res/drawable/ic_screenshot.xml
@@ -17,10 +17,8 @@
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 64f291c..c623c9a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -645,19 +645,7 @@
<!-- Wifi driver supports IEEE80211AC for softap -->
<bool translatable="false" name="config_wifi_softap_ieee80211ac_supported">false</bool>
-
- <!-- Idle Receive current for wifi radio. 0 by default-->
- <integer translatable="false" name="config_wifi_idle_receive_cur_ma">0</integer>
-
- <!-- 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>
-
+
<!-- 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 0c844c9..170ba42 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4821,4 +4821,7 @@
<!-- 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 03a800d..4ef0a6c 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -384,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" />
@@ -3227,5 +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 6e31cd2..bc4b10f 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/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index ec3a6ce..fa0ea5c 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -213,6 +213,7 @@
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,
diff --git a/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java b/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java
index 9166438..6fe19a2 100644
--- a/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java
+++ b/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java
@@ -72,26 +72,26 @@
final LongitudinalReportingEncoder encoder =
LongitudinalReportingEncoder.createInsecureEncoderForTest(
config);
+ 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(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(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 dad98b8..fa0343d 100644
--- a/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java
+++ b/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java
@@ -80,7 +80,7 @@
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/docs/html/reference/images/text/style/absolutesizespan.png b/docs/html/reference/images/text/style/absolutesizespan.png
new file mode 100644
index 0000000..40d5a79
--- /dev/null
+++ b/docs/html/reference/images/text/style/absolutesizespan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/relativesizespan.png b/docs/html/reference/images/text/style/relativesizespan.png
new file mode 100644
index 0000000..eaca5ad
--- /dev/null
+++ b/docs/html/reference/images/text/style/relativesizespan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/scalexspan.png b/docs/html/reference/images/text/style/scalexspan.png
new file mode 100644
index 0000000..a5ca26f
--- /dev/null
+++ b/docs/html/reference/images/text/style/scalexspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/strikethroughspan.png b/docs/html/reference/images/text/style/strikethroughspan.png
new file mode 100644
index 0000000..a49ecad
--- /dev/null
+++ b/docs/html/reference/images/text/style/strikethroughspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/subscriptspan.png b/docs/html/reference/images/text/style/subscriptspan.png
new file mode 100644
index 0000000..aac7092
--- /dev/null
+++ b/docs/html/reference/images/text/style/subscriptspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/superscriptspan.png b/docs/html/reference/images/text/style/superscriptspan.png
new file mode 100644
index 0000000..996f59d
--- /dev/null
+++ b/docs/html/reference/images/text/style/superscriptspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/underlinespan.png b/docs/html/reference/images/text/style/underlinespan.png
new file mode 100644
index 0000000..dbcd0d9
--- /dev/null
+++ b/docs/html/reference/images/text/style/underlinespan.png
Binary files differ
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index e2f02df..77925fd 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -142,6 +142,7 @@
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 @@
// 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 c7f57fe..2953ea8 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -1046,6 +1046,40 @@
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/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 3c49b80..78477f7 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -1380,7 +1380,8 @@
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 @@
}
}
- /**
- * 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 fadb76d..4c96d89 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 @@
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/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index b13de2e..f6541bb 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 764c5922..9b69304 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 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 @@
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 @@
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 4c41b49..ac3599c 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 @@
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 f57d02b..3cda9c9 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.Collection;
import java.util.HashMap;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
/**
@@ -106,6 +110,12 @@
// 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 @@
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 9caff10..fb0f75b 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 @@
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 @@
mRemovedProfiles.add(profile);
mLocalNapRoleConnected = false;
}
+ fetchActiveDevices();
}
CachedBluetoothDevice(Context context,
@@ -359,6 +364,7 @@
fetchName();
fetchBtClass();
updateProfiles();
+ fetchActiveDevices();
migratePhonebookPermissionChoice();
migrateMessagePermissionChoice();
fetchMessageRejectionCount();
@@ -454,6 +460,33 @@
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 @@
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 @@
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 d45fe1a..ee12191 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 @@
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 22674cb..cda4e45 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 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 7227304..0000000
--- 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 4b9f572..0000000
--- 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 1e5b378..0000000
--- 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 facce4e..0000000
--- 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 7983896..0000000
--- 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 8bea51d..0000000
--- 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 d558a64..0000000
--- 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 a264886..0000000
--- 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/SystemUI/res/drawable/ic_screenshot_edit.xml b/packages/SystemUI/res/drawable/ic_screenshot_edit.xml
new file mode 100644
index 0000000..d901292
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_screenshot_edit.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3.0,17.25L3.0,21.0l3.75,0.0L17.81,9.94l-3.75,-3.75L3.0,17.25zM20.71,7.04c0.39,-0.3 0.39,-1.02 0.0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0.0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
+ <path
+ android:pathData="M0 0h24v24H0z"
+ android:fillColor="#00000000"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
index bfe1b62..2c69501 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
@@ -41,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 7762950..5074682 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/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 199ccfc..ab83bcf 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] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 4837fef..bcce6d1 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 13f30b2..7d159b7 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.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.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 @@
}
/**
- * @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/systemui/keyboard/KeyboardUI.java b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
index 4b775a5..b8411e2 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 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/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index e6fd2f4..a97b35c 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.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;
@@ -41,7 +43,8 @@
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;
@@ -54,6 +57,8 @@
protected QuickQSPanel mHeaderQsPanel;
protected QSTileHost mHost;
+ private View mDate;
+
public QuickStatusBarHeader(Context context, AttributeSet attrs) {
super(context, attrs);
}
@@ -64,6 +69,8 @@
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
@@ -145,6 +152,14 @@
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 9e265e22..4ceace3 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.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.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 @@
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 @@
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 @@
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 6db46b5..675aa8f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -293,12 +293,13 @@
sharingIntent.putExtra(Intent.EXTRA_STREAM, uri);
sharingIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
- // Create a share action for the notification. Note, we proxy the call to ShareReceiver
- // because RemoteViews currently forces an activity options on the PendingIntent being
- // launched, and since we don't want to trigger the share sheet in this case, we will
- // start the chooser activitiy directly in ShareReceiver.
+ // Create a share action for the notification. Note, we proxy the call to
+ // ScreenshotActionReceiver because RemoteViews currently forces an activity options
+ // on the PendingIntent being launched, and since we don't want to trigger the share
+ // sheet in this case, we start the chooser activity directly in
+ // ScreenshotActionReceiver.
PendingIntent shareAction = PendingIntent.getBroadcast(context, 0,
- new Intent(context, GlobalScreenshot.ShareReceiver.class)
+ new Intent(context, GlobalScreenshot.ScreenshotActionReceiver.class)
.putExtra(SHARING_INTENT, sharingIntent),
PendingIntent.FLAG_CANCEL_CURRENT);
Notification.Action.Builder shareActionBuilder = new Notification.Action.Builder(
@@ -306,15 +307,19 @@
r.getString(com.android.internal.R.string.share), shareAction);
mNotificationBuilder.addAction(shareActionBuilder.build());
- // Create a delete action for the notification
- PendingIntent deleteAction = PendingIntent.getBroadcast(context, 0,
- new Intent(context, GlobalScreenshot.DeleteScreenshotReceiver.class)
- .putExtra(GlobalScreenshot.SCREENSHOT_URI_ID, uri.toString()),
- PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
- Notification.Action.Builder deleteActionBuilder = new Notification.Action.Builder(
- R.drawable.ic_screenshot_delete,
- r.getString(com.android.internal.R.string.delete), deleteAction);
- mNotificationBuilder.addAction(deleteActionBuilder.build());
+ Intent editIntent = new Intent(Intent.ACTION_EDIT);
+ editIntent.setType("image/png");
+ editIntent.putExtra(Intent.EXTRA_STREAM, uri);
+
+ // Create a edit action for the notification the same way.
+ PendingIntent editAction = PendingIntent.getBroadcast(context, 1,
+ new Intent(context, GlobalScreenshot.ScreenshotActionReceiver.class)
+ .putExtra(SHARING_INTENT, editIntent),
+ PendingIntent.FLAG_CANCEL_CURRENT);
+ Notification.Action.Builder editActionBuilder = new Notification.Action.Builder(
+ R.drawable.ic_screenshot_edit,
+ r.getString(com.android.internal.R.string.screenshot_edit), editAction);
+ mNotificationBuilder.addAction(editActionBuilder.build());
mParams.imageUri = uri;
mParams.image = null;
@@ -879,9 +884,9 @@
}
/**
- * Receiver to proxy the share intent.
+ * Receiver to proxy the share or edit intent.
*/
- public static class ShareReceiver extends BroadcastReceiver {
+ public static class ScreenshotActionReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
try {
@@ -903,7 +908,7 @@
}
/**
- * Removes the notification for a screenshot after a share target is chosen.
+ * Removes the notification for a screenshot after a share or edit target is chosen.
*/
public static class TargetChosenReceiver extends BroadcastReceiver {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
index 9b123cb..7284ee8 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 @@
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 @@
view.setOnClickListener(mClickListener);
view.setOnTouchListener(mTouchListener);
view.setOnLongClickListener(mLongClickListener);
+ view.setOnHoverListener(mOnHoverListener);
if (mLongClickable != null) {
view.setLongClickable(mLongClickable);
}
@@ -174,6 +176,14 @@
}
}
+ 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 4225843..368b36b 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 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 @@
/** 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 @@
private int mNavigationIconHints = 0;
private int mNavigationBarMode;
+ private boolean mAccessibilityFeedbackEnabled;
private AccessibilityManager mAccessibilityManager;
private MagnificationContentObserver mMagnificationObserver;
private ContentResolver mContentResolver;
@@ -143,6 +143,7 @@
public boolean mHomeBlockedThisTouch;
private int mLastRotationSuggestion;
+ private boolean mHoveringRotationSuggestion;
private RotationLockController mRotationLockController;
private TaskStackListenerImpl mTaskStackListener;
@@ -345,40 +346,67 @@
return;
}
- Handler h = getView().getHandler();
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);
@@ -391,18 +419,22 @@
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;
}
@@ -413,12 +445,13 @@
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();
}
});
@@ -532,6 +565,7 @@
ButtonDispatcher rotateSuggestionButton = mNavigationBarView.getRotateSuggestionButton();
rotateSuggestionButton.setOnClickListener(this::onRotateSuggestionClick);
+ rotateSuggestionButton.setOnHoverListener(this::onRotateSuggestionHover);
}
private boolean onHomeTouch(View v, MotionEvent event) {
@@ -707,6 +741,7 @@
} 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 =
@@ -717,8 +752,15 @@
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);
@@ -728,6 +770,14 @@
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) {
@@ -775,18 +825,18 @@
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 e8b28f2..b181212 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -62,6 +62,7 @@
};
private DarkReceiver mBattery;
private int mLastOrientation;
+ @Nullable
private View mCutoutSpace;
@Nullable
private DisplayCutout mDisplayCutout;
@@ -284,6 +285,11 @@
}
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);
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 6220fcb..1130b6d 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 @@
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 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/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index 3b15c2b..fcf084b 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 @@
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/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
index 8e584bc..5a4478f 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 @@
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 @@
TextUtils.join(",", iconBlacklist), mCurrentUser);
}
}
+ if (oldVersion < 2) {
+ setTunerEnabled(mContext, false);
+ }
setValue(TUNER_VERSION, newVersion);
}
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 cc3af8c..289dd14 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 @@
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 @@
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 @@
// 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 @@
// 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 @@
}
}
- 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 @@
}
// 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 @@
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 @@
}
}
- void revertAndEndBackup() {
+ private void revertAndEndBackup() {
if (MORE_DEBUG) {
Slog.i(TAG, "Reverting backup queue - restaging everything");
}
@@ -1085,14 +1085,14 @@
}
- 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 @@
}
}
- 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/core/java/com/android/server/ForceAppStandbyTracker.java b/services/core/java/com/android/server/ForceAppStandbyTracker.java
index a538bde..de113a6 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.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 @@
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 @@
@GuardedBy("mLock")
boolean mStarted;
+ /**
+ * Only used for small battery use-case.
+ */
@GuardedBy("mLock")
- boolean mForceAllAppsStandby; // True if device is in extreme battery saver mode
+ boolean mIsPluggedIn;
@GuardedBy("mLock")
- boolean mForcedAppStandbyEnabled; // True if the forced app standby feature is enabled
+ boolean mBatterySaverEnabled;
- private class FeatureFlagObserver extends ContentObserver {
- FeatureFlagObserver() {
+ /**
+ * True if the forced app standby is currently enabled
+ */
+ @GuardedBy("mLock")
+ boolean mForceAllAppsStandby;
+
+ /**
+ * 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 @@
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 @@
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 @@
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 @@
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 @@
return LocalServices.getService(PowerManagerInternal.class);
}
+ @VisibleForTesting
+ boolean isSmallBatteryDevice() {
+ return ActivityManager.isSmallBatteryDevice();
+ }
+
/**
* Update {@link #mRunAnyRestrictedPackages} with the current app ops state.
*/
@@ -369,18 +437,26 @@
}
}
+ private void updateForceAllAppStandbyState() {
+ synchronized (mLock) {
+ if (mForceAllAppStandbyForSmallBattery && isSmallBatteryDevice()) {
+ toggleForceAllAppsStandbyLocked(!mIsPluggedIn);
+ } else {
+ toggleForceAllAppsStandbyLocked(mBatterySaverEnabled);
+ }
+ }
+ }
+
/**
* Update {@link #mForceAllAppsStandby} and notifies the listeners.
*/
- void updateForceAllAppsStandby(boolean enable) {
- synchronized (mLock) {
- if (enable == mForceAllAppsStandby) {
- return;
- }
- mForceAllAppsStandby = enable;
-
- mHandler.notifyForceAllAppsStandbyChanged();
+ 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 @@
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 @@
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 @@
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 @@
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) {
@@ -845,6 +926,18 @@
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 = "";
@@ -883,6 +976,11 @@
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 46a35ec..ef6bc43 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -148,7 +148,7 @@
* 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 @@
}
}
- /* 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 @@
@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 @@
}
}
+ /**
+ * 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 @@
@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 @@
}
};
- // 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 @@
}
}
- 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 @@
}
}
- 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 @@
}
}
- 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 @@
* 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 @@
* 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 80f8e51..833def3 100644
--- a/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java
+++ b/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java
@@ -26,4 +26,7 @@
/** 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 c32a2d1..4298140 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -668,5 +668,13 @@
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 0000000..6c1ffdd
--- /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 e38148c..db21ef1 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 @@
}
}
+ 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 c0c684c..254d49b 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.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.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_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.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.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.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.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 {
@@ -10414,10 +10417,9 @@
@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());
@@ -21480,6 +21482,17 @@
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 */,
@@ -25159,6 +25172,10 @@
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 8168cba..3d8863e 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 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 8910274..26d65bc 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 @@
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 1fcaeef..927b72c 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 @@
// 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 @@
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 @@
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 @@
// 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 207aaa7..04b49ba 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -40,6 +40,7 @@
import android.os.WorkSource;
import android.os.WorkSource.WorkChain;
import android.os.connectivity.CellularBatteryStats;
+import android.os.connectivity.WifiBatteryStats;
import android.os.connectivity.GpsBatteryStats;
import android.os.health.HealthStatsParceler;
import android.os.health.HealthStatsWriter;
@@ -1455,6 +1456,16 @@
}
/**
+ * Gets a snapshot of Wifi stats
+ * @hide
+ */
+ public WifiBatteryStats getWifiBatteryStats() {
+ synchronized (mStats) {
+ return mStats.getWifiBatteryStats();
+ }
+ }
+
+ /**
* Gets a snapshot of Gps stats
* @hide
*/
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 1a47aa5..5ada484 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 @@
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 @@
mService.mLockTaskController.clearLockedTasks(reason);
}
}
+
+ protected boolean isCallerRecents(int callingUid) {
+ return mService.getRecentTasks().isCallerRecents(callingUid);
+ }
}
}
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index bd1dbf9..fa5fdf5 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.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 @@
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/oemlock/OemLockService.java b/services/core/java/com/android/server/oemlock/OemLockService.java
index 2a2ff06..a6200bf 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.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 @@
* 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 cc448da..a6ff4f7 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 @@
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 10b377d..faf6114 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -118,6 +118,7 @@
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;
@@ -992,6 +993,7 @@
private List<String> mKeepUninstalledPackages;
private UserManagerInternal mUserManagerInternal;
+ private ActivityManagerInternal mActivityManagerInternal;
private DeviceIdleController.LocalService mDeviceIdleController;
@@ -4575,6 +4577,14 @@
return mUserManagerInternal;
}
+ private ActivityManagerInternal getActivityManagerInternal() {
+ if (mActivityManagerInternal == null) {
+ mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
+ }
+ return mActivityManagerInternal;
+ }
+
+
private DeviceIdleController.LocalService getDeviceIdleController() {
if (mDeviceIdleController == null) {
mDeviceIdleController =
@@ -4735,8 +4745,12 @@
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);
@@ -4758,6 +4772,22 @@
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) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index a33f071..47cd813 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 @@
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 @@
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 @@
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 6599fd9..0f394a4 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -4352,6 +4352,7 @@
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;
@@ -4374,8 +4375,12 @@
}
}
- 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() &&
diff --git a/services/core/java/com/android/server/slice/PinnedSliceState.java b/services/core/java/com/android/server/slice/PinnedSliceState.java
index 09f6da9..5811714 100644
--- a/services/core/java/com/android/server/slice/PinnedSliceState.java
+++ b/services/core/java/com/android/server/slice/PinnedSliceState.java
@@ -21,6 +21,8 @@
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;
@@ -49,11 +51,13 @@
@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<ISliceListener, String> mPkgMap = new ArrayMap<>();
+ private final ArrayMap<IBinder, String> mPkgMap = new ArrayMap<>();
+
+ private final DeathRecipient mDeathRecipient = this::handleRecheckListeners;
public PinnedSliceState(SliceManagerService service, Uri uri) {
mService = service;
@@ -107,20 +111,27 @@
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);
}
- mPkgMap.put(listener, pkg);
+ 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) {
- mPkgMap.remove(listener);
- 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();
}
@@ -159,25 +170,44 @@
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 cachedSlice = doBind(null);
synchronized (mLock) {
- mListeners.removeIf(l -> {
+ 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) {
- return true;
+ mListeners.removeAt(i);
+ continue;
}
try {
l.onSliceUpdated(s);
- return false;
} catch (RemoteException e) {
Log.e(TAG, "Unable to notify slice " + mUri, e);
- return true;
+ mListeners.removeAt(i);
+ continue;
}
- });
+ }
if (!isPinned()) {
// All the listeners died, remove from pinned state.
mService.removePinnedSlice(mUri);
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index ca7632c..c191580 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -91,9 +91,9 @@
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);
}
@@ -204,7 +204,7 @@
}
/// ----- internal code -----
- void removePinnedSlice(Uri uri) {
+ protected void removePinnedSlice(Uri uri) {
synchronized (mLock) {
mPinnedSlicesByUri.remove(uri).destroy();
}
@@ -233,7 +233,7 @@
}
@VisibleForTesting
- PinnedSliceState createPinnedSlice(Uri uri) {
+ protected PinnedSliceState createPinnedSlice(Uri uri) {
return new PinnedSliceState(this, uri);
}
@@ -352,7 +352,7 @@
// 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<>();
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 688b4ff..8515dcb 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 @@
}
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 @@
}
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/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index e660c50..94a356e 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1172,6 +1172,15 @@
}
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 429dd8f..de54e52 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.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.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 @@
PowerManagerInternal injectPowerManagerInternal() {
return mMockPowerManagerInternal;
}
+
+ @Override
+ boolean isSmallBatteryDevice() { return mIsSmallBatteryDevice; };
}
private static final int UID_1 = Process.FIRST_APPLICATION_UID + 1;
@@ -144,6 +149,7 @@
private FakeSettingsProvider mFakeSettingsProvider;
private boolean mPowerSaveMode;
+ private boolean mIsSmallBatteryDevice;
private final ArraySet<Pair<Integer, String>> mRestrictedPackages = new ArraySet();
@@ -232,6 +238,7 @@
assertNotNull(mAppOpsCallback);
assertNotNull(mPowerSaveObserver);
assertNotNull(mReceiver);
+ assertNotNull(instance.mFlagsObserver);
}
private void setAppOps(int uid, String packageName, boolean restrict) throws RemoteException {
@@ -852,6 +859,56 @@
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/net/watchlist/PrivacyUtilsTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/PrivacyUtilsTests.java
index a31b46c..999dce5 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 @@
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 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 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/policy/PhoneWindowManagerLayoutTest.java b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
index b6c370e..293f9af 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.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 @@
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/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
index aada682..d3bb804 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.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.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 org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -147,6 +152,7 @@
@Test
public void testListenerPin() {
ISliceListener listener = mock(ISliceListener.class);
+ when(listener.asBinder()).thenReturn(new Binder());
assertFalse(mPinnedSliceManager.isPinned());
mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS);
@@ -159,7 +165,11 @@
@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, mContext.getPackageName(), FIRST_SPECS);
@@ -172,8 +182,30 @@
}
@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, mContext.getPackageName(), FIRST_SPECS);
@@ -191,6 +223,7 @@
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);
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 8c7d6b3..d17bdc8 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -419,7 +419,6 @@
/**
* 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 aaef8d3..7522443 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -35,6 +35,7 @@
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 @@
/**
* 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 @@
}
/**
+ * 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 d292db3..91d5da3 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -602,12 +602,17 @@
/**
* 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 0000000..3e848c6
--- /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 ce0b551..649d478 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1644,6 +1644,13 @@
"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 @@
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/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index de9e691..17f809d 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1024,8 +1024,8 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 2ab8d4f..73a05af 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -21,22 +21,23 @@
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.MalformedURLException;
-import java.net.UnknownHostException;
-import java.net.URL;
import java.net.InetAddress;
-import java.util.Arrays;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.UnknownHostException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Objects;
@@ -67,8 +68,8 @@
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 @@
}
/**
- * 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 @@
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 @@
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 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 @@
.setProtocol(protocol)
.setRoamingProtocol(roamingProtocol)
.setCarrierEnabled(carrierEnabled)
- .setBearer(bearer)
- .setBearerBitmask(bearerBitmask)
+ .setNetworkTypeBitmask(networkTypeBitmask)
.setProfileId(profileId)
.setModemCognitive(modemCognitive)
.setMaxConns(maxConns)
@@ -504,6 +489,14 @@
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 @@
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 @@
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 @@
/** @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 @@
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 @@
sb.append(", ").append(mMvnoType);
sb.append(", ").append(mMvnoMatchData);
sb.append(", ").append(mPermanentFailed);
+ sb.append(", ").append(mNetworkTypeBitmask);
return sb.toString();
}
@@ -678,8 +668,6 @@
&& 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 @@
&& 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 @@
&& 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 @@
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 @@
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 @@
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 @@
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,6 +1071,16 @@
public Builder() {}
/**
+ * Sets the unique database id for this entry.
+ *
+ * @param id the unique database id to set for this entry
+ */
+ private Builder setId(int id) {
+ this.mId = id;
+ return this;
+ }
+
+ /**
* Set the MTU size of the mobile interface to which the APN connected.
*
* @param mtu the MTU size to set for the APN
@@ -1089,28 +1092,6 @@
}
/**
- * Sets bearer info.
- *
- * @param bearer the bearer info to set for the APN
- * @hide
- */
- public Builder setBearer(int bearer) {
- this.mBearer = bearer;
- return this;
- }
-
- /**
- * Sets the radio access technology bitmask for this APN.
- *
- * @param bearerBitmask the radio access technology bitmask to set for this APN
- * @hide
- */
- public Builder setBearerBitmask(int bearerBitmask) {
- this.mBearerBitmask = bearerBitmask;
- return this;
- }
-
- /**
* Sets the profile id to which the APN saved in modem.
*
* @param profileId the profile id to set for the APN
@@ -1298,16 +1279,6 @@
}
/**
- * 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 @@
}
/**
- * 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 @@
}
/**
+ * 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 2534327..d146707 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -71,8 +71,18 @@
* 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 1bd1af5..994f3cc 100644
--- a/tests/net/Android.mk
+++ b/tests/net/Android.mk
@@ -31,32 +31,34 @@
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 \
- libui \
- libunwind \
- libutils \
- libvndksupport \
- libcrypto \
- libhidl-gen-utils \
- libhidlbase \
- libhidltransport \
libpackagelistparser \
libpcre2 \
libselinux \
- libtinyxml2 \
+ libui \
+ libunwind \
+ libutils \
libvintf \
- libhwbinder \
+ libvndksupport \
+ libtinyxml2 \
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 4fbb228..d9d4eeb 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -204,13 +204,13 @@
}
@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 @@
}
@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 @@
}
@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 @@
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 @@
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 3eba881..a375b60 100644
--- a/tests/net/java/com/android/server/IpSecServiceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceTest.java
@@ -166,6 +166,7 @@
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 @@
@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 @@
refcountedRecord.binderDied();
+ // Verify quota and RefcountedResource objects cleaned up
assertEquals(0, userRecord.mSocketQuotaTracker.mCurrent);
try {
userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(udpEncapResp.resourceId);
@@ -412,9 +412,9 @@
}
@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/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java b/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java
index 29bf02c..03c9fbe 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java
+++ b/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java
@@ -56,6 +56,11 @@
/**
* @hide
*/
+ public long mControllerScanTimeMs;
+
+ /**
+ * @hide
+ */
public long mControllerIdleTimeMs;
/**
@@ -69,13 +74,14 @@
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 @@
+ " mControllerTxTimeMs=" + mControllerTxTimeMs
+ " mControllerTxTimePerLevelMs=" + Arrays.toString(mControllerTxTimePerLevelMs)
+ " mControllerRxTimeMs=" + mControllerRxTimeMs
+ + " mControllerScanTimeMs=" + mControllerScanTimeMs
+ " mControllerIdleTimeMs=" + mControllerIdleTimeMs
+ " mControllerEnergyUsed=" + mControllerEnergyUsed
+ " }";
@@ -101,10 +108,11 @@
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 @@
out.writeLong(mControllerTxTimeMs);
out.writeLongArray(mControllerTxTimePerLevelMs);
out.writeLong(mControllerRxTimeMs);
+ out.writeLong(mControllerScanTimeMs);
out.writeLong(mControllerIdleTimeMs);
out.writeLong(mControllerEnergyUsed);
}
@@ -157,6 +166,13 @@
}
/**
+ * @return scan time in ms
+ */
+ public long getControllerScanTimeMillis() {
+ return mControllerScanTimeMs;
+ }
+
+ /**
* @return idle time in ms
*/
public long getControllerIdleTimeMillis() {
@@ -183,6 +199,7 @@
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/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 99080d6..e4b510d 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1625,6 +1625,7 @@
*
* @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 @@
/** @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 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 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 @@
* {@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 @@
* @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 @@
* @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 0000000..610edb6
--- /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 0000000..8aba56a
--- /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 d5ca8f7..201833b 100644
--- a/wifi/java/android/net/wifi/rtt/RangingResult.java
+++ b/wifi/java/android/net/wifi/rtt/RangingResult.java
@@ -65,29 +65,37 @@
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 @@
}
/**
+ * @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 @@
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 @@
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 @@
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 @@
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 72e95b9..41c7f86 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.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 @@
/**
* Unit test harness for WifiRttManager class.
*/
-@SmallTest
public class WifiRttManagerTest {
private WifiRttManager mDut;
private TestLooper mMockLooper;
@@ -80,7 +79,7 @@
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 @@
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 @@
// 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 @@
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();
+ }
}