Merge "AutoTileManager: Auto add cast tile when the device is casting."
diff --git a/Android.bp b/Android.bp
index 6288940..4b22f9e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -845,6 +845,7 @@
// specified on the build command line.
java_library {
name: "framework-atb-backward-compatibility",
+ installable: true,
srcs: [
"core/java/android/content/pm/AndroidTestBaseUpdater.java",
],
@@ -1265,10 +1266,7 @@
"test-base/src/**/*.java",
":opt-telephony-srcs",
":opt-net-voip-srcs",
- ":openjdk_javadoc_files",
- ":non_openjdk_javadoc_files",
- ":android_icu4j_src_files_for_docs",
- ":conscrypt_public_api_files",
+ ":core_public_api_files",
":updatable-media-srcs-without-aidls",
"test-mock/src/**/*.java",
"test-runner/src/**/*.java",
@@ -1327,10 +1325,7 @@
srcs: [
":opt-telephony-srcs",
":opt-net-voip-srcs",
- ":openjdk_javadoc_files",
- ":non_openjdk_javadoc_files",
- ":android_icu4j_src_files_for_docs",
- ":conscrypt_public_api_files",
+ ":core_public_api_files",
":updatable-media-srcs-without-aidls",
],
srcs_lib: "framework",
@@ -1514,7 +1509,7 @@
],
proofread_file: "ds-docs-proofrerad.txt",
args: framework_docs_only_args +
- " -toroot / -samplegroup Admin " +
+ " -toroot / -yamlV2 -metalavaApiSince -samplegroup Admin " +
" -samplegroup Background " +
" -samplegroup Connectivity " +
" -samplegroup Content " +
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/RenderNodePerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/RenderNodePerfTest.java
index e7fe235..e805ab9 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/RenderNodePerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/RenderNodePerfTest.java
@@ -71,7 +71,7 @@
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
RenderNode node = RenderNode.create("LinearLayout", null);
while (state.keepRunning()) {
- node.startRecording(100, 100);
+ node.beginRecording(100, 100);
node.endRecording();
}
}
@@ -86,7 +86,7 @@
while (state.keepRunning()) {
for (int i = 0; i < nodes.length; i++) {
- nodes[i].startRecording(100, 100);
+ nodes[i].beginRecording(100, 100);
}
for (int i = nodes.length - 1; i >= 0; i--) {
nodes[i].endRecording();
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
index bd7522d..8a6c60f 100644
--- a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
@@ -265,7 +265,7 @@
state.pauseTiming();
final StaticLayout layout =
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
- final RecordingCanvas c = node.startRecording(1200, 200);
+ final RecordingCanvas c = node.beginRecording(1200, 200);
state.resumeTiming();
layout.draw(c);
@@ -282,7 +282,7 @@
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
final StaticLayout layout =
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
- final RecordingCanvas c = node.startRecording(1200, 200);
+ final RecordingCanvas c = node.beginRecording(1200, 200);
state.resumeTiming();
layout.draw(c);
@@ -299,7 +299,7 @@
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
final StaticLayout layout =
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
- final RecordingCanvas c = node.startRecording(1200, 200);
+ final RecordingCanvas c = node.beginRecording(1200, 200);
state.resumeTiming();
layout.draw(c);
@@ -316,7 +316,7 @@
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
final StaticLayout layout =
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
- final RecordingCanvas c = node.startRecording(1200, 200);
+ final RecordingCanvas c = node.beginRecording(1200, 200);
Canvas.freeTextLayoutCaches();
state.resumeTiming();
@@ -334,7 +334,7 @@
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
final StaticLayout layout =
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
- final RecordingCanvas c = node.startRecording(1200, 200);
+ final RecordingCanvas c = node.beginRecording(1200, 200);
Canvas.freeTextLayoutCaches();
state.resumeTiming();
@@ -353,7 +353,7 @@
mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT);
final StaticLayout layout =
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
- final RecordingCanvas c = node.startRecording(1200, 200);
+ final RecordingCanvas c = node.beginRecording(1200, 200);
state.resumeTiming();
layout.draw(c);
@@ -371,7 +371,7 @@
mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT);
final StaticLayout layout =
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
- final RecordingCanvas c = node.startRecording(1200, 200);
+ final RecordingCanvas c = node.beginRecording(1200, 200);
state.resumeTiming();
layout.draw(c);
@@ -389,7 +389,7 @@
mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT);
final StaticLayout layout =
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
- final RecordingCanvas c = node.startRecording(1200, 200);
+ final RecordingCanvas c = node.beginRecording(1200, 200);
Canvas.freeTextLayoutCaches();
state.resumeTiming();
@@ -408,7 +408,7 @@
mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT);
final StaticLayout layout =
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
- final RecordingCanvas c = node.startRecording(1200, 200);
+ final RecordingCanvas c = node.beginRecording(1200, 200);
Canvas.freeTextLayoutCaches();
state.resumeTiming();
diff --git a/apct-tests/perftests/core/src/android/widget/TextViewPrecomputedTextPerfTest.java b/apct-tests/perftests/core/src/android/widget/TextViewPrecomputedTextPerfTest.java
index 0bc9ee4..55d54e4 100644
--- a/apct-tests/perftests/core/src/android/widget/TextViewPrecomputedTextPerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/TextViewPrecomputedTextPerfTest.java
@@ -344,7 +344,7 @@
textView.setText(text);
textView.measure(width, height);
textView.layout(0, 0, textView.getMeasuredWidth(), textView.getMeasuredHeight());
- final RecordingCanvas c = node.startRecording(
+ final RecordingCanvas c = node.beginRecording(
textView.getMeasuredWidth(), textView.getMeasuredHeight());
textView.nullLayouts();
Canvas.freeTextLayoutCaches();
@@ -371,7 +371,7 @@
textView.setText(text);
textView.measure(width, height);
textView.layout(0, 0, textView.getMeasuredWidth(), textView.getMeasuredHeight());
- final RecordingCanvas c = node.startRecording(
+ final RecordingCanvas c = node.beginRecording(
textView.getMeasuredWidth(), textView.getMeasuredHeight());
textView.nullLayouts();
Canvas.freeTextLayoutCaches();
@@ -400,7 +400,7 @@
textView.setText(text);
textView.measure(width, height);
textView.layout(0, 0, textView.getMeasuredWidth(), textView.getMeasuredHeight());
- final RecordingCanvas c = node.startRecording(
+ final RecordingCanvas c = node.beginRecording(
textView.getMeasuredWidth(), textView.getMeasuredHeight());
textView.nullLayouts();
Canvas.freeTextLayoutCaches();
@@ -430,7 +430,7 @@
textView.setText(text);
textView.measure(width, height);
textView.layout(0, 0, textView.getMeasuredWidth(), textView.getMeasuredHeight());
- final RecordingCanvas c = node.startRecording(
+ final RecordingCanvas c = node.beginRecording(
textView.getMeasuredWidth(), textView.getMeasuredHeight());
textView.nullLayouts();
Canvas.freeTextLayoutCaches();
diff --git a/api/current.txt b/api/current.txt
index 7e0156f..32747e7 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -7435,7 +7435,10 @@
method @NonNull public android.content.Intent createRequestRoleIntent(@NonNull String);
method public boolean isRoleAvailable(@NonNull String);
method public boolean isRoleHeld(@NonNull String);
+ field public static final String ROLE_ASSISTANT = "android.app.role.ASSISTANT";
field public static final String ROLE_BROWSER = "android.app.role.BROWSER";
+ field public static final String ROLE_CALL_REDIRECTION = "android.app.role.CALL_REDIRECTION";
+ field public static final String ROLE_CALL_SCREENING = "android.app.role.CALL_SCREENING";
field public static final String ROLE_DIALER = "android.app.role.DIALER";
field public static final String ROLE_EMERGENCY = "android.app.role.EMERGENCY";
field public static final String ROLE_GALLERY = "android.app.role.GALLERY";
@@ -11238,7 +11241,7 @@
public class LauncherApps {
method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(String, android.os.UserHandle);
method @NonNull public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getAllPackageInstallerSessions();
- method @Nullable public android.content.pm.LauncherApps.AppUsageLimit getAppUsageLimit(String, android.os.UserHandle);
+ method @Nullable public android.content.pm.LauncherApps.AppUsageLimit getAppUsageLimit(@NonNull String, @NonNull android.os.UserHandle);
method public android.content.pm.ApplicationInfo getApplicationInfo(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
method public android.content.pm.LauncherApps.PinItemRequest getPinItemRequest(android.content.Intent);
method public java.util.List<android.os.UserHandle> getProfiles();
@@ -11457,10 +11460,10 @@
method public boolean isActive();
method public boolean isMultiPackage();
method public boolean isSealed();
- method public boolean isSessionApplied();
- method public boolean isSessionFailed();
- method public boolean isSessionReady();
method public boolean isStaged();
+ method public boolean isStagedSessionApplied();
+ method public boolean isStagedSessionFailed();
+ method public boolean isStagedSessionReady();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.content.pm.PackageInstaller.SessionInfo> CREATOR;
field public static final int INVALID_ID = -1; // 0xffffffff
@@ -11664,6 +11667,7 @@
field public static final String FEATURE_FAKETOUCH_MULTITOUCH_DISTINCT = "android.hardware.faketouch.multitouch.distinct";
field public static final String FEATURE_FAKETOUCH_MULTITOUCH_JAZZHAND = "android.hardware.faketouch.multitouch.jazzhand";
field public static final String FEATURE_FINGERPRINT = "android.hardware.fingerprint";
+ field public static final String FEATURE_FOLDABLE = "android.hardware.type.foldable";
field public static final String FEATURE_FREEFORM_WINDOW_MANAGEMENT = "android.software.freeform_window_management";
field public static final String FEATURE_GAMEPAD = "android.hardware.gamepad";
field public static final String FEATURE_HIFI_SENSORS = "android.hardware.sensor.hifi_sensors";
@@ -14945,13 +14949,15 @@
public final class RenderNode {
ctor public RenderNode(@Nullable String);
- method public int computeApproximateMemoryUsage();
+ method public android.graphics.RecordingCanvas beginRecording(int, int);
+ method public android.graphics.RecordingCanvas beginRecording();
+ method public long computeApproximateMemoryUsage();
method public void discardDisplayList();
method public void endRecording();
method public float getAlpha();
- method public int getAmbientShadowColor();
+ method @ColorInt public int getAmbientShadowColor();
method public int getBottom();
- method public float getCameraDistance();
+ method @FloatRange(from=0.0f, to=java.lang.Float.MAX_VALUE) public float getCameraDistance();
method public boolean getClipToBounds();
method public boolean getClipToOutline();
method public float getElevation();
@@ -14962,12 +14968,12 @@
method public float getPivotX();
method public float getPivotY();
method public int getRight();
- method public float getRotation();
method public float getRotationX();
method public float getRotationY();
+ method public float getRotationZ();
method public float getScaleX();
method public float getScaleY();
- method public int getSpotShadowColor();
+ method @ColorInt public int getSpotShadowColor();
method public int getTop();
method public float getTranslationX();
method public float getTranslationY();
@@ -14985,36 +14991,31 @@
method public boolean offsetTopAndBottom(int);
method public boolean resetPivot();
method public boolean setAlpha(float);
- method public boolean setAmbientShadowColor(int);
- method public boolean setBottom(int);
- method public boolean setCameraDistance(float);
- method public boolean setClipBounds(@Nullable android.graphics.Rect);
+ method public boolean setAmbientShadowColor(@ColorInt int);
+ method public boolean setCameraDistance(@FloatRange(from=0.0f, to=java.lang.Float.MAX_VALUE) float);
+ method public boolean setClipRect(@Nullable android.graphics.Rect);
method public boolean setClipToBounds(boolean);
method public boolean setClipToOutline(boolean);
method public boolean setElevation(float);
method public boolean setForceDarkAllowed(boolean);
method public boolean setHasOverlappingRendering(boolean);
- method public boolean setLeft(int);
- method public boolean setLeftTopRightBottom(int, int, int, int);
method public boolean setOutline(@Nullable android.graphics.Outline);
method public boolean setPivotX(float);
method public boolean setPivotY(float);
+ method public boolean setPosition(int, int, int, int);
+ method public boolean setPosition(android.graphics.Rect);
method public boolean setProjectBackwards(boolean);
method public boolean setProjectionReceiver(boolean);
- method public boolean setRight(int);
- method public boolean setRotation(float);
method public boolean setRotationX(float);
method public boolean setRotationY(float);
+ method public boolean setRotationZ(float);
method public boolean setScaleX(float);
method public boolean setScaleY(float);
- method public boolean setSpotShadowColor(int);
- method public boolean setTop(int);
+ method public boolean setSpotShadowColor(@ColorInt int);
method public boolean setTranslationX(float);
method public boolean setTranslationY(float);
method public boolean setTranslationZ(float);
method public boolean setUseCompositingLayer(boolean, @Nullable android.graphics.Paint);
- method public android.graphics.RecordingCanvas startRecording(int, int);
- method public android.graphics.RecordingCanvas startRecording();
}
public class Shader {
@@ -26034,6 +26035,7 @@
public static final class MediaSession2.Builder {
ctor public MediaSession2.Builder(@NonNull android.content.Context);
method @NonNull public android.media.MediaSession2 build();
+ method @NonNull public android.media.MediaSession2.Builder setExtras(@Nullable android.os.Bundle);
method @NonNull public android.media.MediaSession2.Builder setId(@NonNull String);
method @NonNull public android.media.MediaSession2.Builder setSessionActivity(@Nullable android.app.PendingIntent);
method @NonNull public android.media.MediaSession2.Builder setSessionCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaSession2.SessionCallback);
@@ -26396,6 +26398,7 @@
public final class Session2Token implements android.os.Parcelable {
ctor public Session2Token(@NonNull android.content.Context, @NonNull android.content.ComponentName);
method public int describeContents();
+ method @Nullable public android.os.Bundle getExtras();
method @NonNull public String getPackageName();
method @Nullable public String getServiceName();
method public int getType();
@@ -29355,7 +29358,7 @@
method public android.os.ParcelFileDescriptor establish();
method public android.net.VpnService.Builder setBlocking(boolean);
method public android.net.VpnService.Builder setConfigureIntent(android.app.PendingIntent);
- method public android.net.VpnService.Builder setHttpProxy(android.net.ProxyInfo);
+ method public android.net.VpnService.Builder setHttpProxy(@NonNull android.net.ProxyInfo);
method public android.net.VpnService.Builder setMetered(boolean);
method public android.net.VpnService.Builder setMtu(int);
method public android.net.VpnService.Builder setSession(String);
@@ -38898,6 +38901,7 @@
field public static final String AIRPLANE_MODE_RADIOS = "airplane_mode_radios";
field public static final String ALWAYS_FINISH_ACTIVITIES = "always_finish_activities";
field public static final String ANIMATOR_DURATION_SCALE = "animator_duration_scale";
+ field public static final String APPLY_RAMPING_RINGER = "apply_ramping_ringer";
field public static final String AUTO_TIME = "auto_time";
field public static final String AUTO_TIME_ZONE = "auto_time_zone";
field public static final String BLUETOOTH_ON = "bluetooth_on";
@@ -44861,6 +44865,7 @@
method @Deprecated public int getCdmaDbm();
method @Deprecated public int getCdmaEcio();
method @NonNull public java.util.List<android.telephony.CellSignalStrength> getCellSignalStrengths();
+ method public <T extends android.telephony.CellSignalStrength> java.util.List<T> getCellSignalStrengths(@NonNull Class<T>);
method @Deprecated public int getEvdoDbm();
method @Deprecated public int getEvdoEcio();
method @Deprecated public int getEvdoSnr();
@@ -45347,13 +45352,13 @@
}
public final class UiccCardInfo implements android.os.Parcelable {
- ctor public UiccCardInfo(boolean, int, String, String, int);
method public int describeContents();
method public int getCardId();
method public String getEid();
method public String getIccId();
method public int getSlotIndex();
method public boolean isEuicc();
+ method public boolean isRemovable();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.telephony.UiccCardInfo> CREATOR;
}
@@ -45668,465 +45673,6 @@
}
-package android.telephony.ims {
-
- public class Rcs1To1Thread extends android.telephony.ims.RcsThread {
- method @WorkerThread public long getFallbackThreadId() throws android.telephony.ims.RcsMessageStoreException;
- method @NonNull @WorkerThread public android.telephony.ims.RcsParticipant getRecipient() throws android.telephony.ims.RcsMessageStoreException;
- method public boolean isGroup();
- method @WorkerThread public void setFallbackThreadId(long) throws android.telephony.ims.RcsMessageStoreException;
- }
-
- public abstract class RcsEvent {
- ctor protected RcsEvent(long);
- method public long getTimestamp();
- }
-
- public final class RcsEventQueryParams implements android.os.Parcelable {
- method public int describeContents();
- method @android.telephony.ims.RcsEventQueryParams.EventType public int getEventType();
- method public int getLimit();
- method public boolean getSortDirection();
- method public int getSortingProperty();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final int ALL_EVENTS = -1; // 0xffffffff
- field public static final int ALL_GROUP_THREAD_EVENTS = 0; // 0x0
- field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsEventQueryParams> CREATOR;
- field public static final int GROUP_THREAD_ICON_CHANGED_EVENT = 8; // 0x8
- field public static final int GROUP_THREAD_NAME_CHANGED_EVENT = 16; // 0x10
- field public static final int GROUP_THREAD_PARTICIPANT_JOINED_EVENT = 2; // 0x2
- field public static final int GROUP_THREAD_PARTICIPANT_LEFT_EVENT = 4; // 0x4
- field public static final int PARTICIPANT_ALIAS_CHANGED_EVENT = 1; // 0x1
- field public static final int SORT_BY_CREATION_ORDER = 0; // 0x0
- field public static final int SORT_BY_TIMESTAMP = 1; // 0x1
- }
-
- public static class RcsEventQueryParams.Builder {
- ctor public RcsEventQueryParams.Builder();
- method public android.telephony.ims.RcsEventQueryParams build();
- method @CheckResult public android.telephony.ims.RcsEventQueryParams.Builder setEventType(@android.telephony.ims.RcsEventQueryParams.EventType int);
- method @CheckResult public android.telephony.ims.RcsEventQueryParams.Builder setGroupThread(@NonNull android.telephony.ims.RcsGroupThread);
- method @CheckResult public android.telephony.ims.RcsEventQueryParams.Builder setResultLimit(@IntRange(from=0) int) throws java.security.InvalidParameterException;
- method @CheckResult public android.telephony.ims.RcsEventQueryParams.Builder setSortDirection(boolean);
- method @CheckResult public android.telephony.ims.RcsEventQueryParams.Builder setSortProperty(@android.telephony.ims.RcsEventQueryParams.SortingProperty int);
- }
-
- @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsEventQueryParams.ALL_EVENTS, android.telephony.ims.RcsEventQueryParams.ALL_GROUP_THREAD_EVENTS, android.telephony.ims.RcsEventQueryParams.PARTICIPANT_ALIAS_CHANGED_EVENT, android.telephony.ims.RcsEventQueryParams.GROUP_THREAD_PARTICIPANT_JOINED_EVENT, android.telephony.ims.RcsEventQueryParams.GROUP_THREAD_PARTICIPANT_LEFT_EVENT, android.telephony.ims.RcsEventQueryParams.GROUP_THREAD_NAME_CHANGED_EVENT, android.telephony.ims.RcsEventQueryParams.GROUP_THREAD_ICON_CHANGED_EVENT}) public static @interface RcsEventQueryParams.EventType {
- }
-
- @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsEventQueryParams.SORT_BY_CREATION_ORDER, android.telephony.ims.RcsEventQueryParams.SORT_BY_TIMESTAMP}) public static @interface RcsEventQueryParams.SortingProperty {
- }
-
- public class RcsEventQueryResult {
- method public android.telephony.ims.RcsQueryContinuationToken getContinuationToken();
- method public java.util.List<android.telephony.ims.RcsEvent> getEvents();
- }
-
- public final class RcsFileTransferCreationParams implements android.os.Parcelable {
- method public int describeContents();
- method public String getContentMimeType();
- method public android.net.Uri getContentUri();
- method public long getFileSize();
- method @android.telephony.ims.RcsFileTransferPart.RcsFileTransferStatus public int getFileTransferStatus();
- method public int getHeight();
- method public long getMediaDuration();
- method public String getPreviewMimeType();
- method public android.net.Uri getPreviewUri();
- method public String getRcsFileTransferSessionId();
- method public long getTransferOffset();
- method public int getWidth();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsFileTransferCreationParams> CREATOR;
- }
-
- public class RcsFileTransferCreationParams.Builder {
- ctor public RcsFileTransferCreationParams.Builder();
- method public android.telephony.ims.RcsFileTransferCreationParams build();
- method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setContentMimeType(String);
- method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setContentUri(android.net.Uri);
- method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setFileSize(long);
- method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setFileTransferSessionId(String);
- method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setFileTransferStatus(@android.telephony.ims.RcsFileTransferPart.RcsFileTransferStatus int);
- method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setHeight(int);
- method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setMediaDuration(long);
- method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setPreviewMimeType(String);
- method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setPreviewUri(android.net.Uri);
- method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setTransferOffset(long);
- method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setWidth(int);
- }
-
- public class RcsFileTransferPart {
- method @WorkerThread @Nullable public String getContentMimeType() throws android.telephony.ims.RcsMessageStoreException;
- method @Nullable @WorkerThread public android.net.Uri getContentUri() throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public long getFileSize() throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public String getFileTransferSessionId() throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread @android.telephony.ims.RcsFileTransferPart.RcsFileTransferStatus public int getFileTransferStatus() throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public int getHeight() throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public long getLength() throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public String getPreviewMimeType() throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public android.net.Uri getPreviewUri() throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public long getTransferOffset() throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public int getWidth() throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public void setContentMimeType(String) throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public void setContentUri(android.net.Uri) throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public void setFileSize(long) throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public void setFileTransferSessionId(String) throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public void setFileTransferStatus(@android.telephony.ims.RcsFileTransferPart.RcsFileTransferStatus int) throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public void setHeight(int) throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public void setLength(long) throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public void setPreviewMimeType(String) throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public void setPreviewUri(android.net.Uri) throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public void setTransferOffset(long) throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public void setWidth(int) throws android.telephony.ims.RcsMessageStoreException;
- field public static final int DOWNLOADING = 6; // 0x6
- field public static final int DOWNLOADING_CANCELLED = 9; // 0x9
- field public static final int DOWNLOADING_FAILED = 8; // 0x8
- field public static final int DOWNLOADING_PAUSED = 7; // 0x7
- field public static final int DRAFT = 1; // 0x1
- field public static final int NOT_SET = 0; // 0x0
- field public static final int SENDING = 2; // 0x2
- field public static final int SENDING_CANCELLED = 5; // 0x5
- field public static final int SENDING_FAILED = 4; // 0x4
- field public static final int SENDING_PAUSED = 3; // 0x3
- field public static final int SUCCEEDED = 10; // 0xa
- }
-
- @IntDef({android.telephony.ims.RcsFileTransferPart.DRAFT, android.telephony.ims.RcsFileTransferPart.SENDING, android.telephony.ims.RcsFileTransferPart.SENDING_PAUSED, android.telephony.ims.RcsFileTransferPart.SENDING_FAILED, android.telephony.ims.RcsFileTransferPart.SENDING_CANCELLED, android.telephony.ims.RcsFileTransferPart.DOWNLOADING, android.telephony.ims.RcsFileTransferPart.DOWNLOADING_PAUSED, android.telephony.ims.RcsFileTransferPart.DOWNLOADING_FAILED, android.telephony.ims.RcsFileTransferPart.DOWNLOADING_CANCELLED, android.telephony.ims.RcsFileTransferPart.SUCCEEDED}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface RcsFileTransferPart.RcsFileTransferStatus {
- }
-
- public class RcsGroupThread extends android.telephony.ims.RcsThread {
- method @WorkerThread public void addParticipant(@NonNull android.telephony.ims.RcsParticipant) throws android.telephony.ims.RcsMessageStoreException;
- method @Nullable @WorkerThread public android.net.Uri getConferenceUri() throws android.telephony.ims.RcsMessageStoreException;
- method @Nullable public android.net.Uri getGroupIcon() throws android.telephony.ims.RcsMessageStoreException;
- method @Nullable @WorkerThread public String getGroupName() throws android.telephony.ims.RcsMessageStoreException;
- method @Nullable @WorkerThread public android.telephony.ims.RcsParticipant getOwner() throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread @NonNull public java.util.Set<android.telephony.ims.RcsParticipant> getParticipants() throws android.telephony.ims.RcsMessageStoreException;
- method public boolean isGroup();
- method @WorkerThread public void removeParticipant(@NonNull android.telephony.ims.RcsParticipant) throws android.telephony.ims.RcsMessageStoreException;
- method @Nullable @WorkerThread public void setConferenceUri(android.net.Uri) throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public void setGroupIcon(@Nullable android.net.Uri) throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public void setGroupName(String) throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public void setOwner(@Nullable android.telephony.ims.RcsParticipant) throws android.telephony.ims.RcsMessageStoreException;
- }
-
- public abstract class RcsGroupThreadEvent extends android.telephony.ims.RcsEvent {
- method @NonNull public android.telephony.ims.RcsParticipant getOriginatingParticipant();
- method @NonNull public android.telephony.ims.RcsGroupThread getRcsGroupThread();
- }
-
- public final class RcsGroupThreadIconChangedEvent extends android.telephony.ims.RcsGroupThreadEvent {
- ctor public RcsGroupThreadIconChangedEvent(long, @NonNull android.telephony.ims.RcsGroupThread, @NonNull android.telephony.ims.RcsParticipant, @Nullable android.net.Uri);
- method @Nullable public android.net.Uri getNewIcon();
- }
-
- public final class RcsGroupThreadNameChangedEvent extends android.telephony.ims.RcsGroupThreadEvent {
- ctor public RcsGroupThreadNameChangedEvent(long, @NonNull android.telephony.ims.RcsGroupThread, @NonNull android.telephony.ims.RcsParticipant, @Nullable String);
- method @Nullable public String getNewName();
- }
-
- public final class RcsGroupThreadParticipantJoinedEvent extends android.telephony.ims.RcsGroupThreadEvent {
- ctor public RcsGroupThreadParticipantJoinedEvent(long, @NonNull android.telephony.ims.RcsGroupThread, @NonNull android.telephony.ims.RcsParticipant, @NonNull android.telephony.ims.RcsParticipant);
- method public android.telephony.ims.RcsParticipant getJoinedParticipant();
- }
-
- public final class RcsGroupThreadParticipantLeftEvent extends android.telephony.ims.RcsGroupThreadEvent {
- ctor public RcsGroupThreadParticipantLeftEvent(long, @NonNull android.telephony.ims.RcsGroupThread, @NonNull android.telephony.ims.RcsParticipant, @NonNull android.telephony.ims.RcsParticipant);
- method @NonNull public android.telephony.ims.RcsParticipant getLeavingParticipant();
- method public void persist() throws android.telephony.ims.RcsMessageStoreException;
- }
-
- public class RcsIncomingMessage extends android.telephony.ims.RcsMessage {
- method @WorkerThread public long getArrivalTimestamp() throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public long getSeenTimestamp() throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public android.telephony.ims.RcsParticipant getSenderParticipant() throws android.telephony.ims.RcsMessageStoreException;
- method public boolean isIncoming();
- method @WorkerThread public void setArrivalTimestamp(long) throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public void setSeenTimestamp(long) throws android.telephony.ims.RcsMessageStoreException;
- }
-
- public final class RcsIncomingMessageCreationParams extends android.telephony.ims.RcsMessageCreationParams implements android.os.Parcelable {
- method public int describeContents();
- method public long getArrivalTimestamp();
- method public long getSeenTimestamp();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsIncomingMessageCreationParams> CREATOR;
- }
-
- public static class RcsIncomingMessageCreationParams.Builder extends android.telephony.ims.RcsMessageCreationParams.Builder {
- ctor public RcsIncomingMessageCreationParams.Builder(long, long, int);
- method public android.telephony.ims.RcsIncomingMessageCreationParams build();
- method @CheckResult public android.telephony.ims.RcsIncomingMessageCreationParams.Builder setArrivalTimestamp(long);
- method @CheckResult public android.telephony.ims.RcsIncomingMessageCreationParams.Builder setSeenTimestamp(long);
- method @CheckResult public android.telephony.ims.RcsIncomingMessageCreationParams.Builder setSenderParticipant(android.telephony.ims.RcsParticipant);
- }
-
- public class RcsManager {
- method public android.telephony.ims.RcsMessageStore getRcsMessageStore();
- }
-
- public abstract class RcsMessage {
- method @NonNull @WorkerThread public java.util.Set<android.telephony.ims.RcsFileTransferPart> getFileTransferParts() throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public double getLatitude() throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public double getLongitude() throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public long getOriginationTimestamp() throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public String getRcsMessageId() throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread @android.telephony.ims.RcsMessage.RcsMessageStatus public int getStatus() throws android.telephony.ims.RcsMessageStoreException;
- method public int getSubscriptionId() throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public String getText() throws android.telephony.ims.RcsMessageStoreException;
- method @NonNull @WorkerThread public android.telephony.ims.RcsFileTransferPart insertFileTransfer(android.telephony.ims.RcsFileTransferCreationParams) throws android.telephony.ims.RcsMessageStoreException;
- method public abstract boolean isIncoming();
- method @WorkerThread public void removeFileTransferPart(@NonNull android.telephony.ims.RcsFileTransferPart) throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public void setLatitude(double) throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public void setLongitude(double) throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public void setOriginationTimestamp(long) throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public void setRcsMessageId(String) throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public void setStatus(@android.telephony.ims.RcsMessage.RcsMessageStatus int) throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public void setSubscriptionId(int) throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public void setText(String) throws android.telephony.ims.RcsMessageStoreException;
- field public static final int DRAFT = 1; // 0x1
- field public static final int FAILED = 6; // 0x6
- field public static final double LOCATION_NOT_SET = 4.9E-324;
- field public static final int NOT_SET = 0; // 0x0
- field public static final int QUEUED = 2; // 0x2
- field public static final int RECEIVED = 7; // 0x7
- field public static final int RETRYING = 5; // 0x5
- field public static final int SEEN = 9; // 0x9
- field public static final int SENDING = 3; // 0x3
- field public static final int SENT = 4; // 0x4
- }
-
- @IntDef({android.telephony.ims.RcsMessage.DRAFT, android.telephony.ims.RcsMessage.QUEUED, android.telephony.ims.RcsMessage.SENDING, android.telephony.ims.RcsMessage.SENT, android.telephony.ims.RcsMessage.RETRYING, android.telephony.ims.RcsMessage.FAILED, android.telephony.ims.RcsMessage.RECEIVED, android.telephony.ims.RcsMessage.SEEN}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface RcsMessage.RcsMessageStatus {
- }
-
- public class RcsMessageCreationParams {
- ctor protected RcsMessageCreationParams(android.telephony.ims.RcsMessageCreationParams.Builder);
- method public double getLatitude();
- method public double getLongitude();
- method public int getMessageStatus();
- method public long getOriginationTimestamp();
- method @Nullable public String getRcsMessageGlobalId();
- method public int getSubId();
- method @Nullable public String getText();
- }
-
- public static class RcsMessageCreationParams.Builder {
- method public android.telephony.ims.RcsMessageCreationParams build();
- method @CheckResult public android.telephony.ims.RcsMessageCreationParams.Builder setLatitude(double);
- method @CheckResult public android.telephony.ims.RcsMessageCreationParams.Builder setLongitude(double);
- method @CheckResult public android.telephony.ims.RcsMessageCreationParams.Builder setRcsMessageId(String);
- method @CheckResult public android.telephony.ims.RcsMessageCreationParams.Builder setStatus(@android.telephony.ims.RcsMessage.RcsMessageStatus int);
- method @CheckResult public android.telephony.ims.RcsMessageCreationParams.Builder setText(String);
- }
-
- public final class RcsMessageQueryParams implements android.os.Parcelable {
- method public int describeContents();
- method public int getFileTransferPresence();
- method public int getLimit();
- method public String getMessageLike();
- method public int getMessageType();
- method public boolean getSortDirection();
- method @android.telephony.ims.RcsMessageQueryParams.SortingProperty public int getSortingProperty();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsMessageQueryParams> CREATOR;
- field public static final int MESSAGES_WITHOUT_FILE_TRANSFERS = 8; // 0x8
- field public static final int MESSAGES_WITH_FILE_TRANSFERS = 4; // 0x4
- field public static final int MESSAGE_TYPE_INCOMING = 1; // 0x1
- field public static final int MESSAGE_TYPE_OUTGOING = 2; // 0x2
- field public static final int SORT_BY_CREATION_ORDER = 0; // 0x0
- field public static final int SORT_BY_TIMESTAMP = 1; // 0x1
- }
-
- public static class RcsMessageQueryParams.Builder {
- ctor public RcsMessageQueryParams.Builder();
- method public android.telephony.ims.RcsMessageQueryParams build();
- method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setFileTransferPresence(int);
- method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setMessageLike(String);
- method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setMessageType(int);
- method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setResultLimit(@IntRange(from=0) int) throws java.security.InvalidParameterException;
- method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setSortDirection(boolean);
- method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setSortProperty(@android.telephony.ims.RcsMessageQueryParams.SortingProperty int);
- method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setThread(@Nullable android.telephony.ims.RcsThread);
- }
-
- @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsMessageQueryParams.SORT_BY_CREATION_ORDER, android.telephony.ims.RcsMessageQueryParams.SORT_BY_TIMESTAMP}) public static @interface RcsMessageQueryParams.SortingProperty {
- }
-
- public final class RcsMessageQueryResult implements android.os.Parcelable {
- method public int describeContents();
- method @Nullable public android.telephony.ims.RcsQueryContinuationToken getContinuationToken();
- method @NonNull public java.util.List<android.telephony.ims.RcsMessage> getMessages();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsMessageQueryResult> CREATOR;
- }
-
- public final class RcsMessageSnippet implements android.os.Parcelable {
- method public int describeContents();
- method @android.telephony.ims.RcsMessage.RcsMessageStatus public int getSnippetStatus();
- method @Nullable public String getSnippetText();
- method public long getSnippetTimestamp();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsMessageSnippet> CREATOR;
- }
-
- public class RcsMessageStore {
- ctor public RcsMessageStore();
- method @WorkerThread @NonNull public android.telephony.ims.RcsGroupThread createGroupThread(@Nullable java.util.List<android.telephony.ims.RcsParticipant>, @Nullable String, @Nullable android.net.Uri) throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread @NonNull public android.telephony.ims.Rcs1To1Thread createRcs1To1Thread(@NonNull android.telephony.ims.RcsParticipant) throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread @NonNull public android.telephony.ims.RcsParticipant createRcsParticipant(String, @Nullable String) throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public void deleteThread(@NonNull android.telephony.ims.RcsThread) throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread @NonNull public android.telephony.ims.RcsEventQueryResult getRcsEvents(@Nullable android.telephony.ims.RcsEventQueryParams) throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread @NonNull public android.telephony.ims.RcsEventQueryResult getRcsEvents(@NonNull android.telephony.ims.RcsQueryContinuationToken) throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread @NonNull public android.telephony.ims.RcsMessageQueryResult getRcsMessages(@Nullable android.telephony.ims.RcsMessageQueryParams) throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread @NonNull public android.telephony.ims.RcsMessageQueryResult getRcsMessages(@NonNull android.telephony.ims.RcsQueryContinuationToken) throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread @NonNull public android.telephony.ims.RcsParticipantQueryResult getRcsParticipants(@Nullable android.telephony.ims.RcsParticipantQueryParams) throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread @NonNull public android.telephony.ims.RcsParticipantQueryResult getRcsParticipants(@NonNull android.telephony.ims.RcsQueryContinuationToken) throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread @NonNull public android.telephony.ims.RcsThreadQueryResult getRcsThreads(@Nullable android.telephony.ims.RcsThreadQueryParams) throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread @NonNull public android.telephony.ims.RcsThreadQueryResult getRcsThreads(@NonNull android.telephony.ims.RcsQueryContinuationToken) throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread @NonNull public void persistRcsEvent(android.telephony.ims.RcsEvent) throws android.telephony.ims.RcsMessageStoreException;
- }
-
- public class RcsMessageStoreException extends java.lang.Exception {
- ctor public RcsMessageStoreException(String);
- }
-
- public class RcsOutgoingMessage extends android.telephony.ims.RcsMessage {
- method @NonNull @WorkerThread public java.util.List<android.telephony.ims.RcsOutgoingMessageDelivery> getOutgoingDeliveries() throws android.telephony.ims.RcsMessageStoreException;
- method public boolean isIncoming();
- }
-
- public final class RcsOutgoingMessageCreationParams extends android.telephony.ims.RcsMessageCreationParams implements android.os.Parcelable {
- method public int describeContents();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsOutgoingMessageCreationParams> CREATOR;
- }
-
- public static class RcsOutgoingMessageCreationParams.Builder extends android.telephony.ims.RcsMessageCreationParams.Builder {
- ctor public RcsOutgoingMessageCreationParams.Builder(long, int);
- method public android.telephony.ims.RcsOutgoingMessageCreationParams build();
- }
-
- public class RcsOutgoingMessageDelivery {
- method @WorkerThread public long getDeliveredTimestamp() throws android.telephony.ims.RcsMessageStoreException;
- method @NonNull public android.telephony.ims.RcsOutgoingMessage getMessage();
- method @NonNull public android.telephony.ims.RcsParticipant getRecipient();
- method @WorkerThread public long getSeenTimestamp() throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread @android.telephony.ims.RcsMessage.RcsMessageStatus public int getStatus() throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public void setDeliveredTimestamp(long) throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public void setSeenTimestamp(long) throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public void setStatus(@android.telephony.ims.RcsMessage.RcsMessageStatus int) throws android.telephony.ims.RcsMessageStoreException;
- }
-
- public class RcsParticipant {
- method @Nullable @WorkerThread public String getAlias() throws android.telephony.ims.RcsMessageStoreException;
- method @Nullable @WorkerThread public String getCanonicalAddress() throws android.telephony.ims.RcsMessageStoreException;
- method @Nullable @WorkerThread public String getContactId() throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public void setAlias(String) throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public void setContactId(String) throws android.telephony.ims.RcsMessageStoreException;
- }
-
- public final class RcsParticipantAliasChangedEvent extends android.telephony.ims.RcsEvent {
- ctor public RcsParticipantAliasChangedEvent(long, @NonNull android.telephony.ims.RcsParticipant, @Nullable String);
- method @Nullable public String getNewAlias();
- method @NonNull public android.telephony.ims.RcsParticipant getParticipant();
- }
-
- public final class RcsParticipantQueryParams implements android.os.Parcelable {
- method public int describeContents();
- method public String getAliasLike();
- method public String getCanonicalAddressLike();
- method public int getLimit();
- method public boolean getSortDirection();
- method public int getSortingProperty();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsParticipantQueryParams> CREATOR;
- field public static final int SORT_BY_ALIAS = 1; // 0x1
- field public static final int SORT_BY_CANONICAL_ADDRESS = 2; // 0x2
- field public static final int SORT_BY_CREATION_ORDER = 0; // 0x0
- }
-
- public static class RcsParticipantQueryParams.Builder {
- ctor public RcsParticipantQueryParams.Builder();
- method public android.telephony.ims.RcsParticipantQueryParams build();
- method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setAliasLike(String);
- method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setCanonicalAddressLike(String);
- method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setResultLimit(@IntRange(from=0) int) throws java.security.InvalidParameterException;
- method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setSortDirection(boolean);
- method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setSortProperty(@android.telephony.ims.RcsParticipantQueryParams.SortingProperty int);
- method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setThread(android.telephony.ims.RcsThread);
- }
-
- @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsParticipantQueryParams.SORT_BY_CREATION_ORDER, android.telephony.ims.RcsParticipantQueryParams.SORT_BY_ALIAS, android.telephony.ims.RcsParticipantQueryParams.SORT_BY_CANONICAL_ADDRESS}) public static @interface RcsParticipantQueryParams.SortingProperty {
- }
-
- public final class RcsParticipantQueryResult implements android.os.Parcelable {
- method public int describeContents();
- method @Nullable public android.telephony.ims.RcsQueryContinuationToken getContinuationToken();
- method @NonNull public java.util.List<android.telephony.ims.RcsParticipant> getParticipants();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsParticipantQueryResult> CREATOR;
- }
-
- public final class RcsQueryContinuationToken implements android.os.Parcelable {
- method public int describeContents();
- method @android.telephony.ims.RcsQueryContinuationToken.ContinuationTokenType public int getQueryType();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsQueryContinuationToken> CREATOR;
- field public static final int EVENT_QUERY_CONTINUATION_TOKEN_TYPE = 0; // 0x0
- field public static final int MESSAGE_QUERY_CONTINUATION_TOKEN_TYPE = 1; // 0x1
- field public static final int PARTICIPANT_QUERY_CONTINUATION_TOKEN_TYPE = 2; // 0x2
- field public static final int THREAD_QUERY_CONTINUATION_TOKEN_TYPE = 3; // 0x3
- }
-
- @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsQueryContinuationToken.EVENT_QUERY_CONTINUATION_TOKEN_TYPE, android.telephony.ims.RcsQueryContinuationToken.MESSAGE_QUERY_CONTINUATION_TOKEN_TYPE, android.telephony.ims.RcsQueryContinuationToken.PARTICIPANT_QUERY_CONTINUATION_TOKEN_TYPE, android.telephony.ims.RcsQueryContinuationToken.THREAD_QUERY_CONTINUATION_TOKEN_TYPE}) public static @interface RcsQueryContinuationToken.ContinuationTokenType {
- }
-
- public abstract class RcsThread {
- method @WorkerThread @NonNull public android.telephony.ims.RcsIncomingMessage addIncomingMessage(@NonNull android.telephony.ims.RcsIncomingMessageCreationParams) throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread @NonNull public android.telephony.ims.RcsOutgoingMessage addOutgoingMessage(@NonNull android.telephony.ims.RcsOutgoingMessageCreationParams) throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread public void deleteMessage(@NonNull android.telephony.ims.RcsMessage) throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread @NonNull public android.telephony.ims.RcsMessageQueryResult getMessages() throws android.telephony.ims.RcsMessageStoreException;
- method @WorkerThread @NonNull public android.telephony.ims.RcsMessageSnippet getSnippet() throws android.telephony.ims.RcsMessageStoreException;
- method public abstract boolean isGroup();
- }
-
- public final class RcsThreadQueryParams implements android.os.Parcelable {
- method public int describeContents();
- method public int getLimit();
- method public boolean getSortDirection();
- method @android.telephony.ims.RcsThreadQueryParams.SortingProperty public int getSortingProperty();
- method public int getThreadType();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsThreadQueryParams> CREATOR;
- field public static final int SORT_BY_CREATION_ORDER = 0; // 0x0
- field public static final int SORT_BY_TIMESTAMP = 1; // 0x1
- field public static final int THREAD_TYPE_1_TO_1 = 2; // 0x2
- field public static final int THREAD_TYPE_GROUP = 1; // 0x1
- }
-
- public static class RcsThreadQueryParams.Builder {
- ctor public RcsThreadQueryParams.Builder();
- method public android.telephony.ims.RcsThreadQueryParams build();
- method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setParticipant(@NonNull android.telephony.ims.RcsParticipant);
- method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setParticipants(@NonNull java.util.List<android.telephony.ims.RcsParticipant>);
- method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setResultLimit(@IntRange(from=0) int) throws java.security.InvalidParameterException;
- method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setSortDirection(boolean);
- method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setSortProperty(@android.telephony.ims.RcsThreadQueryParams.SortingProperty int);
- method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setThreadType(int);
- }
-
- @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsThreadQueryParams.SORT_BY_CREATION_ORDER, android.telephony.ims.RcsThreadQueryParams.SORT_BY_TIMESTAMP}) public static @interface RcsThreadQueryParams.SortingProperty {
- }
-
- public final class RcsThreadQueryResult implements android.os.Parcelable {
- method public int describeContents();
- method @Nullable public android.telephony.ims.RcsQueryContinuationToken getContinuationToken();
- method @NonNull public java.util.List<android.telephony.ims.RcsThread> getThreads();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsThreadQueryResult> CREATOR;
- }
-
-}
-
package android.telephony.mbms {
public class DownloadProgressListener {
@@ -51661,8 +51207,8 @@
method public int getScaledHoverSlop();
method public int getScaledMaximumDrawingCacheSize();
method public int getScaledMaximumFlingVelocity();
- method public int getScaledMinScalingSpan();
method public int getScaledMinimumFlingVelocity();
+ method public int getScaledMinimumScalingSpan();
method public int getScaledOverflingDistance();
method public int getScaledOverscrollDistance();
method public int getScaledPagingTouchSlop();
@@ -52436,16 +51982,16 @@
method public boolean hasInsets();
method public boolean hasStableInsets();
method public boolean hasSystemWindowInsets();
- method @NonNull public android.view.WindowInsets inset(int, int, int, int);
+ method @NonNull public android.view.WindowInsets inset(@IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int);
method public boolean isConsumed();
method public boolean isRound();
method @Deprecated @NonNull public android.view.WindowInsets replaceSystemWindowInsets(int, int, int, int);
method @Deprecated @NonNull public android.view.WindowInsets replaceSystemWindowInsets(android.graphics.Rect);
}
- public static class WindowInsets.Builder {
+ public static final class WindowInsets.Builder {
ctor public WindowInsets.Builder();
- ctor public WindowInsets.Builder(android.view.WindowInsets);
+ ctor public WindowInsets.Builder(@NonNull android.view.WindowInsets);
method @NonNull public android.view.WindowInsets build();
method @NonNull public android.view.WindowInsets.Builder setDisplayCutout(@Nullable android.view.DisplayCutout);
method @NonNull public android.view.WindowInsets.Builder setStableInsets(@NonNull android.graphics.Insets);
diff --git a/api/system-current.txt b/api/system-current.txt
index b580deb..b5d9f3c 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1111,7 +1111,6 @@
method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean removeRoleHolderFromController(@NonNull String, @NonNull String);
method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public void setRoleNamesFromController(@NonNull java.util.List<java.lang.String>);
field public static final int MANAGE_HOLDERS_FLAG_DONT_KILL_APP = 1; // 0x1
- field public static final String ROLE_ASSISTANT = "android.app.role.ASSISTANT";
}
public interface RoleManagerCallback {
@@ -1173,7 +1172,7 @@
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getAppStandbyBucket(String);
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public java.util.Map<java.lang.String,java.lang.Integer> getAppStandbyBuckets();
method public int getUsageSource();
- method @RequiresPermission(allOf={android.Manifest.permission.SUSPEND_APPS, android.Manifest.permission.OBSERVE_APP_USAGE}) public void registerAppUsageLimitObserver(int, @NonNull String[], long, @NonNull java.util.concurrent.TimeUnit, android.app.PendingIntent);
+ method @RequiresPermission(allOf={android.Manifest.permission.SUSPEND_APPS, android.Manifest.permission.OBSERVE_APP_USAGE}) public void registerAppUsageLimitObserver(int, @NonNull String[], long, @NonNull java.util.concurrent.TimeUnit, @Nullable android.app.PendingIntent);
method @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE) public void registerAppUsageObserver(int, @NonNull String[], long, @NonNull java.util.concurrent.TimeUnit, @NonNull android.app.PendingIntent);
method @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE) public void registerUsageSessionObserver(int, @NonNull String[], long, @NonNull java.util.concurrent.TimeUnit, long, @NonNull java.util.concurrent.TimeUnit, @NonNull android.app.PendingIntent, @Nullable android.app.PendingIntent);
method public void reportUsageStart(@NonNull android.app.Activity, @NonNull String);
@@ -1715,8 +1714,13 @@
field public boolean handleAllWebDataURI;
}
+ public final class ShortcutInfo implements android.os.Parcelable {
+ method @Nullable public android.app.Person[] getPersons();
+ }
+
public class ShortcutManager {
method @NonNull public java.util.List<android.content.pm.ShortcutManager.ShareShortcutInfo> getShareTargets(@NonNull android.content.IntentFilter);
+ method public boolean hasShareTargets(@NonNull String);
}
public static final class ShortcutManager.ShareShortcutInfo implements android.os.Parcelable {
@@ -1894,7 +1898,7 @@
public final class BrightnessConfiguration implements android.os.Parcelable {
method public int describeContents();
method @Nullable public android.hardware.display.BrightnessCorrection getCorrectionByCategory(int);
- method @Nullable public android.hardware.display.BrightnessCorrection getCorrectionByPackageName(String);
+ method @Nullable public android.hardware.display.BrightnessCorrection getCorrectionByPackageName(@NonNull String);
method public android.util.Pair<float[],float[]> getCurve();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.hardware.display.BrightnessConfiguration> CREATOR;
@@ -1902,8 +1906,8 @@
public static class BrightnessConfiguration.Builder {
ctor public BrightnessConfiguration.Builder(float[], float[]);
- method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByCategory(int, android.hardware.display.BrightnessCorrection);
- method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByPackageName(String, android.hardware.display.BrightnessCorrection);
+ method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByCategory(int, @NonNull android.hardware.display.BrightnessCorrection);
+ method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByPackageName(@NonNull String, @NonNull android.hardware.display.BrightnessCorrection);
method public android.hardware.display.BrightnessConfiguration build();
method public int getMaxCorrectionsByCategory();
method public int getMaxCorrectionsByPackageName();
@@ -1911,7 +1915,7 @@
}
public final class BrightnessCorrection implements android.os.Parcelable {
- method public float apply(float);
+ method @FloatRange(from=0.0) public float apply(@FloatRange(from=0.0) float);
method @NonNull public static android.hardware.display.BrightnessCorrection createScaleAndTranslateLog(float, float);
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
@@ -3451,6 +3455,7 @@
method @Deprecated public int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes);
method public void clearAudioServerStateCallback();
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int dispatchAudioFocusChange(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
+ method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.audiopolicy.AudioProductStrategies getAudioProductStrategies();
method public boolean isAudioServerRunning();
method public boolean isHdmiSystemAudioSupported();
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int registerAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
@@ -3594,10 +3599,10 @@
method public int detachMixes(@NonNull java.util.List<android.media.audiopolicy.AudioMix>);
method public int getFocusDuckingBehavior();
method public int getStatus();
- method public int removeUidDeviceAffinity(int);
+ method public boolean removeUidDeviceAffinity(int);
method public int setFocusDuckingBehavior(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException;
method public void setRegistration(String);
- method public int setUidDeviceAffinity(int, @NonNull java.util.List<android.media.AudioDeviceInfo>);
+ method public boolean setUidDeviceAffinity(int, @NonNull java.util.List<android.media.AudioDeviceInfo>);
method public String toLogFriendlyString();
field public static final int FOCUS_POLICY_DUCKING_DEFAULT = 0; // 0x0
field public static final int FOCUS_POLICY_DUCKING_IN_APP = 0; // 0x0
@@ -3621,6 +3626,7 @@
}
public abstract static class AudioPolicy.AudioPolicyVolumeCallback {
+ ctor public AudioPolicy.AudioPolicyVolumeCallback();
method public void onVolumeAdjustment(int);
}
@@ -3642,6 +3648,7 @@
method @NonNull public android.media.AudioAttributes getAudioAttributesForProductStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
method @Nullable public android.media.audiopolicy.AudioProductStrategy getById(int);
method public int getLegacyStreamTypeForAudioAttributes(@NonNull android.media.AudioAttributes);
+ method @Nullable public android.media.audiopolicy.AudioProductStrategy getProductStrategyForAudioAttributes(@NonNull android.media.AudioAttributes);
method public java.util.Iterator<android.media.audiopolicy.AudioProductStrategy> iterator();
method public int size();
method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -4844,7 +4851,8 @@
ctor public WifiScanner.ScanSettings();
field public int band;
field public android.net.wifi.WifiScanner.ChannelSpec[] channels;
- field @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean ignoreLocationSettings;
+ field public boolean hideFromAppOps;
+ field public boolean ignoreLocationSettings;
field public int maxPeriodInMs;
field public int maxScansToCache;
field public int numBssidsPerScan;
@@ -5128,6 +5136,7 @@
method public void onError(int);
method public void onFinished();
method public void onProgress(float);
+ field public static final int BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS = 5; // 0x5
field public static final int BUGREPORT_ERROR_INVALID_INPUT = 1; // 0x1
field public static final int BUGREPORT_ERROR_RUNTIME = 2; // 0x2
field public static final int BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT = 4; // 0x4
@@ -7470,7 +7479,7 @@
field public static final int VSNCP_TIMEOUT = 2236; // 0x8bc
}
- public class DisconnectCause {
+ public final class DisconnectCause {
field public static final int ALREADY_DIALING = 72; // 0x48
field public static final int ANSWERED_ELSEWHERE = 52; // 0x34
field public static final int BUSY = 4; // 0x4
@@ -7648,18 +7657,18 @@
public class PhoneStateListener {
method public void onCallAttributesChanged(@NonNull android.telephony.CallAttributes);
- method public void onCallDisconnectCauseChanged(int, int);
- method public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo);
- method public void onPreciseCallStateChanged(android.telephony.PreciseCallState);
- method public void onPreciseDataConnectionStateChanged(android.telephony.PreciseDataConnectionState);
+ method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onCallDisconnectCauseChanged(int, int);
+ method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo);
+ method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState);
+ method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onPreciseDataConnectionStateChanged(android.telephony.PreciseDataConnectionState);
method public void onRadioPowerStateChanged(int);
method public void onSrvccStateChanged(int);
method public void onVoiceActivationStateChanged(int);
field public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864; // 0x4000000
- field public static final int LISTEN_CALL_DISCONNECT_CAUSES = 33554432; // 0x2000000
- field public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 134217728; // 0x8000000
- field public static final int LISTEN_PRECISE_CALL_STATE = 2048; // 0x800
- field public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000
+ field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_CALL_DISCONNECT_CAUSES = 33554432; // 0x2000000
+ field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 134217728; // 0x8000000
+ field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_PRECISE_CALL_STATE = 2048; // 0x800
+ field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000
field public static final int LISTEN_RADIO_POWER_STATE_CHANGED = 8388608; // 0x800000
field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_SRVCC_STATE_CHANGED = 16384; // 0x4000
field public static final int LISTEN_VOICE_ACTIVATION_STATE = 131072; // 0x20000
@@ -7694,7 +7703,7 @@
field public static final android.os.Parcelable.Creator<android.telephony.PreciseDataConnectionState> CREATOR;
}
- public class PreciseDisconnectCause {
+ public final class PreciseDisconnectCause {
field public static final int ACCESS_CLASS_BLOCKED = 260; // 0x104
field public static final int ACCESS_INFORMATION_DISCARDED = 43; // 0x2b
field public static final int ACM_LIMIT_EXCEEDED = 68; // 0x44
@@ -7912,7 +7921,7 @@
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public int getRadioPowerState();
method public int getSimApplicationState();
method public int getSimCardState();
- method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getSimLocale();
+ method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.Locale getSimLocale();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getSupportedRadioAccessFamily();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public java.util.List<android.telephony.TelephonyHistogram> getTelephonyHistograms();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.UiccSlotInfo[] getUiccSlotsInfo();
@@ -8021,7 +8030,7 @@
}
public class UiccSlotInfo implements android.os.Parcelable {
- ctor public UiccSlotInfo(boolean, boolean, String, int, int, boolean);
+ ctor @Deprecated public UiccSlotInfo(boolean, boolean, String, int, int, boolean);
method public int describeContents();
method public String getCardId();
method public int getCardStateInfo();
@@ -8029,6 +8038,7 @@
method public boolean getIsEuicc();
method public boolean getIsExtendedApduSupported();
method public int getLogicalSlotIdx();
+ method public boolean isRemovable();
method public void writeToParcel(android.os.Parcel, int);
field public static final int CARD_STATE_INFO_ABSENT = 1; // 0x1
field public static final int CARD_STATE_INFO_ERROR = 3; // 0x3
@@ -8824,12 +8834,12 @@
method public int describeContents();
method public int getAudioDirection();
method public int getAudioQuality();
- method public boolean getRttAudioSpeech();
method public int getRttMode();
method public int getVideoDirection();
method public int getVideoQuality();
+ method public boolean isReceivingRttAudio();
method public boolean isRttCall();
- method public void setRttAudioSpeech(boolean);
+ method public void setReceivingRttAudio(boolean);
method public void setRttMode(int);
method public void writeToParcel(android.os.Parcel, int);
field public static final int AUDIO_QUALITY_AMR = 1; // 0x1
@@ -9405,11 +9415,13 @@
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.view.contentcapture.ContentCaptureEvent> CREATOR;
field public static final int TYPE_CONTEXT_UPDATED = 6; // 0x6
- field public static final int TYPE_INITIAL_VIEW_TREE_APPEARED = 5; // 0x5
- field public static final int TYPE_INITIAL_VIEW_TREE_APPEARING = 4; // 0x4
+ field public static final int TYPE_SESSION_PAUSED = 8; // 0x8
+ field public static final int TYPE_SESSION_RESUMED = 7; // 0x7
field public static final int TYPE_VIEW_APPEARED = 1; // 0x1
field public static final int TYPE_VIEW_DISAPPEARED = 2; // 0x2
field public static final int TYPE_VIEW_TEXT_CHANGED = 3; // 0x3
+ field public static final int TYPE_VIEW_TREE_APPEARED = 5; // 0x5
+ field public static final int TYPE_VIEW_TREE_APPEARING = 4; // 0x4
}
public final class ContentCaptureManager {
diff --git a/api/test-current.txt b/api/test-current.txt
index ba497d4..1089761 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -476,7 +476,6 @@
method @NonNull @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public java.util.List<java.lang.String> getRoleHolders(@NonNull String);
method @NonNull @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public java.util.List<java.lang.String> getRoleHoldersAsUser(@NonNull String, @NonNull android.os.UserHandle);
method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void removeRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.app.role.RoleManagerCallback);
- field public static final String ROLE_ASSISTANT = "android.app.role.ASSISTANT";
}
public interface RoleManagerCallback {
@@ -685,7 +684,6 @@
method public static int getWALAutoCheckpoint();
method public static int getWALConnectionPoolSize();
method public static String getWALSyncMode();
- method public static boolean isCompatibilityWalSupported();
method public static int releaseMemory();
}
@@ -742,6 +740,13 @@
field public static final android.os.Parcelable.Creator<android.hardware.display.AmbientBrightnessDayStats> CREATOR;
}
+ public class AmbientDisplayConfiguration {
+ ctor public AmbientDisplayConfiguration(android.content.Context);
+ method public boolean alwaysOnAvailable();
+ method public boolean alwaysOnAvailableForUser(int);
+ method public boolean alwaysOnEnabled(int);
+ }
+
public final class BrightnessChangeEvent implements android.os.Parcelable {
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
@@ -765,7 +770,7 @@
public final class BrightnessConfiguration implements android.os.Parcelable {
method public int describeContents();
method @Nullable public android.hardware.display.BrightnessCorrection getCorrectionByCategory(int);
- method @Nullable public android.hardware.display.BrightnessCorrection getCorrectionByPackageName(String);
+ method @Nullable public android.hardware.display.BrightnessCorrection getCorrectionByPackageName(@NonNull String);
method public android.util.Pair<float[],float[]> getCurve();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.hardware.display.BrightnessConfiguration> CREATOR;
@@ -773,8 +778,8 @@
public static class BrightnessConfiguration.Builder {
ctor public BrightnessConfiguration.Builder(float[], float[]);
- method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByCategory(int, android.hardware.display.BrightnessCorrection);
- method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByPackageName(String, android.hardware.display.BrightnessCorrection);
+ method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByCategory(int, @NonNull android.hardware.display.BrightnessCorrection);
+ method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByPackageName(@NonNull String, @NonNull android.hardware.display.BrightnessCorrection);
method public android.hardware.display.BrightnessConfiguration build();
method public int getMaxCorrectionsByCategory();
method public int getMaxCorrectionsByPackageName();
@@ -782,7 +787,7 @@
}
public final class BrightnessCorrection implements android.os.Parcelable {
- method public float apply(float);
+ method @FloatRange(from=0.0) public float apply(@FloatRange(from=0.0) float);
method @NonNull public static android.hardware.display.BrightnessCorrection createScaleAndTranslateLog(float, float);
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
@@ -1952,6 +1957,7 @@
public static final class Settings.Global extends android.provider.Settings.NameValueTable {
field public static final String AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES = "autofill_compat_mode_allowed_packages";
field public static final String AUTOMATIC_POWER_SAVER_MODE = "automatic_power_saver_mode";
+ field public static final String BATTERY_SAVER_CONSTANTS = "battery_saver_constants";
field public static final String CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS = "captive_portal_fallback_probe_specs";
field public static final String CAPTIVE_PORTAL_FALLBACK_URL = "captive_portal_fallback_url";
field public static final String CAPTIVE_PORTAL_HTTPS_URL = "captive_portal_https_url";
@@ -1991,6 +1997,7 @@
field public static final String AUTOFILL_USER_DATA_MIN_VALUE_LENGTH = "autofill_user_data_min_value_length";
field public static final String CONTENT_CAPTURE_ENABLED = "content_capture_enabled";
field public static final String DISABLED_PRINT_SERVICES = "disabled_print_services";
+ field public static final String DOZE_ALWAYS_ON = "doze_always_on";
field @Deprecated public static final String ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES = "enabled_notification_policy_access_packages";
field public static final String ENABLED_VR_LISTENERS = "enabled_vr_listeners";
field public static final String LOCATION_ACCESS_CHECK_DELAY_MILLIS = "location_access_check_delay_millis";
@@ -2863,11 +2870,13 @@
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.view.contentcapture.ContentCaptureEvent> CREATOR;
field public static final int TYPE_CONTEXT_UPDATED = 6; // 0x6
- field public static final int TYPE_INITIAL_VIEW_TREE_APPEARED = 5; // 0x5
- field public static final int TYPE_INITIAL_VIEW_TREE_APPEARING = 4; // 0x4
+ field public static final int TYPE_SESSION_PAUSED = 8; // 0x8
+ field public static final int TYPE_SESSION_RESUMED = 7; // 0x7
field public static final int TYPE_VIEW_APPEARED = 1; // 0x1
field public static final int TYPE_VIEW_DISAPPEARED = 2; // 0x2
field public static final int TYPE_VIEW_TEXT_CHANGED = 3; // 0x3
+ field public static final int TYPE_VIEW_TREE_APPEARED = 5; // 0x5
+ field public static final int TYPE_VIEW_TREE_APPEARING = 4; // 0x4
}
public final class ContentCaptureManager {
diff --git a/cmds/bootanimation/bootanim.rc b/cmds/bootanimation/bootanim.rc
index 469c964..3666d6a 100644
--- a/cmds/bootanimation/bootanim.rc
+++ b/cmds/bootanimation/bootanim.rc
@@ -2,6 +2,9 @@
class core animation
user graphics
group graphics audio
+ # bootanimation depends on libandroidicu in the Runtime APEX.
+ # TODO(b/124939955): Remove this dependency on libandroidicu
+ updatable
disabled
oneshot
writepid /dev/stune/top-app/tasks
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index f408655..ca48881 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -346,3 +346,9 @@
javacflags: ["-XepDisableAllChecks"],
},
}
+
+// Filegroup for statsd config proto definition.
+filegroup {
+ name: "statsd-config-proto-def",
+ srcs: ["src/statsd_config.proto"],
+}
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 78e994f..1dd68df 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -45,6 +45,7 @@
import "frameworks/base/core/proto/android/stats/devicepolicy/device_policy.proto";
import "frameworks/base/core/proto/android/stats/devicepolicy/device_policy_enums.proto";
import "frameworks/base/core/proto/android/stats/launcher/launcher.proto";
+import "frameworks/base/core/proto/android/stats/style/style_enums.proto";
import "frameworks/base/core/proto/android/telecomm/enums.proto";
import "frameworks/base/core/proto/android/telephony/enums.proto";
import "frameworks/base/core/proto/android/view/enums.proto";
@@ -247,6 +248,7 @@
AssistGestureProgressReported assist_gesture_progress_reported = 176;
TouchGestureClassified touch_gesture_classified = 177;
HiddenApiUsed hidden_api_used = 178 [(allow_from_any_uid) = true];
+ StyleUIChanged style_ui_changed = 179;
}
// Pulled events will start at field 10000.
@@ -283,7 +285,7 @@
CategorySize category_size = 10028;
ProcStats proc_stats = 10029;
BatteryVoltage battery_voltage = 10030;
- NumBiometricsEnrolled num_fingerprints_enrolled = 10031;
+ NumFingerprintsEnrolled num_fingerprints_enrolled = 10031;
DiskIo disk_io = 10032;
PowerProfile power_profile = 10033;
ProcStatsPkgProc proc_stats_pkg_proc = 10034;
@@ -300,7 +302,7 @@
BatteryCycleCount battery_cycle_count = 10045;
DebugElapsedClock debug_elapsed_clock = 10046;
DebugFailingElapsedClock debug_failing_elapsed_clock = 10047;
- NumBiometricsEnrolled num_faces_enrolled = 10048;
+ NumFacesEnrolled num_faces_enrolled = 10048;
RoleHolder role_holder = 10049;
DangerousPermissionState dangerous_permission_state = 10050;
TrainInfo train_info = 10051;
@@ -2349,6 +2351,17 @@
optional bool is_swipe_up_enabled = 5;
}
+message StyleUIChanged {
+ optional android.stats.style.Action action = 1;
+ optional int32 color_package_hash = 2;
+ optional int32 font_package_hash = 3;
+ optional int32 shape_package_hash = 4;
+ optional int32 clock_package_hash = 5;
+ optional int32 launcher_grid = 6;
+ optional int32 wallpaper_category_hash = 7;
+ optional int32 wallpaper_id_hash = 8;
+}
+
/**
* Logs when Settings UI has changed.
*
@@ -4114,15 +4127,27 @@
*
* Pulled from StatsCompanionService, which queries <Biometric>Manager.
*/
-message NumBiometricsEnrolled {
+message NumFingerprintsEnrolled {
// The associated user. Eg: 0 for owners, 10+ for others.
// Defined in android/os/UserHandle.java
optional int32 user = 1;
// Number of fingerprints registered to that user.
- optional int32 num_enrolled = 2;
+ optional int32 num_fingerprints_enrolled = 2;
}
/**
+ * Pulls the number of faces for each user.
+ *
+ * Pulled from StatsCompanionService, which queries <Biometric>Manager.
+ */
+message NumFacesEnrolled {
+ // The associated user. Eg: 0 for owners, 10+ for others.
+ // Defined in android/os/UserHandle.java
+ optional int32 user = 1;
+ // Number of faces registered to that user.
+ optional int32 num_faces_enrolled = 2;
+}
+/**
* A mapping of role holder -> role
*/
message RoleHolder {
diff --git a/cmds/statsd/src/external/PowerStatsPuller.cpp b/cmds/statsd/src/external/PowerStatsPuller.cpp
index 71e5fa0..c56f9a2 100644
--- a/cmds/statsd/src/external/PowerStatsPuller.cpp
+++ b/cmds/statsd/src/external/PowerStatsPuller.cpp
@@ -39,17 +39,40 @@
namespace os {
namespace statsd {
-sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHal = nullptr;
-std::mutex gPowerStatsHalMutex;
-bool gPowerStatsExist = true; // Initialized to ensure making a first attempt.
-std::vector<RailInfo> gRailInfo;
+static sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHal = nullptr;
+static std::mutex gPowerStatsHalMutex;
+static bool gPowerStatsExist = true; // Initialized to ensure making a first attempt.
+static std::vector<RailInfo> gRailInfo;
-bool getPowerStatsHal() {
+struct PowerStatsPullerDeathRecipient : virtual public hardware::hidl_death_recipient {
+ virtual void serviceDied(uint64_t cookie,
+ const wp<android::hidl::base::V1_0::IBase>& who) override {
+ // The HAL just died. Reset all handles to HAL services.
+ std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
+ gPowerStatsHal = nullptr;
+ }
+};
+
+static sp<PowerStatsPullerDeathRecipient> gDeathRecipient = new PowerStatsPullerDeathRecipient();
+
+static bool getPowerStatsHalLocked() {
if (gPowerStatsHal == nullptr && gPowerStatsExist) {
gPowerStatsHal = android::hardware::power::stats::V1_0::IPowerStats::getService();
if (gPowerStatsHal == nullptr) {
ALOGW("Couldn't load power.stats HAL service");
gPowerStatsExist = false;
+ } else {
+ // Link death recipient to power.stats service handle
+ hardware::Return<bool> linked = gPowerStatsHal->linkToDeath(gDeathRecipient, 0);
+ if (!linked.isOk()) {
+ ALOGE("Transaction error in linking to power.stats HAL death: %s",
+ linked.description().c_str());
+ gPowerStatsHal = nullptr;
+ return false;
+ } else if (!linked) {
+ ALOGW("Unable to link to power.stats HAL death notifications");
+ // We should still continue even though linking failed
+ }
}
}
return gPowerStatsHal != nullptr;
@@ -61,7 +84,7 @@
bool PowerStatsPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
- if (!getPowerStatsHal()) {
+ if (!getPowerStatsHalLocked()) {
ALOGE("power.stats Hal not loaded");
return false;
}
diff --git a/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp b/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp
index d822959..f6a4aea 100644
--- a/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp
+++ b/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp
@@ -60,41 +60,43 @@
namespace os {
namespace statsd {
-std::function<bool(vector<shared_ptr<LogEvent>>* data)> gPuller = {};
+static std::function<bool(vector<shared_ptr<LogEvent>>* data)> gPuller = {};
-sp<android::hardware::power::V1_0::IPower> gPowerHalV1_0 = nullptr;
-sp<android::hardware::power::V1_1::IPower> gPowerHalV1_1 = nullptr;
-sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHalV1_0 = nullptr;
+static sp<android::hardware::power::V1_0::IPower> gPowerHalV1_0 = nullptr;
+static sp<android::hardware::power::V1_1::IPower> gPowerHalV1_1 = nullptr;
+static sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHalV1_0 = nullptr;
-std::unordered_map<uint32_t, std::string> gEntityNames = {};
-std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::string>> gStateNames = {};
+static std::unordered_map<uint32_t, std::string> gEntityNames = {};
+static std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::string>> gStateNames = {};
-std::mutex gPowerHalMutex;
+static std::mutex gPowerHalMutex;
// The caller must be holding gPowerHalMutex.
-void deinitPowerStatsLocked() {
+static void deinitPowerStatsLocked() {
gPowerHalV1_0 = nullptr;
gPowerHalV1_1 = nullptr;
gPowerStatsHalV1_0 = nullptr;
}
-struct PowerHalDeathRecipient : virtual public hardware::hidl_death_recipient {
+struct SubsystemSleepStatePullerDeathRecipient : virtual public hardware::hidl_death_recipient {
virtual void serviceDied(uint64_t cookie,
- const wp<android::hidl::base::V1_0::IBase>& who) override {
+ const wp<android::hidl::base::V1_0::IBase>& who) override {
+
// The HAL just died. Reset all handles to HAL services.
std::lock_guard<std::mutex> lock(gPowerHalMutex);
deinitPowerStatsLocked();
}
};
-sp<PowerHalDeathRecipient> gDeathRecipient = new PowerHalDeathRecipient();
+static sp<SubsystemSleepStatePullerDeathRecipient> gDeathRecipient =
+ new SubsystemSleepStatePullerDeathRecipient();
SubsystemSleepStatePuller::SubsystemSleepStatePuller() :
StatsPuller(android::util::SUBSYSTEM_SLEEP_STATE) {
}
// The caller must be holding gPowerHalMutex.
-bool checkResultLocked(const Return<void> &ret, const char* function) {
+static bool checkResultLocked(const Return<void> &ret, const char* function) {
if (!ret.isOk()) {
ALOGE("%s failed: requested HAL service not available. Description: %s",
function, ret.description().c_str());
@@ -108,7 +110,7 @@
// The caller must be holding gPowerHalMutex.
// gPowerStatsHalV1_0 must not be null
-bool initializePowerStats() {
+static bool initializePowerStats() {
using android::hardware::power::stats::V1_0::Status;
// Clear out previous content if we are re-initializing
@@ -155,7 +157,7 @@
}
// The caller must be holding gPowerHalMutex.
-bool getPowerStatsHalLocked() {
+static bool getPowerStatsHalLocked() {
if(gPowerStatsHalV1_0 == nullptr) {
gPowerStatsHalV1_0 = android::hardware::power::stats::V1_0::IPowerStats::getService();
if (gPowerStatsHalV1_0 == nullptr) {
@@ -180,7 +182,7 @@
}
// The caller must be holding gPowerHalMutex.
-bool getIPowerStatsDataLocked(vector<shared_ptr<LogEvent>>* data) {
+static bool getIPowerStatsDataLocked(vector<shared_ptr<LogEvent>>* data) {
using android::hardware::power::stats::V1_0::Status;
if(!getPowerStatsHalLocked()) {
@@ -225,7 +227,7 @@
}
// The caller must be holding gPowerHalMutex.
-bool getPowerHalLocked() {
+static bool getPowerHalLocked() {
if(gPowerHalV1_0 == nullptr) {
gPowerHalV1_0 = android::hardware::power::V1_0::IPower::getService();
if(gPowerHalV1_0 == nullptr) {
@@ -250,7 +252,7 @@
}
// The caller must be holding gPowerHalMutex.
-bool getIPowerDataLocked(vector<shared_ptr<LogEvent>>* data) {
+static bool getIPowerDataLocked(vector<shared_ptr<LogEvent>>* data) {
using android::hardware::power::V1_0::Status;
if(!getPowerHalLocked()) {
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 8d1cf33..9de62a2 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -155,7 +155,7 @@
mCurrentBucketStartTimeNs = startTimeNs;
// Kicks off the puller immediately if condition is true and diff based.
if (mIsPulled && mCondition == ConditionState::kTrue && mUseDiff) {
- pullAndMatchEventsLocked(startTimeNs);
+ pullAndMatchEventsLocked(startTimeNs, mCondition);
}
VLOG("value metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
(long long)mBucketSizeNs, (long long)mTimeBaseNs);
@@ -174,13 +174,17 @@
}
void ValueMetricProducer::dropDataLocked(const int64_t dropTimeNs) {
- flushIfNeededLocked(dropTimeNs);
StatsdStats::getInstance().noteBucketDropped(mMetricId);
- mPastBuckets.clear();
+ // We are going to flush the data without doing a pull first so we need to invalidte the data.
+ bool pullNeeded = mIsPulled && mCondition == ConditionState::kTrue;
+ if (pullNeeded) {
+ invalidateCurrentBucket();
+ }
+ flushIfNeededLocked(dropTimeNs);
+ clearPastBucketsLocked(dropTimeNs);
}
void ValueMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) {
- flushIfNeededLocked(dumpTimeNs);
mPastBuckets.clear();
mSkippedBuckets.clear();
}
@@ -192,7 +196,6 @@
std::set<string> *str_set,
ProtoOutputStream* protoOutput) {
VLOG("metric %lld dump report now...", (long long)mMetricId);
- flushIfNeededLocked(dumpTimeNs);
if (include_current_partial_bucket) {
// For pull metrics, we need to do a pull at bucket boundaries. If we do not do that the
// current bucket will have incomplete data and the next will have the wrong snapshot to do
@@ -205,10 +208,10 @@
invalidateCurrentBucket();
break;
case NO_TIME_CONSTRAINTS:
- pullAndMatchEventsLocked(dumpTimeNs);
+ pullAndMatchEventsLocked(dumpTimeNs, mCondition);
break;
}
- }
+ }
flushCurrentBucketLocked(dumpTimeNs, dumpTimeNs);
}
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
@@ -325,12 +328,16 @@
}
}
-void ValueMetricProducer::invalidateCurrentBucket() {
+void ValueMetricProducer::invalidateCurrentBucketWithoutResetBase() {
if (!mCurrentBucketIsInvalid) {
// Only report once per invalid bucket.
StatsdStats::getInstance().noteInvalidatedBucket(mMetricId);
}
mCurrentBucketIsInvalid = true;
+}
+
+void ValueMetricProducer::invalidateCurrentBucket() {
+ invalidateCurrentBucketWithoutResetBase();
resetBase();
}
@@ -345,86 +352,112 @@
void ValueMetricProducer::onConditionChangedLocked(const bool condition,
const int64_t eventTimeNs) {
- if (eventTimeNs < mCurrentBucketStartTimeNs) {
+ bool isEventTooLate = eventTimeNs < mCurrentBucketStartTimeNs;
+ if (!isEventTooLate) {
+ if (mCondition == ConditionState::kUnknown) {
+ // If the condition was unknown, we mark the bucket as invalid since the bucket will
+ // contain partial data. For instance, the condition change might happen close to the
+ // end of the bucket and we might miss lots of data.
+ //
+ // We still want to pull to set the base.
+ invalidateCurrentBucket();
+ }
+
+ // Pull on condition changes.
+ ConditionState newCondition = condition ? ConditionState::kTrue : ConditionState::kFalse;
+ bool conditionChanged =
+ (mCondition == ConditionState::kTrue && newCondition == ConditionState::kFalse)
+ || (mCondition == ConditionState::kFalse && newCondition == ConditionState::kTrue);
+ // We do not need to pull when we go from unknown to false.
+ //
+ // We also pull if the condition was already true in order to be able to flush the bucket at
+ // the end if needed.
+ //
+ // onConditionChangedLocked might happen on bucket boundaries if this is called before
+ // #onDataPulled.
+ if (mIsPulled && (conditionChanged || condition)) {
+ pullAndMatchEventsLocked(eventTimeNs, newCondition);
+ }
+
+ // When condition change from true to false, clear diff base but don't
+ // reset other counters as we may accumulate more value in the bucket.
+ if (mUseDiff && mCondition == ConditionState::kTrue
+ && newCondition == ConditionState::kFalse) {
+ resetBase();
+ }
+ mCondition = newCondition;
+
+ } else {
VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
(long long)mCurrentBucketStartTimeNs);
StatsdStats::getInstance().noteConditionChangeInNextBucket(mMetricId);
invalidateCurrentBucket();
- return;
+ // Something weird happened. If we received another event if the future, the condition might
+ // be wrong.
+ mCondition = ConditionState::kUnknown;
}
+ // This part should alway be called.
flushIfNeededLocked(eventTimeNs);
-
- if (mCondition != ConditionState::kUnknown) {
- // Pull on condition changes.
- bool conditionChanged = mCondition != condition;
- // We do not need to pull when we go from unknown to false.
- if (mIsPulled && conditionChanged) {
- pullAndMatchEventsLocked(eventTimeNs);
- }
-
- // when condition change from true to false, clear diff base but don't
- // reset other counters as we may accumulate more value in the bucket.
- if (mUseDiff && mCondition == ConditionState::kTrue && condition == ConditionState::kFalse) {
- resetBase();
- }
- } else {
- // If the condition was unknown, we mark the bucket as invalid since the bucket will contain
- // partial data. For instance, the condition change might happen close to the end of the
- // bucket and we might miss lots of data.
- invalidateCurrentBucket();
- }
- mCondition = condition ? ConditionState::kTrue : ConditionState::kFalse;
}
-void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) {
+void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs, ConditionState condition) {
vector<std::shared_ptr<LogEvent>> allData;
if (!mPullerManager->Pull(mPullTagId, &allData)) {
- ALOGE("Gauge Stats puller failed for tag: %d at %lld", mPullTagId, (long long)timestampNs);
+ ALOGE("Stats puller failed for tag: %d at %lld", mPullTagId, (long long)timestampNs);
invalidateCurrentBucket();
return;
}
- accumulateEvents(allData, timestampNs, timestampNs);
+ accumulateEvents(allData, timestampNs, timestampNs, condition);
}
int64_t ValueMetricProducer::calcPreviousBucketEndTime(const int64_t currentTimeNs) {
return mTimeBaseNs + ((currentTimeNs - mTimeBaseNs) / mBucketSizeNs) * mBucketSizeNs;
}
+// By design, statsd pulls data at bucket boundaries using AlarmManager. These pulls are likely
+// to be delayed. Other events like condition changes or app upgrade which are not based on
+// AlarmManager might have arrived earlier and close the bucket.
void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData,
bool pullSuccess, int64_t originalPullTimeNs) {
std::lock_guard<std::mutex> lock(mMutex);
- if (mCondition == ConditionState::kTrue) {
- if (!pullSuccess) {
+ if (mCondition == ConditionState::kTrue) {
// If the pull failed, we won't be able to compute a diff.
- invalidateCurrentBucket();
- return;
+ if (!pullSuccess) {
+ invalidateCurrentBucket();
+ } else {
+ bool isEventLate = originalPullTimeNs < getCurrentBucketEndTimeNs();
+ if (isEventLate) {
+ // If the event is late, we are in the middle of a bucket. Just
+ // process the data without trying to snap the data to the nearest bucket.
+ accumulateEvents(allData, originalPullTimeNs, originalPullTimeNs, mCondition);
+ } else {
+ // For scheduled pulled data, the effective event time is snap to the nearest
+ // bucket end. In the case of waking up from a deep sleep state, we will
+ // attribute to the previous bucket end. If the sleep was long but not very
+ // long, we will be in the immediate next bucket. Previous bucket may get a
+ // larger number as we pull at a later time than real bucket end.
+ //
+ // If the sleep was very long, we skip more than one bucket before sleep. In
+ // this case, if the diff base will be cleared and this new data will serve as
+ // new diff base.
+ int64_t bucketEndTime = calcPreviousBucketEndTime(originalPullTimeNs) - 1;
+ StatsdStats::getInstance().noteBucketBoundaryDelayNs(
+ mMetricId, originalPullTimeNs - bucketEndTime);
+ accumulateEvents(allData, originalPullTimeNs, bucketEndTime, mCondition);
+ }
+ }
}
- // For scheduled pulled data, the effective event time is snap to the nearest
- // bucket end. In the case of waking up from a deep sleep state, we will
- // attribute to the previous bucket end. If the sleep was long but not very long, we
- // will be in the immediate next bucket. Previous bucket may get a larger number as
- // we pull at a later time than real bucket end.
- // If the sleep was very long, we skip more than one bucket before sleep. In this case,
- // if the diff base will be cleared and this new data will serve as new diff base.
- int64_t bucketEndTime = calcPreviousBucketEndTime(originalPullTimeNs) - 1;
- StatsdStats::getInstance().noteBucketBoundaryDelayNs(
- mMetricId, originalPullTimeNs - bucketEndTime);
- accumulateEvents(allData, originalPullTimeNs, bucketEndTime);
-
- // We can probably flush the bucket. Since we used bucketEndTime when calling
- // #onMatchedLogEventInternalLocked, the current bucket will not have been flushed.
- flushIfNeededLocked(originalPullTimeNs);
-
- } else {
- VLOG("No need to commit data on condition false.");
- }
+ // We can probably flush the bucket. Since we used bucketEndTime when calling
+ // #onMatchedLogEventInternalLocked, the current bucket will not have been flushed.
+ flushIfNeededLocked(originalPullTimeNs);
}
-void ValueMetricProducer::accumulateEvents(const std::vector<std::shared_ptr<LogEvent>>& allData,
- int64_t originalPullTimeNs, int64_t eventElapsedTimeNs) {
+void ValueMetricProducer::accumulateEvents(const std::vector<std::shared_ptr<LogEvent>>& allData,
+ int64_t originalPullTimeNs, int64_t eventElapsedTimeNs,
+ ConditionState condition) {
bool isEventLate = eventElapsedTimeNs < mCurrentBucketStartTimeNs;
if (isEventLate) {
VLOG("Skip bucket end pull due to late arrival: %lld vs %lld",
@@ -463,7 +496,7 @@
// If the new pulled data does not contains some keys we track in our intervals, we need to
// reset the base.
for (auto& slice : mCurrentSlicedBucket) {
- bool presentInPulledData = mMatchedMetricDimensionKeys.find(slice.first)
+ bool presentInPulledData = mMatchedMetricDimensionKeys.find(slice.first)
!= mMatchedMetricDimensionKeys.end();
if (!presentInPulledData) {
for (auto& interval : slice.second) {
@@ -587,7 +620,10 @@
}
mMatchedMetricDimensionKeys.insert(eventKey);
- flushIfNeededLocked(eventTimeNs);
+ if (!mIsPulled) {
+ // We cannot flush without doing a pull first.
+ flushIfNeededLocked(eventTimeNs);
+ }
// For pulled data, we already check condition when we decide to pull or
// in onDataPulled. So take all of them.
@@ -722,26 +758,26 @@
}
}
+// For pulled metrics, we always need to make sure we do a pull before flushing the bucket
+// if mCondition is true!
void ValueMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) {
int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs();
-
if (eventTimeNs < currentBucketEndTimeNs) {
VLOG("eventTime is %lld, less than next bucket start time %lld", (long long)eventTimeNs,
(long long)(currentBucketEndTimeNs));
return;
}
-
- int64_t numBucketsForward = 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs;
+ int64_t numBucketsForward = calcBucketsForwardCount(eventTimeNs);
int64_t nextBucketStartTimeNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs;
flushCurrentBucketLocked(eventTimeNs, nextBucketStartTimeNs);
+}
- mCurrentBucketNum += numBucketsForward;
- if (numBucketsForward > 1) {
- VLOG("Skipping forward %lld buckets", (long long)numBucketsForward);
- StatsdStats::getInstance().noteSkippedForwardBuckets(mMetricId);
- // take base again in future good bucket.
- resetBase();
+int64_t ValueMetricProducer::calcBucketsForwardCount(const int64_t& eventTimeNs) const {
+ int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs();
+ if (eventTimeNs < currentBucketEndTimeNs) {
+ return 0;
}
+ return 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs;
}
void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs,
@@ -750,6 +786,16 @@
StatsdStats::getInstance().noteBucketUnknownCondition(mMetricId);
}
+ int64_t numBucketsForward = calcBucketsForwardCount(eventTimeNs);
+ mCurrentBucketNum += numBucketsForward;
+ if (numBucketsForward > 1) {
+ VLOG("Skipping forward %lld buckets", (long long)numBucketsForward);
+ StatsdStats::getInstance().noteSkippedForwardBuckets(mMetricId);
+ // Something went wrong. Maybe the device was sleeping for a long time. It is better
+ // to mark the current bucket as invalid. The last pull might have been successful through.
+ invalidateCurrentBucketWithoutResetBase();
+ }
+
VLOG("finalizing bucket for %ld, dumping %d slices", (long)mCurrentBucketStartTimeNs,
(int)mCurrentSlicedBucket.size());
int64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs();
@@ -773,12 +819,7 @@
if (!mCurrentBucketIsInvalid) {
appendToFullBucket(eventTimeNs, fullBucketEndTimeNs);
}
- StatsdStats::getInstance().noteBucketCount(mMetricId);
- initCurrentSlicedBucket();
- mCurrentBucketIsInvalid = false;
- mCurrentBucketStartTimeNs = nextBucketStartTimeNs;
- VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId,
- (long long)mCurrentBucketStartTimeNs);
+ initCurrentSlicedBucket(nextBucketStartTimeNs);
}
ValueBucket ValueMetricProducer::buildPartialBucket(int64_t bucketEndTime,
@@ -805,7 +846,9 @@
return bucket;
}
-void ValueMetricProducer::initCurrentSlicedBucket() {
+void ValueMetricProducer::initCurrentSlicedBucket(int64_t nextBucketStartTimeNs) {
+ StatsdStats::getInstance().noteBucketCount(mMetricId);
+ // Cleanup data structure to aggregate values.
for (auto it = mCurrentSlicedBucket.begin(); it != mCurrentSlicedBucket.end();) {
bool obsolete = true;
for (auto& interval : it->second) {
@@ -823,6 +866,16 @@
it++;
}
}
+
+ mCurrentBucketIsInvalid = false;
+ // If we do not have a global base when the condition is true,
+ // we will have incomplete bucket for the next bucket.
+ if (mUseDiff && !mHasGlobalBase && mCondition) {
+ mCurrentBucketIsInvalid = false;
+ }
+ mCurrentBucketStartTimeNs = nextBucketStartTimeNs;
+ VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId,
+ (long long)mCurrentBucketStartTimeNs);
}
void ValueMetricProducer::appendToFullBucket(int64_t eventTimeNs, int64_t fullBucketEndTimeNs) {
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 24e14b1..f317c37 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -39,6 +39,13 @@
std::vector<Value> values;
};
+
+// Aggregates values within buckets.
+//
+// There are different events that might complete a bucket
+// - a condition change
+// - an app upgrade
+// - an alarm set to the end of the bucket
class ValueMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
public:
ValueMetricProducer(const ConfigKey& key, const ValueMetric& valueMetric,
@@ -61,9 +68,8 @@
if (!mSplitBucketForAppUpgrade) {
return;
}
- flushIfNeededLocked(eventTimeNs - 1);
if (mIsPulled && mCondition) {
- pullAndMatchEventsLocked(eventTimeNs - 1);
+ pullAndMatchEventsLocked(eventTimeNs, mCondition);
}
flushCurrentBucketLocked(eventTimeNs, eventTimeNs);
};
@@ -94,9 +100,12 @@
void dumpStatesLocked(FILE* out, bool verbose) const override;
- // Util function to flush the old packet.
+ // For pulled metrics, this method should only be called if a pull has be done. Else we will
+ // not have complete data for the bucket.
void flushIfNeededLocked(const int64_t& eventTime) override;
+ // For pulled metrics, this method should only be called if a pulled have be done. Else we will
+ // not have complete data for the bucket.
void flushCurrentBucketLocked(const int64_t& eventTimeNs,
const int64_t& nextBucketStartTimeNs) override;
@@ -105,8 +114,12 @@
// Calculate previous bucket end time based on current time.
int64_t calcPreviousBucketEndTime(const int64_t currentTimeNs);
+ // Calculate how many buckets are present between the current bucket and eventTimeNs.
+ int64_t calcBucketsForwardCount(const int64_t& eventTimeNs) const;
+
// Mark the data as invalid.
void invalidateCurrentBucket();
+ void invalidateCurrentBucketWithoutResetBase();
const int mWhatMatcherIndex;
@@ -163,14 +176,15 @@
bool hitFullBucketGuardRailLocked(const MetricDimensionKey& newKey);
- void pullAndMatchEventsLocked(const int64_t timestampNs);
+ void pullAndMatchEventsLocked(const int64_t timestampNs, ConditionState condition);
void accumulateEvents(const std::vector<std::shared_ptr<LogEvent>>& allData,
- int64_t originalPullTimeNs, int64_t eventElapsedTimeNs);
+ int64_t originalPullTimeNs, int64_t eventElapsedTimeNs,
+ ConditionState condition);
ValueBucket buildPartialBucket(int64_t bucketEndTime,
const std::vector<Interval>& intervals);
- void initCurrentSlicedBucket();
+ void initCurrentSlicedBucket(int64_t nextBucketStartTimeNs);
void appendToFullBucket(int64_t eventTimeNs, int64_t fullBucketEndTimeNs);
// Reset diff base and mHasGlobalBase
@@ -214,48 +228,54 @@
const bool mSplitBucketForAppUpgrade;
- FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsNoCondition);
- FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering);
- FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset);
- FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset);
- FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition);
- FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade);
- FRIEND_TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade);
- FRIEND_TEST(ValueMetricProducerTest, TestPartialBucketCreated);
- FRIEND_TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse);
- FRIEND_TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled);
- FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition);
- FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithCondition);
FRIEND_TEST(ValueMetricProducerTest, TestAnomalyDetection);
+ FRIEND_TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange);
+ FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundariesOnAppUpgrade);
+ FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundariesOnConditionChange);
FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition);
FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition);
FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2);
- FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateMin);
- FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateMax);
- FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateAvg);
- FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateSum);
- FRIEND_TEST(ValueMetricProducerTest, TestFirstBucket);
+ FRIEND_TEST(ValueMetricProducerTest, TestBucketIncludingUnknownConditionIsInvalid);
+ FRIEND_TEST(ValueMetricProducerTest, TestBucketInvalidIfGlobalBaseIsNotSet);
FRIEND_TEST(ValueMetricProducerTest, TestCalcPreviousBucketEndTime);
- FRIEND_TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput);
- FRIEND_TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue);
- FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBase);
- FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures);
- FRIEND_TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey);
- FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailBeforeConditionChange);
- FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange);
- FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange_EndOfBucket);
- FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate);
- FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenOneConditionFailed);
+ FRIEND_TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged);
+ FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary);
+ FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onConditionChanged);
+ FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled);
+ FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition);
+ FRIEND_TEST(ValueMetricProducerTest, TestFirstBucket);
FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenGuardRailHit);
FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenInitialPullFailed);
FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenLastPullFailed);
- FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullDelayExceeded);
- FRIEND_TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange);
- FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled);
- FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onConditionChanged);
- FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary);
+ FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenOneConditionFailed);
+ FRIEND_TEST(ValueMetricProducerTest, TestLateOnDataPulledWithDiff);
+ FRIEND_TEST(ValueMetricProducerTest, TestLateOnDataPulledWithoutDiff);
+ FRIEND_TEST(ValueMetricProducerTest, TestPartialBucketCreated);
FRIEND_TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries);
- FRIEND_TEST(ValueMetricProducerTest, TestBucketIncludingUnknownConditionIsInvalid);
+ FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsNoCondition);
+ FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset);
+ FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset);
+ FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering);
+ FRIEND_TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade);
+ FRIEND_TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse);
+ FRIEND_TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled);
+ FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateAvg);
+ FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateMax);
+ FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateMin);
+ FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateSum);
+ FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithCondition);
+ FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade);
+ FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition);
+ FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullDelayExceeded);
+ FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange);
+ FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange_EndOfBucket);
+ FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailBeforeConditionChange);
+ FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate);
+ FRIEND_TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput);
+ FRIEND_TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue);
+ FRIEND_TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey);
+ FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBase);
+ FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures);
friend class ValueMetricProducerTestHelper;
};
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 28caede..e5e4534 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -52,10 +52,38 @@
const int64_t bucket6StartTimeNs = bucketStartTimeNs + 5 * bucketSizeNs;
double epsilon = 0.001;
+static void assertPastBucketValuesSingleKey(
+ const std::unordered_map<MetricDimensionKey, std::vector<ValueBucket>>& mPastBuckets,
+ const std::initializer_list<int>& expectedValuesList) {
+ std::vector<int> expectedValues(expectedValuesList);
+ if (expectedValues.size() == 0) {
+ ASSERT_EQ(0, mPastBuckets.size());
+ return;
+ }
+
+ ASSERT_EQ(1, mPastBuckets.size());
+ ASSERT_EQ(expectedValues.size(), mPastBuckets.begin()->second.size());
+
+ auto buckets = mPastBuckets.begin()->second;
+ for (int i = 0; i < expectedValues.size(); i++) {
+ EXPECT_EQ(expectedValues[i], buckets[i].values[0].long_value)
+ << "Values differ at index " << i;
+ }
+}
+
class ValueMetricProducerTestHelper {
public:
+ static shared_ptr<LogEvent> createEvent(int64_t eventTimeNs, int64_t value) {
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, eventTimeNs);
+ event->write(tagId);
+ event->write(value);
+ event->write(value);
+ event->init();
+ return event;
+ }
+
static sp<ValueMetricProducer> createValueProducerNoConditions(
sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric) {
UidMap uidMap;
@@ -541,7 +569,7 @@
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
event->write(tagId);
- event->write(120);
+ event->write(130);
event->init();
data->push_back(event);
return true;
@@ -569,6 +597,7 @@
event->init();
allData.push_back(event);
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10});
// has one slice
EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
@@ -577,16 +606,15 @@
EXPECT_EQ(110, curInterval.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(10, curInterval.value.long_value);
- EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
- EXPECT_EQ(10, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1);
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10});
// has one slice
EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(true, curInterval.hasValue);
- EXPECT_EQ(10, curInterval.value.long_value);
+ EXPECT_EQ(20, curInterval.value.long_value);
EXPECT_EQ(false, curInterval.hasBase);
}
@@ -805,10 +833,8 @@
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(30, curInterval.value.long_value);
- valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
- EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
- EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(30, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value);
+ valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
+ assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {30});
}
TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) {
@@ -872,10 +898,8 @@
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(50, curInterval.value.long_value);
- valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
- EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
- EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(50, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value);
+ valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
+ assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {50});
}
TEST(ValueMetricProducerTest, TestAnomalyDetection) {
@@ -1006,8 +1030,7 @@
EXPECT_EQ(true, curInterval.hasBase);
EXPECT_EQ(23, curInterval.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
- EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
- EXPECT_EQ(12, valueProducer->mPastBuckets.begin()->second.back().values[0].long_value);
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12});
// pull 3 come late.
// The previous bucket gets closed with error. (Has start value 23, no ending)
@@ -1026,9 +1049,7 @@
EXPECT_EQ(true, curInterval.hasBase);
EXPECT_EQ(36, curInterval.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
- EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
- EXPECT_EQ(1UL, valueProducer->mPastBuckets.begin()->second.size());
- EXPECT_EQ(12, valueProducer->mPastBuckets.begin()->second.back().values[0].long_value);
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12});
}
/*
@@ -1076,27 +1097,19 @@
// pull on bucket boundary come late, condition change happens before it
valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1);
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20});
EXPECT_EQ(false, curInterval.hasBase);
- EXPECT_EQ(true, curInterval.hasValue);
- EXPECT_EQ(20, curInterval.value.long_value);
- EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
// Now the alarm is delivered.
// since the condition turned to off before this pull finish, it has no effect
vector<shared_ptr<LogEvent>> allData;
- allData.clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 30);
- event->write(1);
- event->write(110);
- event->init();
- allData.push_back(event);
+ allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 30, 110));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20});
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(false, curInterval.hasBase);
- EXPECT_EQ(true, curInterval.hasValue);
- EXPECT_EQ(20, curInterval.value.long_value);
- EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
+ EXPECT_EQ(false, curInterval.hasValue);
}
/*
@@ -1155,37 +1168,37 @@
// pull on bucket boundary come late, condition change happens before it
valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1);
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20});
+ EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(false, curInterval.hasBase);
- EXPECT_EQ(true, curInterval.hasValue);
- EXPECT_EQ(20, curInterval.value.long_value);
- EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
+ EXPECT_EQ(false, curInterval.hasValue);
// condition changed to true again, before the pull alarm is delivered
valueProducer->onConditionChanged(true, bucket2StartTimeNs + 25);
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20});
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(true, curInterval.hasBase);
EXPECT_EQ(130, curInterval.base.long_value);
- EXPECT_EQ(true, curInterval.hasValue);
- EXPECT_EQ(20, curInterval.value.long_value);
- EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
+ EXPECT_EQ(false, curInterval.hasValue);
- // Now the alarm is delivered, but it is considered late, the bucket is invalidated.
+ // Now the alarm is delivered, but it is considered late, the data will be used
+ // for the new bucket since it was just pulled.
vector<shared_ptr<LogEvent>> allData;
- allData.clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 50);
- event->write(1);
- event->write(110);
- event->init();
- allData.push_back(event);
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+ allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 50, 140));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 50);
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
- EXPECT_EQ(false, curInterval.hasBase);
- EXPECT_EQ(130, curInterval.base.long_value);
+ EXPECT_EQ(true, curInterval.hasBase);
+ EXPECT_EQ(140, curInterval.base.long_value);
EXPECT_EQ(true, curInterval.hasValue);
- EXPECT_EQ(20, curInterval.value.long_value);
- EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
+ EXPECT_EQ(10, curInterval.value.long_value);
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20});
+
+ allData.clear();
+ allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket3StartTimeNs, 160));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20, 30});
}
TEST(ValueMetricProducerTest, TestPushedAggregateMin) {
@@ -1227,10 +1240,8 @@
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(10, curInterval.value.long_value);
- valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
- EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
- EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value);
+ valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
+ assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10});
}
TEST(ValueMetricProducerTest, TestPushedAggregateMax) {
@@ -1273,9 +1284,9 @@
EXPECT_EQ(20, curInterval.value.long_value);
valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
- EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
- EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(20, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value);
+ /* EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); */
+ /* EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size()); */
+ /* EXPECT_EQ(20, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value); */
}
TEST(ValueMetricProducerTest, TestPushedAggregateAvg) {
@@ -1320,7 +1331,7 @@
EXPECT_EQ(25, curInterval.value.long_value);
EXPECT_EQ(2, curInterval.sampleSize);
- valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
+ valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
EXPECT_TRUE(std::abs(valueProducer.mPastBuckets.begin()->second.back().values[0].double_value - 12.5) < epsilon);
@@ -1365,10 +1376,8 @@
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(25, curInterval.value.long_value);
- valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
- EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
- EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(25, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value);
+ valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
+ assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {25});
}
TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) {
@@ -1700,10 +1709,8 @@
EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
EXPECT_EQ(true, interval2.hasBase);
- EXPECT_EQ(4, interval2.base.long_value);
+ EXPECT_EQ(5, interval2.base.long_value);
EXPECT_EQ(false, interval2.hasValue);
- EXPECT_EQ(true, interval1.hasBase);
- EXPECT_EQ(false, interval1.hasValue);
EXPECT_EQ(true, valueProducer->mHasGlobalBase);
EXPECT_EQ(2UL, valueProducer->mPastBuckets.size());
@@ -1721,14 +1728,16 @@
valueProducer->onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs);
EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
- EXPECT_EQ(true, interval2.hasBase);
- EXPECT_EQ(5, interval2.base.long_value);
- EXPECT_EQ(false, interval2.hasValue);
- EXPECT_EQ(5, interval2.value.long_value);
- EXPECT_EQ(true, interval1.hasBase);
- EXPECT_EQ(13, interval1.base.long_value);
- EXPECT_EQ(false, interval1.hasValue);
- EXPECT_EQ(8, interval1.value.long_value);
+ auto it1 = std::next(valueProducer->mCurrentSlicedBucket.begin())->second[0];
+ EXPECT_EQ(true, it1.hasBase);
+ EXPECT_EQ(13, it1.base.long_value);
+ EXPECT_EQ(false, it1.hasValue);
+ EXPECT_EQ(8, it1.value.long_value);
+ auto it2 = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ EXPECT_EQ(true, it2.hasBase);
+ EXPECT_EQ(5, it2.base.long_value);
+ EXPECT_EQ(false, it2.hasValue);
+ EXPECT_EQ(5, it2.value.long_value);
EXPECT_EQ(true, valueProducer->mHasGlobalBase);
EXPECT_EQ(2UL, valueProducer->mPastBuckets.size());
}
@@ -1785,8 +1794,7 @@
EXPECT_EQ(false, interval1.hasValue);
EXPECT_EQ(8, interval1.value.long_value);
EXPECT_FALSE(interval1.seenNewData);
- EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
- EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8});
auto it = valueProducer->mCurrentSlicedBucket.begin();
for (; it != valueProducer->mCurrentSlicedBucket.end(); it++) {
@@ -1801,7 +1809,7 @@
EXPECT_EQ(4, interval2.base.long_value);
EXPECT_EQ(false, interval2.hasValue);
EXPECT_FALSE(interval2.seenNewData);
- EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8});
// next pull somehow did not happen, skip to end of bucket 3
allData.clear();
@@ -1811,7 +1819,6 @@
event1->init();
allData.push_back(event1);
valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
-
// Only one interval left. One was trimmed.
EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
interval2 = valueProducer->mCurrentSlicedBucket.begin()->second[0];
@@ -1820,7 +1827,7 @@
EXPECT_EQ(5, interval2.base.long_value);
EXPECT_EQ(false, interval2.hasValue);
EXPECT_FALSE(interval2.seenNewData);
- EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8});
allData.clear();
event1 = make_shared<LogEvent>(tagId, bucket5StartTimeNs + 1);
@@ -1835,7 +1842,7 @@
EXPECT_EQ(14, interval2.base.long_value);
EXPECT_EQ(false, interval2.hasValue);
EXPECT_FALSE(interval2.seenNewData);
- EXPECT_EQ(2UL, valueProducer->mPastBuckets.size());
+ ASSERT_EQ(2UL, valueProducer->mPastBuckets.size());
auto iterator = valueProducer->mPastBuckets.begin();
EXPECT_EQ(9, iterator->second[0].values[0].long_value);
iterator++;
@@ -2106,7 +2113,7 @@
// First onConditionChanged
.WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
for (int i = 0; i < 2000; i++) {
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
event->write(i);
event->write(i);
event->init();
@@ -2117,9 +2124,9 @@
sp<ValueMetricProducer> valueProducer =
ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
-
valueProducer->mCondition = ConditionState::kFalse;
- valueProducer->onConditionChanged(true, bucket2StartTimeNs + 2);
+
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 2);
EXPECT_EQ(true, valueProducer->mCurrentBucketIsInvalid);
EXPECT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
}
@@ -2493,6 +2500,201 @@
EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
}
+TEST(ValueMetricProducerTest, TestBucketBoundariesOnConditionChange) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ // Second onConditionChanged.
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 10, 5));
+ return true;
+ }))
+ // Third onConditionChanged.
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(ValueMetricProducerTestHelper::createEvent(bucket3StartTimeNs + 10, 7));
+ return true;
+ }));
+
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+ valueProducer->mCondition = ConditionState::kUnknown;
+
+ valueProducer->onConditionChanged(false, bucketStartTimeNs);
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+
+ // End of first bucket
+ vector<shared_ptr<LogEvent>> allData;
+ allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 1, 4));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 1);
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+
+ valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10);
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ EXPECT_EQ(true, curInterval.hasBase);
+ EXPECT_EQ(5, curInterval.base.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
+
+ valueProducer->onConditionChanged(false, bucket3StartTimeNs + 10);
+
+ // Bucket should have been completed.
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {2});
+}
+
+TEST(ValueMetricProducerTest, TestLateOnDataPulledWithoutDiff) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+ metric.set_use_diff(false);
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
+
+ vector<shared_ptr<LogEvent>> allData;
+ allData.push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs + 30, 10));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + 30);
+
+ allData.clear();
+ allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs, 20));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+
+ // Bucket should have been completed.
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30});
+}
+
+TEST(ValueMetricProducerTest, TestLateOnDataPulledWithDiff) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ // Initialization.
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs, 1));
+ return true;
+ }));
+
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
+
+ vector<shared_ptr<LogEvent>> allData;
+ allData.push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs + 30, 10));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + 30);
+
+ allData.clear();
+ allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs, 20));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+
+ // Bucket should have been completed.
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {19});
+}
+
+TEST(ValueMetricProducerTest, TestBucketBoundariesOnAppUpgrade) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ // Initialization.
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs, 1));
+ return true;
+ }))
+ // notifyAppUpgrade.
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 2, 10));
+ return true;
+ }));
+
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
+
+ valueProducer->notifyAppUpgrade(bucket2StartTimeNs + 2, "com.foo", 10000, 1);
+
+ // Bucket should have been completed.
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {9});
+}
+
+TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ // First on condition changed.
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs, 1));
+ return true;
+ }))
+ // Second on condition changed.
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs, 3));
+ return true;
+ }));
+
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
+ valueProducer->onConditionChanged(false, bucketStartTimeNs + 10);
+ valueProducer->onConditionChanged(false, bucketStartTimeNs + 10);
+
+ EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ EXPECT_EQ(true, curInterval.hasValue);
+ EXPECT_EQ(2, curInterval.value.long_value);
+}
+
+TEST(ValueMetricProducerTest, TestBucketInvalidIfGlobalBaseIsNotSet) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ // First condition change.
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs, 1));
+ return true;
+ }))
+ // 2nd condition change.
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs, 1));
+ return true;
+ }))
+ // 3rd condition change.
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs, 1));
+ return true;
+ }));
+
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+ valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10);
+
+ vector<shared_ptr<LogEvent>> allData;
+ allData.push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs + 3, 10));
+ valueProducer->onDataPulled(allData, /** succeed */ false, bucketStartTimeNs + 3);
+
+ allData.clear();
+ allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs, 20));
+ valueProducer->onDataPulled(allData, /** succeed */ false, bucket2StartTimeNs);
+
+ valueProducer->onConditionChanged(false, bucket2StartTimeNs + 8);
+ valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10);
+
+ allData.clear();
+ allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket3StartTimeNs, 30));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+
+ // There was not global base available so all buckets are invalid.
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {});
+}
+
static StatsLogReport outputStreamToProto(ProtoOutputStream* proto) {
vector<uint8_t> bytes;
bytes.resize(proto->size());
diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt
index aaff76e..e25f463 100644
--- a/config/hiddenapi-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -2909,15 +2909,8 @@
Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->loge(Ljava/lang/String;)V
Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->mAdnCache:Lcom/android/internal/telephony/uicc/AdnRecordCache;
Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->mBaseHandler:Landroid/os/Handler;
-Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->mCurrentApp:Lcom/android/internal/telephony/uicc/UiccCardApplication;
-Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->mIs3gCard:Z
-Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->mLock:Ljava/lang/Object;
Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->mPhone:Lcom/android/internal/telephony/Phone;
-Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->mRecords:Ljava/util/List;
-Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->mRecordSize:[I
-Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->mSuccess:Z
Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->updateEfForIccType(I)I
-Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->waitForResult(Ljava/util/concurrent/atomic/AtomicBoolean;)V
Lcom/android/internal/telephony/IccProvider;-><init>()V
Lcom/android/internal/telephony/IccProvider;->ADDRESS_BOOK_COLUMN_NAMES:[Ljava/lang/String;
Lcom/android/internal/telephony/IccProvider;->DBG:Z
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 91fc188..de0f2bc 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -127,7 +127,6 @@
import android.view.autofill.IAutofillWindowPresenter;
import android.view.contentcapture.ContentCaptureContext;
import android.view.contentcapture.ContentCaptureManager;
-import android.view.contentcapture.ContentCaptureSession;
import android.widget.AdapterView;
import android.widget.Toast;
import android.widget.Toolbar;
@@ -1038,15 +1037,15 @@
}
/** @hide */ private static final int CONTENT_CAPTURE_START = 1;
- /** @hide */ private static final int CONTENT_CAPTURE_PAUSE = 2;
- /** @hide */ private static final int CONTENT_CAPTURE_RESUME = 3;
+ /** @hide */ private static final int CONTENT_CAPTURE_RESUME = 2;
+ /** @hide */ private static final int CONTENT_CAPTURE_PAUSE = 3;
/** @hide */ private static final int CONTENT_CAPTURE_STOP = 4;
/** @hide */
@IntDef(prefix = { "CONTENT_CAPTURE_" }, value = {
CONTENT_CAPTURE_START,
- CONTENT_CAPTURE_PAUSE,
CONTENT_CAPTURE_RESUME,
+ CONTENT_CAPTURE_PAUSE,
CONTENT_CAPTURE_STOP
})
@Retention(RetentionPolicy.SOURCE)
@@ -1056,10 +1055,10 @@
switch (type) {
case CONTENT_CAPTURE_START:
return "START";
- case CONTENT_CAPTURE_PAUSE:
- return "PAUSE";
case CONTENT_CAPTURE_RESUME:
return "RESUME";
+ case CONTENT_CAPTURE_PAUSE:
+ return "PAUSE";
case CONTENT_CAPTURE_STOP:
return "STOP";
default:
@@ -1086,16 +1085,16 @@
& WindowManager.LayoutParams.FLAG_SECURE) != 0) {
flags |= ContentCaptureContext.FLAG_DISABLED_BY_FLAG_SECURE;
}
- cm.onActivityStarted(mToken, getComponentName(), flags);
- break;
- case CONTENT_CAPTURE_PAUSE:
- cm.flush(ContentCaptureSession.FLUSH_REASON_ACTIVITY_PAUSED);
+ cm.onActivityCreated(mToken, getComponentName(), flags);
break;
case CONTENT_CAPTURE_RESUME:
- cm.flush(ContentCaptureSession.FLUSH_REASON_ACTIVITY_RESUMED);
+ cm.onActivityResumed();
+ break;
+ case CONTENT_CAPTURE_PAUSE:
+ cm.onActivityPaused();
break;
case CONTENT_CAPTURE_STOP:
- cm.onActivityStopped();
+ cm.onActivityDestroyed();
break;
default:
Log.wtf(TAG, "Invalid @ContentCaptureNotificationType: " + type);
@@ -1654,6 +1653,8 @@
}
mCalled = true;
+
+ notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_START);
}
/**
@@ -1697,7 +1698,6 @@
if (mAutoFillResetNeeded) {
getAutofillManager().onVisibleForAutofill();
}
- notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_START);
}
/**
@@ -1780,8 +1780,10 @@
}
}
}
- mCalled = true;
+
notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_RESUME);
+
+ mCalled = true;
}
/**
@@ -2197,8 +2199,9 @@
mAutoFillIgnoreFirstResumePause = false;
}
}
- mCalled = true;
+
notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_PAUSE);
+ mCalled = true;
}
/**
@@ -2388,7 +2391,6 @@
mIntent.getIBinderExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN));
}
}
- notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_STOP);
mEnterAnimationComplete = false;
}
@@ -2459,6 +2461,8 @@
}
dispatchActivityDestroyed();
+
+ notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_STOP);
}
/**
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index dcef9d2..780dd63 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -489,4 +489,10 @@
/** Returns a file descriptor that'll be closed when the system server process dies. */
ParcelFileDescriptor getLifeMonitor();
+
+ /**
+ * Start user, if it us not already running, and bring it to foreground.
+ * unlockProgressListener can be null if monitoring progress is not necessary.
+ */
+ boolean startUserInForegroundWithListener(int userid, IProgressListener unlockProgressListener);
}
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 497d5ba..b01cd0e 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -277,12 +277,8 @@
*
* @param showingKeyguard True if the keyguard is showing, false otherwise.
* @param showingAod True if AOD is showing, false otherwise.
- * @param secondaryDisplaysShowing The displayId's of the secondary displays on which the
- * keyguard is showing, or {@code null} if there is no such display. Only meaningful if showing
- * is {@code true}.
*/
- void setLockScreenShown(boolean showingKeyguard, boolean showingAod,
- in int[] secondaryDisplaysShowing);
+ void setLockScreenShown(boolean showingKeyguard, boolean showingAod);
Bundle getAssistContextExtras(int requestType);
boolean launchAssistIntent(in Intent intent, int requestType, in String hint, int userHandle,
in Bundle args);
diff --git a/core/java/android/app/admin/PasswordMetrics.java b/core/java/android/app/admin/PasswordMetrics.java
index e5df2c7..a6bf501 100644
--- a/core/java/android/app/admin/PasswordMetrics.java
+++ b/core/java/android/app/admin/PasswordMetrics.java
@@ -221,7 +221,10 @@
}
};
- public static PasswordMetrics computeForPassword(@NonNull String password) {
+ /**
+ * Returns the {@code PasswordMetrics} for a given password
+ */
+ public static PasswordMetrics computeForPassword(@NonNull byte[] password) {
// Analyse the characters used
int letters = 0;
int upperCase = 0;
@@ -229,9 +232,9 @@
int numeric = 0;
int symbols = 0;
int nonLetter = 0;
- final int length = password.length();
+ final int length = password.length;
for (int i = 0; i < length; i++) {
- switch (categoryChar(password.charAt(i))) {
+ switch (categoryChar((char) password[i])) {
case CHAR_LOWER_CASE:
letters++;
lowerCase++;
@@ -296,7 +299,7 @@
return false;
}
- /*
+ /**
* Returns the maximum length of a sequential characters. A sequence is defined as
* monotonically increasing characters with a constant interval or the same character repeated.
*
@@ -310,19 +313,19 @@
* maxLengthSequence(";;;;") == 4 (anything that repeats)
* maxLengthSequence(":;<=>") == 1 (ordered, but not composed of alphas or digits)
*
- * @param string the pass
+ * @param bytes the pass
* @return the number of sequential letters or digits
*/
- public static int maxLengthSequence(@NonNull String string) {
- if (string.length() == 0) return 0;
- char previousChar = string.charAt(0);
+ public static int maxLengthSequence(@NonNull byte[] bytes) {
+ if (bytes.length == 0) return 0;
+ char previousChar = (char) bytes[0];
@CharacterCatagory int category = categoryChar(previousChar); //current sequence category
int diff = 0; //difference between two consecutive characters
boolean hasDiff = false; //if we are currently targeting a sequence
int maxLength = 0; //maximum length of a sequence already found
int startSequence = 0; //where the current sequence started
- for (int current = 1; current < string.length(); current++) {
- char currentChar = string.charAt(current);
+ for (int current = 1; current < bytes.length; current++) {
+ char currentChar = (char) bytes[current];
@CharacterCatagory int categoryCurrent = categoryChar(currentChar);
int currentDiff = (int) currentChar - (int) previousChar;
if (categoryCurrent != category || Math.abs(currentDiff) > maxDiffCategory(category)) {
@@ -341,7 +344,7 @@
}
previousChar = currentChar;
}
- maxLength = Math.max(maxLength, string.length() - startSequence);
+ maxLength = Math.max(maxLength, bytes.length - startSequence);
return maxLength;
}
diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java
index edd3ef98..7ec21f6 100644
--- a/core/java/android/app/role/RoleManager.java
+++ b/core/java/android/app/role/RoleManager.java
@@ -71,10 +71,8 @@
/**
* The name of the assistant app role.
*
- * @hide
+ * @see android.service.voice.VoiceInteractionService
*/
- @SystemApi
- @TestApi
public static final String ROLE_ASSISTANT = "android.app.role.ASSISTANT";
/**
@@ -149,9 +147,6 @@
* place the call through a call redirection service.
*
* @see android.telecom.CallRedirectionService
- *
- * TODO: STOPSHIP: Make name of required roles public API
- * @hide
*/
public static final String ROLE_CALL_REDIRECTION = "android.app.role.CALL_REDIRECTION";
@@ -159,9 +154,6 @@
* The name of the call screening and caller id role.
*
* @see android.telecom.CallScreeningService
- *
- * TODO: STOPSHIP: Make name of required roles public API
- * @hide
*/
public static final String ROLE_CALL_SCREENING = "android.app.role.CALL_SCREENING";
diff --git a/core/java/android/app/timezone/RulesState.java b/core/java/android/app/timezone/RulesState.java
index e86d348..38dd1eb 100644
--- a/core/java/android/app/timezone/RulesState.java
+++ b/core/java/android/app/timezone/RulesState.java
@@ -33,7 +33,7 @@
*
* <p>The following properties are included:
* <dl>
- * <dt>systemRulesVersion</dt>
+ * <dt>baseRulesVersion</dt>
* <dd>the IANA rules version that shipped with the OS. Always present. e.g. "2017a".</dd>
* <dt>distroFormatVersionSupported</dt>
* <dd>the distro format version supported by this device. Always present.</dd>
@@ -98,7 +98,7 @@
private static final byte BYTE_FALSE = 0;
private static final byte BYTE_TRUE = 1;
- private final String mSystemRulesVersion;
+ private final String mBaseRulesVersion;
private final DistroFormatVersion mDistroFormatVersionSupported;
private final boolean mOperationInProgress;
@StagedOperationType private final int mStagedOperationType;
@@ -106,13 +106,13 @@
@DistroStatus private final int mDistroStatus;
@Nullable private final DistroRulesVersion mInstalledDistroRulesVersion;
- public RulesState(String systemRulesVersion, DistroFormatVersion distroFormatVersionSupported,
+ public RulesState(String baseRulesVersion, DistroFormatVersion distroFormatVersionSupported,
boolean operationInProgress,
@StagedOperationType int stagedOperationType,
@Nullable DistroRulesVersion stagedDistroRulesVersion,
@DistroStatus int distroStatus,
@Nullable DistroRulesVersion installedDistroRulesVersion) {
- this.mSystemRulesVersion = validateRulesVersion("systemRulesVersion", systemRulesVersion);
+ this.mBaseRulesVersion = validateRulesVersion("baseRulesVersion", baseRulesVersion);
this.mDistroFormatVersionSupported =
validateNotNull("distroFormatVersionSupported", distroFormatVersionSupported);
this.mOperationInProgress = operationInProgress;
@@ -132,8 +132,8 @@
"installedDistroRulesVersion", installedDistroRulesVersion);
}
- public String getSystemRulesVersion() {
- return mSystemRulesVersion;
+ public String getBaseRulesVersion() {
+ return mBaseRulesVersion;
}
public boolean isOperationInProgress() {
@@ -172,14 +172,14 @@
}
/**
- * Returns true if the system image data files contain IANA rules data that are newer than the
+ * Returns true if the base data files contain IANA rules data that are newer than the
* distro IANA rules version supplied, i.e. true when the version specified would be "worse"
- * than the one that is in the system image. Returns false if the system image version is the
+ * than the one that is in the base data. Returns false if the base version is the
* same or older, i.e. false when the version specified would be "better" than the one that is
- * in the system image.
+ * in the base set.
*/
- public boolean isSystemVersionNewerThan(DistroRulesVersion distroRulesVersion) {
- return mSystemRulesVersion.compareTo(distroRulesVersion.getRulesVersion()) > 0;
+ public boolean isBaseVersionNewerThan(DistroRulesVersion distroRulesVersion) {
+ return mBaseRulesVersion.compareTo(distroRulesVersion.getRulesVersion()) > 0;
}
public static final Parcelable.Creator<RulesState> CREATOR =
@@ -194,14 +194,14 @@
};
private static RulesState createFromParcel(Parcel in) {
- String systemRulesVersion = in.readString();
+ String baseRulesVersion = in.readString();
DistroFormatVersion distroFormatVersionSupported = in.readParcelable(null);
boolean operationInProgress = in.readByte() == BYTE_TRUE;
int distroStagedState = in.readByte();
DistroRulesVersion stagedDistroRulesVersion = in.readParcelable(null);
int installedDistroStatus = in.readByte();
DistroRulesVersion installedDistroRulesVersion = in.readParcelable(null);
- return new RulesState(systemRulesVersion, distroFormatVersionSupported, operationInProgress,
+ return new RulesState(baseRulesVersion, distroFormatVersionSupported, operationInProgress,
distroStagedState, stagedDistroRulesVersion,
installedDistroStatus, installedDistroRulesVersion);
}
@@ -213,7 +213,7 @@
@Override
public void writeToParcel(Parcel out, int flags) {
- out.writeString(mSystemRulesVersion);
+ out.writeString(mBaseRulesVersion);
out.writeParcelable(mDistroFormatVersionSupported, 0);
out.writeByte(mOperationInProgress ? BYTE_TRUE : BYTE_FALSE);
out.writeByte((byte) mStagedOperationType);
@@ -242,7 +242,7 @@
if (mDistroStatus != that.mDistroStatus) {
return false;
}
- if (!mSystemRulesVersion.equals(that.mSystemRulesVersion)) {
+ if (!mBaseRulesVersion.equals(that.mBaseRulesVersion)) {
return false;
}
if (!mDistroFormatVersionSupported.equals(that.mDistroFormatVersionSupported)) {
@@ -259,7 +259,7 @@
@Override
public int hashCode() {
- int result = mSystemRulesVersion.hashCode();
+ int result = mBaseRulesVersion.hashCode();
result = 31 * result + mDistroFormatVersionSupported.hashCode();
result = 31 * result + (mOperationInProgress ? 1 : 0);
result = 31 * result + mStagedOperationType;
@@ -275,7 +275,7 @@
@Override
public String toString() {
return "RulesState{"
- + "mSystemRulesVersion='" + mSystemRulesVersion + '\''
+ + "mBaseRulesVersion='" + mBaseRulesVersion + '\''
+ ", mDistroFormatVersionSupported=" + mDistroFormatVersionSupported
+ ", mOperationInProgress=" + mOperationInProgress
+ ", mStagedOperationType=" + mStagedOperationType
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index dc5bdc6..9c6bd92 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -779,7 +779,7 @@
android.Manifest.permission.SUSPEND_APPS,
android.Manifest.permission.OBSERVE_APP_USAGE})
public void registerAppUsageLimitObserver(int observerId, @NonNull String[] observedEntities,
- long timeLimit, @NonNull TimeUnit timeUnit, PendingIntent callbackIntent) {
+ long timeLimit, @NonNull TimeUnit timeUnit, @Nullable PendingIntent callbackIntent) {
try {
mService.registerAppUsageLimitObserver(observerId, observedEntities,
timeUnit.toMillis(timeLimit), callbackIntent, mContext.getOpPackageName());
diff --git a/core/java/android/content/pm/AndroidTestBaseUpdater.java b/core/java/android/content/pm/AndroidTestBaseUpdater.java
index 2aaac02..6a1778c 100644
--- a/core/java/android/content/pm/AndroidTestBaseUpdater.java
+++ b/core/java/android/content/pm/AndroidTestBaseUpdater.java
@@ -19,11 +19,12 @@
import static android.content.pm.SharedLibraryNames.ANDROID_TEST_RUNNER;
import android.content.pm.PackageParser.Package;
+import android.os.Build;
import com.android.internal.annotations.VisibleForTesting;
/**
- * Updates a package to ensure that if it targets < P that the android.test.base library is
+ * Updates a package to ensure that if it targets <= P that the android.test.base library is
* included by default.
*
* <p>This is separated out so that it can be conditionally included at build time depending on
@@ -37,12 +38,17 @@
@VisibleForTesting
public class AndroidTestBaseUpdater extends PackageSharedLibraryUpdater {
+ private static boolean apkTargetsApiLevelLessThanOrEqualToP(Package pkg) {
+ int targetSdkVersion = pkg.applicationInfo.targetSdkVersion;
+ return targetSdkVersion <= Build.VERSION_CODES.P;
+ }
+
@Override
public void updatePackage(Package pkg) {
- // Packages targeted at <= O_MR1 expect the classes in the android.test.base library
+ // Packages targeted at <= P expect the classes in the android.test.base library
// to be accessible so this maintains backward compatibility by adding the
// android.test.base library to those packages.
- if (apkTargetsApiLevelLessThanOrEqualToOMR1(pkg)) {
+ if (apkTargetsApiLevelLessThanOrEqualToP(pkg)) {
prefixRequiredLibrary(pkg, ANDROID_TEST_BASE);
} else {
// If a package already depends on android.test.runner then add a dependency on
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index f87ce82..bf556ba 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -795,7 +795,8 @@
* @throws SecurityException when the caller is not the active launcher.
*/
@Nullable
- public LauncherApps.AppUsageLimit getAppUsageLimit(String packageName, UserHandle user) {
+ public LauncherApps.AppUsageLimit getAppUsageLimit(@NonNull String packageName,
+ @NonNull UserHandle user) {
try {
return mService.getAppUsageLimit(mContext.getPackageName(), packageName, user);
} catch (RemoteException re) {
diff --git a/core/java/android/content/pm/OrgApacheHttpLegacyUpdater.java b/core/java/android/content/pm/OrgApacheHttpLegacyUpdater.java
index 7790067..707443b 100644
--- a/core/java/android/content/pm/OrgApacheHttpLegacyUpdater.java
+++ b/core/java/android/content/pm/OrgApacheHttpLegacyUpdater.java
@@ -18,6 +18,7 @@
import static android.content.pm.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
import android.content.pm.PackageParser.Package;
+import android.os.Build;
import com.android.internal.annotations.VisibleForTesting;
@@ -30,6 +31,11 @@
@VisibleForTesting
public class OrgApacheHttpLegacyUpdater extends PackageSharedLibraryUpdater {
+ private static boolean apkTargetsApiLevelLessThanOrEqualToOMR1(Package pkg) {
+ int targetSdkVersion = pkg.applicationInfo.targetSdkVersion;
+ return targetSdkVersion < Build.VERSION_CODES.P;
+ }
+
@Override
public void updatePackage(Package pkg) {
// Packages targeted at <= O_MR1 expect the classes in the org.apache.http.legacy library
diff --git a/core/java/android/content/pm/PackageBackwardCompatibility.java b/core/java/android/content/pm/PackageBackwardCompatibility.java
index b19196a..4331bd4 100644
--- a/core/java/android/content/pm/PackageBackwardCompatibility.java
+++ b/core/java/android/content/pm/PackageBackwardCompatibility.java
@@ -116,7 +116,7 @@
private final PackageSharedLibraryUpdater[] mPackageUpdaters;
- public PackageBackwardCompatibility(
+ private PackageBackwardCompatibility(
boolean bootClassPathContainsATB, PackageSharedLibraryUpdater[] packageUpdaters) {
this.mBootClassPathContainsATB = bootClassPathContainsATB;
this.mPackageUpdaters = packageUpdaters;
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 6302071..0304f19 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1743,11 +1743,11 @@
public int[] childSessionIds = NO_SESSIONS;
/** {@hide} */
- public boolean isSessionApplied;
+ public boolean isStagedSessionApplied;
/** {@hide} */
- public boolean isSessionReady;
+ public boolean isStagedSessionReady;
/** {@hide} */
- public boolean isSessionFailed;
+ public boolean isStagedSessionFailed;
private int mStagedSessionErrorCode;
private String mStagedSessionErrorMessage;
@@ -1786,9 +1786,9 @@
if (childSessionIds == null) {
childSessionIds = NO_SESSIONS;
}
- isSessionApplied = source.readBoolean();
- isSessionReady = source.readBoolean();
- isSessionFailed = source.readBoolean();
+ isStagedSessionApplied = source.readBoolean();
+ isStagedSessionReady = source.readBoolean();
+ isStagedSessionFailed = source.readBoolean();
mStagedSessionErrorCode = source.readInt();
mStagedSessionErrorMessage = source.readString();
}
@@ -2082,36 +2082,46 @@
return childSessionIds;
}
+ private void checkSessionIsStaged() {
+ if (!isStaged) {
+ throw new IllegalStateException("Session is not marked as staged.");
+ }
+ }
+
/**
* Whether the staged session has been applied successfully, meaning that all of its
* packages have been activated and no further action is required.
* Only meaningful if {@code isStaged} is true.
*/
- public boolean isSessionApplied() {
- return isSessionApplied;
+ public boolean isStagedSessionApplied() {
+ checkSessionIsStaged();
+ return isStagedSessionApplied;
}
/**
* Whether the staged session is ready to be applied at next reboot. Only meaningful if
* {@code isStaged} is true.
*/
- public boolean isSessionReady() {
- return isSessionReady;
+ public boolean isStagedSessionReady() {
+ checkSessionIsStaged();
+ return isStagedSessionReady;
}
/**
* Whether something went wrong and the staged session is declared as failed, meaning that
* it will be ignored at next reboot. Only meaningful if {@code isStaged} is true.
*/
- public boolean isSessionFailed() {
- return isSessionFailed;
+ public boolean isStagedSessionFailed() {
+ checkSessionIsStaged();
+ return isStagedSessionFailed;
}
/**
* If something went wrong with a staged session, clients can check this error code to
* understand which kind of failure happened. Only meaningful if {@code isStaged} is true.
*/
- public int getStagedSessionErrorCode() {
+ public @StagedSessionErrorCode int getStagedSessionErrorCode() {
+ checkSessionIsStaged();
return mStagedSessionErrorCode;
}
@@ -2120,6 +2130,7 @@
* empty string if no error was encountered.
*/
public String getStagedSessionErrorMessage() {
+ checkSessionIsStaged();
return mStagedSessionErrorMessage;
}
@@ -2162,9 +2173,9 @@
dest.writeBoolean(isStaged);
dest.writeInt(parentSessionId);
dest.writeIntArray(childSessionIds);
- dest.writeBoolean(isSessionApplied);
- dest.writeBoolean(isSessionReady);
- dest.writeBoolean(isSessionFailed);
+ dest.writeBoolean(isStagedSessionApplied);
+ dest.writeBoolean(isStagedSessionReady);
+ dest.writeBoolean(isStagedSessionFailed);
dest.writeInt(mStagedSessionErrorCode);
dest.writeString(mStagedSessionErrorMessage);
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 5e70ecb..a5464c2 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2565,6 +2565,14 @@
public static final String FEATURE_PC = "android.hardware.type.pc";
/**
+ * Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: This is a foldable device. Properties such as
+ * the display size may change in response to being folded.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_FOLDABLE = "android.hardware.type.foldable";
+
+ /**
* Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
* The device supports printing.
*/
diff --git a/core/java/android/content/pm/PackageSharedLibraryUpdater.java b/core/java/android/content/pm/PackageSharedLibraryUpdater.java
index b14b321..1565d9c 100644
--- a/core/java/android/content/pm/PackageSharedLibraryUpdater.java
+++ b/core/java/android/content/pm/PackageSharedLibraryUpdater.java
@@ -17,7 +17,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.os.Build;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
@@ -60,11 +59,6 @@
|| ArrayUtils.contains(usesOptionalLibraries, apacheHttpLegacy);
}
- static boolean apkTargetsApiLevelLessThanOrEqualToOMR1(PackageParser.Package pkg) {
- int targetSdkVersion = pkg.applicationInfo.targetSdkVersion;
- return targetSdkVersion < Build.VERSION_CODES.P;
- }
-
/**
* Add an implicit dependency.
*
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index b155325..e674b8b 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -18,6 +18,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.annotation.UserIdInt;
@@ -1482,6 +1483,7 @@
* @hide
*/
@Nullable
+ @SystemApi
public Person[] getPersons() {
return clonePersons(mPersons);
}
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
index 849fd03..bd327b0 100644
--- a/core/java/android/content/pm/ShortcutManager.java
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -644,6 +644,7 @@
* @return True if the package has any share target definitions, False otherwise.
* @hide
*/
+ @SystemApi
public boolean hasShareTargets(@NonNull String packageName) {
try {
return mService.hasShareTargets(mContext.getPackageName(), packageName,
diff --git a/core/java/android/database/sqlite/SQLiteCompatibilityWalFlags.java b/core/java/android/database/sqlite/SQLiteCompatibilityWalFlags.java
index 8ea1db2..a269072 100644
--- a/core/java/android/database/sqlite/SQLiteCompatibilityWalFlags.java
+++ b/core/java/android/database/sqlite/SQLiteCompatibilityWalFlags.java
@@ -40,8 +40,7 @@
private static final String TAG = "SQLiteCompatibilityWalFlags";
private static volatile boolean sInitialized;
- private static volatile boolean sFlagsSet;
- private static volatile boolean sCompatibilityWalSupported;
+ private static volatile boolean sLegacyCompatibilityWalEnabled;
private static volatile String sWALSyncMode;
private static volatile long sTruncateSize = -1;
// This flag is used to avoid recursive initialization due to circular dependency on Settings
@@ -54,18 +53,9 @@
* @hide
*/
@VisibleForTesting
- public static boolean areFlagsSet() {
+ public static boolean isLegacyCompatibilityWalEnabled() {
initIfNeeded();
- return sFlagsSet;
- }
-
- /**
- * @hide
- */
- @VisibleForTesting
- public static boolean isCompatibilityWalSupported() {
- initIfNeeded();
- return sCompatibilityWalSupported;
+ return sLegacyCompatibilityWalEnabled;
}
/**
@@ -74,6 +64,14 @@
@VisibleForTesting
public static String getWALSyncMode() {
initIfNeeded();
+ // The configurable WAL sync mode should only ever be used if the legacy compatibility
+ // WAL is enabled. It should *not* have any effect if app developers explicitly turn on
+ // WAL for their database using setWriteAheadLoggingEnabled. Throwing an exception here
+ // adds an extra layer of checking that we never use it in the wrong place.
+ if (!sLegacyCompatibilityWalEnabled) {
+ throw new IllegalStateException("isLegacyCompatibilityWalEnabled() == false");
+ }
+
return sWALSyncMode;
}
@@ -131,13 +129,12 @@
sInitialized = true;
return;
}
- sCompatibilityWalSupported = parser.getBoolean("compatibility_wal_supported",
- SQLiteGlobal.isCompatibilityWalSupported());
+ sLegacyCompatibilityWalEnabled = parser.getBoolean(
+ "legacy_compatibility_wal_enabled", false);
sWALSyncMode = parser.getString("wal_syncmode", SQLiteGlobal.getWALSyncMode());
sTruncateSize = parser.getInt("truncate_size", -1);
- Log.i(TAG, "Read compatibility WAL flags: compatibility_wal_supported="
- + sCompatibilityWalSupported + ", wal_syncmode=" + sWALSyncMode);
- sFlagsSet = true;
+ Log.i(TAG, "Read compatibility WAL flags: legacy_compatibility_wal_enabled="
+ + sLegacyCompatibilityWalEnabled + ", wal_syncmode=" + sWALSyncMode);
sInitialized = true;
}
@@ -148,8 +145,7 @@
@TestApi
public static void reset() {
sInitialized = false;
- sFlagsSet = false;
- sCompatibilityWalSupported = false;
+ sLegacyCompatibilityWalEnabled = false;
sWALSyncMode = null;
}
}
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index 3c8e236..3844794 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -300,12 +300,13 @@
(mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0;
// Use compatibility WAL unless an app explicitly set journal/synchronous mode
// or DISABLE_COMPATIBILITY_WAL flag is set
- final boolean useCompatibilityWal = mConfiguration.useCompatibilityWal();
- if (walEnabled || useCompatibilityWal) {
+ final boolean isCompatibilityWalEnabled =
+ mConfiguration.isLegacyCompatibilityWalEnabled();
+ if (walEnabled || isCompatibilityWalEnabled) {
setJournalMode("WAL");
if (mConfiguration.syncMode != null) {
setSyncMode(mConfiguration.syncMode);
- } else if (useCompatibilityWal && SQLiteCompatibilityWalFlags.areFlagsSet()) {
+ } else if (isCompatibilityWalEnabled) {
setSyncMode(SQLiteCompatibilityWalFlags.getWALSyncMode());
} else {
setSyncMode(SQLiteGlobal.getWALSyncMode());
@@ -504,7 +505,7 @@
!= mConfiguration.foreignKeyConstraintsEnabled;
boolean walModeChanged = ((configuration.openFlags ^ mConfiguration.openFlags)
& (SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING
- | SQLiteDatabase.DISABLE_COMPATIBILITY_WAL)) != 0;
+ | SQLiteDatabase.ENABLE_LEGACY_COMPATIBILITY_WAL)) != 0;
boolean localeChanged = !configuration.locale.equals(mConfiguration.locale);
// Update configuration parameters.
diff --git a/core/java/android/database/sqlite/SQLiteConnectionPool.java b/core/java/android/database/sqlite/SQLiteConnectionPool.java
index dbc1766..852f8f2 100644
--- a/core/java/android/database/sqlite/SQLiteConnectionPool.java
+++ b/core/java/android/database/sqlite/SQLiteConnectionPool.java
@@ -321,7 +321,7 @@
// We should do in-place switching when transitioning from compatibility WAL
// to rollback journal. Otherwise transient connection state will be lost
boolean onlyCompatWalChanged = (mConfiguration.openFlags ^ configuration.openFlags)
- == SQLiteDatabase.DISABLE_COMPATIBILITY_WAL;
+ == SQLiteDatabase.ENABLE_LEGACY_COMPATIBILITY_WAL;
if (!onlyCompatWalChanged && mConfiguration.openFlags != configuration.openFlags) {
// If we are changing open flags and WAL mode at the same time, then
@@ -1113,19 +1113,18 @@
if (directories != null) {
directories.add(new File(mConfiguration.path).getParent());
}
+ boolean isCompatibilityWalEnabled = mConfiguration.isLegacyCompatibilityWalEnabled();
printer.println("Connection pool for " + mConfiguration.path + ":");
printer.println(" Open: " + mIsOpen);
printer.println(" Max connections: " + mMaxConnectionPoolSize);
printer.println(" Total execution time: " + mTotalExecutionTimeCounter);
printer.println(" Configuration: openFlags=" + mConfiguration.openFlags
- + ", useCompatibilityWal=" + mConfiguration.useCompatibilityWal()
+ + ", isLegacyCompatibilityWalEnabled=" + isCompatibilityWalEnabled
+ ", journalMode=" + TextUtils.emptyIfNull(mConfiguration.journalMode)
+ ", syncMode=" + TextUtils.emptyIfNull(mConfiguration.syncMode));
- if (SQLiteCompatibilityWalFlags.areFlagsSet()) {
- printer.println(" Compatibility WAL settings: compatibility_wal_supported="
- + SQLiteCompatibilityWalFlags
- .isCompatibilityWalSupported() + ", wal_syncmode="
+ if (isCompatibilityWalEnabled) {
+ printer.println(" Compatibility WAL enabled: wal_syncmode="
+ SQLiteCompatibilityWalFlags.getWALSyncMode());
}
if (mConfiguration.isLookasideConfigSet()) {
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index ae456af..dffbd89 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -266,12 +266,17 @@
*/
public static final int ENABLE_WRITE_AHEAD_LOGGING = 0x20000000;
+
+ // Note: The below value was only used on Android Pie.
+ // public static final int DISABLE_COMPATIBILITY_WAL = 0x40000000;
+
/**
- * Open flag: Flag for {@link #openDatabase} to disable Compatibility WAL when opening database.
+ * Open flag: Flag for {@link #openDatabase} to enable the legacy Compatibility WAL when opening
+ * database.
*
* @hide
*/
- public static final int DISABLE_COMPATIBILITY_WAL = 0x40000000;
+ public static final int ENABLE_LEGACY_COMPATIBILITY_WAL = 0x80000000;
/**
* Absolute max value that can be set by {@link #setMaxSqlCacheSize(int)}.
@@ -309,10 +314,8 @@
mConfigurationLocked.idleConnectionTimeoutMs = effectiveTimeoutMs;
mConfigurationLocked.journalMode = journalMode;
mConfigurationLocked.syncMode = syncMode;
- if (!SQLiteGlobal.isCompatibilityWalSupported() || (
- SQLiteCompatibilityWalFlags.areFlagsSet() && !SQLiteCompatibilityWalFlags
- .isCompatibilityWalSupported())) {
- mConfigurationLocked.openFlags |= DISABLE_COMPATIBILITY_WAL;
+ if (SQLiteCompatibilityWalFlags.isLegacyCompatibilityWalEnabled()) {
+ mConfigurationLocked.openFlags |= ENABLE_LEGACY_COMPATIBILITY_WAL;
}
}
@@ -2123,15 +2126,18 @@
throwIfNotOpenLocked();
final int oldFlags = mConfigurationLocked.openFlags;
- final boolean walDisabled = (oldFlags & ENABLE_WRITE_AHEAD_LOGGING) == 0;
- final boolean compatibilityWalDisabled = (oldFlags & DISABLE_COMPATIBILITY_WAL) != 0;
- if (walDisabled && compatibilityWalDisabled) {
+ final boolean walEnabled = (oldFlags & ENABLE_WRITE_AHEAD_LOGGING) != 0;
+ final boolean compatibilityWalEnabled =
+ (oldFlags & ENABLE_LEGACY_COMPATIBILITY_WAL) != 0;
+ // WAL was never enabled for this database, so there's nothing left to do.
+ if (!walEnabled && !compatibilityWalEnabled) {
return;
}
+ // If an app explicitly disables WAL, it takes priority over any directive
+ // to use the legacy "compatibility WAL" mode.
mConfigurationLocked.openFlags &= ~ENABLE_WRITE_AHEAD_LOGGING;
- // If an app explicitly disables WAL, compatibility mode should be disabled too
- mConfigurationLocked.openFlags |= DISABLE_COMPATIBILITY_WAL;
+ mConfigurationLocked.openFlags &= ~ENABLE_LEGACY_COMPATIBILITY_WAL;
try {
mConnectionPoolLocked.reconfigure(mConfigurationLocked);
diff --git a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
index 48f1021..fcdaf0a 100644
--- a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
+++ b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
@@ -17,6 +17,7 @@
package android.database.sqlite;
import android.annotation.UnsupportedAppUsage;
+
import java.util.ArrayList;
import java.util.Locale;
import java.util.regex.Pattern;
@@ -197,9 +198,9 @@
return path.equalsIgnoreCase(MEMORY_DB_PATH);
}
- boolean useCompatibilityWal() {
+ boolean isLegacyCompatibilityWalEnabled() {
return journalMode == null && syncMode == null
- && (openFlags & SQLiteDatabase.DISABLE_COMPATIBILITY_WAL) == 0;
+ && (openFlags & SQLiteDatabase.ENABLE_LEGACY_COMPATIBILITY_WAL) != 0;
}
private static String stripPathForLogs(String path) {
diff --git a/core/java/android/database/sqlite/SQLiteGlobal.java b/core/java/android/database/sqlite/SQLiteGlobal.java
index ff286fd..d796003 100644
--- a/core/java/android/database/sqlite/SQLiteGlobal.java
+++ b/core/java/android/database/sqlite/SQLiteGlobal.java
@@ -91,16 +91,6 @@
}
/**
- * Returns true if compatibility WAL mode is supported. In this mode, only
- * database journal mode is changed. Connection pool will use at most one connection.
- */
- public static boolean isCompatibilityWalSupported() {
- return SystemProperties.getBoolean("debug.sqlite.compatibility_wal_supported",
- Resources.getSystem().getBoolean(
- com.android.internal.R.bool.db_compatibility_wal_supported));
- }
-
- /**
* Gets the journal size limit in bytes.
*/
public static int getJournalSizeLimit() {
diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java
index 0e869c8..8163c4d 100644
--- a/core/java/android/database/sqlite/SQLiteOpenHelper.java
+++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java
@@ -202,8 +202,9 @@
}
mOpenParamsBuilder.setWriteAheadLoggingEnabled(enabled);
}
+
// Compatibility WAL is disabled if an app disables or enables WAL
- mOpenParamsBuilder.addOpenFlags(SQLiteDatabase.DISABLE_COMPATIBILITY_WAL);
+ mOpenParamsBuilder.removeOpenFlags(SQLiteDatabase.ENABLE_LEGACY_COMPATIBILITY_WAL);
}
}
diff --git a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java b/core/java/android/hardware/display/AmbientDisplayConfiguration.java
similarity index 80%
rename from core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
rename to core/java/android/hardware/display/AmbientDisplayConfiguration.java
index e19a32e..b122f19 100644
--- a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
+++ b/core/java/android/hardware/display/AmbientDisplayConfiguration.java
@@ -11,11 +11,12 @@
* 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
+ * limitations under the License.
*/
-package com.android.internal.hardware;
+package android.hardware.display;
+import android.annotation.TestApi;
import android.content.Context;
import android.os.Build;
import android.os.SystemProperties;
@@ -24,16 +25,25 @@
import com.android.internal.R;
+/**
+ * AmbientDisplayConfiguration encapsulates reading access to the configuration of ambient display.
+ *
+ * {@hide}
+ */
+@TestApi
public class AmbientDisplayConfiguration {
private final Context mContext;
private final boolean mAlwaysOnByDefault;
+ /** {@hide} */
+ @TestApi
public AmbientDisplayConfiguration(Context context) {
mContext = context;
mAlwaysOnByDefault = mContext.getResources().getBoolean(R.bool.config_dozeAlwaysOnEnabled);
}
+ /** {@hide} */
public boolean enabled(int user) {
return pulseOnNotificationEnabled(user)
|| pulseOnLongPressEnabled(user)
@@ -41,63 +51,83 @@
|| wakeScreenGestureEnabled(user);
}
+ /** {@hide} */
public boolean pulseOnNotificationEnabled(int user) {
- return boolSettingDefaultOn(Settings.Secure.DOZE_ENABLED, user) && pulseOnNotificationAvailable();
+ return boolSettingDefaultOn(Settings.Secure.DOZE_ENABLED, user)
+ && pulseOnNotificationAvailable();
}
+ /** {@hide} */
public boolean pulseOnNotificationAvailable() {
return ambientDisplayAvailable();
}
+ /** {@hide} */
public boolean pickupGestureEnabled(int user) {
return boolSettingDefaultOn(Settings.Secure.DOZE_PICK_UP_GESTURE, user)
&& dozePickupSensorAvailable();
}
+ /** {@hide} */
public boolean dozePickupSensorAvailable() {
return mContext.getResources().getBoolean(R.bool.config_dozePulsePickup);
}
+ /** {@hide} */
public boolean tapGestureEnabled(int user) {
return boolSettingDefaultOn(Settings.Secure.DOZE_TAP_SCREEN_GESTURE, user)
&& tapSensorAvailable();
}
+ /** {@hide} */
public boolean tapSensorAvailable() {
return !TextUtils.isEmpty(tapSensorType());
}
+ /** {@hide} */
public boolean doubleTapGestureEnabled(int user) {
return boolSettingDefaultOn(Settings.Secure.DOZE_DOUBLE_TAP_GESTURE, user)
&& doubleTapSensorAvailable();
}
+ /** {@hide} */
public boolean doubleTapSensorAvailable() {
return !TextUtils.isEmpty(doubleTapSensorType());
}
+ /** {@hide} */
public boolean wakeScreenGestureAvailable() {
return mContext.getResources()
.getBoolean(R.bool.config_dozeWakeLockScreenSensorAvailable);
}
+ /** {@hide} */
public boolean wakeScreenGestureEnabled(int user) {
return boolSettingDefaultOn(Settings.Secure.DOZE_WAKE_SCREEN_GESTURE, user)
&& wakeScreenGestureAvailable();
}
+ /** {@hide} */
+ public long getWakeLockScreenDebounce() {
+ return mContext.getResources().getInteger(R.integer.config_dozeWakeLockScreenDebounce);
+ }
+
+ /** {@hide} */
public String doubleTapSensorType() {
return mContext.getResources().getString(R.string.config_dozeDoubleTapSensorType);
}
+ /** {@hide} */
public String tapSensorType() {
return mContext.getResources().getString(R.string.config_dozeTapSensorType);
}
+ /** {@hide} */
public String longPressSensorType() {
return mContext.getResources().getString(R.string.config_dozeLongPressSensorType);
}
+ /** {@hide} */
public boolean pulseOnLongPressEnabled(int user) {
return pulseOnLongPressAvailable() && boolSettingDefaultOff(
Settings.Secure.DOZE_PULSE_ON_LONG_PRESS, user);
@@ -107,28 +137,49 @@
return !TextUtils.isEmpty(longPressSensorType());
}
+ /**
+ * Returns if Always-on-Display functionality is enabled on the display for a specified user.
+ *
+ * {@hide}
+ */
+ @TestApi
public boolean alwaysOnEnabled(int user) {
return boolSetting(Settings.Secure.DOZE_ALWAYS_ON, user, mAlwaysOnByDefault ? 1 : 0)
&& alwaysOnAvailable() && !accessibilityInversionEnabled(user);
}
+ /**
+ * Returns if Always-on-Display functionality is available on the display.
+ *
+ * {@hide}
+ */
+ @TestApi
public boolean alwaysOnAvailable() {
return (alwaysOnDisplayDebuggingEnabled() || alwaysOnDisplayAvailable())
&& ambientDisplayAvailable();
}
+ /**
+ * Returns if Always-on-Display functionality is available on the display for a specified user.
+ *
+ * {@hide}
+ */
+ @TestApi
public boolean alwaysOnAvailableForUser(int user) {
return alwaysOnAvailable() && !accessibilityInversionEnabled(user);
}
+ /** {@hide} */
public String ambientDisplayComponent() {
return mContext.getResources().getString(R.string.config_dozeComponent);
}
+ /** {@hide} */
public boolean accessibilityInversionEnabled(int user) {
return boolSettingDefaultOff(Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, user);
}
+ /** {@hide} */
public boolean ambientDisplayAvailable() {
return !TextUtils.isEmpty(ambientDisplayComponent());
}
@@ -141,7 +192,6 @@
return SystemProperties.getBoolean("debug.doze.aod", false) && Build.IS_DEBUGGABLE;
}
-
private boolean boolSettingDefaultOn(String name, int user) {
return boolSetting(name, user, 1);
}
diff --git a/core/java/android/hardware/display/BrightnessConfiguration.java b/core/java/android/hardware/display/BrightnessConfiguration.java
index 8c74ddc..ed8a97c 100644
--- a/core/java/android/hardware/display/BrightnessConfiguration.java
+++ b/core/java/android/hardware/display/BrightnessConfiguration.java
@@ -16,6 +16,7 @@
package android.hardware.display;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
@@ -93,7 +94,7 @@
*
*/
@Nullable
- public BrightnessCorrection getCorrectionByPackageName(String packageName) {
+ public BrightnessCorrection getCorrectionByPackageName(@NonNull String packageName) {
return mCorrectionsByPackageName.get(packageName);
}
@@ -106,7 +107,7 @@
* @return The matching brightness correction, or null.
*/
@Nullable
- public BrightnessCorrection getCorrectionByCategory(int category) {
+ public BrightnessCorrection getCorrectionByCategory(@ApplicationInfo.Category int category) {
return mCorrectionsByCategory.get(category);
}
@@ -238,7 +239,7 @@
*
* @hide
*/
- public void saveToXml(XmlSerializer serializer) throws IOException {
+ public void saveToXml(@NonNull XmlSerializer serializer) throws IOException {
serializer.startTag(null, TAG_BRIGHTNESS_CURVE);
if (mDescription != null) {
serializer.attribute(null, ATTR_DESCRIPTION, mDescription);
@@ -284,7 +285,7 @@
*
* @hide
*/
- public static BrightnessConfiguration loadFromXml(XmlPullParser parser)
+ public static BrightnessConfiguration loadFromXml(@NonNull XmlPullParser parser)
throws IOException, XmlPullParserException {
String description = null;
List<Float> luxList = new ArrayList<>();
@@ -443,8 +444,10 @@
* {@link #getMaxCorrectionsByPackageName}).
*
*/
- public Builder addCorrectionByPackageName(String packageName,
- BrightnessCorrection correction) {
+ public Builder addCorrectionByPackageName(@NonNull String packageName,
+ @NonNull BrightnessCorrection correction) {
+ Objects.requireNonNull(packageName, "packageName must not be null");
+ Objects.requireNonNull(correction, "correction must not be null");
if (mCorrectionsByPackageName.size() >= getMaxCorrectionsByPackageName()) {
throw new IllegalArgumentException("Too many corrections by package name");
}
@@ -470,7 +473,8 @@
*
*/
public Builder addCorrectionByCategory(@ApplicationInfo.Category int category,
- BrightnessCorrection correction) {
+ @NonNull BrightnessCorrection correction) {
+ Objects.requireNonNull(correction, "correction must not be null");
if (mCorrectionsByCategory.size() >= getMaxCorrectionsByCategory()) {
throw new IllegalArgumentException("Too many corrections by category");
}
diff --git a/core/java/android/hardware/display/BrightnessCorrection.java b/core/java/android/hardware/display/BrightnessCorrection.java
index 6a073ff..ee8d846 100644
--- a/core/java/android/hardware/display/BrightnessCorrection.java
+++ b/core/java/android/hardware/display/BrightnessCorrection.java
@@ -16,6 +16,7 @@
package android.hardware.display;
+import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.TestApi;
@@ -59,15 +60,15 @@
/**
* Creates a BrightnessCorrection that given {@code brightness}, corrects it to be
- * {@code exp(scale * log(brightness) + translate)}.
+ * {@code exp(scale * ln(brightness) + translate)}.
*
* @param scale
- * How much to scale the log brightness.
+ * How much to scale the log (base e) brightness.
* @param translate
- * How much to translate the log brightness.
+ * How much to translate the log (base e) brightness.
*
* @return A BrightnessCorrection that given {@code brightness}, corrects it to be
- * {@code exp(scale * log(brightness) + translate)}.
+ * {@code exp(scale * ln(brightness) + translate)}.
*
* @throws IllegalArgumentException
* - scale or translate are NaN.
@@ -87,7 +88,8 @@
*
* @return The corrected brightness.
*/
- public float apply(float brightness) {
+ @FloatRange(from = 0.0)
+ public float apply(@FloatRange(from = 0.0) float brightness) {
return mImplementation.apply(brightness);
}
@@ -202,7 +204,7 @@
/**
* A BrightnessCorrection that given {@code brightness}, corrects it to be
- * {@code exp(scale * log(brightness) + translate)}.
+ * {@code exp(scale * ln(brightness) + translate)}.
*/
private static class ScaleAndTranslateLog implements BrightnessCorrectionImplementation {
private static final float MIN_SCALE = 0.5f;
diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java
index 90dccb5..45860b3 100644
--- a/core/java/android/net/SSLCertificateSocketFactory.java
+++ b/core/java/android/net/SSLCertificateSocketFactory.java
@@ -23,8 +23,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.RoSystemProperties;
-import com.android.org.conscrypt.Conscrypt;
-import com.android.org.conscrypt.OpenSSLContextImpl;
+import com.android.org.conscrypt.ClientSessionContext;
import com.android.org.conscrypt.OpenSSLSocketImpl;
import com.android.org.conscrypt.SSLClientSessionCache;
@@ -33,6 +32,8 @@
import java.net.Socket;
import java.net.SocketException;
import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
@@ -40,6 +41,7 @@
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManager;
+import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
@@ -251,11 +253,12 @@
private SSLSocketFactory makeSocketFactory(
KeyManager[] keyManagers, TrustManager[] trustManagers) {
try {
- OpenSSLContextImpl sslContext = (OpenSSLContextImpl) Conscrypt.newPreferredSSLContextSpi();
- sslContext.engineInit(keyManagers, trustManagers, null);
- sslContext.engineGetClientSessionContext().setPersistentCache(mSessionCache);
- return sslContext.engineGetSocketFactory();
- } catch (KeyManagementException e) {
+ SSLContext sslContext = SSLContext.getInstance("TLS", "AndroidOpenSSL");
+ sslContext.init(keyManagers, trustManagers, null);
+ ((ClientSessionContext) sslContext.getClientSessionContext())
+ .setPersistentCache(mSessionCache);
+ return sslContext.getSocketFactory();
+ } catch (KeyManagementException | NoSuchAlgorithmException | NoSuchProviderException e) {
Log.wtf(TAG, e);
return (SSLSocketFactory) SSLSocketFactory.getDefault(); // Fallback
}
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index 784f233..ebb1ae4 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -19,6 +19,7 @@
import static android.system.OsConstants.AF_INET;
import static android.system.OsConstants.AF_INET6;
+import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
@@ -512,7 +513,7 @@
* Sets an HTTP proxy for the VPN network. This proxy is only a recommendation
* and it is possible that some apps will ignore it.
*/
- public Builder setHttpProxy(ProxyInfo proxyInfo) {
+ public Builder setHttpProxy(@NonNull ProxyInfo proxyInfo) {
mConfig.proxyInfo = proxyInfo;
return this;
}
diff --git a/core/java/android/net/metrics/ValidationProbeEvent.java b/core/java/android/net/metrics/ValidationProbeEvent.java
index 42e8aa6..a43dc60 100644
--- a/core/java/android/net/metrics/ValidationProbeEvent.java
+++ b/core/java/android/net/metrics/ValidationProbeEvent.java
@@ -79,7 +79,7 @@
}
/**
- * Utility to create an instance of {@link ApfProgramEvent}.
+ * Utility to create an instance of {@link ValidationProbeEvent}.
*/
public static class Builder {
private long mDurationMs;
diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java
index 27f7e22..684369a 100644
--- a/core/java/android/os/BugreportManager.java
+++ b/core/java/android/os/BugreportManager.java
@@ -59,7 +59,8 @@
BUGREPORT_ERROR_INVALID_INPUT,
BUGREPORT_ERROR_RUNTIME,
BUGREPORT_ERROR_USER_DENIED_CONSENT,
- BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT
+ BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT,
+ BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS
})
/** Possible error codes taking a bugreport can encounter */
@@ -81,6 +82,10 @@
public static final int BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT =
IDumpstateListener.BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT;
+ /** There is currently a bugreport running. The caller should try again later. */
+ public static final int BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS =
+ IDumpstateListener.BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS;
+
/**
* Called when there is a progress update.
* @param progress the progress in [0.0, 100.0]
@@ -96,6 +101,9 @@
* <p>If {@code BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT} is passed, then the consent timed
* out, but the bugreport could be available in the internal directory of dumpstate for
* manual retrieval.
+ *
+ * <p> If {@code BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS} is passed, then the
+ * caller should try later, as only one bugreport can be in progress at a time.
*/
public void onError(@BugreportErrorCode int errorCode) {}
diff --git a/core/java/android/os/FileObserver.java b/core/java/android/os/FileObserver.java
index da03895..330bde5 100644
--- a/core/java/android/os/FileObserver.java
+++ b/core/java/android/os/FileObserver.java
@@ -16,11 +16,14 @@
package android.os;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.Log;
import java.io.File;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.HashMap;
@@ -45,6 +48,24 @@
* keep a reference to the FileObserver instance from some other live object.</p>
*/
public abstract class FileObserver {
+ /** @hide */
+ @IntDef(flag = true, value = {
+ ACCESS,
+ MODIFY,
+ ATTRIB,
+ CLOSE_WRITE,
+ CLOSE_NOWRITE,
+ OPEN,
+ MOVED_FROM,
+ MOVED_TO,
+ CREATE,
+ DELETE,
+ DELETE_SELF,
+ MOVE_SELF
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface NotifyEventType {}
+
/** Event type: Data was read from a file */
public static final int ACCESS = 0x00000001;
/** Event type: Data was written to a file */
@@ -71,6 +92,7 @@
public static final int MOVE_SELF = 0x00000800;
/** Event mask: All valid event types, combined */
+ @NotifyEventType
public static final int ALL_EVENTS = ACCESS | MODIFY | ATTRIB | CLOSE_WRITE
| CLOSE_NOWRITE | OPEN | MOVED_FROM | MOVED_TO | DELETE | CREATE
| DELETE_SELF | MOVE_SELF;
@@ -90,7 +112,8 @@
observe(m_fd);
}
- public int[] startWatching(List<File> files, int mask, FileObserver observer) {
+ public int[] startWatching(List<File> files,
+ @NotifyEventType int mask, FileObserver observer) {
final int count = files.size();
final String[] paths = new String[count];
for (int i = 0; i < count; ++i) {
@@ -118,7 +141,7 @@
stopWatching(m_fd, descriptors);
}
- public void onEvent(int wfd, int mask, String path) {
+ public void onEvent(int wfd, @NotifyEventType int mask, String path) {
// look up our observer, fixing up the map if necessary...
FileObserver observer = null;
@@ -144,7 +167,8 @@
private native int init();
private native void observe(int fd);
- private native void startWatching(int fd, String[] paths, int mask, int[] wfds);
+ private native void startWatching(int fd, String[] paths,
+ @NotifyEventType int mask, int[] wfds);
private native void stopWatching(int fd, int[] wfds);
}
@@ -197,7 +221,7 @@
* @deprecated use {@link #FileObserver(File, int)} instead.
*/
@Deprecated
- public FileObserver(String path, int mask) {
+ public FileObserver(String path, @NotifyEventType int mask) {
this(new File(path), mask);
}
@@ -209,7 +233,7 @@
* @param file The file or directory to monitor
* @param mask The event or events (added together) to watch for
*/
- public FileObserver(@NonNull File file, int mask) {
+ public FileObserver(@NonNull File file, @NotifyEventType int mask) {
this(Arrays.asList(file), mask);
}
@@ -220,7 +244,7 @@
* @param files The files or directories to monitor
* @param mask The event or events (added together) to watch for
*/
- public FileObserver(@NonNull List<File> files, int mask) {
+ public FileObserver(@NonNull List<File> files, @NotifyEventType int mask) {
mFiles = files;
mMask = mask;
}
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 6bd1f2b..91ddf82 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -1569,7 +1569,7 @@
@LocationPowerSaveMode
public int getLocationPowerSaveMode() {
final PowerSaveState powerSaveState = getPowerSaveState(ServiceType.LOCATION);
- if (!powerSaveState.globalBatterySaverEnabled) {
+ if (!powerSaveState.batterySaverEnabled) {
return LOCATION_MODE_NO_CHANGE;
}
return powerSaveState.locationMode;
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 9e97e37..cd43b42 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -505,7 +505,6 @@
* @param invokeWith null-ok the command to invoke with.
* @param packageName null-ok the name of the package this process belongs to.
* @param packagesForUid null-ok all the packages with the same uid as this process.
- * @param visibleVols null-ok storage volumes that can be accessed by this process.
* @param zygoteArgs Additional arguments to supply to the zygote process.
*
* @return An object that describes the result of the attempt to start the process.
@@ -525,13 +524,12 @@
@Nullable String invokeWith,
@Nullable String packageName,
@Nullable String[] packagesForUid,
- @Nullable String[] visibleVols,
@Nullable String sandboxId,
@Nullable String[] zygoteArgs) {
return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, packageName,
- packagesForUid, visibleVols, sandboxId, /*useBlastulaPool=*/ true, zygoteArgs);
+ packagesForUid, sandboxId, /*useBlastulaPool=*/ true, zygoteArgs);
}
/** @hide */
@@ -547,13 +545,12 @@
@Nullable String invokeWith,
@Nullable String packageName,
@Nullable String[] packagesForUid,
- @Nullable String[] visibleVols,
@Nullable String sandboxId,
@Nullable String[] zygoteArgs) {
return WebViewZygote.getProcess().start(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, packageName,
- packagesForUid, visibleVols, sandboxId, /*useBlastulaPool=*/ false, zygoteArgs);
+ packagesForUid, sandboxId, /*useBlastulaPool=*/ false, zygoteArgs);
}
/**
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index de378b0..e49b65e 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -27,7 +27,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.Zygote;
-import com.android.internal.util.Preconditions;
import java.io.BufferedWriter;
import java.io.DataInputStream;
@@ -122,8 +121,9 @@
new LocalSocketAddress(Zygote.BLASTULA_POOL_SECONDARY_SOCKET_NAME,
LocalSocketAddress.Namespace.RESERVED);
- // TODO (chriswailes): Uncomment when the blastula pool can be enabled.
-// fetchBlastulaPoolEnabledProp();
+ if (fetchBlastulaPoolEnabledProp()) {
+ informZygotesOfBlastulaPoolStatus();
+ }
}
public ZygoteProcess(LocalSocketAddress primarySocketAddress,
@@ -305,7 +305,6 @@
* @param invokeWith null-ok the command to invoke with.
* @param packageName null-ok the name of the package this process belongs to.
* @param packagesForUid null-ok all the packages with the same uid as this process.
- * @param visibleVols null-ok storage volumes that can be accessed by this process.
* @param zygoteArgs Additional arguments to supply to the zygote process.
*
* @return An object that describes the result of the attempt to start the process.
@@ -323,23 +322,19 @@
@Nullable String invokeWith,
@Nullable String packageName,
@Nullable String[] packagesForUid,
- @Nullable String[] visibleVols,
@Nullable String sandboxId,
boolean useBlastulaPool,
@Nullable String[] zygoteArgs) {
- if (fetchBlastulaPoolEnabledProp()) {
- // TODO (chriswailes): Send the appropriate command to the zygotes
- Log.i(LOG_TAG, "Blastula pool enabled property set to: " + mBlastulaPoolEnabled);
-
- // This can't be enabled yet, but we do want to test this code path.
- mBlastulaPoolEnabled = false;
+ // TODO (chriswailes): Is there a better place to check this value?
+ if (fetchBlastulaPoolEnabledPropWithMinInterval()) {
+ informZygotesOfBlastulaPoolStatus();
}
try {
return startViaZygote(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, /*startChildZygote=*/false,
- packageName, packagesForUid, visibleVols, sandboxId,
+ packageName, packagesForUid, sandboxId,
useBlastulaPool, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
@@ -387,7 +382,7 @@
* @throws ZygoteStartFailedEx if process start failed for any reason
*/
@GuardedBy("mLock")
- private static Process.ProcessStartResult zygoteSendArgsAndGetResult(
+ private Process.ProcessStartResult zygoteSendArgsAndGetResult(
ZygoteState zygoteState, boolean useBlastulaPool, ArrayList<String> args)
throws ZygoteStartFailedEx {
// Throw early if any of the arguments are malformed. This means we can
@@ -415,7 +410,7 @@
Process.ProcessStartResult result = new Process.ProcessStartResult();
// TODO (chriswailes): Move branch body into separate function.
- if (useBlastulaPool && isValidBlastulaCommand(args)) {
+ if (useBlastulaPool && mBlastulaPoolEnabled && isValidBlastulaCommand(args)) {
LocalSocket blastulaSessionSocket = null;
try {
@@ -444,7 +439,7 @@
// If there was an IOException using the blastula pool we will log the error and
// attempt to start the process through the Zygote.
Log.e(LOG_TAG, "IO Exception while communicating with blastula pool - "
- + ex.toString());
+ + ex.getMessage());
} finally {
try {
blastulaSessionSocket.close();
@@ -531,7 +526,6 @@
* that has its state cloned from this zygote process.
* @param packageName null-ok the name of the package this process belongs to.
* @param packagesForUid null-ok all the packages with the same uid as this process.
- * @param visibleVols null-ok storage volumes that can be accessed by this process.
* @param extraArgs Additional arguments to supply to the zygote process.
* @return An object that describes the result of the attempt to start the process.
* @throws ZygoteStartFailedEx if process start failed for any reason
@@ -550,7 +544,6 @@
boolean startChildZygote,
@Nullable String packageName,
@Nullable String[] packagesForUid,
- @Nullable String[] visibleVols,
@Nullable String sandboxId,
boolean useBlastulaPool,
@Nullable String[] extraArgs)
@@ -638,19 +631,6 @@
argsForZygote.add(sb.toString());
}
- if (visibleVols != null && visibleVols.length > 0) {
- final StringBuilder sb = new StringBuilder();
- sb.append("--visible-vols=");
-
- for (int i = 0; i < visibleVols.length; ++i) {
- if (i != 0) {
- sb.append(',');
- }
- sb.append(visibleVols[i]);
- }
- argsForZygote.add(sb.toString());
- }
-
if (sandboxId != null) {
argsForZygote.add("--sandbox-id=" + sandboxId);
}
@@ -665,7 +645,7 @@
synchronized(mLock) {
return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi),
- useBlastulaPool && mBlastulaPoolEnabled,
+ useBlastulaPool,
argsForZygote);
}
}
@@ -685,6 +665,10 @@
Boolean.parseBoolean(BLASTULA_POOL_ENABLED_DEFAULT));
}
+ if (origVal != mBlastulaPoolEnabled) {
+ Log.i(LOG_TAG, "blastulaPoolEnabled = " + mBlastulaPoolEnabled);
+ }
+
return origVal != mBlastulaPoolEnabled;
}
@@ -847,49 +831,57 @@
}
/**
+ * Creates a ZygoteState for the primary zygote if it doesn't exist or has been disconnected.
+ */
+ @GuardedBy("mLock")
+ private void attemptConnectionToPrimaryZygote() throws IOException {
+ if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
+ primaryZygoteState =
+ ZygoteState.connect(mZygoteSocketAddress, mBlastulaPoolSocketAddress);
+
+ maybeSetApiBlacklistExemptions(primaryZygoteState, false);
+ maybeSetHiddenApiAccessLogSampleRate(primaryZygoteState);
+ }
+ }
+
+ /**
+ * Creates a ZygoteState for the secondary zygote if it doesn't exist or has been disconnected.
+ */
+ @GuardedBy("mLock")
+ private void attemptConnectionToSecondaryZygote() throws IOException {
+ if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
+ secondaryZygoteState =
+ ZygoteState.connect(mZygoteSecondarySocketAddress,
+ mBlastulaPoolSecondarySocketAddress);
+
+ maybeSetApiBlacklistExemptions(secondaryZygoteState, false);
+ maybeSetHiddenApiAccessLogSampleRate(secondaryZygoteState);
+ }
+ }
+
+ /**
* Tries to open a session socket to a Zygote process with a compatible ABI if one is not
* already open. If a compatible session socket is already open that session socket is returned.
* This function may block and may have to try connecting to multiple Zygotes to find the
* appropriate one. Requires that mLock be held.
*/
@GuardedBy("mLock")
- private ZygoteState openZygoteSocketIfNeeded(String abi)
- throws ZygoteStartFailedEx {
+ private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
+ try {
+ attemptConnectionToPrimaryZygote();
- Preconditions.checkState(Thread.holdsLock(mLock), "ZygoteProcess lock not held");
-
- if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
- try {
- primaryZygoteState =
- ZygoteState.connect(mZygoteSocketAddress, mBlastulaPoolSocketAddress);
- } catch (IOException ioe) {
- throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
+ if (primaryZygoteState.matches(abi)) {
+ return primaryZygoteState;
}
- maybeSetApiBlacklistExemptions(primaryZygoteState, false);
- maybeSetHiddenApiAccessLogSampleRate(primaryZygoteState);
- }
+ // The primary zygote didn't match. Try the secondary.
+ attemptConnectionToSecondaryZygote();
- if (primaryZygoteState.matches(abi)) {
- return primaryZygoteState;
- }
-
- // The primary zygote didn't match. Try the secondary.
- if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
- try {
- secondaryZygoteState =
- ZygoteState.connect(mZygoteSecondarySocketAddress,
- mBlastulaPoolSecondarySocketAddress);
- } catch (IOException ioe) {
- throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);
+ if (secondaryZygoteState.matches(abi)) {
+ return secondaryZygoteState;
}
-
- maybeSetApiBlacklistExemptions(secondaryZygoteState, false);
- maybeSetHiddenApiAccessLogSampleRate(secondaryZygoteState);
- }
-
- if (secondaryZygoteState.matches(abi)) {
- return secondaryZygoteState;
+ } catch (IOException ioe) {
+ throw new ZygoteStartFailedEx("Error connecting to zygote", ioe);
}
throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi);
@@ -1015,6 +1007,57 @@
}
/**
+ * Sends messages to the zygotes telling them to change the status of their blastula pools. If
+ * this notification fails the ZygoteProcess will fall back to the previous behavior.
+ */
+ private void informZygotesOfBlastulaPoolStatus() {
+ final String command = "1\n--blastula-pool-enabled=" + mBlastulaPoolEnabled + "\n";
+
+ synchronized (mLock) {
+ try {
+ attemptConnectionToPrimaryZygote();
+
+ primaryZygoteState.mZygoteOutputWriter.write(command);
+ primaryZygoteState.mZygoteOutputWriter.flush();
+ } catch (IOException ioe) {
+ mBlastulaPoolEnabled = !mBlastulaPoolEnabled;
+ Log.w(LOG_TAG, "Failed to inform zygotes of blastula pool status: "
+ + ioe.getMessage());
+ return;
+ }
+
+ try {
+ attemptConnectionToSecondaryZygote();
+
+ try {
+ secondaryZygoteState.mZygoteOutputWriter.write(command);
+ secondaryZygoteState.mZygoteOutputWriter.flush();
+
+ // Wait for the secondary Zygote to finish its work.
+ secondaryZygoteState.mZygoteInputStream.readInt();
+ } catch (IOException ioe) {
+ throw new IllegalStateException(
+ "Blastula pool state change cause an irrecoverable error",
+ ioe);
+ }
+ } catch (IOException ioe) {
+ // No secondary zygote present. This is expected on some devices.
+ }
+
+ // Wait for the response from the primary zygote here so the primary/secondary zygotes
+ // can work concurrently.
+ try {
+ // Wait for the primary zygote to finish its work.
+ primaryZygoteState.mZygoteInputStream.readInt();
+ } catch (IOException ioe) {
+ throw new IllegalStateException(
+ "Blastula pool state change cause an irrecoverable error",
+ ioe);
+ }
+ }
+ }
+
+ /**
* Starts a new zygote process as a child of this zygote. This is used to create
* secondary zygotes that inherit data from the zygote that this object
* communicates with. This returns a new ZygoteProcess representing a connection
@@ -1061,7 +1104,7 @@
gids, runtimeFlags, 0 /* mountExternal */, 0 /* targetSdkVersion */, seInfo,
abi, instructionSet, null /* appDataDir */, null /* invokeWith */,
true /* startChildZygote */, null /* packageName */,
- null /* packagesForUid */, null /* visibleVolumes */, null /* sandboxId */,
+ null /* packagesForUid */, null /* sandboxId */,
false /* useBlastulaPool */, extraArgs);
} catch (ZygoteStartFailedEx ex) {
throw new RuntimeException("Starting child-zygote through Zygote failed", ex);
diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java
index 03b2c2c..f1c3138 100644
--- a/core/java/android/os/storage/StorageManagerInternal.java
+++ b/core/java/android/os/storage/StorageManagerInternal.java
@@ -109,11 +109,6 @@
@Nullable String sharedUserId, int userId);
/**
- * @return Labels of storage volumes that are visible to the given userId.
- */
- public abstract String[] getVisibleVolumesForUser(int userId);
-
- /**
* A listener for reset events in the StorageManagerService.
*/
public interface ResetListener {
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 868a36b..ce28c30 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -197,6 +197,7 @@
*/
@SystemApi
public interface MediaNative {
+ /** The flag namespace for media native features. */
String NAMESPACE = "media_native";
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1588718..0d7e00f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7483,6 +7483,7 @@
* @hide
*/
@SystemApi
+ @TestApi
public static final String DOZE_ALWAYS_ON = "doze_always_on";
private static final Validator DOZE_ALWAYS_ON_VALIDATOR = BOOLEAN_VALIDATOR;
@@ -9044,7 +9045,6 @@
* Whether applying ramping ringer on incoming phone call ringtone.
* <p>1 = apply ramping ringer
* <p>0 = do not apply ramping ringer
- * @hide
*/
public static final String APPLY_RAMPING_RINGER = "apply_ramping_ringer";
@@ -11417,7 +11417,7 @@
/**
* Feature flag to enable or disable the activity starts logging feature.
* Type: int (0 for false, 1 for true)
- * Default: 0
+ * Default: 1
* @hide
*/
public static final String ACTIVITY_STARTS_LOGGING_ENABLED
@@ -11523,6 +11523,7 @@
* @hide
* @see com.android.server.power.batterysaver.BatterySaverPolicy
*/
+ @TestApi
public static final String BATTERY_SAVER_CONSTANTS = "battery_saver_constants";
/**
@@ -11946,6 +11947,7 @@
* bcast_deferral (long)
* bcast_deferral_decay_factor (float)
* bcast_deferral_floor (long)
+ * bcast_allow_bg_activity_start_timeout (long)
* </pre>
*
* @hide
@@ -14199,10 +14201,24 @@
* Configuration flags for SQLite Compatibility WAL. Encoded as a key-value list, separated
* by commas. E.g.: compatibility_wal_supported=true, wal_syncmode=OFF
*
- * Supported keys:
- * compatibility_wal_supported (boolean)
- * wal_syncmode (String)
- * truncate_size (int)
+ * Supported keys:<br/>
+ * <li>
+ * <ul> {@code legacy_compatibility_wal_enabled} : A {code boolean} flag that determines
+ * whether or not "compatibility WAL" mode is enabled by default. This is a legacy flag
+ * and is honoured on Android Q and higher. This flag will be removed in a future release.
+ * </ul>
+ * <ul> {@code wal_syncmode} : A {@code String} representing the synchronization mode to use
+ * when WAL is enabled, either via {@code legacy_compatibility_wal_enabled} or using the
+ * obsolete {@code compatibility_wal_supported} flag.
+ * </ul>
+ * <ul> {@code truncate_size} : A {@code int} flag that specifies the truncate size of the
+ * WAL journal.
+ * </ul>
+ * <ul> {@code compatibility_wal_supported} : A {code boolean} flag that specifies whether
+ * the legacy "compatibility WAL" mode is enabled by default. This flag is obsolete and is
+ * only supported on Android Pie.
+ * </ul>
+ * </li>
*
* @hide
*/
diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java
index 1d0c987..cbd0cd9 100644
--- a/core/java/android/service/autofill/FillRequest.java
+++ b/core/java/android/service/autofill/FillRequest.java
@@ -71,6 +71,14 @@
*/
public static final int FLAG_COMPATIBILITY_MODE_REQUEST = 0x2;
+ // Private flags below start from the highest-significative bit (0x80000000)
+ /**
+ * Request was only triggered for augmented autofill.
+ *
+ * @hide
+ */
+ public static final int FLAG_AUGMENTED_AUTOFILL_REQUEST = 0x80000000;
+
/** @hide */
public static final int INVALID_REQUEST_ID = Integer.MIN_VALUE;
@@ -115,7 +123,8 @@
/**
* Gets the flags associated with this request.
*
- * @see #FLAG_MANUAL_REQUEST
+ * @return any combination of {@link #FLAG_MANUAL_REQUEST} and
+ * {@link #FLAG_COMPATIBILITY_MODE_REQUEST}.
*/
public @RequestFlags int getFlags() {
return mFlags;
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index 792eda7..463eae68 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -209,7 +209,7 @@
} else {
// TODO(b/123099468): figure out if it's ok to reuse the proxy; add logging
if (DEBUG) Log.d(TAG, "Reusing proxy for session " + sessionId);
- proxy.update(focusedId, focusedValue);
+ proxy.update(focusedId, focusedValue, callback);
}
// TODO(b/123101711): set cancellation signal
final CancellationSignal cancellationSignal = null;
@@ -299,13 +299,14 @@
private final Object mLock = new Object();
private final IAugmentedAutofillManagerClient mClient;
private final int mSessionId;
- private final IFillCallback mCallback;
public final int taskId;
public final ComponentName componentName;
@GuardedBy("mLock")
private AutofillId mFocusedId;
@GuardedBy("mLock")
private AutofillValue mFocusedValue;
+ @GuardedBy("mLock")
+ private IFillCallback mCallback;
/**
* Id of the last field that cause the Autofill UI to be shown.
@@ -316,8 +317,8 @@
private AutofillId mLastShownId;
// Objects used to log metrics
- private final long mRequestTime;
- private long mOnSuccessTime;
+ private final long mFirstRequestTime;
+ private long mFirstOnSuccessTime;
private long mUiFirstShownTime;
private long mUiFirstDestroyedTime;
@@ -338,7 +339,7 @@
this.componentName = componentName;
this.mFocusedId = focusedId;
this.mFocusedValue = focusedValue;
- this.mRequestTime = requestTime;
+ this.mFirstRequestTime = requestTime;
// TODO(b/123099468): linkToDeath
}
@@ -400,11 +401,18 @@
mClient.requestHideFillUi(mSessionId, mFocusedId);
}
- private void update(@NonNull AutofillId focusedId, @NonNull AutofillValue focusedValue) {
+ private void update(@NonNull AutofillId focusedId, @NonNull AutofillValue focusedValue,
+ @NonNull IFillCallback callback) {
synchronized (mLock) {
// TODO(b/123099468): should we close the popupwindow if the focused id changed?
mFocusedId = focusedId;
mFocusedValue = focusedValue;
+ if (mCallback != null) {
+ // TODO(b/123101711): we need to check whether the previous request was
+ // completed or not, and if not, cancel it first.
+ Slog.d(TAG, "mCallback is updated.");
+ }
+ mCallback = callback;
}
}
@@ -426,11 +434,11 @@
public void report(@ReportEvent int event) {
switch (event) {
case REPORT_EVENT_ON_SUCCESS:
- if (mOnSuccessTime == 0) {
- mOnSuccessTime = SystemClock.elapsedRealtime();
+ if (mFirstOnSuccessTime == 0) {
+ mFirstOnSuccessTime = SystemClock.elapsedRealtime();
if (DEBUG) {
- Slog.d(TAG, "Service responsed in "
- + TimeUtils.formatDuration(mOnSuccessTime - mRequestTime));
+ Slog.d(TAG, "Service responded in " + TimeUtils.formatDuration(
+ mFirstOnSuccessTime - mFirstRequestTime));
}
}
try {
@@ -443,8 +451,8 @@
if (mUiFirstShownTime == 0) {
mUiFirstShownTime = SystemClock.elapsedRealtime();
if (DEBUG) {
- Slog.d(TAG, "UI shown in "
- + TimeUtils.formatDuration(mUiFirstShownTime - mRequestTime));
+ Slog.d(TAG, "UI shown in " + TimeUtils.formatDuration(
+ mUiFirstShownTime - mFirstRequestTime));
}
}
break;
@@ -452,9 +460,8 @@
if (mUiFirstDestroyedTime == 0) {
mUiFirstDestroyedTime = SystemClock.elapsedRealtime();
if (DEBUG) {
- Slog.d(TAG, "UI destroyed in "
- + TimeUtils.formatDuration(
- mUiFirstDestroyedTime - mRequestTime));
+ Slog.d(TAG, "UI destroyed in " + TimeUtils.formatDuration(
+ mUiFirstDestroyedTime - mFirstRequestTime));
}
}
break;
@@ -486,20 +493,20 @@
pw.print(prefix); pw.println("smartSuggestion:");
mSmartSuggestion.dump(prefix2, pw);
}
- if (mOnSuccessTime > 0) {
- final long responseTime = mOnSuccessTime - mRequestTime;
+ if (mFirstOnSuccessTime > 0) {
+ final long responseTime = mFirstOnSuccessTime - mFirstRequestTime;
pw.print(prefix); pw.print("response time: ");
TimeUtils.formatDuration(responseTime, pw); pw.println();
}
if (mUiFirstShownTime > 0) {
- final long uiRenderingTime = mUiFirstShownTime - mRequestTime;
+ final long uiRenderingTime = mUiFirstShownTime - mFirstRequestTime;
pw.print(prefix); pw.print("UI rendering time: ");
TimeUtils.formatDuration(uiRenderingTime, pw); pw.println();
}
if (mUiFirstDestroyedTime > 0) {
- final long uiTotalTime = mUiFirstDestroyedTime - mRequestTime;
+ final long uiTotalTime = mUiFirstDestroyedTime - mFirstRequestTime;
pw.print(prefix); pw.print("UI life time: ");
TimeUtils.formatDuration(uiTotalTime, pw); pw.println();
}
@@ -510,6 +517,7 @@
if (mFillWindow != null) {
if (DEBUG) Log.d(TAG, "destroying window");
mFillWindow.destroy();
+ mFillWindow = null;
}
}
}
diff --git a/core/java/android/service/autofill/augmented/FillWindow.java b/core/java/android/service/autofill/augmented/FillWindow.java
index 6e06754..bd532a3 100644
--- a/core/java/android/service/autofill/augmented/FillWindow.java
+++ b/core/java/android/service/autofill/augmented/FillWindow.java
@@ -82,6 +82,8 @@
private Rect mBounds;
@GuardedBy("mLock")
+ private boolean mUpdateCalled;
+ @GuardedBy("mLock")
private boolean mDestroyed;
private AutofillProxy mProxy;
@@ -103,6 +105,7 @@
}
// TODO(b/123100712): add test case for null
Preconditions.checkNotNull(area);
+ Preconditions.checkNotNull(area.proxy);
Preconditions.checkNotNull(rootView);
// TODO(b/123100712): must check the area is a valid object returned by
// SmartSuggestionParams, throw IAE if not
@@ -149,6 +152,7 @@
if (DEBUG) {
Log.d(TAG, "Created FillWindow: params= " + smartSuggestion + " view=" + rootView);
}
+ mUpdateCalled = true;
mDestroyed = false;
mProxy.setFillWindow(this);
return true;
@@ -237,8 +241,10 @@
}
synchronized (mLock) {
if (mDestroyed) return;
- hide();
- mProxy.report(AutofillProxy.REPORT_EVENT_UI_DESTROYED);
+ if (mUpdateCalled) {
+ hide();
+ mProxy.report(AutofillProxy.REPORT_EVENT_UI_DESTROYED);
+ }
mDestroyed = true;
mCloseGuard.close();
}
@@ -266,6 +272,7 @@
public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
synchronized (this) {
pw.print(prefix); pw.print("destroyed: "); pw.println(mDestroyed);
+ pw.print(prefix); pw.print("updateCalled: "); pw.println(mUpdateCalled);
if (mFillView != null) {
pw.print(prefix); pw.print("fill window: ");
pw.println(mShowing ? "shown" : "hidden");
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 1d89628..140c34f 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -51,7 +51,7 @@
DEFAULT_FLAGS.put("settings_slice_injection", "true");
DEFAULT_FLAGS.put("settings_systemui_theme", "true");
DEFAULT_FLAGS.put("settings_wifi_mac_randomization", "true");
- DEFAULT_FLAGS.put("settings_mainline_module", "false");
+ DEFAULT_FLAGS.put("settings_mainline_module", "true");
DEFAULT_FLAGS.put("settings_dynamic_android", "false");
DEFAULT_FLAGS.put(SEAMLESS_TRANSFER, "false");
DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false");
diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java
index 3e8002f..60daddd 100644
--- a/core/java/android/view/DisplayEventReceiver.java
+++ b/core/java/android/view/DisplayEventReceiver.java
@@ -156,6 +156,17 @@
}
/**
+ * Called when a display config changed event is received.
+ *
+ * @param timestampNanos The timestamp of the event, in the {@link System#nanoTime()}
+ * timebase.
+ * @param physicalDisplayId Stable display ID that uniquely describes a (display, port) pair.
+ * @param configId The new config Id
+ */
+ public void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId) {
+ }
+
+ /**
* Schedules a single vertical sync pulse to be delivered when the next
* display frame begins.
*/
@@ -182,4 +193,11 @@
private void dispatchHotplug(long timestampNanos, long physicalDisplayId, boolean connected) {
onHotplug(timestampNanos, physicalDisplayId, connected);
}
+
+ // Called from native code.
+ @SuppressWarnings("unused")
+ private void dispatchConfigChanged(long timestampNanos, long physicalDisplayId, int configId) {
+ onConfigChanged(timestampNanos, physicalDisplayId, configId);
+ }
+
}
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index dd88e3c..50ef91f 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -20,6 +20,7 @@
import static android.view.InsetsState.INSET_SIDE_LEFT;
import static android.view.InsetsState.INSET_SIDE_RIGHT;
import static android.view.InsetsState.INSET_SIDE_TOP;
+import static android.view.InsetsState.toPublicType;
import android.annotation.Nullable;
import android.graphics.Insets;
@@ -33,6 +34,7 @@
import android.view.InsetsState.InsetSide;
import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
import android.view.WindowInsets.Type.InsetType;
+import android.view.WindowInsetsAnimationListener.InsetsAnimation;
import android.view.WindowManager.LayoutParams;
import com.android.internal.annotations.VisibleForTesting;
@@ -66,8 +68,12 @@
private final Supplier<SyncRtSurfaceTransactionApplier> mTransactionApplierSupplier;
private final InsetsController mController;
private final WindowInsetsAnimationListener.InsetsAnimation mAnimation;
+ private final Rect mFrame;
private Insets mCurrentInsets;
private Insets mPendingInsets;
+ private boolean mFinished;
+ private boolean mCancelled;
+ private int mFinishedShownTypes;
@VisibleForTesting
public InsetsAnimationControlImpl(SparseArray<InsetsSourceConsumer> consumers, Rect frame,
@@ -86,6 +92,7 @@
null /* typeSideMap */);
mShownInsets = calculateInsets(mInitialInsetsState, frame, consumers, true /* shown */,
mTypeSideMap);
+ mFrame = new Rect(frame);
buildTypeSourcesMap(mTypeSideMap, mSideSourceMap, mConsumers);
// TODO: Check for controllability first and wait for IME if needed.
@@ -119,12 +126,26 @@
@Override
public void changeInsets(Insets insets) {
+ if (mFinished) {
+ throw new IllegalStateException(
+ "Can't change insets on an animation that is finished.");
+ }
+ if (mCancelled) {
+ throw new IllegalStateException(
+ "Can't change insets on an animation that is cancelled.");
+ }
mPendingInsets = sanitize(insets);
mController.scheduleApplyChangeInsets();
}
@VisibleForTesting
- public void applyChangeInsets(InsetsState state) {
+ /**
+ * @return Whether the finish callback of this animation should be invoked.
+ */
+ public boolean applyChangeInsets(InsetsState state) {
+ if (mCancelled) {
+ return false;
+ }
final Insets offset = Insets.subtract(mShownInsets, mPendingInsets);
ArrayList<SurfaceParams> params = new ArrayList<>();
if (offset.left != 0) {
@@ -144,13 +165,40 @@
SyncRtSurfaceTransactionApplier applier = mTransactionApplierSupplier.get();
applier.scheduleApply(params.toArray(new SurfaceParams[params.size()]));
mCurrentInsets = mPendingInsets;
+ if (mFinished) {
+ mController.notifyFinished(this, mFinishedShownTypes);
+ }
+ return mFinished;
}
@Override
public void finish(int shownTypes) {
- // TODO
+ if (mCancelled) {
+ return;
+ }
+ InsetsState state = new InsetsState(mController.getState());
+ for (int i = mConsumers.size() - 1; i >= 0; i--) {
+ InsetsSourceConsumer consumer = mConsumers.valueAt(i);
+ boolean visible = (shownTypes & toPublicType(consumer.getType())) != 0;
+ state.getSource(consumer.getType()).setVisible(visible);
+ }
+ Insets insets = getInsetsFromState(state, mFrame, null /* typeSideMap */);
+ changeInsets(insets);
+ mFinished = true;
+ mFinishedShownTypes = shownTypes;
+ }
- mController.dispatchAnimationFinished(mAnimation);
+ @VisibleForTesting
+ public void onCancelled() {
+ if (mFinished) {
+ return;
+ }
+ mCancelled = true;
+ mListener.onCancelled();
+ }
+
+ InsetsAnimation getAnimation() {
+ return mAnimation;
}
private Insets calculateInsets(InsetsState state, Rect frame,
@@ -225,4 +273,3 @@
}
}
}
-
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 2586000..08f2e8d 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -17,6 +17,8 @@
package android.view;
import static android.view.InsetsState.TYPE_IME;
+import static android.view.InsetsState.toPublicType;
+import static android.view.WindowInsets.Type.all;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -99,6 +101,7 @@
private final SparseArray<InsetsSourceControl> mTmpControlArray = new SparseArray<>();
private final ArrayList<InsetsAnimationControlImpl> mAnimationControls = new ArrayList<>();
+ private final ArrayList<InsetsAnimationControlImpl> mTmpFinishedControls = new ArrayList<>();
private WindowInsets mLastInsets;
private boolean mAnimCallbackScheduled;
@@ -107,7 +110,6 @@
private final Rect mLastLegacyContentInsets = new Rect();
private final Rect mLastLegacyStableInsets = new Rect();
- private ObjectAnimator mAnimator;
private @AnimationDirection int mAnimationDirection;
private int mPendingTypesToShow;
@@ -122,19 +124,29 @@
return;
}
+ mTmpFinishedControls.clear();
InsetsState state = new InsetsState(mState, true /* copySources */);
for (int i = mAnimationControls.size() - 1; i >= 0; i--) {
- mAnimationControls.get(i).applyChangeInsets(state);
+ InsetsAnimationControlImpl control = mAnimationControls.get(i);
+ if (mAnimationControls.get(i).applyChangeInsets(state)) {
+ mTmpFinishedControls.add(control);
+ }
}
+
WindowInsets insets = state.calculateInsets(mFrame, mLastInsets.isRound(),
mLastInsets.shouldAlwaysConsumeNavBar(), mLastInsets.getDisplayCutout(),
mLastLegacyContentInsets, mLastLegacyStableInsets, mLastLegacySoftInputMode,
null /* typeSideMap */);
mViewRoot.mView.dispatchWindowInsetsAnimationProgress(insets);
+
+ for (int i = mTmpFinishedControls.size() - 1; i >= 0; i--) {
+ dispatchAnimationFinished(mTmpFinishedControls.get(i).getAnimation());
+ }
};
}
- void onFrameChanged(Rect frame) {
+ @VisibleForTesting
+ public void onFrameChanged(Rect frame) {
if (mFrame.equals(frame)) {
return;
}
@@ -279,7 +291,8 @@
// nothing to animate.
return;
}
- // TODO: Check whether we already have a controller.
+ cancelExistingControllers(types);
+
final ArraySet<Integer> internalTypes = mState.toInternalType(types);
final SparseArray<InsetsSourceConsumer> consumers = new SparseArray<>();
@@ -321,7 +334,7 @@
// Show request
switch(consumer.requestShow(fromIme)) {
case ShowResult.SHOW_IMMEDIATELY:
- typesReady |= InsetsState.toPublicType(TYPE_IME);
+ typesReady |= InsetsState.toPublicType(consumer.getType());
break;
case ShowResult.SHOW_DELAYED:
isReady = false;
@@ -365,6 +378,36 @@
return typesReady;
}
+ private void cancelExistingControllers(@InsetType int types) {
+ for (int i = mAnimationControls.size() - 1; i >= 0; i--) {
+ InsetsAnimationControlImpl control = mAnimationControls.get(i);
+ if ((control.getTypes() & types) != 0) {
+ cancelAnimation(control);
+ }
+ }
+ }
+
+ @VisibleForTesting
+ public void notifyFinished(InsetsAnimationControlImpl controller, int shownTypes) {
+ mAnimationControls.remove(controller);
+ hideDirectly(controller.getTypes() & ~shownTypes);
+ showDirectly(controller.getTypes() & shownTypes);
+ }
+
+ void notifyControlRevoked(InsetsSourceConsumer consumer) {
+ for (int i = mAnimationControls.size() - 1; i >= 0; i--) {
+ InsetsAnimationControlImpl control = mAnimationControls.get(i);
+ if ((control.getTypes() & toPublicType(consumer.getType())) != 0) {
+ cancelAnimation(control);
+ }
+ }
+ }
+
+ private void cancelAnimation(InsetsAnimationControlImpl control) {
+ control.onCancelled();
+ mAnimationControls.remove(control);
+ }
+
private void applyLocalVisibilityOverride() {
for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
final InsetsSourceConsumer controller = mSourceConsumers.valueAt(i);
@@ -455,8 +498,13 @@
}
WindowInsetsAnimationControlListener listener = new WindowInsetsAnimationControlListener() {
+
+ private WindowInsetsAnimationController mController;
+ private ObjectAnimator mAnimator;
+
@Override
public void onReady(WindowInsetsAnimationController controller, int types) {
+ mController = controller;
if (show) {
showDirectly(types);
} else {
@@ -474,10 +522,6 @@
: ANIMATION_DURATION_HIDE_MS);
mAnimator.setInterpolator(INTERPOLATOR);
mAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationCancel(Animator animation) {
- onAnimationFinish();
- }
@Override
public void onAnimationEnd(Animator animation) {
@@ -488,15 +532,15 @@
}
@Override
- public void onCancelled() {}
+ public void onCancelled() {
+ mAnimator.cancel();
+ }
private void onAnimationFinish() {
mAnimationDirection = DIRECTION_NONE;
+ mController.finish(show ? types : 0);
}
};
- // TODO: Instead of clearing this here, properly wire up
- // InsetsAnimationControlImpl.finish() to remove this from mAnimationControls.
- mAnimationControls.clear();
// Show/hide animations always need to be relative to the display frame, in order that shown
// and hidden state insets are correct.
@@ -522,10 +566,7 @@
*/
@VisibleForTesting
public void cancelExistingAnimation() {
- mAnimationDirection = DIRECTION_NONE;
- if (mAnimator != null) {
- mAnimator.cancel();
- }
+ cancelExistingControllers(all());
}
void dump(String prefix, PrintWriter pw) {
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index eab83ce..1383463 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -77,6 +77,9 @@
if (applyLocalVisibilityOverride()) {
mController.notifyVisibilityChanged();
}
+ if (mSourceControl == null) {
+ mController.notifyControlRevoked(this);
+ }
}
@VisibleForTesting
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index c24b8b2..7c69cfd 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -201,7 +201,7 @@
mListener = listener;
final ViewConfiguration viewConfiguration = ViewConfiguration.get(context);
mSpanSlop = viewConfiguration.getScaledTouchSlop() * 2;
- mMinSpan = viewConfiguration.getScaledMinScalingSpan();
+ mMinSpan = viewConfiguration.getScaledMinimumScalingSpan();
mHandler = handler;
// Quick scale is enabled by default after JB_MR2
final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion;
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index d0194f9..a15a20a 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -922,7 +922,7 @@
if (mCanvas != null) {
throw new IllegalStateException("Surface was already locked!");
}
- mCanvas = mRenderNode.start(width, height);
+ mCanvas = mRenderNode.beginRecording(width, height);
return mCanvas;
}
@@ -931,7 +931,7 @@
throw new IllegalArgumentException("canvas object must be the same instance that "
+ "was previously returned by lockCanvas");
}
- mRenderNode.end(mCanvas);
+ mRenderNode.endRecording();
mCanvas = null;
nHwuiDraw(mHwuiRenderer);
}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index aaf85af..ea7f31d 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -88,7 +88,8 @@
private static native void nativeDisconnect(long nativeObject);
private static native GraphicBuffer nativeScreenshot(IBinder displayToken,
- Rect sourceCrop, int width, int height, boolean useIdentityTransform, int rotation);
+ Rect sourceCrop, int width, int height, boolean useIdentityTransform, int rotation,
+ boolean captureSecureLayers);
private static native GraphicBuffer nativeCaptureLayers(IBinder layerHandleToken,
Rect sourceCrop, float frameScale);
@@ -157,6 +158,8 @@
IBinder displayToken, long numFrames, long timestamp);
private static native int nativeGetActiveConfig(IBinder displayToken);
private static native boolean nativeSetActiveConfig(IBinder displayToken, int id);
+ private static native boolean nativeSetAllowedDisplayConfigs(IBinder displayToken,
+ int[] allowedConfigs);
private static native int[] nativeGetDisplayColorModes(IBinder displayToken);
private static native SurfaceControl.DisplayPrimaries nativeGetDisplayNativePrimaries(
IBinder displayToken);
@@ -1521,6 +1524,20 @@
/**
* @hide
*/
+ public static boolean setAllowedDisplayConfigs(IBinder displayToken, int[] allowedConfigs) {
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
+ }
+ if (allowedConfigs == null) {
+ throw new IllegalArgumentException("allowedConfigs must not be null");
+ }
+
+ return nativeSetAllowedDisplayConfigs(displayToken, allowedConfigs);
+ }
+
+ /**
+ * @hide
+ */
public static int[] getDisplayColorModes(IBinder displayToken) {
if (displayToken == null) {
throw new IllegalArgumentException("displayToken must not be null");
@@ -1853,7 +1870,29 @@
throw new IllegalArgumentException("displayToken must not be null");
}
- return nativeScreenshot(display, sourceCrop, width, height, useIdentityTransform, rotation);
+ return nativeScreenshot(display, sourceCrop, width, height, useIdentityTransform, rotation,
+ false /* captureSecureLayers */);
+ }
+
+ /**
+ * Like screenshotToBuffer, but if the caller is AID_SYSTEM, allows
+ * for the capture of secure layers. This is used for the screen rotation
+ * animation where the system server takes screenshots but does
+ * not persist them or allow them to leave the server. However in other
+ * cases in the system server, we mostly want to omit secure layers
+ * like when we take a screenshot on behalf of the assistant.
+ *
+ * @hide
+ */
+ public static GraphicBuffer screenshotToBufferWithSecureLayersUnsafe(IBinder display,
+ Rect sourceCrop, int width, int height, boolean useIdentityTransform,
+ int rotation) {
+ if (display == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
+ }
+
+ return nativeScreenshot(display, sourceCrop, width, height, useIdentityTransform, rotation,
+ true /* captureSecureLayers */);
}
private static void rotateCropForSF(Rect crop, int rot) {
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 2097812..3d3d5dc 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -590,7 +590,7 @@
}
if (mRootNodeNeedsUpdate || !mRootNode.hasDisplayList()) {
- RecordingCanvas canvas = mRootNode.startRecording(mSurfaceWidth, mSurfaceHeight);
+ RecordingCanvas canvas = mRootNode.beginRecording(mSurfaceWidth, mSurfaceHeight);
try {
final int saveCount = canvas.save();
canvas.translate(mInsetLeft, mInsetTop);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 278b9ff..7afdc70 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -40,6 +40,7 @@
import android.annotation.TestApi;
import android.annotation.UiThread;
import android.annotation.UnsupportedAppUsage;
+import android.content.AutofillOptions;
import android.content.ClipData;
import android.content.Context;
import android.content.ContextWrapper;
@@ -9408,20 +9409,13 @@
}
setNotifiedContentCaptureAppeared();
- // TODO(b/123307965): instead of post, we should queue it on AttachInfo and then
- // dispatch on RootImpl, as we're doing with the removed ones (in that case, we should
- // merge the delayNotifyContentCaptureDisappeared() into a more generic method that
- // takes a session and a command, where the command is either view added or removed
-
- // The code below doesn't take much for a unique view, but it's called for all views
- // the first time the view hiearchy is laid off, which could acccumulative delay the
- // initial layout. Hence, we're postponing it to a later stage - it might still cost a
- // lost frame (or more), but that jank cost would only happen after the 1st layout.
- Choreographer.getInstance().postCallback(Choreographer.CALLBACK_COMMIT, () -> {
- final ViewStructure structure = session.newViewStructure(this);
- onProvideContentCaptureStructure(structure, /* flags= */ 0);
- session.notifyViewAppeared(structure);
- }, /* token= */ null);
+ if (ai != null) {
+ ai.delayNotifyContentCaptureEvent(session, this, appeared);
+ } else {
+ if (DEBUG_CONTENT_CAPTURE) {
+ Log.w(CONTENT_CAPTURE_LOG_TAG, "no AttachInfo on appeared for " + this);
+ }
+ }
} else {
if ((mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) == 0
|| (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0) {
@@ -9440,13 +9434,11 @@
mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED;
if (ai != null) {
- ai.delayNotifyContentCaptureDisappeared(session, getAutofillId());
+ ai.delayNotifyContentCaptureEvent(session, this, appeared);
} else {
if (DEBUG_CONTENT_CAPTURE) {
- Log.v(CONTENT_CAPTURE_LOG_TAG, "no AttachInfo on gone for " + this);
+ Log.v(CONTENT_CAPTURE_LOG_TAG, "no AttachInfo on disappeared for " + this);
}
- Choreographer.getInstance().postCallback(Choreographer.CALLBACK_COMMIT,
- () -> session.notifyViewDisappeared(getAutofillId()), /* token= */ null);
}
}
}
@@ -9533,8 +9525,22 @@
}
private boolean isAutofillable() {
- return getAutofillType() != AUTOFILL_TYPE_NONE && isImportantForAutofill()
- && getAutofillViewId() > LAST_APP_AUTOFILL_ID;
+ if (getAutofillType() == AUTOFILL_TYPE_NONE) return false;
+
+ if (!isImportantForAutofill()) {
+ // View is not important for "regular" autofill, so we must check if Augmented Autofill
+ // is enabled for the activity
+ final AutofillOptions options = mContext.getAutofillOptions();
+ if (options == null || !options.augmentedEnabled) {
+ // TODO(b/123100824): should also check if activity is whitelisted
+ return false;
+ }
+ final AutofillManager afm = getAutofillManager();
+ if (afm == null) return false;
+ afm.notifyViewEnteredForAugmentedAutofill(this);
+ }
+
+ return getAutofillViewId() > LAST_APP_AUTOFILL_ID;
}
/** @hide */
@@ -9703,13 +9709,19 @@
*
* @hide
*/
- public void dispatchInitialProvideContentCaptureStructure(@NonNull ContentCaptureManager ccm) {
+ public void dispatchInitialProvideContentCaptureStructure() {
AttachInfo ai = mAttachInfo;
if (ai == null) {
Log.w(CONTENT_CAPTURE_LOG_TAG,
"dispatchProvideContentCaptureStructure(): no AttachInfo for " + this);
return;
}
+ ContentCaptureManager ccm = ai.mContentCaptureManager;
+ if (ccm == null) {
+ Log.w(CONTENT_CAPTURE_LOG_TAG, "dispatchProvideContentCaptureStructure(): "
+ + "no ContentCaptureManager for " + this);
+ return;
+ }
// We must set it before checkign if the view itself is important, because it might
// initially not be (for example, if it's empty), although that might change later (for
@@ -9735,11 +9747,11 @@
return;
}
- session.internalNotifyViewHierarchyEvent(/* started= */ true);
+ session.internalNotifyViewTreeEvent(/* started= */ true);
try {
dispatchProvideContentCaptureStructure();
} finally {
- session.internalNotifyViewHierarchyEvent(/* started= */ false);
+ session.internalNotifyViewTreeEvent(/* started= */ false);
}
}
@@ -10956,7 +10968,7 @@
}
void dispatchWindowInsetsAnimationFinished(InsetsAnimation animation) {
- if (mListenerInfo != null && mListenerInfo.mOnApplyWindowInsetsListener != null) {
+ if (mListenerInfo != null && mListenerInfo.mWindowInsetsAnimationListener != null) {
mListenerInfo.mWindowInsetsAnimationListener.onFinished(animation);
}
}
@@ -16043,7 +16055,7 @@
@ViewDebug.ExportedProperty(category = "drawing")
@InspectableProperty
public float getRotation() {
- return mRenderNode.getRotation();
+ return mRenderNode.getRotationZ();
}
/**
@@ -16064,7 +16076,7 @@
if (rotation != getRotation()) {
// Double-invalidation is necessary to capture view's old and new areas
invalidateViewProperty(true, false);
- mRenderNode.setRotation(rotation);
+ mRenderNode.setRotationZ(rotation);
invalidateViewProperty(false, true);
invalidateParentIfNeededAndWasQuickRejected();
@@ -20578,7 +20590,7 @@
int height = mBottom - mTop;
int layerType = getLayerType();
- final RecordingCanvas canvas = renderNode.startRecording(width, height);
+ final RecordingCanvas canvas = renderNode.beginRecording(width, height);
try {
if (layerType == LAYER_TYPE_SOFTWARE) {
@@ -21233,7 +21245,7 @@
} else {
mClipBounds = null;
}
- mRenderNode.setClipBounds(mClipBounds);
+ mRenderNode.setClipRect(mClipBounds);
invalidateViewProperty(false, false);
}
@@ -21978,7 +21990,7 @@
final Rect bounds = drawable.getBounds();
final int width = bounds.width();
final int height = bounds.height();
- final RecordingCanvas canvas = renderNode.startRecording(width, height);
+ final RecordingCanvas canvas = renderNode.beginRecording(width, height);
// Reverse left/top translation done by drawable canvas, which will
// instead be applied by rendernode's LTRB bounds below. This way, the
@@ -28358,11 +28370,12 @@
boolean mReadyForContentCaptureUpdates;
/**
- * Map of ids (per session) that need to be notified after as gone the view hierchy is
- * traversed.
+ * Map(keyed by session) of content capture events that need to be notified after the view
+ * hierarchy is traversed: value is either the view itself for appearead events, or its
+ * autofill id for disappeared.
*/
// TODO(b/121197119): use SparseArray once session id becomes integer
- ArrayMap<String, ArrayList<AutofillId>> mContentCaptureRemovedIds;
+ ArrayMap<String, ArrayList<Object>> mContentCaptureEvents;
/**
* Cached reference to the {@link ContentCaptureManager}.
@@ -28388,24 +28401,24 @@
mTreeObserver = new ViewTreeObserver(context);
}
- private void delayNotifyContentCaptureDisappeared(@NonNull ContentCaptureSession session,
- @NonNull AutofillId id) {
- if (mContentCaptureRemovedIds == null) {
+ private void delayNotifyContentCaptureEvent(@NonNull ContentCaptureSession session,
+ @NonNull View view, boolean appeared) {
+ if (mContentCaptureEvents == null) {
// Most of the time there will be just one session, so intial capacity is 1
- mContentCaptureRemovedIds = new ArrayMap<>(1);
+ mContentCaptureEvents = new ArrayMap<>(1);
}
String sessionId = session.getId();
// TODO: life would be much easier if we provided a MultiMap implementation somwhere...
- ArrayList<AutofillId> ids = mContentCaptureRemovedIds.get(sessionId);
- if (ids == null) {
- ids = new ArrayList<>();
- mContentCaptureRemovedIds.put(sessionId, ids);
+ ArrayList<Object> events = mContentCaptureEvents.get(sessionId);
+ if (events == null) {
+ events = new ArrayList<>();
+ mContentCaptureEvents.put(sessionId, events);
}
- ids.add(id);
+ events.add(appeared ? view : view.getAutofillId());
}
@Nullable
- private ContentCaptureManager getContentCaptureManager(@NonNull Context context) {
+ ContentCaptureManager getContentCaptureManager(@NonNull Context context) {
if (mContentCaptureManager != null) {
return mContentCaptureManager;
}
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index bb29ed6..81e9c13 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -928,8 +928,8 @@
}
/**
- * If a MotionEvent has CLASSIFICATION_AMBIGUOUS_GESTURE set, then certain actions, such as
- * scrolling, will be inhibited.
+ * If a MotionEvent has {@link android.view.MotionEvent#CLASSIFICATION_AMBIGUOUS_GESTURE} set,
+ * then certain actions, such as scrolling, will be inhibited.
* However, to account for the possibility of incorrect classification,
* the default scrolling will only be inhibited if the pointer travels less than
* (getScaledTouchSlop() * this factor).
@@ -979,7 +979,7 @@
* @throws IllegalStateException if this method is called on a ViewConfiguration that was
* instantiated using a constructor with no Context parameter.
*/
- public int getScaledMinScalingSpan() {
+ public int getScaledMinimumScalingSpan() {
if (!mConstructedWithContext) {
throw new IllegalStateException("Min scaling span cannot be determined when this "
+ "method is called on a ViewConfiguration that was instantiated using a "
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 6f9ee4b..a390db2 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -611,11 +611,11 @@
}
if (view.isHardwareAccelerated()) {
- RecordingCanvas canvas = node.start(dm.widthPixels, dm.heightPixels);
+ RecordingCanvas canvas = node.beginRecording(dm.widthPixels, dm.heightPixels);
try {
return profileViewOperation(view, () -> view.draw(canvas));
} finally {
- node.end(canvas);
+ node.endRecording();
}
} else {
Bitmap bitmap = Bitmap.createBitmap(
diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java
index 68c0d9e..afee1e5 100644
--- a/core/java/android/view/ViewPropertyAnimator.java
+++ b/core/java/android/view/ViewPropertyAnimator.java
@@ -985,7 +985,7 @@
renderNode.setTranslationZ(value);
break;
case ROTATION:
- renderNode.setRotation(value);
+ renderNode.setRotationZ(value);
break;
case ROTATION_X:
renderNode.setRotationX(value);
@@ -1031,7 +1031,7 @@
case TRANSLATION_Z:
return node.getTranslationZ();
case ROTATION:
- return node.getRotation();
+ return node.getRotationZ();
case ROTATION_X:
return node.getRotationX();
case ROTATION_Y:
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index be6b56c..ad0aaa6 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -108,6 +108,7 @@
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
import android.view.contentcapture.ContentCaptureManager;
+import android.view.contentcapture.ContentCaptureSession;
import android.view.contentcapture.MainContentCaptureSession;
import android.view.inputmethod.InputMethodManager;
import android.widget.Scroller;
@@ -223,10 +224,25 @@
*/
static final int MAX_TRACKBALL_DELAY = 250;
+ /**
+ * Initial value for {@link #mContentCaptureEnabled}.
+ */
+ private static final int CONTENT_CAPTURE_ENABLED_NOT_CHECKED = 0;
+
+ /**
+ * Value for {@link #mContentCaptureEnabled} when it was checked and set to {@code true}.
+ */
+ private static final int CONTENT_CAPTURE_ENABLED_TRUE = 1;
+
+ /**
+ * Value for {@link #mContentCaptureEnabled} when it was checked and set to {@code false}.
+ */
+ private static final int CONTENT_CAPTURE_ENABLED_FALSE = 2;
+
@UnsupportedAppUsage
static final ThreadLocal<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>();
- static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList();
+ static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList<>();
static boolean sFirstDrawComplete = false;
/**
@@ -418,7 +434,11 @@
boolean mApplyInsetsRequested;
boolean mLayoutRequested;
boolean mFirst;
+
+ @Nullable
+ int mContentCaptureEnabled = CONTENT_CAPTURE_ENABLED_NOT_CHECKED;
boolean mPerformContentCapture;
+
boolean mReportNextDraw;
boolean mFullRedrawNeeded;
boolean mNewSurfaceNeeded;
@@ -2776,27 +2796,56 @@
}
}
- if (mAttachInfo.mContentCaptureRemovedIds != null) {
- MainContentCaptureSession mainSession = mAttachInfo.mContentCaptureManager
- .getMainContentCaptureSession();
- Trace.traceBegin(Trace.TRACE_TAG_VIEW, "notifyContentCaptureViewsGone");
- try {
- for (int i = 0; i < mAttachInfo.mContentCaptureRemovedIds.size(); i++) {
- String sessionId = mAttachInfo.mContentCaptureRemovedIds
- .keyAt(i);
- ArrayList<AutofillId> ids = mAttachInfo.mContentCaptureRemovedIds
- .valueAt(i);
- mainSession.notifyViewsDisappeared(sessionId, ids);
- }
- mAttachInfo.mContentCaptureRemovedIds = null;
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIEW);
- }
+ if (mAttachInfo.mContentCaptureEvents != null) {
+ notifyContentCatpureEvents();
}
mIsInTraversal = false;
}
+ private void notifyContentCatpureEvents() {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "notifyContentCaptureEvents");
+ try {
+ MainContentCaptureSession mainSession = mAttachInfo.mContentCaptureManager
+ .getMainContentCaptureSession();
+ for (int i = 0; i < mAttachInfo.mContentCaptureEvents.size(); i++) {
+ String sessionId = mAttachInfo.mContentCaptureEvents
+ .keyAt(i);
+ mainSession.notifyViewTreeEvent(sessionId, /* started= */ true);
+ ArrayList<Object> events = mAttachInfo.mContentCaptureEvents
+ .valueAt(i);
+ for_each_event: for (int j = 0; j < events.size(); j++) {
+ Object event = events.get(j);
+ if (event instanceof AutofillId) {
+ mainSession.notifyViewDisappeared(sessionId, (AutofillId) event);
+ } else if (event instanceof View) {
+ View view = (View) event;
+ ContentCaptureSession session = view.getContentCaptureSession();
+ if (session == null) {
+ Log.w(mTag, "no content capture session on view: " + view);
+ continue for_each_event;
+ }
+ String actualId = session.getId().toString();
+ if (!actualId.equals(sessionId)) {
+ Log.w(mTag, "content capture session mismatch for view (" + view
+ + "): was " + sessionId + " before, it's " + actualId + " now");
+ continue for_each_event;
+ }
+ ViewStructure structure = session.newViewStructure(view);
+ view.onProvideContentCaptureStructure(structure, /* flags= */ 0);
+ session.notifyViewAppeared(structure);
+ } else {
+ Log.w(mTag, "invalid content capture event: " + event);
+ }
+ }
+ mainSession.notifyViewTreeEvent(sessionId, /* started= */ false);
+ }
+ mAttachInfo.mContentCaptureEvents = null;
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ }
+ }
+
private void notifySurfaceDestroyed() {
mSurfaceHolder.ungetCallbacks();
SurfaceHolder.Callback[] callbacks = mSurfaceHolder.getCallbacks();
@@ -2927,6 +2976,13 @@
}
}
mFirstInputStage.onWindowFocusChanged(hasWindowFocus);
+
+ // NOTE: there's no view visibility (appeared / disapparead) events when the windows focus
+ // is lost, so we don't need to to force a flush - there might be other events such as
+ // text changes, but these should be flushed independently.
+ if (hasWindowFocus) {
+ handleContentCaptureFlush();
+ }
}
private void fireAccessibilityFocusEventIfHasFocusedNode() {
@@ -3494,31 +3550,82 @@
}
}
if (mPerformContentCapture) {
- performContentCapture();
+ performContentCaptureInitialReport();
}
}
- private void performContentCapture() {
+ /**
+ * Checks (and caches) if content capture is enabled for this context.
+ */
+ private boolean isContentCaptureEnabled() {
+ switch (mContentCaptureEnabled) {
+ case CONTENT_CAPTURE_ENABLED_TRUE:
+ return true;
+ case CONTENT_CAPTURE_ENABLED_FALSE:
+ return false;
+ case CONTENT_CAPTURE_ENABLED_NOT_CHECKED:
+ final boolean reallyEnabled = isContentCaptureReallyEnabled();
+ mContentCaptureEnabled = reallyEnabled ? CONTENT_CAPTURE_ENABLED_TRUE
+ : CONTENT_CAPTURE_ENABLED_FALSE;
+ return reallyEnabled;
+ default:
+ Log.w(TAG, "isContentCaptureEnabled(): invalid state " + mContentCaptureEnabled);
+ return false;
+ }
+
+ }
+
+ /**
+ * Checks (without caching) if content capture is enabled for this context.
+ */
+ private boolean isContentCaptureReallyEnabled() {
+ // First check if context supports it, so it saves a service lookup when it doesn't
+ if (mContext.getContentCaptureOptions() == null) return false;
+
+ final ContentCaptureManager ccm = mAttachInfo.getContentCaptureManager(mContext);
+ // Then check if it's enabled in the contex itself.
+ if (ccm == null || !ccm.isContentCaptureEnabled()) return false;
+
+ return true;
+ }
+
+ private void performContentCaptureInitialReport() {
mPerformContentCapture = false; // One-time offer!
final View rootView = mView;
if (DEBUG_CONTENT_CAPTURE) {
- Log.v(mTag, "dispatchContentCapture() on " + rootView);
+ Log.v(mTag, "performContentCaptureInitialReport() on " + rootView);
}
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "dispatchContentCapture() for "
+ getClass().getSimpleName());
}
try {
- // First check if context supports it, so it saves a service lookup when it doesn't
- if (mContext.getContentCaptureOptions() == null) return;
-
- // Then check if it's enabled in the contex itself.
- final ContentCaptureManager ccm = mContext
- .getSystemService(ContentCaptureManager.class);
- if (ccm == null || !ccm.isContentCaptureEnabled()) return;
+ if (!isContentCaptureEnabled()) return;
// Content capture is a go!
- rootView.dispatchInitialProvideContentCaptureStructure(ccm);
+ rootView.dispatchInitialProvideContentCaptureStructure();
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ }
+ }
+
+ private void handleContentCaptureFlush() {
+ if (DEBUG_CONTENT_CAPTURE) {
+ Log.v(mTag, "handleContentCaptureFlush()");
+ }
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "flushContentCapture for "
+ + getClass().getSimpleName());
+ }
+ try {
+ if (!isContentCaptureEnabled()) return;
+
+ final ContentCaptureManager ccm = mAttachInfo.mContentCaptureManager;
+ if (ccm == null) {
+ Log.w(TAG, "No ContentCapture on AttachInfo");
+ return;
+ }
+ ccm.flush(ContentCaptureSession.FLUSH_REASON_VIEW_ROOT_ENTERED);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
@@ -5163,11 +5270,8 @@
protected int onProcess(QueuedInputEvent q) {
if (q.mEvent instanceof KeyEvent) {
return processKeyEvent(q);
- } else {
- final int source = q.mEvent.getSource();
- if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
- return processPointerEvent(q);
- }
+ } else if (q.mEvent instanceof MotionEvent) {
+ return processMotionEvent(q);
}
return FORWARD;
}
@@ -5191,6 +5295,23 @@
return FORWARD;
}
+ private int processMotionEvent(QueuedInputEvent q) {
+ final MotionEvent event = (MotionEvent) q.mEvent;
+
+ if (event.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
+ return processPointerEvent(q);
+ }
+
+ // If the motion event is from an absolute position device, exit touch mode
+ final int action = event.getActionMasked();
+ if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_SCROLL) {
+ if (event.isFromSource(InputDevice.SOURCE_CLASS_POSITION)) {
+ ensureTouchMode(false);
+ }
+ }
+ return FORWARD;
+ }
+
private int processPointerEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
@@ -5523,6 +5644,12 @@
private int processGenericMotionEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
+ if (event.isFromSource(InputDevice.SOURCE_TOUCHPAD)) {
+ if (hasPointerCapture() && mView.dispatchCapturedPointerEvent(event)) {
+ return FINISH_HANDLED;
+ }
+ }
+
// Deliver the event to the view.
if (mView.dispatchGenericMotionEvent(event)) {
return FINISH_HANDLED;
@@ -7093,7 +7220,7 @@
RenderNode renderNode = view.mRenderNode;
info[0]++;
if (renderNode != null) {
- info[1] += renderNode.computeApproximateMemoryUsage();
+ info[1] += (int) renderNode.computeApproximateMemoryUsage();
}
if (view instanceof ViewGroup) {
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index c1536ae..135a891 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -28,6 +28,7 @@
import static android.view.WindowInsets.Type.indexOf;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
@@ -700,7 +701,8 @@
* @return the inset insets
*/
@NonNull
- public WindowInsets inset(int left, int top, int right, int bottom) {
+ public WindowInsets inset(@IntRange(from = 0) int left, @IntRange(from = 0) int top,
+ @IntRange(from = 0) int right, @IntRange(from = 0) int bottom) {
Preconditions.checkArgumentNonnegative(left);
Preconditions.checkArgumentNonnegative(top);
Preconditions.checkArgumentNonnegative(right);
@@ -794,7 +796,7 @@
/**
* Builder for WindowInsets.
*/
- public static class Builder {
+ public static final class Builder {
private final Insets[] mTypeInsetsMap;
private final Insets[] mTypeMaxInsetsMap;
@@ -821,7 +823,7 @@
*
* @param insets the instance to initialize from.
*/
- public Builder(WindowInsets insets) {
+ public Builder(@NonNull WindowInsets insets) {
mTypeInsetsMap = insets.mTypeInsetsMap.clone();
mTypeMaxInsetsMap = insets.mTypeMaxInsetsMap.clone();
mTypeVisibilityMap = insets.mTypeVisibilityMap.clone();
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 9dcbe05..70fe230 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -16,6 +16,7 @@
package android.view.autofill;
+import static android.service.autofill.FillRequest.FLAG_AUGMENTED_AUTOFILL_REQUEST;
import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
import static android.view.autofill.Helper.sDebug;
import static android.view.autofill.Helper.sVerbose;
@@ -370,6 +371,24 @@
public static final String DEVICE_CONFIG_AUTOFILL_SMART_SUGGESTION_SUPPORTED_MODES =
"smart_suggestion_supported_modes";
+ /**
+ * Sets how long (in ms) the augmented autofill service is bound while idle.
+ *
+ * <p>Use {@code 0} to keep it permanently bound.
+ *
+ * @hide
+ */
+ public static final String DEVICE_CONFIG_AUGMENTED_SERVICE_IDLE_UNBIND_TIMEOUT =
+ "augmented_service_idle_unbind_timeout";
+
+ /**
+ * Sets how long (in ms) the augmented autofill service request is killed if not replied.
+ *
+ * @hide
+ */
+ public static final String DEVICE_CONFIG_AUGMENTED_SERVICE_REQUEST_TIMEOUT =
+ "augmented_service_request_timeout";
+
/** @hide */
public static final int RESULT_OK = 0;
/** @hide */
@@ -467,6 +486,13 @@
@GuardedBy("mLock")
@Nullable private ArraySet<AutofillId> mEnteredIds;
+ /**
+ * Views that were otherwised not important for autofill but triggered a session because the
+ * context is whitelisted for augmented autofill.
+ */
+ @GuardedBy("mLock")
+ @Nullable private Set<AutofillId> mEnteredForAugmentedAutofillIds;
+
/** If set, session is commited when the field is clicked. */
@GuardedBy("mLock")
@Nullable private AutofillId mSaveTriggerId;
@@ -1626,6 +1652,11 @@
@GuardedBy("mLock")
private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
@NonNull AutofillValue value, int flags) {
+ if (mEnteredForAugmentedAutofillIds != null
+ && mEnteredForAugmentedAutofillIds.contains(id)) {
+ if (sVerbose) Log.v(TAG, "Starting session for augmented autofill on " + id);
+ flags |= FLAG_AUGMENTED_AUTOFILL_REQUEST;
+ }
if (sVerbose) {
Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
+ ", flags=" + flags + ", state=" + getStateAsStringLocked()
@@ -1861,6 +1892,25 @@
return set == null ? null : new ArrayList<T>(set);
}
+ /**
+ * Notifies that a non-autofillable view was entered because the activity is whitelisted for
+ * augmented autofill.
+ *
+ * <p>This method is necessary to set the right flag on start, so the server-side session
+ * doesn't trigger the standard autofill workflow, but the augmented's instead.
+ *
+ * @hide
+ */
+ public void notifyViewEnteredForAugmentedAutofill(@NonNull View view) {
+ final AutofillId id = view.getAutofillId();
+ synchronized (mLock) {
+ if (mEnteredForAugmentedAutofillIds == null) {
+ mEnteredForAugmentedAutofillIds = new ArraySet<>(1);
+ }
+ mEnteredForAugmentedAutofillIds.add(id);
+ }
+ }
+
private void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
Rect anchorBounds, IAutofillWindowPresenter presenter) {
final View anchor = findView(id);
@@ -2360,6 +2410,10 @@
}
pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds);
pw.print(pfx); pw.print("entered ids: "); pw.println(mEnteredIds);
+ if (mEnteredForAugmentedAutofillIds != null) {
+ pw.print(pfx); pw.print("entered ids for augmented autofill: ");
+ pw.println(mEnteredForAugmentedAutofillIds);
+ }
pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId);
pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish);
if (mOptions != null) {
diff --git a/core/java/android/view/contentcapture/ChildContentCaptureSession.java b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
index 13e8a65..b3b0b72 100644
--- a/core/java/android/view/contentcapture/ChildContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
@@ -84,8 +84,8 @@
}
@Override
- public void internalNotifyViewHierarchyEvent(boolean started) {
- getMainCaptureSession().notifyInitialViewHierarchyEvent(mId, started);
+ public void internalNotifyViewTreeEvent(boolean started) {
+ getMainCaptureSession().notifyViewTreeEvent(mId, started);
}
@Override
diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java
index 9cdbefa..2585b74 100644
--- a/core/java/android/view/contentcapture/ContentCaptureEvent.java
+++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java
@@ -72,24 +72,24 @@
public static final int TYPE_VIEW_TEXT_CHANGED = 3;
/**
- * Called before events (such as {@link #TYPE_VIEW_APPEARED}) representing the initial view
- * hierarchy are sent.
+ * Called before events (such as {@link #TYPE_VIEW_APPEARED} and/or
+ * {@link #TYPE_VIEW_DISAPPEARED}) representing a view hierarchy are sent.
*
* <p><b>NOTE</b>: there is no guarantee this event will be sent. For example, it's not sent
* if the initial view hierarchy doesn't initially have any view that's important for content
* capture.
*/
- public static final int TYPE_INITIAL_VIEW_TREE_APPEARING = 4;
+ public static final int TYPE_VIEW_TREE_APPEARING = 4;
/**
- * Called after events (such as {@link #TYPE_VIEW_APPEARED}) representing the initial view
- * hierarchy are sent.
+ * Called after events (such as {@link #TYPE_VIEW_APPEARED} and/or
+ * {@link #TYPE_VIEW_DISAPPEARED}) representing a view hierarchy were sent.
*
* <p><b>NOTE</b>: there is no guarantee this event will be sent. For example, it's not sent
* if the initial view hierarchy doesn't initially have any view that's important for content
* capture.
*/
- public static final int TYPE_INITIAL_VIEW_TREE_APPEARED = 5;
+ public static final int TYPE_VIEW_TREE_APPEARED = 5;
/**
* Called after a call to
@@ -99,14 +99,29 @@
*/
public static final int TYPE_CONTEXT_UPDATED = 6;
+ /**
+ * Called after the session is ready, typically after the activity resumed and the
+ * initial views appeared
+ */
+ public static final int TYPE_SESSION_RESUMED = 7;
+
+ /**
+ * Called after the session is paused, typically after the activity paused and the
+ * views disappeared.
+ */
+ public static final int TYPE_SESSION_PAUSED = 8;
+
+
/** @hide */
@IntDef(prefix = { "TYPE_" }, value = {
TYPE_VIEW_APPEARED,
TYPE_VIEW_DISAPPEARED,
TYPE_VIEW_TEXT_CHANGED,
- TYPE_INITIAL_VIEW_TREE_APPEARING,
- TYPE_INITIAL_VIEW_TREE_APPEARED,
- TYPE_CONTEXT_UPDATED
+ TYPE_VIEW_TREE_APPEARING,
+ TYPE_VIEW_TREE_APPEARED,
+ TYPE_CONTEXT_UPDATED,
+ TYPE_SESSION_PAUSED,
+ TYPE_SESSION_RESUMED
})
@Retention(RetentionPolicy.SOURCE)
public @interface EventType{}
@@ -230,8 +245,9 @@
* Gets the type of the event.
*
* @return one of {@link #TYPE_VIEW_APPEARED}, {@link #TYPE_VIEW_DISAPPEARED},
- * {@link #TYPE_VIEW_TEXT_CHANGED}, {@link #TYPE_INITIAL_VIEW_TREE_APPEARING},
- * {@link #TYPE_INITIAL_VIEW_TREE_APPEARED}, or {@link #TYPE_CONTEXT_UPDATED}.
+ * {@link #TYPE_VIEW_TEXT_CHANGED}, {@link #TYPE_VIEW_TREE_APPEARING},
+ * {@link #TYPE_VIEW_TREE_APPEARED}, {@link #TYPE_CONTEXT_UPDATED},
+ * {@link #TYPE_SESSION_RESUMED}, or {@link #TYPE_SESSION_PAUSED}.
*/
public @EventType int getType() {
return mType;
@@ -411,16 +427,20 @@
return "SESSION_STARTED";
case TYPE_SESSION_FINISHED:
return "SESSION_FINISHED";
+ case TYPE_SESSION_RESUMED:
+ return "SESSION_RESUMED";
+ case TYPE_SESSION_PAUSED:
+ return "SESSION_PAUSED";
case TYPE_VIEW_APPEARED:
return "VIEW_APPEARED";
case TYPE_VIEW_DISAPPEARED:
return "VIEW_DISAPPEARED";
case TYPE_VIEW_TEXT_CHANGED:
return "VIEW_TEXT_CHANGED";
- case TYPE_INITIAL_VIEW_TREE_APPEARING:
- return "INITIAL_VIEW_HIERARCHY_STARTED";
- case TYPE_INITIAL_VIEW_TREE_APPEARED:
- return "INITIAL_VIEW_HIERARCHY_FINISHED";
+ case TYPE_VIEW_TREE_APPEARING:
+ return "VIEW_TREE_APPEARING";
+ case TYPE_VIEW_TREE_APPEARED:
+ return "VIEW_TREE_APPEARED";
case TYPE_CONTEXT_UPDATED:
return "CONTEXT_UPDATED";
default:
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index 336d997..885bd2a 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -129,6 +129,14 @@
@TestApi
public static final String DEVICE_CONFIG_PROPERTY_LOGGING_LEVEL = "logging_level";
+ /**
+ * Sets how long (in ms) the service is bound while idle.
+ *
+ * <p>Use {@code 0} to keep it permanently bound.
+ *
+ * @hide
+ */
+ public static final String DEVICE_CONFIG_PROPERTY_IDLE_UNBIND_TIMEOUT = "idle_unbind_timeout";
/** @hide */
@TestApi
@@ -224,7 +232,7 @@
/** @hide */
@UiThread
- public void onActivityStarted(@NonNull IBinder applicationToken,
+ public void onActivityCreated(@NonNull IBinder applicationToken,
@NonNull ComponentName activityComponent, int flags) {
synchronized (mLock) {
mFlags |= flags;
@@ -234,7 +242,19 @@
/** @hide */
@UiThread
- public void onActivityStopped() {
+ public void onActivityResumed() {
+ getMainContentCaptureSession().notifySessionLifecycle(/* started= */ true);
+ }
+
+ /** @hide */
+ @UiThread
+ public void onActivityPaused() {
+ getMainContentCaptureSession().notifySessionLifecycle(/* started= */ false);
+ }
+
+ /** @hide */
+ @UiThread
+ public void onActivityDestroyed() {
getMainContentCaptureSession().destroy();
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index 1e051a4..ab8f346 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -132,27 +132,24 @@
/** @hide */
public static final int FLUSH_REASON_FULL = 1;
/** @hide */
- public static final int FLUSH_REASON_ACTIVITY_PAUSED = 2;
+ public static final int FLUSH_REASON_VIEW_ROOT_ENTERED = 2;
/** @hide */
- public static final int FLUSH_REASON_ACTIVITY_RESUMED = 3;
+ public static final int FLUSH_REASON_SESSION_STARTED = 3;
/** @hide */
- public static final int FLUSH_REASON_SESSION_STARTED = 4;
+ public static final int FLUSH_REASON_SESSION_FINISHED = 4;
/** @hide */
- public static final int FLUSH_REASON_SESSION_FINISHED = 5;
- /** @hide */
- public static final int FLUSH_REASON_IDLE_TIMEOUT = 6;
+ public static final int FLUSH_REASON_IDLE_TIMEOUT = 5;
/** @hide */
@IntDef(prefix = { "FLUSH_REASON_" }, value = {
FLUSH_REASON_FULL,
- FLUSH_REASON_ACTIVITY_PAUSED,
- FLUSH_REASON_ACTIVITY_RESUMED,
+ FLUSH_REASON_VIEW_ROOT_ENTERED,
FLUSH_REASON_SESSION_STARTED,
FLUSH_REASON_SESSION_FINISHED,
FLUSH_REASON_IDLE_TIMEOUT
})
@Retention(RetentionPolicy.SOURCE)
- @interface FlushReason{}
+ public @interface FlushReason{}
private final Object mLock = new Object();
@@ -414,7 +411,7 @@
@Nullable CharSequence text);
/** @hide */
- public abstract void internalNotifyViewHierarchyEvent(boolean started);
+ public abstract void internalNotifyViewTreeEvent(boolean started);
/**
* Creates a {@link ViewStructure} for a "standard" view.
@@ -500,14 +497,12 @@
/** @hide */
@NonNull
- static String getflushReasonAsString(@FlushReason int reason) {
+ public static String getFlushReasonAsString(@FlushReason int reason) {
switch (reason) {
case FLUSH_REASON_FULL:
return "FULL";
- case FLUSH_REASON_ACTIVITY_PAUSED:
- return "PAUSED";
- case FLUSH_REASON_ACTIVITY_RESUMED:
- return "RESUMED";
+ case FLUSH_REASON_VIEW_ROOT_ENTERED:
+ return "VIEW_ROOT";
case FLUSH_REASON_SESSION_STARTED:
return "STARTED";
case FLUSH_REASON_SESSION_FINISHED:
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 0abf689..dce8ebe 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -16,13 +16,15 @@
package android.view.contentcapture;
import static android.view.contentcapture.ContentCaptureEvent.TYPE_CONTEXT_UPDATED;
-import static android.view.contentcapture.ContentCaptureEvent.TYPE_INITIAL_VIEW_TREE_APPEARED;
-import static android.view.contentcapture.ContentCaptureEvent.TYPE_INITIAL_VIEW_TREE_APPEARING;
import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_FINISHED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_PAUSED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_RESUMED;
import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_STARTED;
import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_APPEARED;
import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_DISAPPEARED;
import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TREE_APPEARED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TREE_APPEARING;
import static android.view.contentcapture.ContentCaptureHelper.getSanitizedString;
import static android.view.contentcapture.ContentCaptureHelper.sDebug;
import static android.view.contentcapture.ContentCaptureHelper.sVerbose;
@@ -126,7 +128,6 @@
@Nullable
private final LocalLog mFlushHistory;
- /** @hide */
protected MainContentCaptureSession(@NonNull Context context,
@NonNull ContentCaptureManager manager, @NonNull Handler handler,
@NonNull IContentCaptureManager systemServerInterface) {
@@ -153,8 +154,6 @@
/**
* Starts this session.
- *
- * @hide
*/
@UiThread
void start(@NonNull IBinder token, @NonNull ComponentName component,
@@ -451,7 +450,7 @@
}
final int numberEvents = mEvents.size();
- final String reasonString = getflushReasonAsString(reason);
+ final String reasonString = getFlushReasonAsString(reason);
if (sDebug) {
Log.d(TAG, "Flushing " + numberEvents + " event(s) for " + getDebugState(reason));
}
@@ -546,8 +545,8 @@
}
@Override
- public void internalNotifyViewHierarchyEvent(boolean started) {
- notifyInitialViewHierarchyEvent(mId, started);
+ public void internalNotifyViewTreeEvent(boolean started) {
+ notifyViewTreeEvent(mId, started);
}
@Override
@@ -581,21 +580,9 @@
.setViewNode(node.mNode));
}
- void notifyViewDisappeared(@NonNull String sessionId, @NonNull AutofillId id) {
- sendEvent(
- new ContentCaptureEvent(sessionId, TYPE_VIEW_DISAPPEARED).setAutofillId(id));
- }
-
- /** @hide */
- public void notifyViewsDisappeared(@NonNull String sessionId,
- @NonNull ArrayList<AutofillId> ids) {
- final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_VIEW_DISAPPEARED);
- if (ids.size() == 1) {
- event.setAutofillId(ids.get(0));
- } else {
- event.setAutofillIds(ids);
- }
- sendEvent(event);
+ /** Public because is also used by ViewRootImpl */
+ public void notifyViewDisappeared(@NonNull String sessionId, @NonNull AutofillId id) {
+ sendEvent(new ContentCaptureEvent(sessionId, TYPE_VIEW_DISAPPEARED).setAutofillId(id));
}
void notifyViewTextChanged(@NonNull String sessionId, @NonNull AutofillId id,
@@ -604,13 +591,16 @@
.setText(text));
}
- void notifyInitialViewHierarchyEvent(@NonNull String sessionId, boolean started) {
- if (started) {
- sendEvent(new ContentCaptureEvent(sessionId, TYPE_INITIAL_VIEW_TREE_APPEARING));
- } else {
- sendEvent(new ContentCaptureEvent(sessionId, TYPE_INITIAL_VIEW_TREE_APPEARED),
- FORCE_FLUSH);
- }
+ /** Public because is also used by ViewRootImpl */
+ public void notifyViewTreeEvent(@NonNull String sessionId, boolean started) {
+ final int type = started ? TYPE_VIEW_TREE_APPEARING : TYPE_VIEW_TREE_APPEARED;
+ sendEvent(new ContentCaptureEvent(sessionId, type), FORCE_FLUSH);
+ }
+
+ /** Public because is also used by ViewRootImpl */
+ public void notifySessionLifecycle(boolean started) {
+ final int type = started ? TYPE_SESSION_RESUMED : TYPE_SESSION_PAUSED;
+ sendEvent(new ContentCaptureEvent(mId, type), FORCE_FLUSH);
}
void notifyContextUpdated(@NonNull String sessionId,
@@ -684,6 +674,6 @@
@NonNull
private String getDebugState(@FlushReason int reason) {
- return getDebugState() + ", reason=" + getflushReasonAsString(reason);
+ return getDebugState() + ", reason=" + getFlushReasonAsString(reason);
}
}
diff --git a/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java b/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
index 4d917a1..efdc968 100644
--- a/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
+++ b/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
@@ -16,6 +16,7 @@
package android.view.textclassifier;
+import android.annotation.Nullable;
import android.app.Person;
import android.content.Context;
import android.text.TextUtils;
@@ -110,6 +111,19 @@
SelectionSessionLogger.CLASSIFIER_ID, modelName, hash);
}
+ /**
+ * Returns a {@link android.view.textclassifier.LabeledIntent.TitleChooser} for
+ * conversation actions use case.
+ */
+ @Nullable
+ public static LabeledIntent.TitleChooser createTitleChooser(String actionType) {
+ if (ConversationAction.TYPE_OPEN_URL.equals(actionType)) {
+ return (labeledIntent, resolveInfo) -> resolveInfo.handleAllWebDataURI
+ ? labeledIntent.titleWithEntity : labeledIntent.titleWithoutEntity;
+ }
+ return null;
+ }
+
private static final class PersonEncoder {
private final Map<Person, Integer> mMapping = new ArrayMap<>();
private int mNextUserId = FIRST_NON_LOCAL_USER;
diff --git a/core/java/android/view/textclassifier/IntentFactory.java b/core/java/android/view/textclassifier/IntentFactory.java
index 9f3b97f..722c812 100644
--- a/core/java/android/view/textclassifier/IntentFactory.java
+++ b/core/java/android/view/textclassifier/IntentFactory.java
@@ -32,7 +32,7 @@
/**
* Return a list of LabeledIntent from the classification result.
*/
- List<TextClassifierImpl.LabeledIntent> create(
+ List<LabeledIntent> create(
Context context,
String text,
boolean foreignText,
@@ -43,9 +43,10 @@
* Inserts translate action to the list if it is a foreign text.
*/
static void insertTranslateAction(
- List<TextClassifierImpl.LabeledIntent> actions, Context context, String text) {
- actions.add(new TextClassifierImpl.LabeledIntent(
+ List<LabeledIntent> actions, Context context, String text) {
+ actions.add(new LabeledIntent(
context.getString(com.android.internal.R.string.translate),
+ /* titleWithEntity */ null,
context.getString(com.android.internal.R.string.translate_desc),
new Intent(Intent.ACTION_TRANSLATE)
// TODO: Probably better to introduce a "translate" scheme instead of
diff --git a/core/java/android/view/textclassifier/LabeledIntent.java b/core/java/android/view/textclassifier/LabeledIntent.java
new file mode 100644
index 0000000..7544dc1
--- /dev/null
+++ b/core/java/android/view/textclassifier/LabeledIntent.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2019 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.view.textclassifier;
+
+import android.annotation.Nullable;
+import android.app.PendingIntent;
+import android.app.RemoteAction;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Icon;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+
+/**
+ * Helper class to store the information from which RemoteActions are built.
+ *
+ * @hide
+ */
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public final class LabeledIntent {
+ private static final String TAG = "LabeledIntent";
+ public static final int DEFAULT_REQUEST_CODE = 0;
+ private static final TitleChooser DEFAULT_TITLE_CHOOSER =
+ (labeledIntent, resolveInfo) -> {
+ if (!TextUtils.isEmpty(labeledIntent.titleWithEntity)) {
+ return labeledIntent.titleWithEntity;
+ }
+ return labeledIntent.titleWithoutEntity;
+ };
+
+ @Nullable
+ public final String titleWithoutEntity;
+ @Nullable
+ public final String titleWithEntity;
+ public final String description;
+ // Do not update this intent.
+ public final Intent intent;
+ public final int requestCode;
+
+ /**
+ * Initializes a LabeledIntent.
+ *
+ * <p>NOTE: {@code requestCode} is required to not be {@link #DEFAULT_REQUEST_CODE}
+ * if distinguishing info (e.g. the classified text) is represented in intent extras only.
+ * In such circumstances, the request code should represent the distinguishing info
+ * (e.g. by generating a hashcode) so that the generated PendingIntent is (somewhat)
+ * unique. To be correct, the PendingIntent should be definitely unique but we try a
+ * best effort approach that avoids spamming the system with PendingIntents.
+ */
+ // TODO: Fix the issue mentioned above so the behaviour is correct.
+ public LabeledIntent(
+ @Nullable String titleWithoutEntity,
+ @Nullable String titleWithEntity,
+ String description,
+ Intent intent,
+ int requestCode) {
+ if (TextUtils.isEmpty(titleWithEntity) && TextUtils.isEmpty(titleWithoutEntity)) {
+ throw new IllegalArgumentException(
+ "titleWithEntity and titleWithoutEntity should not be both null");
+ }
+ this.titleWithoutEntity = titleWithoutEntity;
+ this.titleWithEntity = titleWithEntity;
+ this.description = Preconditions.checkNotNull(description);
+ this.intent = Preconditions.checkNotNull(intent);
+ this.requestCode = requestCode;
+ }
+
+ /**
+ * Return the resolved result.
+ */
+ @Nullable
+ public Result resolve(
+ Context context, @Nullable TitleChooser titleChooser) {
+ final PackageManager pm = context.getPackageManager();
+ final ResolveInfo resolveInfo = pm.resolveActivity(intent, 0);
+ final String packageName = resolveInfo != null && resolveInfo.activityInfo != null
+ ? resolveInfo.activityInfo.packageName : null;
+ Icon icon = null;
+ Intent resolvedIntent = new Intent(intent);
+ boolean shouldShowIcon = false;
+ if (packageName != null && !"android".equals(packageName)) {
+ // There is a default activity handling the intent.
+ resolvedIntent.setComponent(
+ new ComponentName(packageName, resolveInfo.activityInfo.name));
+ if (resolveInfo.activityInfo.getIconResource() != 0) {
+ icon = Icon.createWithResource(
+ packageName, resolveInfo.activityInfo.getIconResource());
+ shouldShowIcon = true;
+ }
+ }
+ if (icon == null) {
+ // RemoteAction requires that there be an icon.
+ icon = Icon.createWithResource("android",
+ com.android.internal.R.drawable.ic_more_items);
+ }
+ final PendingIntent pendingIntent =
+ TextClassification.createPendingIntent(context, resolvedIntent, requestCode);
+ if (pendingIntent == null) {
+ return null;
+ }
+ if (titleChooser == null) {
+ titleChooser = DEFAULT_TITLE_CHOOSER;
+ }
+ CharSequence title = titleChooser.chooseTitle(this, resolveInfo);
+ if (TextUtils.isEmpty(title)) {
+ Log.w(TAG, "Custom titleChooser return null, fallback to the default titleChooser");
+ title = DEFAULT_TITLE_CHOOSER.chooseTitle(this, resolveInfo);
+ }
+ final RemoteAction action =
+ new RemoteAction(icon, title, description, pendingIntent);
+ action.setShouldShowIcon(shouldShowIcon);
+ return new Result(resolvedIntent, action);
+ }
+
+ /**
+ * Data class that holds the result.
+ */
+ public static final class Result {
+ public final Intent resolvedIntent;
+ public final RemoteAction remoteAction;
+
+ public Result(Intent resolvedIntent, RemoteAction remoteAction) {
+ this.resolvedIntent = Preconditions.checkNotNull(resolvedIntent);
+ this.remoteAction = Preconditions.checkNotNull(remoteAction);
+ }
+ }
+
+ /**
+ * An object to choose a title from resolved info. If {@code null} is returned,
+ * {@link #titleWithEntity} will be used if it exists, {@link #titleWithoutEntity} otherwise.
+ */
+ public interface TitleChooser {
+ /**
+ * Picks a title from a {@link LabeledIntent} by looking into resolved info.
+ */
+ @Nullable
+ CharSequence chooseTitle(LabeledIntent labeledIntent, ResolveInfo resolveInfo);
+ }
+}
diff --git a/core/java/android/view/textclassifier/LegacyIntentFactory.java b/core/java/android/view/textclassifier/LegacyIntentFactory.java
index 2d0d032..ea9229d 100644
--- a/core/java/android/view/textclassifier/LegacyIntentFactory.java
+++ b/core/java/android/view/textclassifier/LegacyIntentFactory.java
@@ -29,7 +29,6 @@
import android.provider.Browser;
import android.provider.CalendarContract;
import android.provider.ContactsContract;
-import android.view.textclassifier.TextClassifierImpl.LabeledIntent;
import com.google.android.textclassifier.AnnotatorModel;
@@ -100,8 +99,7 @@
IntentFactory.insertTranslateAction(actions, context, text);
}
actions.forEach(
- action -> action.getIntent()
- .putExtra(TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER, true));
+ action -> action.intent.putExtra(TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER, true));
return actions;
}
@@ -110,12 +108,14 @@
final List<LabeledIntent> actions = new ArrayList<>();
actions.add(new LabeledIntent(
context.getString(com.android.internal.R.string.email),
+ /* titleWithEntity */ null,
context.getString(com.android.internal.R.string.email_desc),
new Intent(Intent.ACTION_SENDTO)
.setData(Uri.parse(String.format("mailto:%s", text))),
LabeledIntent.DEFAULT_REQUEST_CODE));
actions.add(new LabeledIntent(
context.getString(com.android.internal.R.string.add_contact),
+ /* titleWithEntity */ null,
context.getString(com.android.internal.R.string.add_contact_desc),
new Intent(Intent.ACTION_INSERT_OR_EDIT)
.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
@@ -133,6 +133,7 @@
if (!userRestrictions.getBoolean(UserManager.DISALLOW_OUTGOING_CALLS, false)) {
actions.add(new LabeledIntent(
context.getString(com.android.internal.R.string.dial),
+ /* titleWithEntity */ null,
context.getString(com.android.internal.R.string.dial_desc),
new Intent(Intent.ACTION_DIAL).setData(
Uri.parse(String.format("tel:%s", text))),
@@ -140,6 +141,7 @@
}
actions.add(new LabeledIntent(
context.getString(com.android.internal.R.string.add_contact),
+ /* titleWithEntity */ null,
context.getString(com.android.internal.R.string.add_contact_desc),
new Intent(Intent.ACTION_INSERT_OR_EDIT)
.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
@@ -148,6 +150,7 @@
if (!userRestrictions.getBoolean(UserManager.DISALLOW_SMS, false)) {
actions.add(new LabeledIntent(
context.getString(com.android.internal.R.string.sms),
+ /* titleWithEntity */ null,
context.getString(com.android.internal.R.string.sms_desc),
new Intent(Intent.ACTION_SENDTO)
.setData(Uri.parse(String.format("smsto:%s", text))),
@@ -163,6 +166,7 @@
final String encText = URLEncoder.encode(text, "UTF-8");
actions.add(new LabeledIntent(
context.getString(com.android.internal.R.string.map),
+ /* titleWithEntity */ null,
context.getString(com.android.internal.R.string.map_desc),
new Intent(Intent.ACTION_VIEW)
.setData(Uri.parse(String.format("geo:0,0?q=%s", encText))),
@@ -181,6 +185,7 @@
final List<LabeledIntent> actions = new ArrayList<>();
actions.add(new LabeledIntent(
context.getString(com.android.internal.R.string.browse),
+ /* titleWithEntity */ null,
context.getString(com.android.internal.R.string.browse_desc),
new Intent(Intent.ACTION_VIEW)
.setDataAndNormalize(Uri.parse(text))
@@ -211,6 +216,7 @@
final List<LabeledIntent> actions = new ArrayList<>();
actions.add(new LabeledIntent(
context.getString(com.android.internal.R.string.view_flight),
+ /* titleWithEntity */ null,
context.getString(com.android.internal.R.string.view_flight_desc),
new Intent(Intent.ACTION_WEB_SEARCH)
.putExtra(SearchManager.QUERY, text),
@@ -225,6 +231,7 @@
ContentUris.appendId(builder, parsedTime.toEpochMilli());
return new LabeledIntent(
context.getString(com.android.internal.R.string.view_calendar),
+ /* titleWithEntity */ null,
context.getString(com.android.internal.R.string.view_calendar_desc),
new Intent(Intent.ACTION_VIEW).setData(builder.build()),
LabeledIntent.DEFAULT_REQUEST_CODE);
@@ -236,6 +243,7 @@
final boolean isAllDay = TextClassifier.TYPE_DATE.equals(type);
return new LabeledIntent(
context.getString(com.android.internal.R.string.add_calendar_event),
+ /* titleWithEntity */ null,
context.getString(com.android.internal.R.string.add_calendar_event_desc),
new Intent(Intent.ACTION_INSERT)
.setData(CalendarContract.Events.CONTENT_URI)
@@ -252,6 +260,7 @@
final List<LabeledIntent> actions = new ArrayList<>();
actions.add(new LabeledIntent(
context.getString(com.android.internal.R.string.define),
+ /* titleWithEntity */ null,
context.getString(com.android.internal.R.string.define_desc),
new Intent(Intent.ACTION_DEFINE)
.putExtra(Intent.EXTRA_TEXT, text),
diff --git a/core/java/android/view/textclassifier/TemplateClassificationIntentFactory.java b/core/java/android/view/textclassifier/TemplateClassificationIntentFactory.java
index 2467802..ed0259f 100644
--- a/core/java/android/view/textclassifier/TemplateClassificationIntentFactory.java
+++ b/core/java/android/view/textclassifier/TemplateClassificationIntentFactory.java
@@ -48,12 +48,12 @@
}
/**
- * Returns a list of {@link android.view.textclassifier.TextClassifierImpl.LabeledIntent}
+ * Returns a list of {@link android.view.textclassifier.LabeledIntent}
* that are constructed from the classification result.
*/
@NonNull
@Override
- public List<TextClassifierImpl.LabeledIntent> create(
+ public List<LabeledIntent> create(
Context context,
String text,
boolean foreignText,
@@ -68,7 +68,7 @@
Log.w(TAG, "RemoteActionTemplate is missing, fallback to LegacyIntentFactory.");
return mFallback.create(context, text, foreignText, referenceTime, classification);
}
- final List<TextClassifierImpl.LabeledIntent> labeledIntents =
+ final List<LabeledIntent> labeledIntents =
mTemplateIntentFactory.create(remoteActionTemplates);
if (foreignText) {
IntentFactory.insertTranslateAction(labeledIntents, context, text.trim());
diff --git a/core/java/android/view/textclassifier/TemplateIntentFactory.java b/core/java/android/view/textclassifier/TemplateIntentFactory.java
index 95f88c7..0696d98 100644
--- a/core/java/android/view/textclassifier/TemplateIntentFactory.java
+++ b/core/java/android/view/textclassifier/TemplateIntentFactory.java
@@ -42,29 +42,29 @@
private static final String TAG = TextClassifier.DEFAULT_LOG_TAG;
@NonNull
- public List<TextClassifierImpl.LabeledIntent> create(
+ public List<LabeledIntent> create(
@Nullable RemoteActionTemplate[] remoteActionTemplates) {
if (ArrayUtils.isEmpty(remoteActionTemplates)) {
return Collections.emptyList();
}
- final List<TextClassifierImpl.LabeledIntent> labeledIntents = new ArrayList<>();
+ final List<LabeledIntent> labeledIntents = new ArrayList<>();
for (RemoteActionTemplate remoteActionTemplate : remoteActionTemplates) {
if (!isValidTemplate(remoteActionTemplate)) {
Log.w(TAG, "Invalid RemoteActionTemplate skipped.");
continue;
}
labeledIntents.add(
- new TextClassifierImpl.LabeledIntent(
- remoteActionTemplate.title,
+ new LabeledIntent(
+ remoteActionTemplate.titleWithoutEntity,
+ remoteActionTemplate.titleWithEntity,
remoteActionTemplate.description,
createIntent(remoteActionTemplate),
remoteActionTemplate.requestCode == null
- ? TextClassifierImpl.LabeledIntent.DEFAULT_REQUEST_CODE
+ ? LabeledIntent.DEFAULT_REQUEST_CODE
: remoteActionTemplate.requestCode));
}
labeledIntents.forEach(
- action -> action.getIntent()
- .putExtra(TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER, true));
+ action -> action.intent.putExtra(TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER, true));
return labeledIntents;
}
@@ -73,7 +73,8 @@
Log.w(TAG, "Invalid RemoteActionTemplate: is null");
return false;
}
- if (TextUtils.isEmpty(remoteActionTemplate.title)) {
+ if (TextUtils.isEmpty(remoteActionTemplate.titleWithEntity)
+ && TextUtils.isEmpty(remoteActionTemplate.titleWithoutEntity)) {
Log.w(TAG, "Invalid RemoteActionTemplate: title is null");
return false;
}
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index e628f19..632328b 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -19,21 +19,14 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.WorkerThread;
-import android.app.PendingIntent;
import android.app.RemoteAction;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.graphics.drawable.Icon;
import android.icu.util.ULocale;
import android.os.Bundle;
import android.os.LocaleList;
import android.os.ParcelFileDescriptor;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
@@ -430,7 +423,12 @@
// Given that we only support implicit intent here, we should expect there is just one
// intent for each action type.
if (!labeledIntents.isEmpty()) {
- remoteAction = labeledIntents.get(0).asRemoteAction(mContext);
+ LabeledIntent.TitleChooser titleChooser =
+ ActionsSuggestionsHelper.createTitleChooser(actionType);
+ LabeledIntent.Result result = labeledIntents.get(0).resolve(mContext, titleChooser);
+ if (result != null) {
+ remoteAction = result.remoteAction;
+ }
}
conversationActions.add(
new ConversationAction.Builder(actionType)
@@ -593,23 +591,26 @@
foreignLanguageBundle != null,
referenceTime,
highestScoringResult);
+ LabeledIntent.TitleChooser titleChooser =
+ (labeledIntent, resolveInfo) -> labeledIntent.titleWithoutEntity;
for (LabeledIntent labeledIntent : labeledIntents) {
- final RemoteAction action = labeledIntent.asRemoteAction(mContext);
- if (action == null) {
+ LabeledIntent.Result result = labeledIntent.resolve(mContext, titleChooser);
+ if (result == null) {
continue;
}
+ final RemoteAction action = result.remoteAction;
if (isPrimaryAction) {
// For O backwards compatibility, the first RemoteAction is also written to the
// legacy API fields.
builder.setIcon(action.getIcon().loadDrawable(mContext));
builder.setLabel(action.getTitle().toString());
- builder.setIntent(labeledIntent.getIntent());
+ builder.setIntent(result.resolvedIntent);
builder.setOnClickListener(TextClassification.createIntentOnClickListener(
TextClassification.createPendingIntent(mContext,
- labeledIntent.getIntent(), labeledIntent.getRequestCode())));
+ result.resolvedIntent, labeledIntent.requestCode)));
isPrimaryAction = false;
}
- builder.addAction(action, labeledIntent.getIntent());
+ builder.addAction(action, result.resolvedIntent);
}
return builder.setId(createId(text, start, end)).build();
@@ -737,89 +738,5 @@
return LocaleList.getDefault().get(0).toLanguageTag();
}
}
-
- /**
- * Helper class to store the information from which RemoteActions are built.
- */
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
- public static final class LabeledIntent {
-
- static final int DEFAULT_REQUEST_CODE = 0;
-
- private final String mTitle;
- private final String mDescription;
- private final Intent mIntent;
- private final int mRequestCode;
-
- /**
- * Initializes a LabeledIntent.
- *
- * <p>NOTE: {@code reqestCode} is required to not be {@link #DEFAULT_REQUEST_CODE}
- * if distinguishing info (e.g. the classified text) is represented in intent extras only.
- * In such circumstances, the request code should represent the distinguishing info
- * (e.g. by generating a hashcode) so that the generated PendingIntent is (somewhat)
- * unique. To be correct, the PendingIntent should be definitely unique but we try a
- * best effort approach that avoids spamming the system with PendingIntents.
- */
- // TODO: Fix the issue mentioned above so the behaviour is correct.
- LabeledIntent(String title, String description, Intent intent, int requestCode) {
- mTitle = title;
- mDescription = description;
- mIntent = intent;
- mRequestCode = requestCode;
- }
-
- @VisibleForTesting
- public String getTitle() {
- return mTitle;
- }
-
- @VisibleForTesting
- public String getDescription() {
- return mDescription;
- }
-
- @VisibleForTesting
- public Intent getIntent() {
- return mIntent;
- }
-
- @VisibleForTesting
- public int getRequestCode() {
- return mRequestCode;
- }
-
- @Nullable
- RemoteAction asRemoteAction(Context context) {
- final PackageManager pm = context.getPackageManager();
- final ResolveInfo resolveInfo = pm.resolveActivity(mIntent, 0);
- final String packageName = resolveInfo != null && resolveInfo.activityInfo != null
- ? resolveInfo.activityInfo.packageName : null;
- Icon icon = null;
- boolean shouldShowIcon = false;
- if (packageName != null && !"android".equals(packageName)) {
- // There is a default activity handling the intent.
- mIntent.setComponent(new ComponentName(packageName, resolveInfo.activityInfo.name));
- if (resolveInfo.activityInfo.getIconResource() != 0) {
- icon = Icon.createWithResource(
- packageName, resolveInfo.activityInfo.getIconResource());
- shouldShowIcon = true;
- }
- }
- if (icon == null) {
- // RemoteAction requires that there be an icon.
- icon = Icon.createWithResource("android",
- com.android.internal.R.drawable.ic_more_items);
- }
- final PendingIntent pendingIntent =
- TextClassification.createPendingIntent(context, mIntent, mRequestCode);
- if (pendingIntent == null) {
- return null;
- }
- final RemoteAction action = new RemoteAction(icon, mTitle, mDescription, pendingIntent);
- action.setShouldShowIcon(shouldShowIcon);
- return action;
- }
- }
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index ded3be4e..b6ec5f9 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1947,7 +1947,7 @@
// Rebuild display list if it is invalid
if (blockDisplayListIsInvalid) {
- final RecordingCanvas recordingCanvas = blockDisplayList.start(
+ final RecordingCanvas recordingCanvas = blockDisplayList.beginRecording(
right - left, bottom - top);
try {
// drawText is always relative to TextView's origin, this translation
@@ -1958,7 +1958,7 @@
// No need to untranslate, previous context is popped after
// drawDisplayList
} finally {
- blockDisplayList.end(recordingCanvas);
+ blockDisplayList.endRecording();
// Same as drawDisplayList below, handled by our TextView's parent
blockDisplayList.setClipToBounds(false);
}
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index a6129b0..f44c331 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -312,7 +312,7 @@
* @return The view that is currently selected, if it happens to be in the
* range that we draw.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private View fillDown(int pos, int nextTop) {
View selectedView = null;
@@ -412,7 +412,7 @@
*
* @return The view that is currently selected
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private View fillUp(int pos, int nextBottom) {
View selectedView = null;
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 2aa019b..2f44d6e 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -782,7 +782,7 @@
* @return The view that is currently selected, if it happens to be in the
* range that we draw.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private View fillDown(int pos, int nextTop) {
View selectedView = null;
@@ -817,7 +817,7 @@
*
* @return The view that is currently selected
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private View fillUp(int pos, int nextBottom) {
View selectedView = null;
@@ -1490,7 +1490,7 @@
* @return The selected view, or null if the selected view is outside the
* visible area.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private View fillSpecific(int position, int top) {
boolean tempIsSelected = position == mSelectedPosition;
View temp = makeAndAddView(position, top, true, mListPadding.left, tempIsSelected);
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 249f499..b7cdad2 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -859,7 +859,7 @@
);
setupOverlay();
- final RecordingCanvas canvas = mRenderer.getRootNode().start(width, height);
+ final RecordingCanvas canvas = mRenderer.getRootNode().beginRecording(width, height);
try {
canvas.insertReorderBarrier();
canvas.drawRenderNode(mBitmapRenderNode);
@@ -867,7 +867,7 @@
canvas.drawRenderNode(mOverlayRenderNode);
canvas.insertInorderBarrier();
} finally {
- mRenderer.getRootNode().end(canvas);
+ mRenderer.getRootNode().endRecording();
}
if (mCallback != null) {
mCurrentContent =
@@ -898,11 +898,12 @@
bitmapRenderNode.setClipToOutline(true);
// Create a dummy draw, which will be replaced later with real drawing.
- final RecordingCanvas canvas = bitmapRenderNode.start(mContentWidth, mContentHeight);
+ final RecordingCanvas canvas = bitmapRenderNode.beginRecording(
+ mContentWidth, mContentHeight);
try {
canvas.drawColor(0xFF00FF00);
} finally {
- bitmapRenderNode.end(canvas);
+ bitmapRenderNode.endRecording();
}
return bitmapRenderNode;
@@ -954,7 +955,7 @@
// Draw the drawable to the render node. This happens once during
// initialization and whenever the overlay drawable is invalidated.
final RecordingCanvas canvas =
- mOverlayRenderNode.startRecording(mContentWidth, mContentHeight);
+ mOverlayRenderNode.beginRecording(mContentWidth, mContentHeight);
try {
mOverlay.setBounds(0, 0, mContentWidth, mContentHeight);
mOverlay.draw(canvas);
@@ -1035,7 +1036,7 @@
}
final RecordingCanvas canvas =
- mBitmapRenderNode.start(mContentWidth, mContentHeight);
+ mBitmapRenderNode.beginRecording(mContentWidth, mContentHeight);
try {
final Rect srcRect = new Rect(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
final Rect dstRect = new Rect(0, 0, mContentWidth, mContentHeight);
@@ -1043,7 +1044,7 @@
paint.setFilterBitmap(true);
canvas.drawBitmap(mBitmap, srcRect, dstRect, paint);
} finally {
- mBitmapRenderNode.end(canvas);
+ mBitmapRenderNode.endRecording();
}
if (mPendingWindowPositionUpdate || mFirstDraw) {
@@ -1061,7 +1062,6 @@
return;
}
synchronized (mLock) {
- mRenderer.setLightCenter(mDisplay, pendingX, pendingY);
// Show or move the window at the content draw frame.
SurfaceControl.openTransaction();
mSurfaceControl.deferTransactionUntil(mSurface, frame);
@@ -1076,6 +1076,7 @@
}
}
};
+ mRenderer.setLightCenter(mDisplay, pendingX, pendingY);
} else {
callback = null;
}
diff --git a/core/java/com/android/internal/infra/AbstractRemoteService.java b/core/java/com/android/internal/infra/AbstractRemoteService.java
index 732553b..f4c904d 100644
--- a/core/java/com/android/internal/infra/AbstractRemoteService.java
+++ b/core/java/com/android/internal/infra/AbstractRemoteService.java
@@ -67,7 +67,7 @@
private static final int MSG_BIND = 1;
private static final int MSG_UNBIND = 2;
- protected static final long PERMANENT_BOUND_TIMEOUT_MS = 0;
+ public static final long PERMANENT_BOUND_TIMEOUT_MS = 0;
protected static final int LAST_PRIVATE_MSG = MSG_UNBIND;
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index 2c78be7..bab4787 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -568,7 +568,7 @@
}
/**
- * Aggregated data by uid/class/method to be sent through WestWorld.
+ * Aggregated data by uid/class/method to be sent through statsd.
*/
public static class ExportedCallStat {
public int callingUid;
diff --git a/core/java/com/android/internal/os/RoSystemProperties.java b/core/java/com/android/internal/os/RoSystemProperties.java
index f4902d4..524a5cc 100644
--- a/core/java/com/android/internal/os/RoSystemProperties.java
+++ b/core/java/com/android/internal/os/RoSystemProperties.java
@@ -61,17 +61,19 @@
SystemProperties.getBoolean("ro.fw.system_user_split", false);
// ------ ro.crypto.* -------- //
- public static final String CRYPTO_STATE = SystemProperties.get("ro.crypto.state");
- public static final String CRYPTO_TYPE = CryptoProperties.type().orElse("");
+ public static final CryptoProperties.state_values CRYPTO_STATE =
+ CryptoProperties.state().orElse(CryptoProperties.state_values.UNSUPPORTED);
+ public static final CryptoProperties.type_values CRYPTO_TYPE =
+ CryptoProperties.type().orElse(CryptoProperties.type_values.NONE);
// These are pseudo-properties
public static final boolean CRYPTO_ENCRYPTABLE =
- !CRYPTO_STATE.isEmpty() && !"unsupported".equals(CRYPTO_STATE);
+ CRYPTO_STATE != CryptoProperties.state_values.UNSUPPORTED;
public static final boolean CRYPTO_ENCRYPTED =
- "encrypted".equalsIgnoreCase(CRYPTO_STATE);
+ CRYPTO_STATE == CryptoProperties.state_values.ENCRYPTED;
public static final boolean CRYPTO_FILE_ENCRYPTED =
- "file".equalsIgnoreCase(CRYPTO_TYPE);
+ CRYPTO_TYPE == CryptoProperties.type_values.FILE;
public static final boolean CRYPTO_BLOCK_ENCRYPTED =
- "block".equalsIgnoreCase(CRYPTO_TYPE);
+ CRYPTO_TYPE == CryptoProperties.type_values.BLOCK;
public static final boolean CONTROL_PRIVAPP_PERMISSIONS_LOG =
"log".equalsIgnoreCase(CONTROL_PRIVAPP_PERMISSIONS);
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 0604ab2..58b48d8 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -162,9 +162,9 @@
/**
* The duration to wait before re-checking Zygote related system properties.
*
- * Five minutes in milliseconds.
+ * One minute in milliseconds.
*/
- public static final long PROPERTY_CHECK_INTERVAL = 300000;
+ public static final long PROPERTY_CHECK_INTERVAL = 60000;
/**
* @hide for internal use only
@@ -236,14 +236,14 @@
public static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
- String packageName, String[] packagesForUID, String[] visibleVolIDs, String sandboxId) {
+ String packageName, String[] packagesForUID, String sandboxId) {
ZygoteHooks.preFork();
// Resets nice priority for zygote process.
resetNicePriority();
int pid = nativeForkAndSpecialize(
uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
fdsToIgnore, startChildZygote, instructionSet, appDataDir, packageName,
- packagesForUID, visibleVolIDs, sandboxId);
+ packagesForUID, sandboxId);
// Enable tracing as soon as possible for the child process.
if (pid == 0) {
Trace.setTracingEnabled(true, runtimeFlags);
@@ -258,7 +258,7 @@
private static native int nativeForkAndSpecialize(int uid, int gid, int[] gids,
int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName,
int[] fdsToClose, int[] fdsToIgnore, boolean startChildZygote, String instructionSet,
- String appDataDir, String packageName, String[] packagesForUID, String[] visibleVolIDs,
+ String appDataDir, String packageName, String[] packagesForUID,
String sandboxId);
/**
@@ -285,11 +285,11 @@
public static void specializeBlastula(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName,
boolean startChildZygote, String instructionSet, String appDataDir, String packageName,
- String[] packagesForUID, String[] visibleVolIDs, String sandboxId) {
+ String[] packagesForUID, String sandboxId) {
nativeSpecializeBlastula(uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo,
niceName, startChildZygote, instructionSet, appDataDir,
- packageName, packagesForUID, visibleVolIDs, sandboxId);
+ packageName, packagesForUID, sandboxId);
// Enable tracing as soon as possible for the child process.
Trace.setTracingEnabled(true, runtimeFlags);
@@ -309,7 +309,7 @@
private static native void nativeSpecializeBlastula(int uid, int gid, int[] gids,
int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName,
boolean startChildZygote, String instructionSet, String appDataDir, String packageName,
- String[] packagesForUID, String[] visibleVolIDs, String sandboxId);
+ String[] packagesForUID, String sandboxId);
/**
* Called to do any initialization before starting an application.
@@ -427,6 +427,12 @@
defaultValue);
}
+ protected static void emptyBlastulaPool() {
+ nativeEmptyBlastulaPool();
+ }
+
+ private static native void nativeEmptyBlastulaPool();
+
/**
* Returns the value of a system property converted to a boolean using specific logic.
*
@@ -520,7 +526,7 @@
LocalSocket sessionSocket = null;
DataOutputStream blastulaOutputStream = null;
Credentials peerCredentials = null;
- String[] argStrings = null;
+ ZygoteArguments args = null;
while (true) {
try {
@@ -533,25 +539,24 @@
peerCredentials = sessionSocket.getPeerCredentials();
- argStrings = readArgumentList(blastulaReader);
+ String[] argStrings = readArgumentList(blastulaReader);
if (argStrings != null) {
+ args = new ZygoteArguments(argStrings);
+
+ // TODO (chriswailes): Should this only be run for debug builds?
+ validateBlastulaCommand(args);
break;
} else {
Log.e("Blastula", "Truncated command received.");
IoUtils.closeQuietly(sessionSocket);
}
- } catch (IOException ioEx) {
- Log.e("Blastula", "Failed to read command: " + ioEx.getMessage());
+ } catch (Exception ex) {
+ Log.e("Blastula", ex.getMessage());
IoUtils.closeQuietly(sessionSocket);
}
}
- ZygoteArguments args = new ZygoteArguments(argStrings);
-
- // TODO (chriswailes): Should this only be run for debug builds?
- validateBlastulaCommand(args);
-
applyUidSecurityPolicy(args, peerCredentials);
applyDebuggerSystemProperty(args);
@@ -600,7 +605,7 @@
args.mRuntimeFlags, rlimits, args.mMountExternal,
args.mSeInfo, args.mNiceName, args.mStartChildZygote,
args.mInstructionSet, args.mAppDataDir, args.mPackageName,
- args.mPackagesForUid, args.mVisibleVolIds, args.mSandboxId);
+ args.mPackagesForUid, args.mSandboxId);
if (args.mNiceName != null) {
Process.setArgV0(args.mNiceName);
@@ -740,8 +745,8 @@
if (args.mInvokeWith != null && peerUid != 0
&& (args.mRuntimeFlags & Zygote.DEBUG_ENABLE_JDWP) == 0) {
- throw new ZygoteSecurityException("Peer is permitted to specify an"
- + "explicit invoke-with wrapper command only for debuggable"
+ throw new ZygoteSecurityException("Peer is permitted to specify an "
+ + "explicit invoke-with wrapper command only for debuggable "
+ "applications.");
}
}
diff --git a/core/java/com/android/internal/os/ZygoteArguments.java b/core/java/com/android/internal/os/ZygoteArguments.java
index c24a9e0..9cb5820 100644
--- a/core/java/com/android/internal/os/ZygoteArguments.java
+++ b/core/java/com/android/internal/os/ZygoteArguments.java
@@ -101,6 +101,12 @@
String mSeInfo;
/**
+ *
+ */
+ boolean mBlastulaPoolEnabled;
+ boolean mBlastulaPoolStatusSpecified = false;
+
+ /**
* from all --rlimit=r,c,m
*/
ArrayList<int[]> mRLimits;
@@ -116,9 +122,6 @@
/** from --packages-for-uid */
String[] mPackagesForUid;
- /** from --visible-vols */
- String[] mVisibleVolIds;
-
/** from --sandbox-id */
String mSandboxId;
@@ -395,13 +398,15 @@
mPackageName = arg.substring(arg.indexOf('=') + 1);
} else if (arg.startsWith("--packages-for-uid=")) {
mPackagesForUid = arg.substring(arg.indexOf('=') + 1).split(",");
- } else if (arg.startsWith("--visible-vols=")) {
- mVisibleVolIds = arg.substring(arg.indexOf('=') + 1).split(",");
} else if (arg.startsWith("--sandbox-id=")) {
if (mSandboxId != null) {
throw new IllegalArgumentException("Duplicate arg specified");
}
mSandboxId = arg.substring(arg.indexOf('=') + 1);
+ } else if (arg.startsWith("--blastula-pool-enabled=")) {
+ mBlastulaPoolStatusSpecified = true;
+ mBlastulaPoolEnabled = Boolean.parseBoolean(arg.substring(arg.indexOf('=') + 1));
+ expectRuntimeArgs = false;
} else {
break;
}
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 8a878e2..c7ba22d 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -158,6 +158,10 @@
return null;
}
+ if (parsedArgs.mBlastulaPoolStatusSpecified) {
+ return handleBlastulaPoolStatusChange(zygoteServer, parsedArgs.mBlastulaPoolEnabled);
+ }
+
if (parsedArgs.mPreloadDefault) {
handlePreload();
return null;
@@ -185,13 +189,12 @@
}
if (parsedArgs.mApiBlacklistExemptions != null) {
- handleApiBlacklistExemptions(parsedArgs.mApiBlacklistExemptions);
- return null;
+ return handleApiBlacklistExemptions(zygoteServer, parsedArgs.mApiBlacklistExemptions);
}
if (parsedArgs.mHiddenApiAccessLogSampleRate != -1) {
- handleHiddenApiAccessLogSampleRate(parsedArgs.mHiddenApiAccessLogSampleRate);
- return null;
+ return handleHiddenApiAccessLogSampleRate(zygoteServer,
+ parsedArgs.mHiddenApiAccessLogSampleRate);
}
if (parsedArgs.mPermittedCapabilities != 0 || parsedArgs.mEffectiveCapabilities != 0) {
@@ -258,7 +261,7 @@
parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo,
parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mPackageName,
- parsedArgs.mPackagesForUid, parsedArgs.mVisibleVolIds, parsedArgs.mSandboxId);
+ parsedArgs.mPackagesForUid, parsedArgs.mSandboxId);
try {
if (pid == 0) {
@@ -325,10 +328,64 @@
}
}
- private void handleApiBlacklistExemptions(String[] exemptions) {
+ private Runnable stateChangeWithBlastulaPoolReset(ZygoteServer zygoteServer,
+ Runnable stateChangeCode) {
try {
- ZygoteInit.setApiBlacklistExemptions(exemptions);
+ if (zygoteServer.isBlastulaPoolEnabled()) {
+ Zygote.emptyBlastulaPool();
+ }
+
+ stateChangeCode.run();
+
+ if (zygoteServer.isBlastulaPoolEnabled()) {
+ Runnable fpResult =
+ zygoteServer.fillBlastulaPool(
+ new int[]{mSocket.getFileDescriptor().getInt$()});
+
+ if (fpResult != null) {
+ zygoteServer.setForkChild();
+ return fpResult;
+ }
+ }
+
mSocketOutStream.writeInt(0);
+
+ return null;
+ } catch (IOException ioe) {
+ throw new IllegalStateException("Error writing to command socket", ioe);
+ }
+ }
+
+ /**
+ * Makes the necessary changes to implement a new API blacklist exemption policy, and then
+ * responds to the system server, letting it know that the task has been completed.
+ *
+ * This necessitates a change to the internal state of the Zygote. As such, if the blastula
+ * pool is enabled all existing blastulas have an incorrect API blacklist exemption list. To
+ * properly handle this request the pool must be emptied and refilled. This process can return
+ * a Runnable object that must be returned to ZygoteServer.runSelectLoop to be invoked.
+ *
+ * @param zygoteServer The server object that received the request
+ * @param exemptions The new exemption list.
+ * @return A Runnable object representing a new app in any blastulas spawned from here; the
+ * zygote process will always receive a null value from this function.
+ */
+ private Runnable handleApiBlacklistExemptions(ZygoteServer zygoteServer, String[] exemptions) {
+ return stateChangeWithBlastulaPoolReset(zygoteServer,
+ () -> ZygoteInit.setApiBlacklistExemptions(exemptions));
+ }
+
+ private Runnable handleBlastulaPoolStatusChange(ZygoteServer zygoteServer, boolean newStatus) {
+ try {
+ Runnable fpResult = zygoteServer.setBlastulaPoolStatus(newStatus, mSocket);
+
+ if (fpResult == null) {
+ mSocketOutStream.writeInt(0);
+ } else {
+ zygoteServer.setForkChild();
+ }
+
+ return fpResult;
} catch (IOException ioe) {
throw new IllegalStateException("Error writing to command socket", ioe);
}
@@ -384,15 +441,26 @@
}
}
- private void handleHiddenApiAccessLogSampleRate(int samplingRate) {
- try {
+ /**
+ * Changes the API access log sample rate for the Zygote and processes spawned from it.
+ *
+ * This necessitates a change to the internal state of the Zygote. As such, if the blastula
+ * pool is enabled all existing blastulas have an incorrect API access log sample rate. To
+ * properly handle this request the pool must be emptied and refilled. This process can return
+ * a Runnable object that must be returned to ZygoteServer.runSelectLoop to be invoked.
+ *
+ * @param zygoteServer The server object that received the request
+ * @param samplingRate The new sample rate
+ * @return A Runnable object representing a new app in any blastulas spawned from here; the
+ * zygote process will always receive a null value from this function.
+ */
+ private Runnable handleHiddenApiAccessLogSampleRate(ZygoteServer zygoteServer,
+ int samplingRate) {
+ return stateChangeWithBlastulaPoolReset(zygoteServer, () -> {
ZygoteInit.setHiddenApiAccessLogSampleRate(samplingRate);
HiddenApiUsageLogger.setHiddenApiAccessLogSampleRate(samplingRate);
ZygoteInit.setHiddenApiUsageLogger(HiddenApiUsageLogger.getInstance());
- mSocketOutStream.writeInt(0);
- } catch (IOException ioe) {
- throw new IllegalStateException("Error writing to command socket", ioe);
- }
+ });
}
protected void preload() {
diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java
index 2c17540..c4c98ba 100644
--- a/core/java/com/android/internal/os/ZygoteServer.java
+++ b/core/java/com/android/internal/os/ZygoteServer.java
@@ -68,6 +68,15 @@
private static final String BLASTULA_POOL_SIZE_MIN_DEFAULT = "1";
/**
+ * Indicates if this Zygote server can support a blastula pool. Currently this should only be
+ * true for the primary and secondary Zygotes, and not the App Zygotes or the WebView Zygote.
+ *
+ * TODO (chriswailes): Make this an explicit argument to the constructor
+ */
+
+ private final boolean mBlastulaPoolSupported;
+
+ /**
* If the blastula pool should be created and used to start applications.
*
* Setting this value to false will disable the creation, maintenance, and use of the blastula
@@ -127,6 +136,8 @@
mBlastulaPoolEventFD = null;
mZygoteSocket = null;
mBlastulaPoolSocket = null;
+
+ mBlastulaPoolSupported = false;
}
/**
@@ -151,12 +162,18 @@
}
fetchBlastulaPoolPolicyProps();
+
+ mBlastulaPoolSupported = true;
}
void setForkChild() {
mIsForkChild = true;
}
+ public boolean isBlastulaPoolEnabled() {
+ return mBlastulaPoolEnabled;
+ }
+
/**
* Registers a server socket for zygote command connections. This opens the server socket
* at the specified name in the abstract socket namespace.
@@ -224,42 +241,43 @@
}
private void fetchBlastulaPoolPolicyProps() {
- final String blastulaPoolSizeMaxPropString =
- Zygote.getSystemProperty(
- DeviceConfig.RuntimeNative.BLASTULA_POOL_SIZE_MAX,
- BLASTULA_POOL_SIZE_MAX_DEFAULT);
+ if (mBlastulaPoolSupported) {
+ final String blastulaPoolSizeMaxPropString =
+ Zygote.getSystemProperty(
+ DeviceConfig.RuntimeNative.BLASTULA_POOL_SIZE_MAX,
+ BLASTULA_POOL_SIZE_MAX_DEFAULT);
- if (!blastulaPoolSizeMaxPropString.isEmpty()) {
- mBlastulaPoolSizeMax =
- Integer.min(
- Integer.parseInt(blastulaPoolSizeMaxPropString),
- BLASTULA_POOL_SIZE_MAX_LIMIT);
+ if (!blastulaPoolSizeMaxPropString.isEmpty()) {
+ mBlastulaPoolSizeMax =
+ Integer.min(
+ Integer.parseInt(blastulaPoolSizeMaxPropString),
+ BLASTULA_POOL_SIZE_MAX_LIMIT);
+ }
+
+ final String blastulaPoolSizeMinPropString =
+ Zygote.getSystemProperty(
+ DeviceConfig.RuntimeNative.BLASTULA_POOL_SIZE_MIN,
+ BLASTULA_POOL_SIZE_MIN_DEFAULT);
+
+ if (!blastulaPoolSizeMinPropString.isEmpty()) {
+ mBlastulaPoolSizeMin =
+ Integer.max(
+ Integer.parseInt(blastulaPoolSizeMinPropString),
+ BLASTULA_POOL_SIZE_MIN_LIMIT);
+ }
+
+ final String blastulaPoolRefillThresholdPropString =
+ Zygote.getSystemProperty(
+ DeviceConfig.RuntimeNative.BLASTULA_POOL_REFILL_THRESHOLD,
+ Integer.toString(mBlastulaPoolSizeMax / 2));
+
+ if (!blastulaPoolRefillThresholdPropString.isEmpty()) {
+ mBlastulaPoolRefillThreshold =
+ Integer.min(
+ Integer.parseInt(blastulaPoolRefillThresholdPropString),
+ mBlastulaPoolSizeMax);
+ }
}
-
- final String blastulaPoolSizeMinPropString =
- Zygote.getSystemProperty(
- DeviceConfig.RuntimeNative.BLASTULA_POOL_SIZE_MIN,
- BLASTULA_POOL_SIZE_MIN_DEFAULT);
-
- if (!blastulaPoolSizeMinPropString.isEmpty()) {
- mBlastulaPoolSizeMin =
- Integer.max(
- Integer.parseInt(blastulaPoolSizeMinPropString),
- BLASTULA_POOL_SIZE_MIN_LIMIT);
- }
-
- final String blastulaPoolRefillThresholdPropString =
- Zygote.getSystemProperty(
- DeviceConfig.RuntimeNative.BLASTULA_POOL_REFILL_THRESHOLD,
- Integer.toString(mBlastulaPoolSizeMax / 2));
-
- if (!blastulaPoolRefillThresholdPropString.isEmpty()) {
- mBlastulaPoolRefillThreshold =
- Integer.min(
- Integer.parseInt(blastulaPoolRefillThresholdPropString),
- mBlastulaPoolSizeMax);
- }
-
}
private long mLastPropCheckTimestamp = 0;
@@ -282,44 +300,65 @@
* this function will return a Runnable object representing the new application that is
* passed up from blastulaMain.
*/
- private Runnable fillBlastulaPool(int[] sessionSocketRawFDs) {
- if (mBlastulaPoolEnabled) {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Zygote:FillBlastulaPool");
- int blastulaPoolCount = Zygote.getBlastulaPoolCount();
- int numBlastulasToSpawn = mBlastulaPoolSizeMax - blastulaPoolCount;
+ Runnable fillBlastulaPool(int[] sessionSocketRawFDs) {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Zygote:FillBlastulaPool");
- if (blastulaPoolCount < mBlastulaPoolSizeMin
- || numBlastulasToSpawn >= mBlastulaPoolRefillThreshold) {
+ int blastulaPoolCount = Zygote.getBlastulaPoolCount();
+ int numBlastulasToSpawn = mBlastulaPoolSizeMax - blastulaPoolCount;
- // Disable some VM functionality and reset some system values
- // before forking.
- ZygoteHooks.preFork();
- Zygote.resetNicePriority();
+ if (blastulaPoolCount < mBlastulaPoolSizeMin
+ || numBlastulasToSpawn >= mBlastulaPoolRefillThreshold) {
- while (blastulaPoolCount++ < mBlastulaPoolSizeMax) {
- Runnable caller = Zygote.forkBlastula(mBlastulaPoolSocket, sessionSocketRawFDs);
+ // Disable some VM functionality and reset some system values
+ // before forking.
+ ZygoteHooks.preFork();
+ Zygote.resetNicePriority();
- if (caller != null) {
- return caller;
- }
+ while (blastulaPoolCount++ < mBlastulaPoolSizeMax) {
+ Runnable caller = Zygote.forkBlastula(mBlastulaPoolSocket, sessionSocketRawFDs);
+
+ if (caller != null) {
+ return caller;
}
-
- // Re-enable runtime services for the Zygote. Blastula services
- // are re-enabled in specializeBlastula.
- ZygoteHooks.postForkCommon();
-
- Log.i("zygote",
- "Filled the blastula pool. New blastulas: " + numBlastulasToSpawn);
}
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ // Re-enable runtime services for the Zygote. Blastula services
+ // are re-enabled in specializeBlastula.
+ ZygoteHooks.postForkCommon();
+
+ Log.i("zygote",
+ "Filled the blastula pool. New blastulas: " + numBlastulasToSpawn);
}
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+
return null;
}
/**
+ * Empty or fill the blastula pool as dictated by the current and new blastula pool statuses.
+ */
+ Runnable setBlastulaPoolStatus(boolean newStatus, LocalSocket sessionSocket) {
+ if (!mBlastulaPoolSupported) {
+ Log.w(TAG,
+ "Attempting to enable a blastula pool for a Zygote that doesn't support it.");
+ return null;
+ } else if (mBlastulaPoolEnabled == newStatus) {
+ return null;
+ }
+
+ mBlastulaPoolEnabled = newStatus;
+
+ if (newStatus) {
+ return fillBlastulaPool(new int[]{ sessionSocket.getFileDescriptor().getInt$() });
+ } else {
+ Zygote.emptyBlastulaPool();
+ return null;
+ }
+ }
+
+ /**
* Runs the zygote process's select loop. Accepts new connections as
* they happen, and reads commands from connections one spawn-request's
* worth at a time.
@@ -334,12 +373,26 @@
while (true) {
fetchBlastulaPoolPolicyPropsWithMinInterval();
- int[] blastulaPipeFDs = Zygote.getBlastulaPipeFDs();
+ int[] blastulaPipeFDs = null;
+ StructPollfd[] pollFDs = null;
- // Space for all of the socket FDs, the Blastula Pool Event FD, and
- // all of the open blastula read pipe FDs.
- StructPollfd[] pollFDs =
- new StructPollfd[socketFDs.size() + 1 + blastulaPipeFDs.length];
+ // Allocate enough space for the poll structs, taking into account
+ // the state of the blastula pool for this Zygote (could be a
+ // regular Zygote, a WebView Zygote, or an AppZygote).
+ if (mBlastulaPoolEnabled) {
+ blastulaPipeFDs = Zygote.getBlastulaPipeFDs();
+ pollFDs = new StructPollfd[socketFDs.size() + 1 + blastulaPipeFDs.length];
+ } else {
+ pollFDs = new StructPollfd[socketFDs.size()];
+ }
+
+ /*
+ * For reasons of correctness the blastula pool pipe and event FDs
+ * must be processed before the session and server sockets. This
+ * is to ensure that the blastula pool accounting information is
+ * accurate when handling other requests like API blacklist
+ * exemptions.
+ */
int pollIndex = 0;
for (FileDescriptor socketFD : socketFDs) {
@@ -350,19 +403,22 @@
}
final int blastulaPoolEventFDIndex = pollIndex;
- pollFDs[pollIndex] = new StructPollfd();
- pollFDs[pollIndex].fd = mBlastulaPoolEventFD;
- pollFDs[pollIndex].events = (short) POLLIN;
- ++pollIndex;
- for (int blastulaPipeFD : blastulaPipeFDs) {
- FileDescriptor managedFd = new FileDescriptor();
- managedFd.setInt$(blastulaPipeFD);
-
+ if (mBlastulaPoolEnabled) {
pollFDs[pollIndex] = new StructPollfd();
- pollFDs[pollIndex].fd = managedFd;
+ pollFDs[pollIndex].fd = mBlastulaPoolEventFD;
pollFDs[pollIndex].events = (short) POLLIN;
++pollIndex;
+
+ for (int blastulaPipeFD : blastulaPipeFDs) {
+ FileDescriptor managedFd = new FileDescriptor();
+ managedFd.setInt$(blastulaPipeFD);
+
+ pollFDs[pollIndex] = new StructPollfd();
+ pollFDs[pollIndex].fd = managedFd;
+ pollFDs[pollIndex].events = (short) POLLIN;
+ ++pollIndex;
+ }
}
try {
@@ -371,6 +427,8 @@
throw new RuntimeException("poll failed", ex);
}
+ boolean blastulaPoolFDRead = false;
+
while (--pollIndex >= 0) {
if ((pollFDs[pollIndex].revents & POLLIN) == 0) {
continue;
@@ -390,6 +448,7 @@
ZygoteConnection connection = peers.get(pollIndex);
final Runnable command = connection.processOneCommand(this);
+ // TODO (chriswailes): Is this extra check necessary?
if (mIsForkChild) {
// We're in the child. We should always have a command to run at this
// stage if processOneCommand hasn't called "exec".
@@ -480,17 +539,22 @@
Zygote.removeBlastulaTableEntry((int) messagePayload);
}
- int[] sessionSocketRawFDs =
- socketFDs.subList(1, socketFDs.size())
+ blastulaPoolFDRead = true;
+ }
+ }
+
+ // Check to see if the blastula pool needs to be refilled.
+ if (blastulaPoolFDRead) {
+ int[] sessionSocketRawFDs =
+ socketFDs.subList(1, socketFDs.size())
.stream()
.mapToInt(fd -> fd.getInt$())
.toArray();
- final Runnable command = fillBlastulaPool(sessionSocketRawFDs);
+ final Runnable command = fillBlastulaPool(sessionSocketRawFDs);
- if (command != null) {
- return command;
- }
+ if (command != null) {
+ return command;
}
}
}
diff --git a/core/java/com/android/internal/policy/BackdropFrameRenderer.java b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
index cc958f4..fa73758 100644
--- a/core/java/com/android/internal/policy/BackdropFrameRenderer.java
+++ b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
@@ -339,7 +339,7 @@
mFrameAndBackdropNode.setLeftTopRightBottom(left, top, left + width, top + height);
// Draw the caption and content backdrops in to our render node.
- RecordingCanvas canvas = mFrameAndBackdropNode.start(width, height);
+ RecordingCanvas canvas = mFrameAndBackdropNode.beginRecording(width, height);
final Drawable drawable = mUserCaptionBackgroundDrawable != null
? mUserCaptionBackgroundDrawable : mCaptionBackgroundDrawable;
@@ -353,7 +353,7 @@
mResizingBackgroundDrawable.setBounds(0, mLastCaptionHeight, left + width, top + height);
mResizingBackgroundDrawable.draw(canvas);
}
- mFrameAndBackdropNode.end(canvas);
+ mFrameAndBackdropNode.endRecording();
drawColorViews(left, top, width, height, fullscreen, systemInsets, stableInsets);
@@ -368,7 +368,7 @@
if (mSystemBarBackgroundNode == null) {
return;
}
- RecordingCanvas canvas = mSystemBarBackgroundNode.start(width, height);
+ RecordingCanvas canvas = mSystemBarBackgroundNode.beginRecording(width, height);
mSystemBarBackgroundNode.setLeftTopRightBottom(left, top, left + width, top + height);
final int topInset = DecorView.getColorViewTopInset(mStableInsets.top, mSystemInsets.top);
if (mStatusBarColor != null) {
@@ -384,7 +384,7 @@
mNavigationBarColor.setBounds(mTmpRect);
mNavigationBarColor.draw(canvas);
}
- mSystemBarBackgroundNode.end(canvas);
+ mSystemBarBackgroundNode.endRecording();
mRenderer.drawRenderNode(mSystemBarBackgroundNode);
}
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index 9a77802..1c5816c 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -36,17 +36,17 @@
boolean getBoolean(in String key, in boolean defaultValue, in int userId);
long getLong(in String key, in long defaultValue, in int userId);
String getString(in String key, in String defaultValue, in int userId);
- void setLockCredential(in String credential, int type, in String savedCredential, int requestedQuality, int userId);
+ void setLockCredential(in byte[] credential, int type, in byte[] savedCredential, int requestedQuality, int userId);
void resetKeyStore(int userId);
- VerifyCredentialResponse checkCredential(in String credential, int type, int userId,
+ VerifyCredentialResponse checkCredential(in byte[] credential, int type, int userId,
in ICheckCredentialProgressCallback progressCallback);
- VerifyCredentialResponse verifyCredential(in String credential, int type, long challenge, int userId);
- VerifyCredentialResponse verifyTiedProfileChallenge(String credential, int type, long challenge, int userId);
+ VerifyCredentialResponse verifyCredential(in byte[] credential, int type, long challenge, int userId);
+ VerifyCredentialResponse verifyTiedProfileChallenge(in byte[] credential, int type, long challenge, int userId);
boolean checkVoldPassword(int userId);
boolean havePattern(int userId);
boolean havePassword(int userId);
- byte[] getHashFactor(String currentCredential, int userId);
- void setSeparateProfileChallengeEnabled(int userId, boolean enabled, String managedUserPassword);
+ byte[] getHashFactor(in byte[] currentCredential, int userId);
+ void setSeparateProfileChallengeEnabled(int userId, boolean enabled, in byte[] managedUserPassword);
boolean getSeparateProfileChallengeEnabled(int userId);
void registerStrongAuthTracker(in IStrongAuthTracker tracker);
void unregisterStrongAuthTracker(in IStrongAuthTracker tracker);
diff --git a/core/java/com/android/internal/widget/LockPatternChecker.java b/core/java/com/android/internal/widget/LockPatternChecker.java
index 586ece0..bda3b57 100644
--- a/core/java/com/android/internal/widget/LockPatternChecker.java
+++ b/core/java/com/android/internal/widget/LockPatternChecker.java
@@ -150,12 +150,33 @@
* @param challenge The challenge to verify against the pattern.
* @param userId The user to check against the pattern.
* @param callback The callback to be invoked with the verification result.
+ *
+ * @deprecated Pass the password as a byte array.
*/
+ @Deprecated
public static AsyncTask<?, ?, ?> verifyPassword(final LockPatternUtils utils,
final String password,
final long challenge,
final int userId,
final OnVerifyCallback callback) {
+ byte[] passwordBytes = password != null ? password.getBytes() : null;
+ return verifyPassword(utils, passwordBytes, challenge, userId, callback);
+ }
+
+ /**
+ * Verify a password asynchronously.
+ *
+ * @param utils The LockPatternUtils instance to use.
+ * @param password The password to check.
+ * @param challenge The challenge to verify against the pattern.
+ * @param userId The user to check against the pattern.
+ * @param callback The callback to be invoked with the verification result.
+ */
+ public static AsyncTask<?, ?, ?> verifyPassword(final LockPatternUtils utils,
+ final byte[] password,
+ final long challenge,
+ final int userId,
+ final OnVerifyCallback callback) {
AsyncTask<Void, Void, byte[]> task = new AsyncTask<Void, Void, byte[]>() {
private int mThrottleTimeout;
@@ -188,7 +209,7 @@
* @param callback The callback to be invoked with the verification result.
*/
public static AsyncTask<?, ?, ?> verifyTiedProfileChallenge(final LockPatternUtils utils,
- final String password,
+ final byte[] password,
final boolean isPattern,
final long challenge,
final int userId,
@@ -222,18 +243,36 @@
* @param password The password to check.
* @param userId The user to check against the pattern.
* @param callback The callback to be invoked with the check result.
+ * @deprecated Pass passwords as byte[]
*/
+ @Deprecated
public static AsyncTask<?, ?, ?> checkPassword(final LockPatternUtils utils,
final String password,
final int userId,
final OnCheckCallback callback) {
+ byte[] passwordBytes = password != null ? password.getBytes() : null;
+ return checkPassword(utils, passwordBytes, userId, callback);
+ }
+
+ /**
+ * Checks a password asynchronously.
+ *
+ * @param utils The LockPatternUtils instance to use.
+ * @param passwordBytes The password to check.
+ * @param userId The user to check against the pattern.
+ * @param callback The callback to be invoked with the check result.
+ */
+ public static AsyncTask<?, ?, ?> checkPassword(final LockPatternUtils utils,
+ final byte[] passwordBytes,
+ final int userId,
+ final OnCheckCallback callback) {
AsyncTask<Void, Void, Boolean> task = new AsyncTask<Void, Void, Boolean>() {
private int mThrottleTimeout;
@Override
protected Boolean doInBackground(Void... args) {
try {
- return utils.checkPassword(password, userId, callback::onEarlyMatched);
+ return utils.checkPassword(passwordBytes, userId, callback::onEarlyMatched);
} catch (RequestThrottledException ex) {
mThrottleTimeout = ex.getTimeoutMs();
return false;
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 8d3c482..17ed2a0 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -67,6 +67,7 @@
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.StringJoiner;
@@ -356,7 +357,7 @@
null /* componentName */, userId);
}
- private byte[] verifyCredential(String credential, int type, long challenge, int userId)
+ private byte[] verifyCredential(byte[] credential, int type, long challenge, int userId)
throws RequestThrottledException {
try {
VerifyCredentialResponse response = getLockSettings().verifyCredential(credential,
@@ -373,7 +374,7 @@
}
}
- private boolean checkCredential(String credential, int type, int userId,
+ private boolean checkCredential(byte[] credential, int type, int userId,
@Nullable CheckCredentialProgressCallback progressCallback)
throws RequestThrottledException {
try {
@@ -404,7 +405,7 @@
public byte[] verifyPattern(List<LockPatternView.Cell> pattern, long challenge, int userId)
throws RequestThrottledException {
throwIfCalledOnMainThread();
- return verifyCredential(patternToString(pattern), CREDENTIAL_TYPE_PATTERN, challenge,
+ return verifyCredential(patternToByteArray(pattern), CREDENTIAL_TYPE_PATTERN, challenge,
userId);
}
@@ -429,7 +430,7 @@
@Nullable CheckCredentialProgressCallback progressCallback)
throws RequestThrottledException {
throwIfCalledOnMainThread();
- return checkCredential(patternToString(pattern), CREDENTIAL_TYPE_PATTERN, userId,
+ return checkCredential(patternToByteArray(pattern), CREDENTIAL_TYPE_PATTERN, userId,
progressCallback);
}
@@ -442,7 +443,7 @@
* @param challenge The challenge to verify against the password
* @return the attestation that the challenge was verified, or null.
*/
- public byte[] verifyPassword(String password, long challenge, int userId)
+ public byte[] verifyPassword(byte[] password, long challenge, int userId)
throws RequestThrottledException {
throwIfCalledOnMainThread();
return verifyCredential(password, CREDENTIAL_TYPE_PASSWORD, challenge, userId);
@@ -458,7 +459,7 @@
* @param challenge The challenge to verify against the password
* @return the attestation that the challenge was verified, or null.
*/
- public byte[] verifyTiedProfileChallenge(String password, boolean isPattern, long challenge,
+ public byte[] verifyTiedProfileChallenge(byte[] password, boolean isPattern, long challenge,
int userId) throws RequestThrottledException {
throwIfCalledOnMainThread();
try {
@@ -480,22 +481,53 @@
}
/**
+ *
* Check to see if a password matches the saved password. If no password exists,
* always returns true.
* @param password The password to check.
* @return Whether the password matches the stored one.
*/
public boolean checkPassword(String password, int userId) throws RequestThrottledException {
+ byte[] passwordBytes = password != null ? password.getBytes() : null;
+ return checkPassword(passwordBytes, userId, null /* progressCallback */);
+ }
+
+
+ /**
+ *
+ * Check to see if a password matches the saved password. If no password exists,
+ * always returns true.
+ * @param password The password to check.
+ * @return Whether the password matches the stored one.
+ */
+ public boolean checkPassword(byte[] password, int userId) throws RequestThrottledException {
return checkPassword(password, userId, null /* progressCallback */);
}
+ // TODO(b/120484642): This method is necessary for vendor/qcom code and is a hidden api
+ /* *
+ * Check to see if a password matches the saved password. If no password exists,
+ * always returns true.
+ * @param password The password to check.
+ * @return Whether the password matches the stored one.
+ */
+ public boolean checkPassword(String password, int userId,
+ @Nullable CheckCredentialProgressCallback progressCallback)
+ throws RequestThrottledException {
+ byte[] passwordBytes = password != null ? password.getBytes() : null;
+ throwIfCalledOnMainThread();
+ return checkCredential(passwordBytes, CREDENTIAL_TYPE_PASSWORD, userId, progressCallback);
+
+ }
+
/**
* Check to see if a password matches the saved password. If no password exists,
* always returns true.
* @param password The password to check.
* @return Whether the password matches the stored one.
*/
- public boolean checkPassword(String password, int userId,
+
+ public boolean checkPassword(byte[] password, int userId,
@Nullable CheckCredentialProgressCallback progressCallback)
throws RequestThrottledException {
throwIfCalledOnMainThread();
@@ -519,7 +551,7 @@
* Returns the password history hash factor, needed to check new password against password
* history with {@link #checkPasswordHistory(String, byte[], int)}
*/
- public byte[] getPasswordHistoryHashFactor(String currentPassword, int userId) {
+ public byte[] getPasswordHistoryHashFactor(byte[] currentPassword, int userId) {
try {
return getLockSettings().getHashFactor(currentPassword, userId);
} catch (RemoteException e) {
@@ -537,8 +569,8 @@
* {@link ILockSettings#getHashFactor}
* @return Whether the password matches any in the history.
*/
- public boolean checkPasswordHistory(String passwordToCheck, byte[] hashFactor, int userId) {
- if (TextUtils.isEmpty(passwordToCheck)) {
+ public boolean checkPasswordHistory(byte[] passwordToCheck, byte[] hashFactor, int userId) {
+ if (passwordToCheck == null || passwordToCheck.length == 0) {
Log.e(TAG, "checkPasswordHistory: empty password");
return false;
}
@@ -639,13 +671,13 @@
/**
* Clear any lock pattern or password.
*/
- public void clearLock(String savedCredential, int userHandle) {
+ public void clearLock(byte[] savedCredential, int userHandle) {
final int currentQuality = getKeyguardStoredPasswordQuality(userHandle);
setKeyguardStoredPasswordQuality(PASSWORD_QUALITY_UNSPECIFIED, userHandle);
try{
- getLockSettings().setLockCredential(null, CREDENTIAL_TYPE_NONE, savedCredential,
- PASSWORD_QUALITY_UNSPECIFIED, userHandle);
+ getLockSettings().setLockCredential(null, CREDENTIAL_TYPE_NONE,
+ savedCredential, PASSWORD_QUALITY_UNSPECIFIED, userHandle);
} catch (Exception e) {
Log.e(TAG, "Failed to clear lock", e);
setKeyguardStoredPasswordQuality(currentQuality, userHandle);
@@ -704,10 +736,11 @@
/**
* Save a lock pattern.
* @param pattern The new pattern to save.
- * @param savedPattern The previously saved pattern, converted to String format
+ * @param savedPattern The previously saved pattern, converted to byte[] format
* @param userId the user whose pattern is to be saved.
*/
- public void saveLockPattern(List<LockPatternView.Cell> pattern, String savedPattern, int userId) {
+ public void saveLockPattern(List<LockPatternView.Cell> pattern, byte[] savedPattern,
+ int userId) {
if (!hasSecureLockScreen()) {
throw new UnsupportedOperationException(
"This operation requires the lock screen feature.");
@@ -717,12 +750,12 @@
+ MIN_LOCK_PATTERN_SIZE + " dots long.");
}
- final String stringPattern = patternToString(pattern);
+ final byte[] bytePattern = patternToByteArray(pattern);
final int currentQuality = getKeyguardStoredPasswordQuality(userId);
setKeyguardStoredPasswordQuality(PASSWORD_QUALITY_SOMETHING, userId);
try {
- getLockSettings().setLockCredential(stringPattern, CREDENTIAL_TYPE_PATTERN,
- savedPattern, PASSWORD_QUALITY_SOMETHING, userId);
+ getLockSettings().setLockCredential(bytePattern, CREDENTIAL_TYPE_PATTERN, savedPattern,
+ PASSWORD_QUALITY_SOMETHING, userId);
} catch (Exception e) {
Log.e(TAG, "Couldn't save lock pattern", e);
setKeyguardStoredPasswordQuality(currentQuality, userId);
@@ -734,7 +767,7 @@
if (!shouldEncryptWithCredentials(true)) {
clearEncryptionPassword();
} else {
- updateEncryptionPassword(StorageManager.CRYPT_TYPE_PATTERN, stringPattern);
+ updateEncryptionPassword(StorageManager.CRYPT_TYPE_PATTERN, bytePattern);
}
}
@@ -806,7 +839,7 @@
}
/** Update the encryption password if it is enabled **/
- private void updateEncryptionPassword(final int type, final String password) {
+ private void updateEncryptionPassword(final int type, final byte[] password) {
if (!hasSecureLockScreen()) {
throw new UnsupportedOperationException(
"This operation requires the lock screen feature.");
@@ -825,7 +858,9 @@
protected Void doInBackground(Void... dummy) {
IStorageManager storageManager = IStorageManager.Stub.asInterface(service);
try {
- storageManager.changeEncryptionPassword(type, password);
+ // TODO(b/120484642): This is a location where we still use a String for vold
+ String passwordString = password != null ? new String(password) : null;
+ storageManager.changeEncryptionPassword(type, passwordString);
} catch (RemoteException e) {
Log.e(TAG, "Error changing encryption password", e);
}
@@ -842,14 +877,34 @@
* @param savedPassword The previously saved lock password, or null if none
* @param requestedQuality {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
* @param userHandle The userId of the user to change the password for
+ *
+ * @deprecated Pass password as a byte array
*/
+ @Deprecated
public void saveLockPassword(String password, String savedPassword, int requestedQuality,
int userHandle) {
+ byte[] passwordBytes = password != null ? password.getBytes() : null;
+ byte[] savedPasswordBytes = savedPassword != null ? savedPassword.getBytes() : null;
+ saveLockPassword(passwordBytes, savedPasswordBytes, requestedQuality, userHandle);
+ }
+
+ /**
+ * Save a lock password. Does not ensure that the password is as good
+ * as the requested mode, but will adjust the mode to be as good as the
+ * password.
+ * @param password The password to save
+ * @param savedPassword The previously saved lock password, or null if none
+ * @param requestedQuality {@see DevicePolicyManager#getPasswordQuality(
+ * android.content.ComponentName)}
+ * @param userHandle The userId of the user to change the password for
+ */
+ public void saveLockPassword(byte[] password, byte[] savedPassword, int requestedQuality,
+ int userHandle) {
if (!hasSecureLockScreen()) {
throw new UnsupportedOperationException(
"This operation requires the lock screen feature.");
}
- if (password == null || password.length() < MIN_LOCK_PASSWORD_SIZE) {
+ if (password == null || password.length < MIN_LOCK_PASSWORD_SIZE) {
throw new IllegalArgumentException("password must not be null and at least "
+ "of length " + MIN_LOCK_PASSWORD_SIZE);
}
@@ -864,8 +919,8 @@
computePasswordQuality(CREDENTIAL_TYPE_PASSWORD, password, requestedQuality),
userHandle);
try {
- getLockSettings().setLockCredential(password, CREDENTIAL_TYPE_PASSWORD,
- savedPassword, requestedQuality, userHandle);
+ getLockSettings().setLockCredential(password, CREDENTIAL_TYPE_PASSWORD, savedPassword,
+ requestedQuality, userHandle);
} catch (Exception e) {
Log.e(TAG, "Unable to save lock password", e);
setKeyguardStoredPasswordQuality(currentQuality, userHandle);
@@ -882,7 +937,7 @@
* Update device encryption password if calling user is USER_SYSTEM and device supports
* encryption.
*/
- private void updateEncryptionPasswordIfNeeded(String password, int quality, int userHandle) {
+ private void updateEncryptionPasswordIfNeeded(byte[] password, int quality, int userHandle) {
// Update the device encryption password.
if (userHandle == UserHandle.USER_SYSTEM
&& LockPatternUtils.isDeviceEncryptionEnabled()) {
@@ -902,8 +957,8 @@
* Store the hash of the *current* password in the password history list, if device policy
* enforces password history requirement.
*/
- private void updatePasswordHistory(String password, int userHandle) {
- if (TextUtils.isEmpty(password)) {
+ private void updatePasswordHistory(byte[] password, int userHandle) {
+ if (password == null || password.length == 0) {
Log.e(TAG, "checkPasswordHistory: empty password");
return;
}
@@ -982,7 +1037,7 @@
* if DevicePolicyManager has a stronger quality requirement. This value will be written
* to PASSWORD_TYPE_KEY.
*/
- private int computePasswordQuality(int type, String credential, int requestedQuality) {
+ private int computePasswordQuality(int type, byte[] credential, int requestedQuality) {
final int quality;
if (type == CREDENTIAL_TYPE_PASSWORD) {
int computedQuality = PasswordMetrics.computeForPassword(credential).quality;
@@ -1005,7 +1060,7 @@
* true
*/
public void setSeparateProfileChallengeEnabled(int userHandle, boolean enabled,
- String managedUserPassword) {
+ byte[] managedUserPassword) {
if (!isManagedProfile(userHandle)) {
return;
}
@@ -1069,15 +1124,28 @@
* Deserialize a pattern.
* @param string The pattern serialized with {@link #patternToString}
* @return The pattern.
+ * @deprecated Pass patterns as byte[] and use byteArrayToPattern
*/
+ @Deprecated
public static List<LockPatternView.Cell> stringToPattern(String string) {
if (string == null) {
return null;
}
+ return byteArrayToPattern(string.getBytes());
+ }
+
+ /**
+ * Deserialize a pattern.
+ * @param bytes The pattern serialized with {@link #patternToByteArray}
+ * @return The pattern.
+ */
+ public static List<LockPatternView.Cell> byteArrayToPattern(byte[] bytes) {
+ if (bytes == null) {
+ return null;
+ }
List<LockPatternView.Cell> result = Lists.newArrayList();
- final byte[] bytes = string.getBytes();
for (int i = 0; i < bytes.length; i++) {
byte b = (byte) (bytes[i] - '1');
result.add(LockPatternView.Cell.of(b / 3, b % 3));
@@ -1089,10 +1157,22 @@
* Serialize a pattern.
* @param pattern The pattern.
* @return The pattern in string form.
+ * @deprecated Use patternToByteArray instead.
*/
+ @Deprecated
public static String patternToString(List<LockPatternView.Cell> pattern) {
+ return new String(patternToByteArray(pattern));
+ }
+
+
+ /**
+ * Serialize a pattern.
+ * @param pattern The pattern.
+ * @return The pattern in byte array form.
+ */
+ public static byte[] patternToByteArray(List<LockPatternView.Cell> pattern) {
if (pattern == null) {
- return "";
+ return new byte[0];
}
final int patternSize = pattern.size();
@@ -1101,21 +1181,24 @@
LockPatternView.Cell cell = pattern.get(i);
res[i] = (byte) (cell.getRow() * 3 + cell.getColumn() + '1');
}
- return new String(res);
+ return res;
}
- public static String patternStringToBaseZero(String pattern) {
- if (pattern == null) {
- return "";
+ /**
+ * Transform a pattern byte array to base zero form.
+ * @param bytes pattern byte array.
+ * @return The pattern in base zero form.
+ */
+ public static byte[] patternByteArrayToBaseZero(byte[] bytes) {
+ if (bytes == null) {
+ return new byte[0];
}
- final int patternSize = pattern.length();
-
+ final int patternSize = bytes.length;
byte[] res = new byte[patternSize];
- final byte[] bytes = pattern.getBytes();
for (int i = 0; i < patternSize; i++) {
res[i] = (byte) (bytes[i] - '1');
}
- return new String(res);
+ return res;
}
/*
@@ -1169,13 +1252,18 @@
*
* @return the hash of the pattern in a byte array.
*/
- public String legacyPasswordToHash(String password, int userId) {
- if (password == null) {
+ public String legacyPasswordToHash(byte[] password, int userId) {
+ if (password == null || password.length == 0) {
return null;
}
try {
- byte[] saltedPassword = (password + getSalt(userId)).getBytes();
+ // Previously the password was passed as a String with the following code:
+ // byte[] saltedPassword = (password + getSalt(userId)).getBytes();
+ // The code below creates the identical digest preimage using byte arrays:
+ byte[] salt = getSalt(userId).getBytes();
+ byte[] saltedPassword = Arrays.copyOf(password, password.length + salt.length);
+ System.arraycopy(salt, 0, saltedPassword, password.length, salt.length);
byte[] sha1 = MessageDigest.getInstance("SHA-1").digest(saltedPassword);
byte[] md5 = MessageDigest.getInstance("MD5").digest(saltedPassword);
@@ -1184,6 +1272,7 @@
System.arraycopy(md5, 0, combined, sha1.length, md5.length);
final char[] hexEncoded = HexEncoding.encode(combined);
+ Arrays.fill(saltedPassword, (byte) 0);
return new String(hexEncoded);
} catch (NoSuchAlgorithmException e) {
throw new AssertionError("Missing digest algorithm: ", e);
@@ -1193,14 +1282,19 @@
/**
* Hash the password for password history check purpose.
*/
- private String passwordToHistoryHash(String passwordToHash, byte[] hashFactor, int userId) {
- if (TextUtils.isEmpty(passwordToHash) || hashFactor == null) {
+ private String passwordToHistoryHash(byte[] passwordToHash, byte[] hashFactor, int userId) {
+ if (passwordToHash == null || passwordToHash.length == 0 || hashFactor == null) {
return null;
}
try {
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
sha256.update(hashFactor);
- sha256.update((passwordToHash + getSalt(userId)).getBytes());
+ byte[] salt = getSalt(userId).getBytes();
+ byte[] saltedPassword = Arrays.copyOf(passwordToHash, passwordToHash.length
+ + salt.length);
+ System.arraycopy(salt, 0, saltedPassword, passwordToHash.length, salt.length);
+ sha256.update(saltedPassword);
+ Arrays.fill(saltedPassword, (byte) 0);
return new String(HexEncoding.encode(sha256.digest()));
} catch (NoSuchAlgorithmException e) {
throw new AssertionError("Missing digest algorithm: ", e);
@@ -1633,7 +1727,7 @@
* @param userId The user who's lock credential to be changed
* @return {@code true} if the operation is successful.
*/
- public boolean setLockCredentialWithToken(String credential, int type, int requestedQuality,
+ public boolean setLockCredentialWithToken(byte[] credential, int type, int requestedQuality,
long tokenHandle, byte[] token, int userId) {
if (!hasSecureLockScreen()) {
throw new UnsupportedOperationException(
@@ -1641,13 +1735,13 @@
}
LockSettingsInternal localService = getLockSettingsInternal();
if (type != CREDENTIAL_TYPE_NONE) {
- if (TextUtils.isEmpty(credential) || credential.length() < MIN_LOCK_PASSWORD_SIZE) {
+ if (credential == null || credential.length < MIN_LOCK_PASSWORD_SIZE) {
throw new IllegalArgumentException("password must not be null and at least "
+ "of length " + MIN_LOCK_PASSWORD_SIZE);
}
final int quality = computePasswordQuality(type, credential, requestedQuality);
- if (!localService.setLockCredentialWithToken(credential, type, tokenHandle,
- token, quality, userId)) {
+ if (!localService.setLockCredentialWithToken(credential, type, tokenHandle, token,
+ quality, userId)) {
return false;
}
setKeyguardStoredPasswordQuality(quality, userId);
@@ -1656,11 +1750,11 @@
updatePasswordHistory(credential, userId);
onAfterChangingPassword(userId);
} else {
- if (!TextUtils.isEmpty(credential)) {
+ if (!(credential == null || credential.length == 0)) {
throw new IllegalArgumentException("password must be emtpy for NONE type");
}
- if (!localService.setLockCredentialWithToken(null, CREDENTIAL_TYPE_NONE,
- tokenHandle, token, PASSWORD_QUALITY_UNSPECIFIED, userId)) {
+ if (!localService.setLockCredentialWithToken(null, CREDENTIAL_TYPE_NONE, tokenHandle,
+ token, PASSWORD_QUALITY_UNSPECIFIED, userId)) {
return false;
}
setKeyguardStoredPasswordQuality(PASSWORD_QUALITY_UNSPECIFIED, userId);
@@ -1891,4 +1985,22 @@
return FRP_CREDENTIAL_ENABLED && context.getResources().getBoolean(
com.android.internal.R.bool.config_enableCredentialFactoryResetProtection);
}
+
+ /**
+ * Converts a CharSequence to a byte array without requiring a toString(), which creates an
+ * additional copy.
+ *
+ * @param chars The CharSequence to convert
+ * @return A byte array representing the input
+ */
+ public static byte[] charSequenceToByteArray(CharSequence chars) {
+ if (chars == null) {
+ return null;
+ }
+ byte[] bytes = new byte[chars.length()];
+ for (int i = 0; i < chars.length(); i++) {
+ bytes[i] = (byte) chars.charAt(i);
+ }
+ return bytes;
+ }
}
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index 25a5a07..4b26990 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -1273,8 +1273,10 @@
@Override
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
+ byte[] patternBytes = LockPatternUtils.patternToByteArray(mPattern);
+ String patternString = patternBytes != null ? new String(patternBytes) : null;
return new SavedState(superState,
- LockPatternUtils.patternToString(mPattern),
+ patternString,
mPatternDisplayMode.ordinal(),
mInputEnabled, mInStealthMode, mEnableHapticFeedback);
}
diff --git a/core/java/com/android/internal/widget/LockSettingsInternal.java b/core/java/com/android/internal/widget/LockSettingsInternal.java
index 9de9ef7..90397df 100644
--- a/core/java/com/android/internal/widget/LockSettingsInternal.java
+++ b/core/java/com/android/internal/widget/LockSettingsInternal.java
@@ -49,7 +49,11 @@
*/
public abstract boolean isEscrowTokenActive(long handle, int userId);
- public abstract boolean setLockCredentialWithToken(String credential, int type,
+ /**
+ * Set the lock credential.
+ * @return true if password is set.
+ */
+ public abstract boolean setLockCredentialWithToken(byte[] credential, int type,
long tokenHandle, byte[] token, int requestedQuality, int userId);
public abstract boolean unlockUserWithToken(long tokenHandle, byte[] token, int userId);
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 0938e56..16517bf 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -244,6 +244,11 @@
// Copying (CC) garbage collector.
static const char* kNoGenerationalCCRuntimeOption = "-Xgc:nogenerational_cc";
+// Feature flag name for running the JIT in Zygote experiment, b/119800099.
+static const char* ENABLE_APEX_IMAGE = "enable_apex_image";
+// Flag to pass to the runtime when using the apex image.
+static const char* kApexImageOption = "-Ximage:/system/framework/apex.art";
+
static AndroidRuntime* gCurRuntime = NULL;
/*
@@ -678,8 +683,17 @@
char jdwpProviderBuf[sizeof("-XjdwpProvider:") - 1 + PROPERTY_VALUE_MAX];
char bootImageBuf[sizeof("-Ximage:") - 1 + PROPERTY_VALUE_MAX];
- if (parseRuntimeOption("dalvik.vm.boot-image", bootImageBuf, "-Ximage:")) {
- ALOGI("Boot image: '%s'\n", bootImageBuf);
+ std::string use_apex_image =
+ server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE,
+ ENABLE_APEX_IMAGE,
+ /*default_value=*/ "");
+ if (use_apex_image == "true") {
+ addOption(kApexImageOption);
+ ALOGI("Using Apex boot image: '%s'\n", kApexImageOption);
+ } else if (parseRuntimeOption("dalvik.vm.boot-image", bootImageBuf, "-Ximage:")) {
+ ALOGI("Using dalvik.vm.boot-image: '%s'\n", bootImageBuf);
+ } else {
+ ALOGI("Using default boot image");
}
bool checkJni = false;
diff --git a/core/jni/android_media_AudioProductStrategies.cpp b/core/jni/android_media_AudioProductStrategies.cpp
index a18e80a..d7d31e5 100644
--- a/core/jni/android_media_AudioProductStrategies.cpp
+++ b/core/jni/android_media_AudioProductStrategies.cpp
@@ -194,12 +194,34 @@
return jStatus;
}
+static jint
+android_media_AudioSystem_getProductStrategyFromAudioAttributes(JNIEnv *env, jobject clazz,
+ jobject jAudioAttributes)
+{
+ JNIAudioAttributeHelper::UniqueAaPtr attributes = JNIAudioAttributeHelper::makeUnique();
+ jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env,
+ jAudioAttributes,
+ attributes.get());
+ if (jStatus != (jint)AUDIO_JAVA_SUCCESS) {
+ return jStatus;
+ }
+ product_strategy_t psId;
+ status_t status = AudioSystem::getProductStrategyFromAudioAttributes(
+ AudioAttributes(*attributes.get()), psId);
+ if (status != NO_ERROR) {
+ return nativeToJavaStatus(status);
+ }
+ return psId;
+}
+
/*
* JNI registration.
*/
static const JNINativeMethod gMethods[] = {
{"native_list_audio_product_strategies", "(Ljava/util/ArrayList;)I",
(void *)android_media_AudioSystem_listAudioProductStrategies},
+ {"native_get_product_strategies_from_audio_attributes", "(Landroid/media/AudioAttributes;)I",
+ (void *)android_media_AudioSystem_getProductStrategyFromAudioAttributes},
};
int register_android_media_AudioProductStrategies(JNIEnv *env)
diff --git a/core/jni/android_net_LocalSocketImpl.cpp b/core/jni/android_net_LocalSocketImpl.cpp
index a1f2377..1163b86 100644
--- a/core/jni/android_net_LocalSocketImpl.cpp
+++ b/core/jni/android_net_LocalSocketImpl.cpp
@@ -33,14 +33,16 @@
#include <unistd.h>
#include <sys/ioctl.h>
+#include <android-base/cmsg.h>
+#include <android-base/macros.h>
#include <cutils/sockets.h>
#include <netinet/tcp.h>
#include <nativehelper/ScopedUtfChars.h>
-namespace android {
+using android::base::ReceiveFileDescriptorVector;
+using android::base::SendFileDescriptorVector;
-template <typename T>
-void UNUSED(T t) {}
+namespace android {
static jfieldID field_inboundFileDescriptors;
static jfieldID field_outboundFileDescriptors;
@@ -118,67 +120,6 @@
}
/**
- * Processes ancillary data, handling only
- * SCM_RIGHTS. Creates appropriate objects and sets appropriate
- * fields in the LocalSocketImpl object. Returns 0 on success
- * or -1 if an exception was thrown.
- */
-static int socket_process_cmsg(JNIEnv *env, jobject thisJ, struct msghdr * pMsg)
-{
- struct cmsghdr *cmsgptr;
-
- for (cmsgptr = CMSG_FIRSTHDR(pMsg);
- cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(pMsg, cmsgptr)) {
-
- if (cmsgptr->cmsg_level != SOL_SOCKET) {
- continue;
- }
-
- if (cmsgptr->cmsg_type == SCM_RIGHTS) {
- int *pDescriptors = (int *)CMSG_DATA(cmsgptr);
- jobjectArray fdArray;
- int count
- = ((cmsgptr->cmsg_len - CMSG_LEN(0)) / sizeof(int));
-
- if (count < 0) {
- jniThrowException(env, "java/io/IOException",
- "invalid cmsg length");
- return -1;
- }
-
- fdArray = env->NewObjectArray(count, class_FileDescriptor, NULL);
-
- if (fdArray == NULL) {
- return -1;
- }
-
- for (int i = 0; i < count; i++) {
- jobject fdObject
- = jniCreateFileDescriptor(env, pDescriptors[i]);
-
- if (env->ExceptionCheck()) {
- return -1;
- }
-
- env->SetObjectArrayElement(fdArray, i, fdObject);
-
- if (env->ExceptionCheck()) {
- return -1;
- }
- }
-
- env->SetObjectField(thisJ, field_inboundFileDescriptors, fdArray);
-
- if (env->ExceptionCheck()) {
- return -1;
- }
- }
- }
-
- return 0;
-}
-
-/**
* Reads data from a socket into buf, processing any ancillary data
* and adding it to thisJ.
*
@@ -189,47 +130,48 @@
void *buffer, size_t len)
{
ssize_t ret;
- struct msghdr msg;
- struct iovec iv;
- unsigned char *buf = (unsigned char *)buffer;
- // Enough buffer for a pile of fd's. We throw an exception if
- // this buffer is too small.
- struct cmsghdr cmsgbuf[2*sizeof(cmsghdr) + 0x100];
+ std::vector<android::base::unique_fd> received_fds;
- memset(&msg, 0, sizeof(msg));
- memset(&iv, 0, sizeof(iv));
-
- iv.iov_base = buf;
- iv.iov_len = len;
-
- msg.msg_iov = &iv;
- msg.msg_iovlen = 1;
- msg.msg_control = cmsgbuf;
- msg.msg_controllen = sizeof(cmsgbuf);
-
- ret = TEMP_FAILURE_RETRY(recvmsg(fd, &msg, MSG_NOSIGNAL | MSG_CMSG_CLOEXEC));
-
- if (ret < 0 && errno == EPIPE) {
- // Treat this as an end of stream
- return 0;
- }
+ ret = ReceiveFileDescriptorVector(fd, buffer, len, 64, &received_fds);
if (ret < 0) {
+ if (errno == EPIPE) {
+ // Treat this as an end of stream
+ return 0;
+ }
+
jniThrowIOException(env, errno);
return -1;
}
- if ((msg.msg_flags & (MSG_CTRUNC | MSG_OOB | MSG_ERRQUEUE)) != 0) {
- // To us, any of the above flags are a fatal error
+ if (received_fds.size() > 0) {
+ jobjectArray fdArray = env->NewObjectArray(received_fds.size(), class_FileDescriptor, NULL);
- jniThrowException(env, "java/io/IOException",
- "Unexpected error or truncation during recvmsg()");
+ if (fdArray == NULL) {
+ // NewObjectArray has thrown.
+ return -1;
+ }
- return -1;
- }
+ for (size_t i = 0; i < received_fds.size(); i++) {
+ jobject fdObject = jniCreateFileDescriptor(env, received_fds[i].get());
- if (ret >= 0) {
- socket_process_cmsg(env, thisJ, &msg);
+ if (env->ExceptionCheck()) {
+ return -1;
+ }
+
+ env->SetObjectArrayElement(fdArray, i, fdObject);
+
+ if (env->ExceptionCheck()) {
+ return -1;
+ }
+ }
+
+ for (auto &fd : received_fds) {
+ // The fds are stored in java.io.FileDescriptors now.
+ static_cast<void>(fd.release());
+ }
+
+ env->SetObjectField(thisJ, field_inboundFileDescriptors, fdArray);
}
return ret;
@@ -243,7 +185,6 @@
static int socket_write_all(JNIEnv *env, jobject object, int fd,
void *buf, size_t len)
{
- ssize_t ret;
struct msghdr msg;
unsigned char *buffer = (unsigned char *)buf;
memset(&msg, 0, sizeof(msg));
@@ -256,14 +197,11 @@
return -1;
}
- struct cmsghdr *cmsg;
int countFds = outboundFds == NULL ? 0 : env->GetArrayLength(outboundFds);
- int fds[countFds];
- char msgbuf[CMSG_SPACE(countFds)];
+ std::vector<int> fds;
// Add any pending outbound file descriptors to the message
if (outboundFds != NULL) {
-
if (env->ExceptionCheck()) {
return -1;
}
@@ -274,47 +212,25 @@
return -1;
}
- fds[i] = jniGetFDFromFileDescriptor(env, fdObject);
+ fds.push_back(jniGetFDFromFileDescriptor(env, fdObject));
if (env->ExceptionCheck()) {
return -1;
}
}
-
- // See "man cmsg" really
- msg.msg_control = msgbuf;
- msg.msg_controllen = sizeof msgbuf;
- cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = CMSG_LEN(sizeof fds);
- memcpy(CMSG_DATA(cmsg), fds, sizeof fds);
}
- // We only write our msg_control during the first write
- while (len > 0) {
- struct iovec iv;
- memset(&iv, 0, sizeof(iv));
+ ssize_t rc = SendFileDescriptorVector(fd, buffer, len, fds);
- iv.iov_base = buffer;
- iv.iov_len = len;
-
- msg.msg_iov = &iv;
- msg.msg_iovlen = 1;
-
- do {
- ret = sendmsg(fd, &msg, MSG_NOSIGNAL);
- } while (ret < 0 && errno == EINTR);
-
- if (ret < 0) {
+ while (rc != len) {
+ if (rc == -1) {
jniThrowIOException(env, errno);
return -1;
}
- buffer += ret;
- len -= ret;
+ buffer += rc;
+ len -= rc;
- // Wipes out any msg_control too
- memset(&msg, 0, sizeof(msg));
+ rc = send(fd, buffer, len, MSG_NOSIGNAL);
}
return 0;
diff --git a/core/jni/android_opengl_EGL14.cpp b/core/jni/android_opengl_EGL14.cpp
index 15d1944..de5e3a5 100644
--- a/core/jni/android_opengl_EGL14.cpp
+++ b/core/jni/android_opengl_EGL14.cpp
@@ -35,8 +35,6 @@
#include <ui/ANativeObjectBase.h>
-static int initialized = 0;
-
static jclass egldisplayClass;
static jclass eglcontextClass;
static jclass eglsurfaceClass;
@@ -239,7 +237,7 @@
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
- return false;
+ return JNI_FALSE;
}
return (jboolean)_returnValue;
}
@@ -337,7 +335,7 @@
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
- return false;
+ return JNI_FALSE;
}
return (jboolean)_returnValue;
}
@@ -457,7 +455,7 @@
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
- return false;
+ return JNI_FALSE;
}
return (jboolean)_returnValue;
}
@@ -513,7 +511,7 @@
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
- return false;
+ return JNI_FALSE;
}
return (jboolean)_returnValue;
}
@@ -808,7 +806,7 @@
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
- return false;
+ return JNI_FALSE;
}
return (jboolean)_returnValue;
}
@@ -1163,7 +1161,7 @@
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
- return false;
+ return JNI_FALSE;
}
return (jboolean)_returnValue;
}
@@ -1209,7 +1207,7 @@
(JNIEnv *_env, jobject _this, jobject dpy, jobject surface, jint target) {
jniThrowException(_env, "java/lang/UnsupportedOperationException",
"eglCopyBuffers");
- return (EGLBoolean) 0;
+ return JNI_FALSE;
}
static const char *classPathName = "android/opengl/EGL14";
diff --git a/core/jni/android_opengl_EGL15.cpp b/core/jni/android_opengl_EGL15.cpp
index 2abd950..99bdce2 100644
--- a/core/jni/android_opengl_EGL15.cpp
+++ b/core/jni/android_opengl_EGL15.cpp
@@ -14,6 +14,8 @@
** limitations under the License.
*/
+// This source file is automatically generated
+
#pragma GCC diagnostic ignored "-Wunused-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
@@ -27,8 +29,6 @@
#include <ui/ANativeObjectBase.h>
-static int initialized = 0;
-
// classes from EGL 1.4
static jclass egldisplayClass;
static jclass eglsurfaceClass;
@@ -92,7 +92,6 @@
egldisplayGetHandleID = _env->GetMethodID(egldisplayClass, "getNativeHandle", "()J");
eglsurfaceGetHandleID = _env->GetMethodID(eglsurfaceClass, "getNativeHandle", "()J");
-
eglconfigConstructor = _env->GetMethodID(eglconfigClass, "<init>", "(J)V");
eglcontextConstructor = _env->GetMethodID(eglcontextClass, "<init>", "(J)V");
egldisplayConstructor = _env->GetMethodID(egldisplayClass, "<init>", "(J)V");
@@ -105,7 +104,6 @@
jobject localeglNoSurfaceObject = _env->NewObject(eglsurfaceClass, eglsurfaceConstructor, reinterpret_cast<jlong>(EGL_NO_SURFACE));
eglNoSurfaceObject = _env->NewGlobalRef(localeglNoSurfaceObject);
-
jclass eglClass = _env->FindClass("android/opengl/EGL15");
jfieldID noContextFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_CONTEXT", "Landroid/opengl/EGLContext;");
_env->SetStaticObjectField(eglClass, noContextFieldID, eglNoContextObject);
@@ -171,8 +169,6 @@
*array = NULL;
return reinterpret_cast<void*>(pointer);
}
- eglimageGetHandleID = _env->GetMethodID(eglimageClass, "getNativeHandle", "()J");
- eglsyncGetHandleID = _env->GetMethodID(eglsyncClass, "getNativeHandle", "()J");
*array = (jarray) _env->CallStaticObjectMethod(nioAccessClass,
getBaseArrayID, buffer);
@@ -191,7 +187,7 @@
static void *
fromEGLHandle(JNIEnv *_env, jmethodID mid, jobject obj) {
- if (obj == NULL){
+ if (obj == NULL) {
jniThrowException(_env, "java/lang/IllegalArgumentException",
"Object is set to null.");
return nullptr;
@@ -202,10 +198,9 @@
}
static jobject
-toEGLHandle(JNIEnv *_env, jclass cls, jmethodID con, void * handle) {
- if (cls == eglimageClass &&
- (EGLImage)handle == EGL_NO_IMAGE) {
- return eglNoImageObject;
+toEGLHandle(JNIEnv *_env, jclass cls, jmethodID con, void *handle) {
+ if (cls == eglimageClass && (EGLImage)handle == EGL_NO_IMAGE) {
+ return eglNoImageObject;
}
return _env->NewObject(cls, con, reinterpret_cast<jlong>(handle));
@@ -337,7 +332,7 @@
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
- return false;
+ return JNI_FALSE;
}
return (jboolean)_returnValue;
}
@@ -461,12 +456,9 @@
static jobject
android_eglCreatePlatformPixmapSurface
(JNIEnv *_env, jobject _this, jobject dpy, jobject config, jobject native_pixmap_buf, jlongArray attrib_list_ref, jint offset) {
- if ((true)) {
- jniThrowException(_env, "java/lang/UnsupportedOperationException",
- "eglCreatePlatformPixmapSurface");
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
+ "eglCreatePlatformPixmapSurface");
return nullptr;
- }
- return toEGLHandle(_env, eglsurfaceClass, eglsurfaceConstructor, (EGLSurface) 0);
}
/* EGLBoolean eglWaitSync ( EGLDisplay dpy, EGLSync sync, EGLint flags ) */
diff --git a/core/jni/android_opengl_EGLExt.cpp b/core/jni/android_opengl_EGLExt.cpp
index 75a25fe..fef8116 100644
--- a/core/jni/android_opengl_EGLExt.cpp
+++ b/core/jni/android_opengl_EGLExt.cpp
@@ -36,8 +36,6 @@
#include <ui/ANativeObjectBase.h>
-static int initialized = 0;
-
static jclass egldisplayClass;
static jclass eglcontextClass;
static jclass eglsurfaceClass;
@@ -104,6 +102,7 @@
if (obj == NULL){
jniThrowException(_env, "java/lang/IllegalArgumentException",
"Object is set to null.");
+ return nullptr;
}
return reinterpret_cast<void*>(_env->CallLongMethod(obj, mid));
diff --git a/core/jni/android_opengl_GLES10.cpp b/core/jni/android_opengl_GLES10.cpp
index ee5b594..a4ab5db 100644
--- a/core/jni/android_opengl_GLES10.cpp
+++ b/core/jni/android_opengl_GLES10.cpp
@@ -29,8 +29,6 @@
#include <utils/misc.h>
#include <assert.h>
-static int initialized = 0;
-
static jclass nioAccessClass;
static jclass bufferClass;
static jmethodID getBasePointerID;
diff --git a/core/jni/android_opengl_GLES10Ext.cpp b/core/jni/android_opengl_GLES10Ext.cpp
index da7d0f0..a5dcbf7 100644
--- a/core/jni/android_opengl_GLES10Ext.cpp
+++ b/core/jni/android_opengl_GLES10Ext.cpp
@@ -29,8 +29,6 @@
#include <utils/misc.h>
#include <assert.h>
-static int initialized = 0;
-
static jclass nioAccessClass;
static jclass bufferClass;
static jmethodID getBasePointerID;
@@ -531,6 +529,7 @@
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
+ return (jint)0;
}
return (jint)_returnValue;
}
@@ -600,6 +599,7 @@
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
+ return (jint)0;
}
return (jint)_returnValue;
}
diff --git a/core/jni/android_opengl_GLES11.cpp b/core/jni/android_opengl_GLES11.cpp
index 391ae53..be86a03 100644
--- a/core/jni/android_opengl_GLES11.cpp
+++ b/core/jni/android_opengl_GLES11.cpp
@@ -29,8 +29,6 @@
#include <utils/misc.h>
#include <assert.h>
-static int initialized = 0;
-
static jclass nioAccessClass;
static jclass bufferClass;
static jmethodID getBasePointerID;
diff --git a/core/jni/android_opengl_GLES11Ext.cpp b/core/jni/android_opengl_GLES11Ext.cpp
index 09dce32..d28d9a3 100644
--- a/core/jni/android_opengl_GLES11Ext.cpp
+++ b/core/jni/android_opengl_GLES11Ext.cpp
@@ -29,8 +29,6 @@
#include <utils/misc.h>
#include <assert.h>
-static int initialized = 0;
-
static jclass nioAccessClass;
static jclass bufferClass;
static jmethodID getBasePointerID;
diff --git a/core/jni/android_opengl_GLES20.cpp b/core/jni/android_opengl_GLES20.cpp
index 99922cf..0e20d47 100644
--- a/core/jni/android_opengl_GLES20.cpp
+++ b/core/jni/android_opengl_GLES20.cpp
@@ -29,8 +29,6 @@
#include <utils/misc.h>
#include <assert.h>
-static int initialized = 0;
-
static jclass nioAccessClass;
static jclass bufferClass;
static jmethodID getBasePointerID;
@@ -2684,6 +2682,7 @@
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
+ return (jint)0;
}
return (jint)_returnValue;
}
@@ -3909,6 +3908,7 @@
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
+ return (jint)0;
}
return (jint)_returnValue;
}
diff --git a/core/jni/android_opengl_GLES30.cpp b/core/jni/android_opengl_GLES30.cpp
index adc635e..9922398 100644
--- a/core/jni/android_opengl_GLES30.cpp
+++ b/core/jni/android_opengl_GLES30.cpp
@@ -29,8 +29,6 @@
#include <utils/misc.h>
#include <assert.h>
-static int initialized = 0;
-
static jclass nioAccessClass;
static jclass bufferClass;
static jmethodID getBasePointerID;
@@ -3012,6 +3010,7 @@
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
+ return (jint)0;
}
return (jint)_returnValue;
}
@@ -3981,6 +3980,7 @@
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
+ return (jint)0;
}
return (jint)_returnValue;
}
diff --git a/core/jni/android_opengl_GLES31.cpp b/core/jni/android_opengl_GLES31.cpp
index 512f562..27dbd39 100644
--- a/core/jni/android_opengl_GLES31.cpp
+++ b/core/jni/android_opengl_GLES31.cpp
@@ -27,8 +27,6 @@
#include <utils/misc.h>
#include <assert.h>
-static int initialized = 0;
-
static jclass nioAccessClass;
static jclass bufferClass;
static jmethodID getBasePointerID;
@@ -708,6 +706,7 @@
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
+ return (jint)0;
}
return (jint)_returnValue;
}
@@ -919,6 +918,7 @@
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
+ return (jint)0;
}
return (jint)_returnValue;
}
diff --git a/core/jni/android_opengl_GLES31Ext.cpp b/core/jni/android_opengl_GLES31Ext.cpp
index 5543fca..5b671c8 100644
--- a/core/jni/android_opengl_GLES31Ext.cpp
+++ b/core/jni/android_opengl_GLES31Ext.cpp
@@ -28,8 +28,6 @@
#include <utils/misc.h>
#include <assert.h>
-static int initialized = 0;
-
static jclass nioAccessClass;
static jclass bufferClass;
static jmethodID getBasePointerID;
diff --git a/core/jni/android_opengl_GLES32.cpp b/core/jni/android_opengl_GLES32.cpp
index 2f1e31e..d59d25c 100644
--- a/core/jni/android_opengl_GLES32.cpp
+++ b/core/jni/android_opengl_GLES32.cpp
@@ -27,8 +27,6 @@
#include <utils/misc.h>
#include <assert.h>
-static int initialized = 0;
-
static jclass nioAccessClass;
static jclass bufferClass;
static jmethodID getBasePointerID;
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index a212f47..af2d413 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -343,7 +343,9 @@
assetmanager->ForEachPackage([&](const std::string& this_package_name, uint8_t package_id) {
if (this_package_name == std_package_name) {
map = assetmanager->GetOverlayableMapForPackage(package_id);
+ return false;
}
+ return true;
});
if (map == nullptr) {
@@ -521,15 +523,16 @@
return nullptr;
}
- assetmanager->ForEachPackage([&](const std::string& package_name, uint8_t package_id) {
+ assetmanager->ForEachPackage([&](const std::string& package_name, uint8_t package_id) -> bool {
jstring jpackage_name = env->NewStringUTF(package_name.c_str());
if (jpackage_name == nullptr) {
// An exception is pending.
- return;
+ return false;
}
env->CallVoidMethod(sparse_array, gSparseArrayOffsets.put, static_cast<jint>(package_id),
jpackage_name);
+ return true;
});
return sparse_array;
}
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index 191472d..8d702d1 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -41,6 +41,7 @@
jmethodID dispatchVsync;
jmethodID dispatchHotplug;
+ jmethodID dispatchConfigChanged;
} gDisplayEventReceiverClassInfo;
@@ -61,6 +62,8 @@
void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) override;
void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
+ void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
+ int32_t configId) override;
};
@@ -114,6 +117,23 @@
mMessageQueue->raiseAndClearException(env, "dispatchHotplug");
}
+void NativeDisplayEventReceiver::dispatchConfigChanged(nsecs_t timestamp,
+ PhysicalDisplayId displayId,
+ int32_t configId) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+
+ ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
+ if (receiverObj.get()) {
+ ALOGV("receiver %p ~ Invoking config changed handler.", this);
+ env->CallVoidMethod(receiverObj.get(),
+ gDisplayEventReceiverClassInfo.dispatchConfigChanged,
+ timestamp, displayId, configId);
+ ALOGV("receiver %p ~ Returned from config changed handler.", this);
+ }
+
+ mMessageQueue->raiseAndClearException(env, "dispatchConfigChanged");
+}
+
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
jobject messageQueueObj, jint vsyncSource) {
@@ -180,6 +200,8 @@
gDisplayEventReceiverClassInfo.clazz, "dispatchVsync", "(JJI)V");
gDisplayEventReceiverClassInfo.dispatchHotplug = GetMethodIDOrDie(env,
gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug", "(JJZ)V");
+ gDisplayEventReceiverClassInfo.dispatchConfigChanged = GetMethodIDOrDie(env,
+ gDisplayEventReceiverClassInfo.clazz, "dispatchConfigChanged", "(JJI)V");
return res;
}
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 003ee37..4c25fd4 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -205,7 +205,7 @@
static jobject nativeScreenshot(JNIEnv* env, jclass clazz,
jobject displayTokenObj, jobject sourceCropObj, jint width, jint height,
- bool useIdentityTransform, int rotation) {
+ bool useIdentityTransform, int rotation, bool captureSecureLayers) {
sp<IBinder> displayToken = ibinderForJavaObject(env, displayTokenObj);
if (displayToken == NULL) {
return NULL;
@@ -213,9 +213,9 @@
Rect sourceCrop = rectFromObj(env, sourceCropObj);
sp<GraphicBuffer> buffer;
status_t res = ScreenshotClient::capture(displayToken, ui::Dataspace::V0_SRGB,
- ui::PixelFormat::RGBA_8888,
- sourceCrop, width, height,
- useIdentityTransform, rotation, &buffer);
+ ui::PixelFormat::RGBA_8888,
+ sourceCrop, width, height,
+ useIdentityTransform, rotation, captureSecureLayers, &buffer);
if (res != NO_ERROR) {
return NULL;
}
@@ -698,6 +698,25 @@
return configArray;
}
+static jboolean nativeSetAllowedDisplayConfigs(JNIEnv* env, jclass clazz,
+ jobject tokenObj, jintArray configArray) {
+ sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
+ if (token == nullptr) return JNI_FALSE;
+
+ std::vector<int32_t> allowedConfigs;
+ jsize configArraySize = env->GetArrayLength(configArray);
+ allowedConfigs.reserve(configArraySize);
+
+ jint* configArrayElements = env->GetIntArrayElements(configArray, 0);
+ for (int i = 0; i < configArraySize; i++) {
+ allowedConfigs.push_back(configArrayElements[i]);
+ }
+ env->ReleaseIntArrayElements(configArray, configArrayElements, 0);
+
+ size_t result = SurfaceComposerClient::setAllowedDisplayConfigs(token, allowedConfigs);
+ return result == NO_ERROR ? JNI_TRUE : JNI_FALSE;
+}
+
static jint nativeGetActiveConfig(JNIEnv* env, jclass clazz, jobject tokenObj) {
sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
if (token == NULL) return -1;
@@ -1194,6 +1213,8 @@
(void*)nativeGetActiveConfig },
{"nativeSetActiveConfig", "(Landroid/os/IBinder;I)Z",
(void*)nativeSetActiveConfig },
+ {"nativeSetAllowedDisplayConfigs", "(Landroid/os/IBinder;[I)Z",
+ (void*)nativeSetAllowedDisplayConfigs },
{"nativeGetDisplayColorModes", "(Landroid/os/IBinder;)[I",
(void*)nativeGetDisplayColorModes},
{"nativeGetDisplayNativePrimaries", "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DisplayPrimaries;",
@@ -1232,7 +1253,7 @@
(void*)nativeSetOverrideScalingMode },
{"nativeGetHandle", "(J)Landroid/os/IBinder;",
(void*)nativeGetHandle },
- {"nativeScreenshot", "(Landroid/os/IBinder;Landroid/graphics/Rect;IIZI)Landroid/graphics/GraphicBuffer;",
+ {"nativeScreenshot", "(Landroid/os/IBinder;Landroid/graphics/Rect;IIZIZ)Landroid/graphics/GraphicBuffer;",
(void*)nativeScreenshot },
{"nativeCaptureLayers", "(Landroid/os/IBinder;Landroid/graphics/Rect;F)Landroid/graphics/GraphicBuffer;",
(void*)nativeCaptureLayers },
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 73e6789..2ccb01a 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -155,10 +155,10 @@
static int gBlastulaPoolEventFD = -1;
/**
- * The maximum value that the gBlastulaPoolMax variable may take. This value
- * is a mirror of Zygote.BLASTULA_POOL_MAX_LIMIT
+ * The maximum value that the gBlastulaPoolSizeMax variable may take. This value
+ * is a mirror of ZygoteServer.BLASTULA_POOL_SIZE_MAX_LIMIT
*/
-static constexpr int BLASTULA_POOL_MAX_LIMIT = 10;
+static constexpr int BLASTULA_POOL_SIZE_MAX_LIMIT = 100;
/**
* A helper class containing accounting information for Blastulas.
@@ -216,6 +216,19 @@
}
}
+ void Clear() {
+ EntryStorage storage = mStorage.load();
+
+ if (storage != INVALID_ENTRY_VALUE) {
+ close(storage.read_pipe_fd);
+ mStorage.store(INVALID_ENTRY_VALUE);
+ }
+ }
+
+ void Invalidate() {
+ mStorage.store(INVALID_ENTRY_VALUE);
+ }
+
/**
* @return A copy of the data stored in this entry.
*/
@@ -257,7 +270,7 @@
* the BlastulaTableEntry class prevent data races during these concurrent
* operations.
*/
-static std::array<BlastulaTableEntry, BLASTULA_POOL_MAX_LIMIT> gBlastulaTable;
+static std::array<BlastulaTableEntry, BLASTULA_POOL_SIZE_MAX_LIMIT> gBlastulaTable;
/**
* The list of open zygote file descriptors.
@@ -736,10 +749,19 @@
}
static void PreparePkgSpecificDirs(const std::vector<std::string>& package_names,
- const std::vector<std::string>& volume_labels,
bool mount_all_obbs, const std::string& sandbox_id,
userid_t user_id, uid_t uid, fail_fn_t fail_fn) {
- for (auto& label : volume_labels) {
+ std::unique_ptr<DIR, decltype(&closedir)> dirp(opendir("/storage"), closedir);
+ if (!dirp) {
+ fail_fn(CREATE_ERROR("Failed to opendir /storage: %s", strerror(errno)));
+ }
+ struct dirent* ent;
+ while ((ent = readdir(dirp.get()))) {
+ if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..") || !strcmp(ent->d_name, "self")) {
+ continue;
+ }
+ std::string label(ent->d_name);
+
std::string mnt_source = StringPrintf("/mnt/runtime/write/%s", label.c_str());
std::string mnt_target = StringPrintf("/storage/%s", label.c_str());
if (label == "emulated") {
@@ -819,7 +841,7 @@
static void MountEmulatedStorage(uid_t uid, jint mount_mode,
bool force_mount_namespace, const std::string& package_name,
const std::vector<std::string>& packages_for_uid,
- const std::vector<std::string>& visible_vol_ids, const std::string& sandbox_id,
+ const std::string& sandbox_id,
fail_fn_t fail_fn) {
// See storage config details at http://source.android.com/tech/storage/
@@ -878,16 +900,16 @@
userid_t user_id = multiuser_get_user_id(uid);
CreatePkgSandboxTarget(user_id, fail_fn);
- std::string pkgSandboxDir = StringPrintf("/mnt/user/%d/package", user_id);
- if (TEMP_FAILURE_RETRY(mount(pkgSandboxDir.c_str(), "/storage",
+ std::string pkg_sandbox_dir = StringPrintf("/mnt/user/%d/package", user_id);
+ if (TEMP_FAILURE_RETRY(mount(pkg_sandbox_dir.c_str(), "/storage",
nullptr, MS_BIND | MS_REC | MS_SLAVE, nullptr)) == -1) {
fail_fn(CREATE_ERROR("Failed to mount %s to /storage: %s",
- pkgSandboxDir.c_str(), strerror(errno)));
+ pkg_sandbox_dir.c_str(), strerror(errno)));
}
HandleMountModeInstaller(mount_mode, user_id, sandbox_id, fail_fn);
- PreparePkgSpecificDirs(packages_for_uid, visible_vol_ids,
+ PreparePkgSpecificDirs(packages_for_uid,
mount_mode == MOUNT_EXTERNAL_INSTALLER, sandbox_id, user_id, uid, fail_fn);
}
} else {
@@ -900,16 +922,16 @@
// Mount user-specific symlink helper into place
userid_t user_id = multiuser_get_user_id(uid);
- const String8 userSource(String8::format("/mnt/user/%d", user_id));
- if (fs_prepare_dir(userSource.string(), 0751, 0, 0) == -1) {
+ const String8 user_source(String8::format("/mnt/user/%d", user_id));
+ if (fs_prepare_dir(user_source.string(), 0751, 0, 0) == -1) {
fail_fn(CREATE_ERROR("fs_prepare_dir failed on %s",
- userSource.string()));
+ user_source.string()));
}
- if (TEMP_FAILURE_RETRY(mount(userSource.string(), "/storage/self",
+ if (TEMP_FAILURE_RETRY(mount(user_source.string(), "/storage/self",
nullptr, MS_BIND, nullptr)) == -1) {
fail_fn(CREATE_ERROR("Failed to mount %s to /storage/self: %s",
- userSource.string(), strerror(errno)));
+ user_source.string(), strerror(errno)));
}
}
}
@@ -1159,6 +1181,14 @@
}
}
+static void ClearBlastulaTable() {
+ for (BlastulaTableEntry& entry : gBlastulaTable) {
+ entry.Clear();
+ }
+
+ gBlastulaPoolCount = 0;
+}
+
// Utility routine to fork a process from the zygote.
static pid_t ForkCommon(JNIEnv* env, bool is_system_server,
const std::vector<int>& fds_to_close,
@@ -1201,12 +1231,17 @@
// Clean up any descriptors which must be closed immediately
DetachDescriptors(env, fds_to_close, fail_fn);
+ // Invalidate the entries in the blastula table.
+ ClearBlastulaTable();
+
// Re-open all remaining open file descriptors so that they aren't shared
// with the zygote across a fork.
gOpenFdTable->ReopenOrDetach(fail_fn);
// Turn fdsan back on.
android_fdsan_set_error_level(fdsan_error_level);
+ } else {
+ ALOGD("Forked child process %d", pid);
}
// We blocked SIGCHLD prior to a fork, we unblock it here.
@@ -1224,7 +1259,7 @@
bool is_child_zygote, jstring managed_instruction_set,
jstring managed_app_data_dir, jstring managed_package_name,
jobjectArray managed_pacakges_for_uid,
- jobjectArray managed_visible_vol_ids, jstring managed_sandbox_id) {
+ jstring managed_sandbox_id) {
const char* process_name = is_system_server ? "system_server" : "zygote";
auto fail_fn = std::bind(ZygoteFailure, env, process_name, managed_nice_name, _1);
auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1);
@@ -1272,12 +1307,8 @@
ExtractJStringArray(env, process_name, managed_nice_name, managed_pacakges_for_uid).
value_or(std::vector<std::string>());
- std::vector<std::string> visible_vol_ids =
- ExtractJStringArray(env, process_name, managed_nice_name, managed_visible_vol_ids).
- value_or(std::vector<std::string>());
-
MountEmulatedStorage(uid, mount_external, use_native_bridge, package_name.value(),
- packages_for_uid, visible_vol_ids, sandbox_id.value_or(""), fail_fn);
+ packages_for_uid, sandbox_id.value_or(""), fail_fn);
// If this zygote isn't root, it won't be able to create a process group,
// since the directory is owned by root.
@@ -1580,7 +1611,7 @@
jint mount_external, jstring se_info, jstring nice_name,
jintArray managed_fds_to_close, jintArray managed_fds_to_ignore, jboolean is_child_zygote,
jstring instruction_set, jstring app_data_dir, jstring package_name,
- jobjectArray packages_for_uid, jobjectArray visible_vol_ids, jstring sandbox_id) {
+ jobjectArray packages_for_uid, jstring sandbox_id) {
jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote);
if (UNLIKELY(managed_fds_to_close == nullptr)) {
@@ -1612,7 +1643,7 @@
capabilities, capabilities,
mount_external, se_info, nice_name, false,
is_child_zygote == JNI_TRUE, instruction_set, app_data_dir,
- package_name, packages_for_uid, visible_vol_ids, sandbox_id);
+ package_name, packages_for_uid, sandbox_id);
}
return pid;
}
@@ -1638,7 +1669,7 @@
SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
permitted_capabilities, effective_capabilities,
MOUNT_EXTERNAL_DEFAULT, nullptr, nullptr, true,
- false, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
+ false, nullptr, nullptr, nullptr, nullptr, nullptr);
} else if (pid > 0) {
// The zygote process checks whether the child process has died or not.
ALOGI("System server process %d has been created", pid);
@@ -1792,7 +1823,7 @@
jint runtime_flags, jobjectArray rlimits,
jint mount_external, jstring se_info, jstring nice_name,
jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir,
- jstring package_name, jobjectArray packages_for_uid, jobjectArray visible_vol_ids,
+ jstring package_name, jobjectArray packages_for_uid,
jstring sandbox_id) {
jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote);
@@ -1800,7 +1831,7 @@
capabilities, capabilities,
mount_external, se_info, nice_name, false,
is_child_zygote == JNI_TRUE, instruction_set, app_data_dir,
- package_name, packages_for_uid, visible_vol_ids, sandbox_id);
+ package_name, packages_for_uid, sandbox_id);
}
/**
@@ -1887,11 +1918,32 @@
return gBlastulaPoolCount;
}
+/**
+ * Kills all processes currently in the blastula pool.
+ *
+ * @param env Managed runtime environment
+ * @return The number of blastulas currently in the blastula pool
+ */
+static void com_android_internal_os_Zygote_nativeEmptyBlastulaPool(JNIEnv* env, jclass) {
+ for (auto& entry : gBlastulaTable) {
+ auto entry_storage = entry.GetValues();
+
+ if (entry_storage.has_value()) {
+ kill(entry_storage.value().pid, SIGKILL);
+ close(entry_storage.value().read_pipe_fd);
+
+ // Avoid a second atomic load by invalidating instead of clearing.
+ entry.Invalidate();
+ --gBlastulaPoolCount;
+ }
+ }
+}
+
static const JNINativeMethod gMethods[] = {
{ "nativeSecurityInit", "()V",
(void *) com_android_internal_os_Zygote_nativeSecurityInit },
{ "nativeForkAndSpecialize",
- "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)I",
+ "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)I",
(void *) com_android_internal_os_Zygote_nativeForkAndSpecialize },
{ "nativeForkSystemServer", "(II[II[[IJJ)I",
(void *) com_android_internal_os_Zygote_nativeForkSystemServer },
@@ -1906,7 +1958,7 @@
{ "nativeForkBlastula", "(II[I)I",
(void *) com_android_internal_os_Zygote_nativeForkBlastula },
{ "nativeSpecializeBlastula",
- "(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)V",
+ "(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)V",
(void *) com_android_internal_os_Zygote_nativeSpecializeBlastula },
{ "nativeGetSocketFDs", "(Z)V",
(void *) com_android_internal_os_Zygote_nativeGetSocketFDs },
@@ -1917,7 +1969,9 @@
{ "nativeGetBlastulaPoolEventFD", "()I",
(void *) com_android_internal_os_Zygote_nativeGetBlastulaPoolEventFD },
{ "nativeGetBlastulaPoolCount", "()I",
- (void *) com_android_internal_os_Zygote_nativeGetBlastulaPoolCount }
+ (void *) com_android_internal_os_Zygote_nativeGetBlastulaPoolCount },
+ { "nativeEmptyBlastulaPool", "()V",
+ (void *) com_android_internal_os_Zygote_nativeEmptyBlastulaPool }
};
int register_com_android_internal_os_Zygote(JNIEnv* env) {
diff --git a/core/jni/com_google_android_gles_jni_GLImpl.cpp b/core/jni/com_google_android_gles_jni_GLImpl.cpp
index 40ff7e4..c806162 100644
--- a/core/jni/com_google_android_gles_jni_GLImpl.cpp
+++ b/core/jni/com_google_android_gles_jni_GLImpl.cpp
@@ -65,8 +65,6 @@
GLsizei stride, const GLvoid *pointer, GLsizei count);
}
-static int initialized = 0;
-
static jclass nioAccessClass;
static jclass bufferClass;
static jclass G11ImplClass;
@@ -4239,6 +4237,7 @@
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
+ return (jint)0;
}
return (jint)_returnValue;
}
@@ -4308,6 +4307,7 @@
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
+ return (jint)0;
}
return (jint)_returnValue;
}
@@ -4904,6 +4904,7 @@
(JNIEnv *_env, jobject _this, jint target, jint pname, jintArray params_ref, jint offset) {
jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glGetBufferParameteriv");
+ return;
}
/* void glGetBufferParameteriv ( GLenum target, GLenum pname, GLint *params ) */
@@ -4912,6 +4913,7 @@
(JNIEnv *_env, jobject _this, jint target, jint pname, jobject params_buf) {
jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glGetBufferParameteriv");
+ return;
}
/* void glGetClipPlanef ( GLenum pname, GLfloat *eqn ) */
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index f74fc21..efbe8ba 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -615,6 +615,40 @@
// CATEGORY: SETTINGS
// OS: Q
ACTION_PANEL_INTERACTION = 1658;
+
+ // ACTION: Show Contextual homepage, log latency in loading cards
+ ACTION_CONTEXTUAL_HOME_SHOW = 1662;
+
+ // ACTION: Contextual card displays
+ ACTION_CONTEXTUAL_CARD_SHOW = 1663;
+
+ // ACTION: Contextual cards are eligible to be shown, but don't rank high
+ ACTION_CONTEXTUAL_CARD_NOT_SHOW = 1664;
+
+ // ACTION: Settings > long press a card, and click dismiss
+ // Contextual card is dismissed
+ ACTION_CONTEXTUAL_CARD_DISMISS = 1665;
+
+ // ACTION: Settings > click a card
+ // Contextual card is clicked
+ ACTION_CONTEXTUAL_CARD_CLICK = 1666;
+
+ // Mapping: go/at-mapping
+ ACTION_ATSG = 1674;
+
+ ACTION_ATPG = 1675;
+
+ ACTION_ATCLPB = 1676;
+
+ ACTION_ATCGIB = 1677;
+
+ ACTION_ATCPAB = 1678;
+
+ ACTION_ATCSAUC = 1679;
+
+ ACTION_ATCSCUC = 1680;
+
+ ACTION_ATCHNUC = 1681;
}
/**
@@ -2235,4 +2269,19 @@
// Panel for Media Output
PANEL_MEDIA_OUTPUT = 1657;
+
+ // Mapping: go/at-mapping
+ PAGE_ATSSI = 1667;
+
+ PAGE_ATSII = 1668;
+
+ PAGE_ATUS = 1669;
+
+ PAGE_ATSSP = 1670;
+
+ PAGE_ATSAP = 1671;
+
+ PAGE_ATSCP = 1672;
+
+ PAGE_ATHNP = 1673;
}
diff --git a/core/proto/android/stats/style/Android.bp b/core/proto/android/stats/style/Android.bp
new file mode 100644
index 0000000..f085a52
--- /dev/null
+++ b/core/proto/android/stats/style/Android.bp
@@ -0,0 +1,27 @@
+// Copyright (C) 2019 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.
+
+java_library {
+ name: "styleprotosnano",
+ proto: {
+ type: "nano",
+ output_params: ["store_unknown_fields=true"],
+ include_dirs: ["external/protobuf/src"],
+ },
+
+ sdk_version: "current",
+ srcs: [
+ "*.proto",
+ ],
+}
diff --git a/core/proto/android/stats/style/style_enums.proto b/core/proto/android/stats/style/style_enums.proto
new file mode 100644
index 0000000..b0e4391
--- /dev/null
+++ b/core/proto/android/stats/style/style_enums.proto
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+package android.stats.style;
+option java_multiple_files = true;
+
+enum Action {
+ DEFAULT_ACTION = 0;
+ ONRESUME = 1;
+ ONSTOP = 2;
+ PICKER_SELECT = 3;
+ PICKER_APPLIED = 4;
+ WALLPAPER_OPEN_CATEGORY = 5;
+ WALLPAPER_SELECT = 6;
+ WALLPAPER_APPLIED = 7;
+ WALLPAPER_EXPLORE = 8;
+ WALLPAPER_DOWNLOAD = 9;
+ WALLPAPER_REMOVE = 10;
+}
+
diff --git a/core/res/res/drawable/ic_action_open.xml b/core/res/res/drawable/ic_action_open.xml
index 3d3d36e..ba0a38d 100644
--- a/core/res/res/drawable/ic_action_open.xml
+++ b/core/res/res/drawable/ic_action_open.xml
@@ -17,8 +17,10 @@
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
- android:viewportHeight="24.0">
+ android:viewportHeight="24.0"
+ android:tint="?attr/colorControlNormal">
+ >
<path
- android:fillColor="#FF737373"
+ android:fillColor="@color/white"
android:pathData="M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"/>
</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_qs_battery_saver.xml b/core/res/res/drawable/ic_qs_battery_saver.xml
index 89b2569..93975b6 100644
--- a/core/res/res/drawable/ic_qs_battery_saver.xml
+++ b/core/res/res/drawable/ic_qs_battery_saver.xml
@@ -1,5 +1,5 @@
<!--
- Copyright (C) 2018 The Android Open Source Project
+ Copyright (C) 2019 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.
@@ -21,8 +21,10 @@
android:viewportHeight="24.0"
android:tint="?android:attr/colorControlNormal">
<path
- android:pathData="M5,3
- l3.5,0 l0,-1.5 l7,0 l0,1.5 l3.5,0 l0,19.5 l-14,0z
- M10.5,8.5 l0,3 l-3,0 l0,3 l3,0 l0,3 l3,0 l0,-3 l3,0 l0,-3 l-3,0 l0,-3 z"
- android:fillColor="#FFFFFF"/>
+ android:fillColor="#FFF"
+ android:pathData="M16.67,4H14.5V2h-5v2H7.33C6.6,4 6,4.6 6,5.33V15v5.67C6,21.4 6.6,22 7.33,22h9.33C17.4,22 18,21.4 18,20.67V15V5.33C18,4.6 17.4,4 16.67,4zM16,15v5H8v-5V6h8V15z"/>
+ <path
+ android:fillColor="#FFF"
+ android:pathData="M15,12l-2,0l0,-2l-2,0l0,2l-2,0l0,2l2,0l0,2l2,0l0,-2l2,0z"/>
+
</vector>
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index 733878b..8f2d6c3 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -185,4 +185,20 @@
<item>@string/app_info</item>
</string-array>
+ <!-- Device-specific array of SIM slot indexes which are are embedded eUICCs.
+ e.g. If a device has two physical slots with indexes 0, 1, and slot 1 is an
+ eUICC, then the value of this array should be:
+ <integer-array name="non_removable_euicc_slots">
+ <item>1</item>
+ </integer-array>
+ If a device has three physical slots and slot 1 and 2 are eUICCs, then the value of
+ this array should be:
+ <integer-array name="non_removable_euicc_slots">
+ <item>1</item>
+ <item>2</item>
+ </integer-array>
+ This is used to differentiate between removable eUICCs and built in eUICCs, and should
+ be set by OEMs for devices which use eUICCs. -->
+ <integer-array name="non_removable_euicc_slots"></integer-array>
+
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index eb69535..910af4c 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2008,11 +2008,6 @@
a transaction, so it interacts poorly with SECURE_DELETE. -->
<string name="db_default_journal_mode" translatable="false">TRUNCATE</string>
- <!-- Enables compatibility WAL mode.
- In this mode, only database journal mode will be changed, connection pool
- size will still be limited to a single connection. -->
- <bool name="db_compatibility_wal_supported">true</bool>
-
<!-- Maximum size of the persistent journal file in bytes.
If the journal file grows to be larger than this amount then SQLite will
truncate it after committing the transaction. -->
@@ -2272,6 +2267,7 @@
<!-- If the sensor that wakes up the lock screen is available or not. -->
<bool name="config_dozeWakeLockScreenSensorAvailable">false</bool>
+ <integer name="config_dozeWakeLockScreenDebounce">3000</integer>
<!-- Control whether the always on display mode is available. This should only be enabled on
devices where the display has been tuned to be power efficient in DOZE and/or DOZE_SUSPEND
@@ -3363,6 +3359,10 @@
<!-- True if home app should be pinned via Pinner Service -->
<bool name="config_pinnerHomeApp">false</bool>
+ <!-- List of files pinned by the Pinner Service with the apex boot image b/119800099 -->
+ <string-array translatable="false" name="config_apexBootImagePinnerServiceFiles">
+ </string-array>
+
<!-- Number of days preloaded file cache should be preserved on a device before it can be
deleted -->
<integer name="config_keepPreloadsMinDays">7</integer>
@@ -3930,4 +3930,25 @@
The same restrictions apply to this array. -->
<array name="config_displayWhiteBalanceDisplayColorTemperatures">
</array>
+
+ <!-- All of the paths defined for the batterymeter are defined on a 12x20 canvas, and must
+ be parsable by android.utill.PathParser -->
+ <string name="config_batterymeterPerimeterPath" translatable="false">
+ M3.5,2 v0 H1.33 C0.6,2 0,2.6 0,3.33 V13v5.67 C0,19.4 0.6,20 1.33,20 h9.33 C11.4,20 12,19.4 12,18.67 V13V3.33 C12,2.6 11.4,2 10.67,2 H8.5 V0 H3.5 z M2,18v-7V4h8v9v5H2L2,18z
+ </string>
+
+ <string name="config_batterymeterFillMask" translatable="false">
+ M2,18 v-14 h8 v14 z
+ </string>
+ <string name="config_batterymeterBoltPath" translatable="false">
+ M5,17.5 V12 H3 L7,4.5 V10 h2 L5,17.5 z
+ </string>
+ <string name="config_batterymeterPowersavePath" translatable="false">
+ M9,10l-2,0l0,-2l-2,0l0,2l-2,0l0,2l2,0l0,2l2,0l0,-2l2,0z
+ </string>
+
+ <!-- A dual tone battery meter draws the perimeter path twice - once to define the shape
+ and a second time clipped to the fill level to indicate charge -->
+ <bool name="config_batterymeterDualTone">false</bool>
+
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 8df0e19..426d813 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -752,7 +752,6 @@
<java-symbol type="string" name="date_time" />
<java-symbol type="string" name="date_time_set" />
<java-symbol type="string" name="date_time_done" />
- <java-symbol type="bool" name="db_compatibility_wal_supported" />
<java-symbol type="string" name="db_default_journal_mode" />
<java-symbol type="string" name="db_default_sync_mode" />
<java-symbol type="string" name="db_wal_sync_mode" />
@@ -2987,6 +2986,8 @@
<java-symbol type="array" name="resolver_target_actions_pin" />
<java-symbol type="array" name="resolver_target_actions_unpin" />
+ <java-symbol type="array" name="non_removable_euicc_slots" />
+
<java-symbol type="string" name="install_carrier_app_notification_title" />
<java-symbol type="string" name="install_carrier_app_notification_text" />
<java-symbol type="string" name="install_carrier_app_notification_text_app_name" />
@@ -3047,6 +3048,7 @@
<java-symbol type="array" name="config_defaultPinnerServiceFiles" />
<java-symbol type="bool" name="config_pinnerCameraApp" />
<java-symbol type="bool" name="config_pinnerHomeApp" />
+ <java-symbol type="array" name="config_apexBootImagePinnerServiceFiles" />
<java-symbol type="string" name="config_doubleTouchGestureEnableFile" />
@@ -3194,6 +3196,11 @@
<java-symbol type="raw" name="fallback_categories" />
<java-symbol type="string" name="config_icon_mask" />
+ <java-symbol type="string" name="config_batterymeterPerimeterPath" />
+ <java-symbol type="string" name="config_batterymeterFillMask" />
+ <java-symbol type="string" name="config_batterymeterBoltPath" />
+ <java-symbol type="string" name="config_batterymeterPowersavePath" />
+ <java-symbol type="bool" name="config_batterymeterDualTone" />
<!-- Accessibility Shortcut -->
<java-symbol type="string" name="accessibility_shortcut_warning_dialog_title" />
@@ -3403,6 +3410,7 @@
<java-symbol type="string" name="config_dozeLongPressSensorType" />
<java-symbol type="bool" name="config_dozeWakeLockScreenSensorAvailable" />
+ <java-symbol type="integer" name="config_dozeWakeLockScreenDebounce" />
<java-symbol type="array" name="config_allowedGlobalInstantAppSettings" />
<java-symbol type="array" name="config_allowedSystemInstantAppSettings" />
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
new file mode 100644
index 0000000..833c734
--- /dev/null
+++ b/core/tests/coretests/Android.bp
@@ -0,0 +1,113 @@
+android_test {
+ name: "FrameworksCoreTests",
+
+ srcs: [
+ "src/**/*.java",
+ "src/**/I*.aidl",
+ "DisabledTestApp/src/**/*.java",
+ "EnabledTestApp/src/**/*.java",
+ "BinderProxyCountingTestApp/src/**/*.java",
+ "BinderProxyCountingTestService/src/**/*.java",
+ "aidl/**/I*.aidl",
+ ],
+
+ aidl: {
+ local_include_dirs: ["aidl"],
+ },
+
+ dxflags: ["--core-library"],
+
+ aaptflags: [
+ "-0 .dat",
+ "-0 .gld",
+ "-c fa",
+ ],
+ static_libs: [
+ "frameworks-base-testutils",
+ "core-tests-support",
+ "android-common",
+ "frameworks-core-util-lib",
+ "mockwebserver",
+ "guava",
+ "androidx.test.espresso.core",
+ "androidx.test.ext.junit",
+ "androidx.test.runner",
+ "androidx.test.rules",
+ "mockito-target-minus-junit4",
+ "ub-uiautomator",
+ "platform-test-annotations",
+ "truth-prebuilt",
+ "print-test-util-lib",
+ "testng",
+ ],
+
+ libs: [
+ "android.test.runner",
+ "telephony-common",
+ "testables",
+ "org.apache.http.legacy",
+ "android.test.base",
+ "android.test.mock",
+ "framework-atb-backward-compatibility",
+ ],
+
+ platform_apis: true,
+ test_suites: ["device-tests"],
+
+ certificate: "platform",
+
+ resource_dirs: ["res"],
+ resource_zips: [":FrameworksCoreTests_apks_as_resources"],
+}
+
+// Rules to copy all the test apks to the intermediate raw resource directory
+java_genrule {
+ name: "FrameworksCoreTests_apks_as_resources",
+ srcs: [
+ ":FrameworksCoreTests_install",
+ ":FrameworksCoreTests_install_bad_dex",
+ ":FrameworksCoreTests_install_complete_package_info",
+ ":FrameworksCoreTests_install_decl_perm",
+ ":FrameworksCoreTests_install_jni_lib_open_from_apk",
+ ":FrameworksCoreTests_install_loc_auto",
+ ":FrameworksCoreTests_install_loc_internal",
+ ":FrameworksCoreTests_install_loc_sdcard",
+ ":FrameworksCoreTests_install_loc_unspecified",
+ ":FrameworksCoreTests_install_multi_package",
+ ":FrameworksCoreTests_install_split_base",
+ ":FrameworksCoreTests_install_split_feature_a",
+ ":FrameworksCoreTests_install_use_perm_good",
+ ":FrameworksCoreTests_install_uses_feature",
+ ":FrameworksCoreTests_install_verifier_bad",
+ ":FrameworksCoreTests_install_verifier_good",
+ ":FrameworksCoreTests_keyset_permdef_sa_unone",
+ ":FrameworksCoreTests_keyset_permuse_sa_ua_ub",
+ ":FrameworksCoreTests_keyset_permuse_sb_ua_ub",
+ ":FrameworksCoreTests_keyset_sab_ua",
+ ":FrameworksCoreTests_keyset_sa_ua",
+ ":FrameworksCoreTests_keyset_sa_uab",
+ ":FrameworksCoreTests_keyset_sa_ua_ub",
+ ":FrameworksCoreTests_keyset_sa_ub",
+ ":FrameworksCoreTests_keyset_sa_unone",
+ ":FrameworksCoreTests_keyset_sau_ub",
+ ":FrameworksCoreTests_keyset_sb_ua",
+ ":FrameworksCoreTests_keyset_sb_ub",
+ ":FrameworksCoreTests_keyset_splata_api",
+ ":FrameworksCoreTests_keyset_splat_api",
+ ":FrameworksCoreTests_locales",
+ ":FrameworksCoreTests_version_1",
+ ":FrameworksCoreTests_version_1_diff",
+ ":FrameworksCoreTests_version_1_nosys",
+ ":FrameworksCoreTests_version_2",
+ ":FrameworksCoreTests_version_2_diff",
+ ":FrameworksCoreTests_version_3",
+ ],
+ out: ["FrameworkCoreTests_apks_as_resources.res.zip"],
+ tools: ["soong_zip"],
+
+ cmd: "mkdir -p $(genDir)/res/raw && " +
+ "for i in $(in); do " +
+ " x=$${i##*FrameworksCoreTests_}; echo $${x}; cp $$i $(genDir)/res/raw/$${x%.apk};" +
+ "done && " +
+ "$(location soong_zip) -o $(out) -C $(genDir)/res -D $(genDir)/res",
+}
diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk
deleted file mode 100644
index 40ebd44..0000000
--- a/core/tests/coretests/Android.mk
+++ /dev/null
@@ -1,85 +0,0 @@
-ACTUAL_LOCAL_PATH := $(call my-dir)
-
-# this var will hold all the test apk module names later.
-FrameworkCoreTests_all_apks :=
-
-# We have to include the subdir makefiles first
-# so that FrameworkCoreTests_all_apks will be populated correctly.
-include $(call all-makefiles-under,$(ACTUAL_LOCAL_PATH))
-
-LOCAL_PATH := $(ACTUAL_LOCAL_PATH)
-
-include $(CLEAR_VARS)
-
-# We only want this apk build for tests.
-LOCAL_MODULE_TAGS := tests
-
-# Include all test java files.
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, src) \
- $(call all-Iaidl-files-under, src) \
- $(call all-java-files-under, DisabledTestApp/src) \
- $(call all-java-files-under, EnabledTestApp/src) \
- $(call all-java-files-under, BinderProxyCountingTestApp/src) \
- $(call all-java-files-under, BinderProxyCountingTestService/src) \
- $(call all-Iaidl-files-under, aidl)
-
-LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/aidl
-
-LOCAL_DX_FLAGS := --core-library
-LOCAL_JACK_FLAGS := --multi-dex native
-LOCAL_AAPT_FLAGS = -0 dat -0 gld -c fa
-LOCAL_STATIC_JAVA_LIBRARIES := \
- frameworks-base-testutils \
- core-tests-support \
- android-common \
- frameworks-core-util-lib \
- mockwebserver \
- guava \
- androidx.test.espresso.core \
- androidx.test.ext.junit \
- androidx.test.runner \
- androidx.test.rules \
- mockito-target-minus-junit4 \
- ub-uiautomator \
- platform-test-annotations \
- truth-prebuilt \
- print-test-util-lib \
- testng # TODO: remove once Android migrates to JUnit 4.12, which provide assertThrows
-
-LOCAL_JAVA_LIBRARIES := \
- android.test.runner \
- telephony-common \
- testables \
- org.apache.http.legacy \
- android.test.base \
- android.test.mock \
- framework-atb-backward-compatibility \
-
-LOCAL_PACKAGE_NAME := FrameworksCoreTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_CERTIFICATE := platform
-
-# intermediate dir to include all the test apks as raw resource
-FrameworkCoreTests_intermediates := $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME))/test_apks/res
-LOCAL_RESOURCE_DIR := $(FrameworkCoreTests_intermediates) $(LOCAL_PATH)/res
-
-# Disable AAPT2 because the hacks below depend on the AAPT rules implementation
-LOCAL_USE_AAPT2 := false
-
-include $(BUILD_PACKAGE)
-# Rules to copy all the test apks to the intermediate raw resource directory
-FrameworkCoreTests_all_apks_res := $(addprefix $(FrameworkCoreTests_intermediates)/raw/, \
- $(foreach a, $(FrameworkCoreTests_all_apks), $(patsubst FrameworkCoreTests_%,%,$(a))))
-
-$(FrameworkCoreTests_all_apks_res): $(FrameworkCoreTests_intermediates)/raw/%: $(call intermediates-dir-for,APPS,FrameworkCoreTests_%)/package.apk
- $(call copy-file-to-new-target)
-
-# Use R_file_stamp as dependency because we want the test apks in place before the R.java is generated.
-$(R_file_stamp) : $(FrameworkCoreTests_all_apks_res)
-
-FrameworkCoreTests_all_apks :=
-FrameworkCoreTests_intermediates :=
-FrameworkCoreTests_all_apks_res :=
diff --git a/core/tests/coretests/BinderProxyCountingTestApp/Android.bp b/core/tests/coretests/BinderProxyCountingTestApp/Android.bp
new file mode 100644
index 0000000..6279a48
--- /dev/null
+++ b/core/tests/coretests/BinderProxyCountingTestApp/Android.bp
@@ -0,0 +1,25 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test_helper_app {
+ name: "BinderProxyCountingTestApp",
+
+ static_libs: ["coretests-aidl"],
+ srcs: ["**/*.java"],
+
+ sdk_version: "current",
+ certificate: "platform",
+
+ test_suites: ["device-tests"],
+}
diff --git a/core/tests/coretests/BinderProxyCountingTestApp/Android.mk b/core/tests/coretests/BinderProxyCountingTestApp/Android.mk
deleted file mode 100644
index 4642694..0000000
--- a/core/tests/coretests/BinderProxyCountingTestApp/Android.mk
+++ /dev/null
@@ -1,29 +0,0 @@
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_STATIC_JAVA_LIBRARIES := coretests-aidl
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := BinderProxyCountingTestApp
-LOCAL_SDK_VERSION := current
-LOCAL_CERTIFICATE := platform
-
-LOCAL_COMPATIBILITY_SUITE := device-tests
-include $(BUILD_PACKAGE)
-
diff --git a/core/tests/coretests/BinderProxyCountingTestService/Android.bp b/core/tests/coretests/BinderProxyCountingTestService/Android.bp
new file mode 100644
index 0000000..22718cb
--- /dev/null
+++ b/core/tests/coretests/BinderProxyCountingTestService/Android.bp
@@ -0,0 +1,25 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test_helper_app {
+ name: "BinderProxyCountingTestService",
+
+ static_libs: ["coretests-aidl"],
+ srcs: ["**/*.java"],
+
+ platform_apis: true,
+ certificate: "platform",
+
+ test_suites: ["device-tests"],
+}
diff --git a/core/tests/coretests/BinderProxyCountingTestService/Android.mk b/core/tests/coretests/BinderProxyCountingTestService/Android.mk
deleted file mode 100644
index f852c7a..0000000
--- a/core/tests/coretests/BinderProxyCountingTestService/Android.mk
+++ /dev/null
@@ -1,29 +0,0 @@
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_STATIC_JAVA_LIBRARIES := coretests-aidl
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := BinderProxyCountingTestService
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_CERTIFICATE := platform
-
-LOCAL_COMPATIBILITY_SUITE := device-tests
-include $(BUILD_PACKAGE)
-
diff --git a/core/tests/coretests/BstatsTestApp/Android.bp b/core/tests/coretests/BstatsTestApp/Android.bp
new file mode 100644
index 0000000..424c71a
--- /dev/null
+++ b/core/tests/coretests/BstatsTestApp/Android.bp
@@ -0,0 +1,34 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test_helper_app {
+ name: "BstatsTestApp",
+
+ test_suites: [
+ "device-tests",
+ ],
+
+ static_libs: ["coretests-aidl"],
+
+ srcs: ["**/*.java"],
+
+ sdk_version: "current",
+ certificate: "platform",
+ dex_preopt: {
+ enabled: false,
+ },
+ optimize: {
+ enabled: false,
+ },
+}
diff --git a/core/tests/coretests/BstatsTestApp/Android.mk b/core/tests/coretests/BstatsTestApp/Android.mk
deleted file mode 100644
index a5872a5..0000000
--- a/core/tests/coretests/BstatsTestApp/Android.mk
+++ /dev/null
@@ -1,34 +0,0 @@
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_STATIC_JAVA_LIBRARIES := coretests-aidl
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := BstatsTestApp
-LOCAL_SDK_VERSION := current
-LOCAL_CERTIFICATE := platform
-LOCAL_DEX_PREOPT := false
-LOCAL_PROGUARD_ENABLED := disabled
-
-LOCAL_COMPATIBILITY_SUITE := device-tests
-include $(BUILD_PACKAGE)
diff --git a/core/tests/coretests/DisabledTestApp/Android.bp b/core/tests/coretests/DisabledTestApp/Android.bp
new file mode 100644
index 0000000..419816e
--- /dev/null
+++ b/core/tests/coretests/DisabledTestApp/Android.bp
@@ -0,0 +1,8 @@
+android_test_helper_app {
+ name: "DisabledTestApp",
+
+ srcs: ["**/*.java"],
+
+ sdk_version: "current",
+ certificate: "platform",
+}
diff --git a/core/tests/coretests/DisabledTestApp/Android.mk b/core/tests/coretests/DisabledTestApp/Android.mk
deleted file mode 100644
index e4304f7..0000000
--- a/core/tests/coretests/DisabledTestApp/Android.mk
+++ /dev/null
@@ -1,13 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := DisabledTestApp
-LOCAL_SDK_VERSION := current
-LOCAL_CERTIFICATE := platform
-
-include $(BUILD_PACKAGE)
-
diff --git a/core/tests/coretests/EnabledTestApp/Android.bp b/core/tests/coretests/EnabledTestApp/Android.bp
new file mode 100644
index 0000000..bc4f4bd
--- /dev/null
+++ b/core/tests/coretests/EnabledTestApp/Android.bp
@@ -0,0 +1,8 @@
+android_test_helper_app {
+ name: "EnabledTestApp",
+
+ srcs: ["**/*.java"],
+
+ sdk_version: "current",
+ certificate: "platform",
+}
diff --git a/core/tests/coretests/EnabledTestApp/Android.mk b/core/tests/coretests/EnabledTestApp/Android.mk
deleted file mode 100644
index cd37f08..0000000
--- a/core/tests/coretests/EnabledTestApp/Android.mk
+++ /dev/null
@@ -1,13 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := EnabledTestApp
-LOCAL_SDK_VERSION := current
-LOCAL_CERTIFICATE := platform
-
-include $(BUILD_PACKAGE)
-
diff --git a/core/tests/coretests/aidl/Android.bp b/core/tests/coretests/aidl/Android.bp
new file mode 100644
index 0000000..6e442db
--- /dev/null
+++ b/core/tests/coretests/aidl/Android.bp
@@ -0,0 +1,19 @@
+// 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.
+
+java_test {
+ name: "coretests-aidl",
+ sdk_version: "current",
+ srcs: ["**/*.aidl"],
+}
diff --git a/core/tests/coretests/aidl/Android.mk b/core/tests/coretests/aidl/Android.mk
deleted file mode 100644
index 86e36b6..0000000
--- a/core/tests/coretests/aidl/Android.mk
+++ /dev/null
@@ -1,22 +0,0 @@
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_SDK_VERSION := current
-LOCAL_SRC_FILES := $(call all-subdir-Iaidl-files)
-LOCAL_MODULE := coretests-aidl
-include $(BUILD_STATIC_JAVA_LIBRARY)
\ No newline at end of file
diff --git a/core/tests/coretests/apks/Android.bp b/core/tests/coretests/apks/Android.bp
new file mode 100644
index 0000000..20c87b2
--- /dev/null
+++ b/core/tests/coretests/apks/Android.bp
@@ -0,0 +1,7 @@
+java_defaults {
+ name: "FrameworksCoreTests_apks_defaults",
+ sdk_version: "current",
+
+ // Every package should have a native library
+ jni_libs: ["libframeworks_coretests_jni"],
+}
diff --git a/core/tests/coretests/apks/Android.mk b/core/tests/coretests/apks/Android.mk
deleted file mode 100644
index 98c0c2a..0000000
--- a/core/tests/coretests/apks/Android.mk
+++ /dev/null
@@ -1,7 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-FrameworkCoreTests_BUILD_PACKAGE := $(LOCAL_PATH)/FrameworkCoreTests_apk.mk
-
-# build sub packages
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/core/tests/coretests/apks/FrameworkCoreTests_apk.mk b/core/tests/coretests/apks/FrameworkCoreTests_apk.mk
deleted file mode 100644
index 8a7d72a5..0000000
--- a/core/tests/coretests/apks/FrameworkCoreTests_apk.mk
+++ /dev/null
@@ -1,16 +0,0 @@
-
-LOCAL_MODULE_TAGS := tests
-
-# Disable dexpreopt.
-LOCAL_DEX_PREOPT := false
-
-# Make sure every package name gets the FrameworkCoreTests_ prefix.
-LOCAL_PACKAGE_NAME := FrameworkCoreTests_$(LOCAL_PACKAGE_NAME)
-LOCAL_SDK_VERSION := current
-
-# Every package should have a native library
-LOCAL_JNI_SHARED_LIBRARIES := libframeworks_coretests_jni
-
-FrameworkCoreTests_all_apks += $(LOCAL_PACKAGE_NAME)
-
-include $(BUILD_PACKAGE)
diff --git a/core/tests/coretests/apks/install-split-base/Android.bp b/core/tests/coretests/apks/install-split-base/Android.bp
new file mode 100644
index 0000000..ddf75b2
--- /dev/null
+++ b/core/tests/coretests/apks/install-split-base/Android.bp
@@ -0,0 +1,6 @@
+android_test_helper_app {
+ name: "FrameworksCoreTests_install_split_base",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+
+ srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/install-split-base/Android.mk b/core/tests/coretests/apks/install-split-base/Android.mk
deleted file mode 100644
index 5b60e31..0000000
--- a/core/tests/coretests/apks/install-split-base/Android.mk
+++ /dev/null
@@ -1,10 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := install_split_base
-
-include $(FrameworkCoreTests_BUILD_PACKAGE)
\ No newline at end of file
diff --git a/core/tests/coretests/apks/install-split-feature-a/Android.bp b/core/tests/coretests/apks/install-split-feature-a/Android.bp
new file mode 100644
index 0000000..9ec9893
--- /dev/null
+++ b/core/tests/coretests/apks/install-split-feature-a/Android.bp
@@ -0,0 +1,11 @@
+android_test_helper_app {
+ name: "FrameworksCoreTests_install_split_feature_a",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+
+ srcs: ["**/*.java"],
+
+ aaptflags: [
+ "--custom-package com.google.android.dexapis.splitapp.feature_a",
+ "--package-id 0x80",
+ ],
+}
diff --git a/core/tests/coretests/apks/install-split-feature-a/Android.mk b/core/tests/coretests/apks/install-split-feature-a/Android.mk
deleted file mode 100644
index 0f37d16..0000000
--- a/core/tests/coretests/apks/install-split-feature-a/Android.mk
+++ /dev/null
@@ -1,14 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := install_split_feature_a
-
-LOCAL_USE_AAPT2 := true
-LOCAL_AAPT_FLAGS += --custom-package com.google.android.dexapis.splitapp.feature_a
-LOCAL_AAPT_FLAGS += --package-id 0x80
-
-include $(FrameworkCoreTests_BUILD_PACKAGE)
\ No newline at end of file
diff --git a/core/tests/coretests/apks/install/Android.bp b/core/tests/coretests/apks/install/Android.bp
new file mode 100644
index 0000000..e783fe2
--- /dev/null
+++ b/core/tests/coretests/apks/install/Android.bp
@@ -0,0 +1,6 @@
+android_test_helper_app {
+ name: "FrameworksCoreTests_install",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+
+ srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/install/Android.mk b/core/tests/coretests/apks/install/Android.mk
deleted file mode 100644
index b38dc20..0000000
--- a/core/tests/coretests/apks/install/Android.mk
+++ /dev/null
@@ -1,8 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := install
-
-include $(FrameworkCoreTests_BUILD_PACKAGE)
diff --git a/core/tests/coretests/apks/install_bad_dex/Android.bp b/core/tests/coretests/apks/install_bad_dex/Android.bp
new file mode 100644
index 0000000..d156793
--- /dev/null
+++ b/core/tests/coretests/apks/install_bad_dex/Android.bp
@@ -0,0 +1,23 @@
+android_test_helper_app {
+ name: "FrameworksCoreTests_install_bad_dex_",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+
+ srcs: ["src/**/*.java"],
+}
+
+// Inject bad classes.dex file.
+java_genrule {
+ name: "FrameworksCoreTests_install_bad_dex",
+ tools: [
+ "soong_zip",
+ "merge_zips",
+ ],
+ srcs: [
+ ":FrameworksCoreTests_install_bad_dex_",
+ "classes.dex",
+ ],
+ out: ["FrameworksCoreTests_install_bad_dex.apk"],
+ cmd: "$(location soong_zip) -o $(genDir)/classes.dex.zip -j -f $(location classes.dex) && " +
+ "$(location merge_zips) -ignore-duplicates $(out) $(genDir)/classes.dex.zip " +
+ "$(location :FrameworksCoreTests_install_bad_dex_)",
+}
diff --git a/core/tests/coretests/apks/install_bad_dex/Android.mk b/core/tests/coretests/apks/install_bad_dex/Android.mk
deleted file mode 100644
index 05983aa..0000000
--- a/core/tests/coretests/apks/install_bad_dex/Android.mk
+++ /dev/null
@@ -1,11 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := install_bad_dex
-
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-
-# Override target specific variable PRIVATE_DEX_FILE to inject bad classes.dex file.
-$(LOCAL_BUILT_MODULE): PRIVATE_DEX_FILE := $(LOCAL_PATH)/classes.dex
diff --git a/core/tests/coretests/apks/install_complete_package_info/Android.bp b/core/tests/coretests/apks/install_complete_package_info/Android.bp
new file mode 100644
index 0000000..123558bd
--- /dev/null
+++ b/core/tests/coretests/apks/install_complete_package_info/Android.bp
@@ -0,0 +1,7 @@
+android_test_helper_app {
+ name: "FrameworksCoreTests_install_complete_package_info",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+
+ srcs: ["**/*.java"],
+}
+
diff --git a/core/tests/coretests/apks/install_complete_package_info/Android.mk b/core/tests/coretests/apks/install_complete_package_info/Android.mk
deleted file mode 100644
index 19bf356..0000000
--- a/core/tests/coretests/apks/install_complete_package_info/Android.mk
+++ /dev/null
@@ -1,13 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := install_complete_package_info
-#LOCAL_MANIFEST_FILE := api_test/AndroidManifest.xml
-
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-#include $(BUILD_PACKAGE)
-
diff --git a/core/tests/coretests/apks/install_decl_perm/Android.bp b/core/tests/coretests/apks/install_decl_perm/Android.bp
new file mode 100644
index 0000000..868e8b5
--- /dev/null
+++ b/core/tests/coretests/apks/install_decl_perm/Android.bp
@@ -0,0 +1,6 @@
+android_test_helper_app {
+ name: "FrameworksCoreTests_install_decl_perm",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+
+ srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/install_decl_perm/Android.mk b/core/tests/coretests/apks/install_decl_perm/Android.mk
deleted file mode 100644
index 86370c8..0000000
--- a/core/tests/coretests/apks/install_decl_perm/Android.mk
+++ /dev/null
@@ -1,8 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := install_decl_perm
-
-include $(FrameworkCoreTests_BUILD_PACKAGE)
diff --git a/core/tests/coretests/apks/install_jni_lib/Android.bp b/core/tests/coretests/apks/install_jni_lib/Android.bp
index c1a6bd0..f20f599 100644
--- a/core/tests/coretests/apks/install_jni_lib/Android.bp
+++ b/core/tests/coretests/apks/install_jni_lib/Android.bp
@@ -14,6 +14,7 @@
cc_test_library {
name: "libframeworks_coretests_jni",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
srcs: ["com_android_frameworks_coretests_JNITest.cpp"],
diff --git a/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.bp b/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.bp
new file mode 100644
index 0000000..602b704
--- /dev/null
+++ b/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.bp
@@ -0,0 +1,6 @@
+android_test_helper_app {
+ name: "FrameworksCoreTests_install_jni_lib_open_from_apk",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+
+ srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.mk b/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.mk
deleted file mode 100644
index 6b3b55e..0000000
--- a/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.mk
+++ /dev/null
@@ -1,8 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := install_jni_lib_open_from_apk
-
-include $(FrameworkCoreTests_BUILD_PACKAGE)
diff --git a/core/tests/coretests/apks/install_loc_auto/Android.bp b/core/tests/coretests/apks/install_loc_auto/Android.bp
new file mode 100644
index 0000000..6393915
--- /dev/null
+++ b/core/tests/coretests/apks/install_loc_auto/Android.bp
@@ -0,0 +1,6 @@
+android_test_helper_app {
+ name: "FrameworksCoreTests_install_loc_auto",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+
+ srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/install_loc_auto/Android.mk b/core/tests/coretests/apks/install_loc_auto/Android.mk
deleted file mode 100644
index 6435f36..0000000
--- a/core/tests/coretests/apks/install_loc_auto/Android.mk
+++ /dev/null
@@ -1,8 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := install_loc_auto
-
-include $(FrameworkCoreTests_BUILD_PACKAGE)
diff --git a/core/tests/coretests/apks/install_loc_internal/Android.bp b/core/tests/coretests/apks/install_loc_internal/Android.bp
new file mode 100644
index 0000000..770aaa5
--- /dev/null
+++ b/core/tests/coretests/apks/install_loc_internal/Android.bp
@@ -0,0 +1,6 @@
+android_test_helper_app {
+ name: "FrameworksCoreTests_install_loc_internal",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+
+ srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/install_loc_internal/Android.mk b/core/tests/coretests/apks/install_loc_internal/Android.mk
deleted file mode 100644
index 8cc8b8e..0000000
--- a/core/tests/coretests/apks/install_loc_internal/Android.mk
+++ /dev/null
@@ -1,8 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := install_loc_internal
-
-include $(FrameworkCoreTests_BUILD_PACKAGE)
diff --git a/core/tests/coretests/apks/install_loc_sdcard/Android.bp b/core/tests/coretests/apks/install_loc_sdcard/Android.bp
new file mode 100644
index 0000000..1779401
--- /dev/null
+++ b/core/tests/coretests/apks/install_loc_sdcard/Android.bp
@@ -0,0 +1,6 @@
+android_test_helper_app {
+ name: "FrameworksCoreTests_install_loc_sdcard",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+
+ srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/install_loc_sdcard/Android.mk b/core/tests/coretests/apks/install_loc_sdcard/Android.mk
deleted file mode 100644
index e1411c2..0000000
--- a/core/tests/coretests/apks/install_loc_sdcard/Android.mk
+++ /dev/null
@@ -1,8 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := install_loc_sdcard
-
-include $(FrameworkCoreTests_BUILD_PACKAGE)
diff --git a/core/tests/coretests/apks/install_loc_unspecified/Android.bp b/core/tests/coretests/apks/install_loc_unspecified/Android.bp
new file mode 100644
index 0000000..21c0f82
--- /dev/null
+++ b/core/tests/coretests/apks/install_loc_unspecified/Android.bp
@@ -0,0 +1,6 @@
+android_test_helper_app {
+ name: "FrameworksCoreTests_install_loc_unspecified",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+
+ srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/install_loc_unspecified/Android.mk b/core/tests/coretests/apks/install_loc_unspecified/Android.mk
deleted file mode 100644
index 0741d04..0000000
--- a/core/tests/coretests/apks/install_loc_unspecified/Android.mk
+++ /dev/null
@@ -1,8 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := install_loc_unspecified
-
-include $(FrameworkCoreTests_BUILD_PACKAGE)
diff --git a/core/tests/coretests/apks/install_multi_package/Android.bp b/core/tests/coretests/apks/install_multi_package/Android.bp
new file mode 100644
index 0000000..249242e
--- /dev/null
+++ b/core/tests/coretests/apks/install_multi_package/Android.bp
@@ -0,0 +1,6 @@
+android_test_helper_app {
+ name: "FrameworksCoreTests_install_multi_package",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+
+ srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/install_multi_package/Android.mk b/core/tests/coretests/apks/install_multi_package/Android.mk
deleted file mode 100644
index 3f163de..0000000
--- a/core/tests/coretests/apks/install_multi_package/Android.mk
+++ /dev/null
@@ -1,14 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := install_multi_package
-
-LOCAL_USE_AAPT2 := true
-
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-#include $(BUILD_PACKAGE)
-
diff --git a/core/tests/coretests/apks/install_use_perm_good/Android.bp b/core/tests/coretests/apks/install_use_perm_good/Android.bp
new file mode 100644
index 0000000..bb41ebb
--- /dev/null
+++ b/core/tests/coretests/apks/install_use_perm_good/Android.bp
@@ -0,0 +1,6 @@
+android_test_helper_app {
+ name: "FrameworksCoreTests_install_use_perm_good",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+
+ srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/install_use_perm_good/Android.mk b/core/tests/coretests/apks/install_use_perm_good/Android.mk
deleted file mode 100644
index e2661a1..0000000
--- a/core/tests/coretests/apks/install_use_perm_good/Android.mk
+++ /dev/null
@@ -1,8 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := install_use_perm_good
-
-include $(FrameworkCoreTests_BUILD_PACKAGE)
diff --git a/core/tests/coretests/apks/install_uses_feature/Android.bp b/core/tests/coretests/apks/install_uses_feature/Android.bp
new file mode 100644
index 0000000..0ec747b
--- /dev/null
+++ b/core/tests/coretests/apks/install_uses_feature/Android.bp
@@ -0,0 +1,6 @@
+android_test_helper_app {
+ name: "FrameworksCoreTests_install_uses_feature",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+
+ srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/install_uses_feature/Android.mk b/core/tests/coretests/apks/install_uses_feature/Android.mk
deleted file mode 100644
index b60d734..0000000
--- a/core/tests/coretests/apks/install_uses_feature/Android.mk
+++ /dev/null
@@ -1,8 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := install_uses_feature
-
-include $(FrameworkCoreTests_BUILD_PACKAGE)
diff --git a/core/tests/coretests/apks/install_verifier_bad/Android.bp b/core/tests/coretests/apks/install_verifier_bad/Android.bp
new file mode 100644
index 0000000..1265739
--- /dev/null
+++ b/core/tests/coretests/apks/install_verifier_bad/Android.bp
@@ -0,0 +1,6 @@
+android_test_helper_app {
+ name: "FrameworksCoreTests_install_verifier_bad",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+
+ srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/install_verifier_bad/Android.mk b/core/tests/coretests/apks/install_verifier_bad/Android.mk
deleted file mode 100644
index 745b4d3..0000000
--- a/core/tests/coretests/apks/install_verifier_bad/Android.mk
+++ /dev/null
@@ -1,10 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := install_verifier_bad
-
-LOCAL_USE_AAPT2 := true
-
-include $(FrameworkCoreTests_BUILD_PACKAGE)
diff --git a/core/tests/coretests/apks/install_verifier_good/Android.bp b/core/tests/coretests/apks/install_verifier_good/Android.bp
new file mode 100644
index 0000000..4911ffb
--- /dev/null
+++ b/core/tests/coretests/apks/install_verifier_good/Android.bp
@@ -0,0 +1,6 @@
+android_test_helper_app {
+ name: "FrameworksCoreTests_install_verifier_good",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+
+ srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/install_verifier_good/Android.mk b/core/tests/coretests/apks/install_verifier_good/Android.mk
deleted file mode 100644
index 150fd8d..0000000
--- a/core/tests/coretests/apks/install_verifier_good/Android.mk
+++ /dev/null
@@ -1,10 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := install_verifier_good
-
-LOCAL_USE_AAPT2 := true
-
-include $(FrameworkCoreTests_BUILD_PACKAGE)
diff --git a/core/tests/coretests/apks/keyset/Android.bp b/core/tests/coretests/apks/keyset/Android.bp
new file mode 100644
index 0000000..e252b08
--- /dev/null
+++ b/core/tests/coretests/apks/keyset/Android.bp
@@ -0,0 +1,120 @@
+//apks signed by keyset_A
+android_test_helper_app {
+ name: "FrameworksCoreTests_keyset_sa_unone",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+ srcs: ["**/*.java"],
+ certificate: ":FrameworksCoreTests_keyset_A_cert",
+ manifest: "uNone/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+ name: "FrameworksCoreTests_keyset_sa_ua",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+ srcs: ["**/*.java"],
+ certificate: ":FrameworksCoreTests_keyset_A_cert",
+ manifest: "uA/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+ name: "FrameworksCoreTests_keyset_sa_ub",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+ srcs: ["**/*.java"],
+ certificate: ":FrameworksCoreTests_keyset_A_cert",
+ manifest: "uB/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+ name: "FrameworksCoreTests_keyset_sa_uab",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+ srcs: ["**/*.java"],
+ certificate: ":FrameworksCoreTests_keyset_A_cert",
+ manifest: "uAB/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+ name: "FrameworksCoreTests_keyset_sa_ua_ub",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+ srcs: ["**/*.java"],
+ certificate: ":FrameworksCoreTests_keyset_A_cert",
+ manifest: "uAuB/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+ name: "FrameworksCoreTests_keyset_permdef_sa_unone",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+ srcs: ["**/*.java"],
+ certificate: ":FrameworksCoreTests_keyset_A_cert",
+ manifest: "permDef/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+ name: "FrameworksCoreTests_keyset_permuse_sa_ua_ub",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+ srcs: ["**/*.java"],
+ certificate: ":FrameworksCoreTests_keyset_A_cert",
+ manifest: "permUse/AndroidManifest.xml",
+}
+
+//apks signed by keyset_B
+android_test_helper_app {
+ name: "FrameworksCoreTests_keyset_sb_ua",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+ srcs: ["**/*.java"],
+ certificate: ":FrameworksCoreTests_keyset_B_cert",
+ manifest: "uA/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+ name: "FrameworksCoreTests_keyset_sb_ub",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+ srcs: ["**/*.java"],
+ certificate: ":FrameworksCoreTests_keyset_B_cert",
+ manifest: "uB/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+ name: "FrameworksCoreTests_keyset_permuse_sb_ua_ub",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+ srcs: ["**/*.java"],
+ certificate: ":FrameworksCoreTests_keyset_B_cert",
+ manifest: "permUse/AndroidManifest.xml",
+}
+
+//apks signed by keyset_A and keyset_B
+android_test_helper_app {
+ name: "FrameworksCoreTests_keyset_sab_ua",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+ srcs: ["**/*.java"],
+ certificate: ":FrameworksCoreTests_keyset_A_cert",
+ additional_certificates: [":FrameworksCoreTests_keyset_B_cert"],
+ manifest: "uA/AndroidManifest.xml",
+}
+
+//apks signed by keyset_A and unit_test
+android_test_helper_app {
+ name: "FrameworksCoreTests_keyset_sau_ub",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+ srcs: ["**/*.java"],
+ certificate: ":FrameworksCoreTests_keyset_A_cert",
+ additional_certificates: [":FrameworksCoreTests_keyset_B_cert"],
+ manifest: "uB/AndroidManifest.xml",
+}
+
+//apks signed by platform only
+android_test_helper_app {
+ name: "FrameworksCoreTests_keyset_splat_api",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+ srcs: ["**/*.java"],
+ certificate: "platform",
+ manifest: "api_test/AndroidManifest.xml",
+}
+
+//apks signed by platform and keyset_A
+android_test_helper_app {
+ name: "FrameworksCoreTests_keyset_splata_api",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+ srcs: ["**/*.java"],
+ certificate: "platform",
+ additional_certificates: [":FrameworksCoreTests_keyset_A_cert"],
+ manifest: "api_test/AndroidManifest.xml",
+}
diff --git a/core/tests/coretests/apks/keyset/Android.mk b/core/tests/coretests/apks/keyset/Android.mk
deleted file mode 100644
index 306dc90..0000000
--- a/core/tests/coretests/apks/keyset/Android.mk
+++ /dev/null
@@ -1,108 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-#apks signed by keyset_A
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PACKAGE_NAME := keyset_sa_unone
-LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/keyset_A
-LOCAL_MANIFEST_FILE := uNone/AndroidManifest.xml
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PACKAGE_NAME := keyset_sa_ua
-LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/keyset_A
-LOCAL_MANIFEST_FILE := uA/AndroidManifest.xml
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PACKAGE_NAME := keyset_sa_ub
-LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/keyset_A
-LOCAL_MANIFEST_FILE := uB/AndroidManifest.xml
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PACKAGE_NAME := keyset_sa_uab
-LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/keyset_A
-LOCAL_MANIFEST_FILE := uAB/AndroidManifest.xml
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PACKAGE_NAME := keyset_sa_ua_ub
-LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/keyset_A
-LOCAL_MANIFEST_FILE := uAuB/AndroidManifest.xml
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PACKAGE_NAME := keyset_permdef_sa_unone
-LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/keyset_A
-LOCAL_MANIFEST_FILE := permDef/AndroidManifest.xml
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PACKAGE_NAME := keyset_permuse_sa_ua_ub
-LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/keyset_A
-LOCAL_MANIFEST_FILE := permUse/AndroidManifest.xml
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-
-#apks signed by keyset_B
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PACKAGE_NAME := keyset_sb_ua
-LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/keyset_B
-LOCAL_MANIFEST_FILE := uA/AndroidManifest.xml
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PACKAGE_NAME := keyset_sb_ub
-LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/keyset_B
-LOCAL_MANIFEST_FILE := uB/AndroidManifest.xml
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PACKAGE_NAME := keyset_permuse_sb_ua_ub
-LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/keyset_B
-LOCAL_MANIFEST_FILE := permUse/AndroidManifest.xml
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-
-#apks signed by keyset_A and keyset_B
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PACKAGE_NAME := keyset_sab_ua
-LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/keyset_A
-LOCAL_ADDITIONAL_CERTIFICATES := $(LOCAL_PATH)/../../certs/keyset_B
-LOCAL_MANIFEST_FILE := uA/AndroidManifest.xml
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-
-#apks signed by keyset_A and unit_test
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PACKAGE_NAME := keyset_sau_ub
-LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/keyset_A
-LOCAL_ADDITIONAL_CERTIFICATES := $(LOCAL_PATH)/../../certs/keyset_B
-LOCAL_MANIFEST_FILE := uB/AndroidManifest.xml
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-
-#apks signed by platform only
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PACKAGE_NAME := keyset_splat_api
-LOCAL_CERTIFICATE := platform
-LOCAL_MANIFEST_FILE := api_test/AndroidManifest.xml
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-
-#apks signed by platform and keyset_A
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PACKAGE_NAME := keyset_splata_api
-LOCAL_CERTIFICATE := platform
-LOCAL_ADDITIONAL_CERTIFICATES := $(LOCAL_PATH)/../../certs/keyset_A
-LOCAL_MANIFEST_FILE := api_test/AndroidManifest.xml
-include $(FrameworkCoreTests_BUILD_PACKAGE)
\ No newline at end of file
diff --git a/core/tests/coretests/apks/locales/Android.bp b/core/tests/coretests/apks/locales/Android.bp
new file mode 100644
index 0000000..4a730ef
--- /dev/null
+++ b/core/tests/coretests/apks/locales/Android.bp
@@ -0,0 +1,6 @@
+android_test_helper_app {
+ name: "FrameworksCoreTests_locales",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+
+ srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/locales/Android.mk b/core/tests/coretests/apks/locales/Android.mk
deleted file mode 100644
index 9cb13dd..0000000
--- a/core/tests/coretests/apks/locales/Android.mk
+++ /dev/null
@@ -1,8 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := locales
-
-include $(FrameworkCoreTests_BUILD_PACKAGE)
diff --git a/core/tests/coretests/apks/version/Android.bp b/core/tests/coretests/apks/version/Android.bp
new file mode 100644
index 0000000..371ccfc
--- /dev/null
+++ b/core/tests/coretests/apks/version/Android.bp
@@ -0,0 +1,54 @@
+android_test_helper_app {
+ name: "FrameworksCoreTests_version_1",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+ srcs: ["**/*.java"],
+ aaptflags: [
+ "--version-code 1",
+ "--version-name 1.0",
+ ],
+ certificate: ":FrameworksCoreTests_unit_test_cert",
+}
+
+android_test_helper_app {
+ name: "FrameworksCoreTests_version_2",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+ srcs: ["**/*.java"],
+ aaptflags: [
+ "--version-code 2",
+ "--version-name 2.0",
+ ],
+ certificate: ":FrameworksCoreTests_unit_test_cert",
+}
+
+android_test_helper_app {
+ name: "FrameworksCoreTests_version_3",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+ srcs: ["**/*.java"],
+ aaptflags: [
+ "--version-code 3",
+ "--version-name 3.0",
+ ],
+ certificate: ":FrameworksCoreTests_unit_test_cert",
+}
+
+android_test_helper_app {
+ name: "FrameworksCoreTests_version_1_diff",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+ srcs: ["**/*.java"],
+ aaptflags: [
+ "--version-code 1",
+ "--version-name 1.0",
+ ],
+ certificate: ":FrameworksCoreTests_unit_test_cert",
+}
+
+android_test_helper_app {
+ name: "FrameworksCoreTests_version_2_diff",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+ srcs: ["**/*.java"],
+ aaptflags: [
+ "--version-code 2",
+ "--version-name 2.0",
+ ],
+ certificate: ":FrameworksCoreTests_unit_test_cert",
+}
diff --git a/core/tests/coretests/apks/version/Android.mk b/core/tests/coretests/apks/version/Android.mk
deleted file mode 100644
index 3635a58..0000000
--- a/core/tests/coretests/apks/version/Android.mk
+++ /dev/null
@@ -1,36 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PACKAGE_NAME := version_1
-LOCAL_AAPT_FLAGS := --version-code 1 --version-name 1.0
-LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/unit_test
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PACKAGE_NAME := version_2
-LOCAL_AAPT_FLAGS := --version-code 2 --version-name 2.0
-LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/unit_test
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PACKAGE_NAME := version_3
-LOCAL_AAPT_FLAGS := --version-code 3 --version-name 3.0
-LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/unit_test
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PACKAGE_NAME := version_1_diff
-LOCAL_AAPT_FLAGS := --version-code 1 --version-name 1.0
-LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/unit_test_diff
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PACKAGE_NAME := version_2_diff
-LOCAL_AAPT_FLAGS := --version-code 2 --version-name 2.0
-LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/unit_test_diff
-include $(FrameworkCoreTests_BUILD_PACKAGE)
diff --git a/core/tests/coretests/apks/version_nosys/Android.bp b/core/tests/coretests/apks/version_nosys/Android.bp
new file mode 100644
index 0000000..5756678
--- /dev/null
+++ b/core/tests/coretests/apks/version_nosys/Android.bp
@@ -0,0 +1,10 @@
+android_test_helper_app {
+ name: "FrameworksCoreTests_version_1_nosys",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+ srcs: ["**/*.java"],
+ aaptflags: [
+ "--version-code 1",
+ "--version-name 1.0",
+ ],
+ certificate: ":FrameworksCoreTests_unit_test_cert",
+}
diff --git a/core/tests/coretests/apks/version_nosys/Android.mk b/core/tests/coretests/apks/version_nosys/Android.mk
deleted file mode 100644
index bbc8e12..0000000
--- a/core/tests/coretests/apks/version_nosys/Android.mk
+++ /dev/null
@@ -1,9 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PACKAGE_NAME := version_1_nosys
-LOCAL_AAPT_FLAGS := --version-code 1 --version-name 1.0
-LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/unit_test
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-
diff --git a/core/tests/coretests/certs/Android.bp b/core/tests/coretests/certs/Android.bp
new file mode 100644
index 0000000..bd5c829
--- /dev/null
+++ b/core/tests/coretests/certs/Android.bp
@@ -0,0 +1,14 @@
+android_app_certificate {
+ name: "FrameworksCoreTests_keyset_A_cert",
+ certificate: "keyset_A",
+}
+
+android_app_certificate {
+ name: "FrameworksCoreTests_keyset_B_cert",
+ certificate: "keyset_B",
+}
+
+android_app_certificate {
+ name: "FrameworksCoreTests_unit_test_cert",
+ certificate: "unit_test",
+}
diff --git a/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java b/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java
index 5731daa..4ae9494 100644
--- a/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java
+++ b/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java
@@ -99,7 +99,8 @@
@Test
public void testComputeForPassword_metrics() {
- final PasswordMetrics metrics = PasswordMetrics.computeForPassword("6B~0z1Z3*8A");
+ final PasswordMetrics metrics =
+ PasswordMetrics.computeForPassword("6B~0z1Z3*8A".getBytes());
assertEquals(11, metrics.length);
assertEquals(4, metrics.letters);
assertEquals(3, metrics.upperCase);
@@ -112,32 +113,32 @@
@Test
public void testComputeForPassword_quality() {
assertEquals(DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC,
- PasswordMetrics.computeForPassword("a1").quality);
+ PasswordMetrics.computeForPassword("a1".getBytes()).quality);
assertEquals(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC,
- PasswordMetrics.computeForPassword("a").quality);
+ PasswordMetrics.computeForPassword("a".getBytes()).quality);
assertEquals(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC,
- PasswordMetrics.computeForPassword("*~&%$").quality);
+ PasswordMetrics.computeForPassword("*~&%$".getBytes()).quality);
assertEquals(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX,
- PasswordMetrics.computeForPassword("1").quality);
+ PasswordMetrics.computeForPassword("1".getBytes()).quality);
// contains a long sequence so isn't complex
assertEquals(PASSWORD_QUALITY_NUMERIC,
- PasswordMetrics.computeForPassword("1234").quality);
+ PasswordMetrics.computeForPassword("1234".getBytes()).quality);
assertEquals(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
- PasswordMetrics.computeForPassword("").quality);
+ PasswordMetrics.computeForPassword("".getBytes()).quality);
}
@Test
public void testMaxLengthSequence() {
- assertEquals(4, PasswordMetrics.maxLengthSequence("1234"));
- assertEquals(5, PasswordMetrics.maxLengthSequence("13579"));
- assertEquals(4, PasswordMetrics.maxLengthSequence("1234abd"));
- assertEquals(3, PasswordMetrics.maxLengthSequence("aabc"));
- assertEquals(1, PasswordMetrics.maxLengthSequence("qwertyuio"));
- assertEquals(3, PasswordMetrics.maxLengthSequence("@ABC"));
+ assertEquals(4, PasswordMetrics.maxLengthSequence("1234".getBytes()));
+ assertEquals(5, PasswordMetrics.maxLengthSequence("13579".getBytes()));
+ assertEquals(4, PasswordMetrics.maxLengthSequence("1234abd".getBytes()));
+ assertEquals(3, PasswordMetrics.maxLengthSequence("aabc".getBytes()));
+ assertEquals(1, PasswordMetrics.maxLengthSequence("qwertyuio".getBytes()));
+ assertEquals(3, PasswordMetrics.maxLengthSequence("@ABC".getBytes()));
// anything that repeats
- assertEquals(4, PasswordMetrics.maxLengthSequence(";;;;"));
+ assertEquals(4, PasswordMetrics.maxLengthSequence(";;;;".getBytes()));
// ordered, but not composed of alphas or digits
- assertEquals(1, PasswordMetrics.maxLengthSequence(":;<=>"));
+ assertEquals(1, PasswordMetrics.maxLengthSequence(":;<=>".getBytes()));
}
@Test
@@ -158,8 +159,8 @@
assertNotEquals(new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, 4),
new PasswordMetrics(PASSWORD_QUALITY_COMPLEX, 4));
- metrics0 = PasswordMetrics.computeForPassword("1234abcd,./");
- metrics1 = PasswordMetrics.computeForPassword("1234abcd,./");
+ metrics0 = PasswordMetrics.computeForPassword("1234abcd,./".getBytes());
+ metrics1 = PasswordMetrics.computeForPassword("1234abcd,./".getBytes());
assertEquals(metrics0, metrics1);
metrics1.letters++;
assertNotEquals(metrics0, metrics1);
@@ -197,7 +198,7 @@
@Test
public void testDetermineComplexity_none() {
assertEquals(PASSWORD_COMPLEXITY_NONE,
- PasswordMetrics.computeForPassword("").determineComplexity());
+ PasswordMetrics.computeForPassword("".getBytes()).determineComplexity());
}
@Test
@@ -209,61 +210,61 @@
@Test
public void testDetermineComplexity_lowNumeric() {
assertEquals(PASSWORD_COMPLEXITY_LOW,
- PasswordMetrics.computeForPassword("1234").determineComplexity());
+ PasswordMetrics.computeForPassword("1234".getBytes()).determineComplexity());
}
@Test
public void testDetermineComplexity_lowNumericComplex() {
assertEquals(PASSWORD_COMPLEXITY_LOW,
- PasswordMetrics.computeForPassword("124").determineComplexity());
+ PasswordMetrics.computeForPassword("124".getBytes()).determineComplexity());
}
@Test
public void testDetermineComplexity_lowAlphabetic() {
assertEquals(PASSWORD_COMPLEXITY_LOW,
- PasswordMetrics.computeForPassword("a!").determineComplexity());
+ PasswordMetrics.computeForPassword("a!".getBytes()).determineComplexity());
}
@Test
public void testDetermineComplexity_lowAlphanumeric() {
assertEquals(PASSWORD_COMPLEXITY_LOW,
- PasswordMetrics.computeForPassword("a!1").determineComplexity());
+ PasswordMetrics.computeForPassword("a!1".getBytes()).determineComplexity());
}
@Test
public void testDetermineComplexity_mediumNumericComplex() {
assertEquals(PASSWORD_COMPLEXITY_MEDIUM,
- PasswordMetrics.computeForPassword("1238").determineComplexity());
+ PasswordMetrics.computeForPassword("1238".getBytes()).determineComplexity());
}
@Test
public void testDetermineComplexity_mediumAlphabetic() {
assertEquals(PASSWORD_COMPLEXITY_MEDIUM,
- PasswordMetrics.computeForPassword("ab!c").determineComplexity());
+ PasswordMetrics.computeForPassword("ab!c".getBytes()).determineComplexity());
}
@Test
public void testDetermineComplexity_mediumAlphanumeric() {
assertEquals(PASSWORD_COMPLEXITY_MEDIUM,
- PasswordMetrics.computeForPassword("ab!1").determineComplexity());
+ PasswordMetrics.computeForPassword("ab!1".getBytes()).determineComplexity());
}
@Test
public void testDetermineComplexity_highNumericComplex() {
assertEquals(PASSWORD_COMPLEXITY_HIGH,
- PasswordMetrics.computeForPassword("12389647!").determineComplexity());
+ PasswordMetrics.computeForPassword("12389647!".getBytes()).determineComplexity());
}
@Test
public void testDetermineComplexity_highAlphabetic() {
assertEquals(PASSWORD_COMPLEXITY_HIGH,
- PasswordMetrics.computeForPassword("alphabetic!").determineComplexity());
+ PasswordMetrics.computeForPassword("alphabetic!".getBytes()).determineComplexity());
}
@Test
public void testDetermineComplexity_highAlphanumeric() {
- assertEquals(PASSWORD_COMPLEXITY_HIGH,
- PasswordMetrics.computeForPassword("alphanumeric123!").determineComplexity());
+ assertEquals(PASSWORD_COMPLEXITY_HIGH, PasswordMetrics.computeForPassword(
+ "alphanumeric123!".getBytes()).determineComplexity());
}
@Test
diff --git a/core/tests/coretests/src/android/app/timezone/RulesStateTest.java b/core/tests/coretests/src/android/app/timezone/RulesStateTest.java
index bb535b6..30cc7ff 100644
--- a/core/tests/coretests/src/android/app/timezone/RulesStateTest.java
+++ b/core/tests/coretests/src/android/app/timezone/RulesStateTest.java
@@ -46,11 +46,11 @@
RulesState.DISTRO_STATUS_INSTALLED, rulesVersion("2016b", 2));
assertEqualsContract(one, two);
- RulesState differentSystemRules = new RulesState(
+ RulesState differentBaseRules = new RulesState(
"2016b", formatVersion(1, 2), false /* operationInProgress */,
RulesState.STAGED_OPERATION_INSTALL, rulesVersion("2016a", 3),
RulesState.DISTRO_STATUS_INSTALLED, rulesVersion("2016b", 2));
- assertFalse(one.equals(differentSystemRules));
+ assertFalse(one.equals(differentBaseRules));
RulesState differentFormatVersion = new RulesState(
"2016a", formatVersion(1, 1), false /* operationInProgress */,
@@ -121,14 +121,14 @@
}
@Test
- public void isSystemVersionNewerThan() {
+ public void isBaseVersionNewerThan() {
RulesState rulesState = new RulesState(
"2016b", formatVersion(1, 1), false /* operationInProgress */,
RulesState.STAGED_OPERATION_NONE, null /* stagedDistroRulesVersion */,
RulesState.DISTRO_STATUS_INSTALLED, rulesVersion("2016b", 3));
- assertTrue(rulesState.isSystemVersionNewerThan(rulesVersion("2016a", 1)));
- assertFalse(rulesState.isSystemVersionNewerThan(rulesVersion("2016b", 1)));
- assertFalse(rulesState.isSystemVersionNewerThan(rulesVersion("2016c", 1)));
+ assertTrue(rulesState.isBaseVersionNewerThan(rulesVersion("2016a", 1)));
+ assertFalse(rulesState.isBaseVersionNewerThan(rulesVersion("2016b", 1)));
+ assertFalse(rulesState.isBaseVersionNewerThan(rulesVersion("2016c", 1)));
}
private static void assertEqualsContract(RulesState one, RulesState two) {
diff --git a/core/tests/coretests/src/android/content/pm/AndroidTestBaseUpdaterTest.java b/core/tests/coretests/src/android/content/pm/AndroidTestBaseUpdaterTest.java
index 0ed76dc..125b9ff 100644
--- a/core/tests/coretests/src/android/content/pm/AndroidTestBaseUpdaterTest.java
+++ b/core/tests/coretests/src/android/content/pm/AndroidTestBaseUpdaterTest.java
@@ -37,37 +37,37 @@
private static final String OTHER_LIBRARY = "other.library";
@Test
- public void targeted_at_O() {
+ public void targeted_at_P() {
PackageBuilder before = builder()
- .targetSdkVersion(Build.VERSION_CODES.O);
+ .targetSdkVersion(Build.VERSION_CODES.P);
// Should add org.apache.http.legacy.
PackageBuilder after = builder()
- .targetSdkVersion(Build.VERSION_CODES.O)
+ .targetSdkVersion(Build.VERSION_CODES.P)
.requiredLibraries(ANDROID_TEST_BASE);
checkBackwardsCompatibility(before, after);
}
@Test
- public void targeted_at_O_not_empty_usesLibraries() {
+ public void targeted_at_P_not_empty_usesLibraries() {
PackageBuilder before = builder()
- .targetSdkVersion(Build.VERSION_CODES.O)
+ .targetSdkVersion(Build.VERSION_CODES.P)
.requiredLibraries(OTHER_LIBRARY);
// The org.apache.http.legacy jar should be added at the start of the list because it
// is not on the bootclasspath and the package targets pre-P.
PackageBuilder after = builder()
- .targetSdkVersion(Build.VERSION_CODES.O)
+ .targetSdkVersion(Build.VERSION_CODES.P)
.requiredLibraries(ANDROID_TEST_BASE, OTHER_LIBRARY);
checkBackwardsCompatibility(before, after);
}
@Test
- public void targeted_at_O_in_usesLibraries() {
+ public void targeted_at_P_in_usesLibraries() {
PackageBuilder before = builder()
- .targetSdkVersion(Build.VERSION_CODES.O)
+ .targetSdkVersion(Build.VERSION_CODES.P)
.requiredLibraries(ANDROID_TEST_BASE);
// No change is required because although org.apache.http.legacy has been removed from
@@ -76,9 +76,9 @@
}
@Test
- public void targeted_at_O_in_usesOptionalLibraries() {
+ public void targeted_at_P_in_usesOptionalLibraries() {
PackageBuilder before = builder()
- .targetSdkVersion(Build.VERSION_CODES.O)
+ .targetSdkVersion(Build.VERSION_CODES.P)
.optionalLibraries(ANDROID_TEST_BASE);
// No change is required because although org.apache.http.legacy has been removed from
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteCompatibilityWalFlagsTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteCompatibilityWalFlagsTest.java
index 5dbcb3c..82bd588 100644
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteCompatibilityWalFlagsTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteCompatibilityWalFlagsTest.java
@@ -19,6 +19,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import android.content.Context;
import android.database.DatabaseUtils;
@@ -54,44 +55,52 @@
@Test
public void testParseConfig() {
SQLiteCompatibilityWalFlags.init("");
- assertFalse(SQLiteCompatibilityWalFlags.areFlagsSet());
-
SQLiteCompatibilityWalFlags.init(null);
- assertFalse(SQLiteCompatibilityWalFlags.areFlagsSet());
- SQLiteCompatibilityWalFlags.init("compatibility_wal_supported=false,wal_syncmode=OFF");
- assertTrue(SQLiteCompatibilityWalFlags.areFlagsSet());
- assertFalse(SQLiteCompatibilityWalFlags.isCompatibilityWalSupported());
- assertEquals("OFF", SQLiteCompatibilityWalFlags.getWALSyncMode());
+ // Ensure that legacy compatibility wal isn't turned on by the old flag.
+ SQLiteCompatibilityWalFlags.init("compatibility_wal_supported=true,wal_syncmode=OFF");
+ assertFalse(SQLiteCompatibilityWalFlags.isLegacyCompatibilityWalEnabled());
+ try {
+ SQLiteCompatibilityWalFlags.getWALSyncMode();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
assertEquals(-1, SQLiteCompatibilityWalFlags.getTruncateSize());
+
SQLiteCompatibilityWalFlags.init("wal_syncmode=VALUE");
- assertTrue(SQLiteCompatibilityWalFlags.areFlagsSet());
- assertEquals(SQLiteGlobal.isCompatibilityWalSupported(),
- SQLiteCompatibilityWalFlags.isCompatibilityWalSupported());
- assertEquals("VALUE", SQLiteCompatibilityWalFlags.getWALSyncMode());
+ assertFalse(SQLiteCompatibilityWalFlags.isLegacyCompatibilityWalEnabled());
assertEquals(-1, SQLiteCompatibilityWalFlags.getTruncateSize());
+ try {
+ SQLiteCompatibilityWalFlags.getWALSyncMode();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
- SQLiteCompatibilityWalFlags.init("compatibility_wal_supported=true");
- assertTrue(SQLiteCompatibilityWalFlags.areFlagsSet());
+ SQLiteCompatibilityWalFlags.init("legacy_compatibility_wal_enabled=true");
+ assertTrue(SQLiteCompatibilityWalFlags.isLegacyCompatibilityWalEnabled());
assertEquals(SQLiteGlobal.getWALSyncMode(),
SQLiteCompatibilityWalFlags.getWALSyncMode());
- assertTrue(SQLiteCompatibilityWalFlags.isCompatibilityWalSupported());
- assertEquals(-1, SQLiteCompatibilityWalFlags.getTruncateSize());
+
+ SQLiteCompatibilityWalFlags.init(
+ "legacy_compatibility_wal_enabled=true,wal_syncmode=VALUE");
+ assertTrue(SQLiteCompatibilityWalFlags.isLegacyCompatibilityWalEnabled());
+ assertEquals("VALUE", SQLiteCompatibilityWalFlags.getWALSyncMode());
SQLiteCompatibilityWalFlags.init("truncate_size=1024");
assertEquals(1024, SQLiteCompatibilityWalFlags.getTruncateSize());
SQLiteCompatibilityWalFlags.reset();
SQLiteCompatibilityWalFlags.init("Invalid value");
- assertFalse(SQLiteCompatibilityWalFlags.areFlagsSet());
+ assertFalse(SQLiteCompatibilityWalFlags.isLegacyCompatibilityWalEnabled());
}
@Test
public void testApplyFlags() {
Context ctx = InstrumentationRegistry.getContext();
- SQLiteCompatibilityWalFlags.init("compatibility_wal_supported=true,wal_syncmode=NORMAL");
+ SQLiteCompatibilityWalFlags.init(
+ "legacy_compatibility_wal_enabled=true,wal_syncmode=NORMAL");
mDatabase = SQLiteDatabase
.openOrCreateDatabase(ctx.getDatabasePath("SQLiteCompatibilityWalFlagsTest"), null);
String journalMode = DatabaseUtils.stringForQuery(mDatabase, "PRAGMA journal_mode", null);
@@ -100,5 +109,22 @@
assertEquals("Normal mode (1) is expected", "1", syncMode);
}
+ @Test
+ public void testApplyFlags_thenDisableWriteAheadLogging() {
+ Context ctx = InstrumentationRegistry.getContext();
+ SQLiteCompatibilityWalFlags.init(
+ "legacy_compatibility_wal_enabled=true,wal_syncmode=FULL");
+ mDatabase = SQLiteDatabase
+ .openOrCreateDatabase(ctx.getDatabasePath("SQLiteCompatibilityWalFlagsTest"), null);
+
+ mDatabase.disableWriteAheadLogging();
+ String journalMode = DatabaseUtils.stringForQuery(mDatabase, "PRAGMA journal_mode", null);
+ assertEquals(SQLiteGlobal.getDefaultJournalMode(), journalMode.toUpperCase());
+ String syncMode = DatabaseUtils.stringForQuery(mDatabase, "PRAGMA synchronous", null);
+ // TODO: This is the old behaviour and seems incorrect. The specified wal_syncmode was only
+ // intended to be used if the database is in WAL mode, and we should revert to the global
+ // default sync mode if WAL is disabled.
+ assertEquals("Normal mode (2) is expected", "2", syncMode);
+ }
}
diff --git a/core/tests/coretests/src/android/provider/DeviceConfigTest.java b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
index 17e9654..5d5754b 100644
--- a/core/tests/coretests/src/android/provider/DeviceConfigTest.java
+++ b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
@@ -18,15 +18,11 @@
import static android.provider.DeviceConfig.OnPropertyChangedListener;
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.assertNull;
-import static junit.framework.Assert.fail;
+import static com.google.common.truth.Truth.assertThat;
import android.app.ActivityThread;
import android.content.ContentResolver;
import android.os.Bundle;
-import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
@@ -37,8 +33,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.util.concurrent.Executor;
-import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
/** Tests that ensure appropriate settings are backed up. */
@Presubmit
@@ -51,8 +47,6 @@
private static final String sValue = "value1";
private static final long WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS = 2000; // 2 sec
- private final Object mLock = new Object();
-
@After
public void cleanUp() {
deleteViaContentProvider(sNamespace, sKey);
@@ -61,14 +55,14 @@
@Test
public void getProperty_empty() {
String result = DeviceConfig.getProperty(sNamespace, sKey);
- assertNull(result);
+ assertThat(result).isNull();
}
@Test
public void setAndGetProperty_sameNamespace() {
DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
String result = DeviceConfig.getProperty(sNamespace, sKey);
- assertEquals(sValue, result);
+ assertThat(result).isEqualTo(sValue);
}
@Test
@@ -76,7 +70,7 @@
String newNamespace = "namespace2";
DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
String result = DeviceConfig.getProperty(newNamespace, sKey);
- assertNull(result);
+ assertThat(result).isNull();
}
@Test
@@ -86,9 +80,9 @@
DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
DeviceConfig.setProperty(newNamespace, sKey, newValue, false);
String result = DeviceConfig.getProperty(sNamespace, sKey);
- assertEquals(sValue, result);
+ assertThat(result).isEqualTo(sValue);
result = DeviceConfig.getProperty(newNamespace, sKey);
- assertEquals(newValue, result);
+ assertThat(result).isEqualTo(newValue);
// clean up
deleteViaContentProvider(newNamespace, sKey);
@@ -100,59 +94,30 @@
DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
DeviceConfig.setProperty(sNamespace, sKey, newValue, false);
String result = DeviceConfig.getProperty(sNamespace, sKey);
- assertEquals(newValue, result);
+ assertThat(result).isEqualTo(newValue);
}
@Test
- public void testListener() {
- setPropertyAndAssertSuccessfulChange(sNamespace, sKey, sValue);
- }
+ public void testListener() throws InterruptedException {
+ CountDownLatch countDownLatch = new CountDownLatch(1);
- private void setPropertyAndAssertSuccessfulChange(String setNamespace, String setName,
- String setValue) {
- final AtomicBoolean success = new AtomicBoolean();
+ OnPropertyChangedListener changeListener = (namespace, name, value) -> {
+ assertThat(namespace).isEqualTo(sNamespace);
+ assertThat(name).isEqualTo(sKey);
+ assertThat(value).isEqualTo(sValue);
+ countDownLatch.countDown();
+ };
- OnPropertyChangedListener changeListener = new OnPropertyChangedListener() {
- @Override
- public void onPropertyChanged(String namespace, String name, String value) {
- assertEquals(setNamespace, namespace);
- assertEquals(setName, name);
- assertEquals(setValue, value);
- success.set(true);
-
- synchronized (mLock) {
- mLock.notifyAll();
- }
- }
- };
- Executor executor = ActivityThread.currentApplication().getMainExecutor();
- DeviceConfig.addOnPropertyChangedListener(setNamespace, executor, changeListener);
try {
- DeviceConfig.setProperty(setNamespace, setName, setValue, false);
-
- final long startTimeMillis = SystemClock.uptimeMillis();
- synchronized (mLock) {
- while (true) {
- if (success.get()) {
- return;
- }
- final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
- if (elapsedTimeMillis >= WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS) {
- fail("Could not change setting for "
- + WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS + " ms");
- }
- final long remainingTimeMillis = WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS
- - elapsedTimeMillis;
- try {
- mLock.wait(remainingTimeMillis);
- } catch (InterruptedException ie) {
- /* ignore */
- }
- }
- }
+ DeviceConfig.addOnPropertyChangedListener(sNamespace,
+ ActivityThread.currentApplication().getMainExecutor(), changeListener);
+ DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
+ assertThat(countDownLatch.await(
+ WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue();
} finally {
DeviceConfig.removeOnPropertyChangedListener(changeListener);
}
+
}
private static boolean deleteViaContentProvider(String namespace, String key) {
@@ -160,7 +125,7 @@
String compositeName = namespace + "/" + key;
Bundle result = resolver.call(
DeviceConfig.CONTENT_URI, Settings.CALL_METHOD_DELETE_CONFIG, compositeName, null);
- assertNotNull(result);
+ assertThat(result).isNotNull();
return compositeName.equals(result.getString(Settings.NameValueTable.VALUE));
}
diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
index 71ce02d..23ab05e 100644
--- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
+++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
@@ -129,6 +129,29 @@
assertPosition(navParams.matrix, new Rect(400, 0, 500, 500), new Rect(460, 0, 560, 500));
}
+ @Test
+ public void testFinishing() {
+ when(mMockController.getState()).thenReturn(mInsetsState);
+ mController.finish(sideBars());
+ mController.applyChangeInsets(mInsetsState);
+ assertFalse(mInsetsState.getSource(TYPE_TOP_BAR).isVisible());
+ assertTrue(mInsetsState.getSource(TYPE_NAVIGATION_BAR).isVisible());
+ assertEquals(Insets.of(0, 0, 100, 0), mController.getCurrentInsets());
+ verify(mMockController).notifyFinished(eq(mController), eq(sideBars()));
+ }
+
+ @Test
+ public void testCancelled() {
+ mController.onCancelled();
+ try {
+ mController.changeInsets(Insets.NONE);
+ fail("Expected exception to be thrown");
+ } catch (IllegalStateException ignored) {
+ }
+ verify(mMockListener).onCancelled();
+ mController.finish(sideBars());
+ }
+
private void assertPosition(Matrix m, Rect original, Rect transformed) {
RectF rect = new RectF(original);
rect.offsetTo(0, 0);
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 731d564..d71bde83 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -20,6 +20,7 @@
import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
import static android.view.InsetsState.TYPE_TOP_BAR;
+import static android.view.WindowInsets.Type.topBar;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
@@ -48,6 +49,9 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+import java.util.concurrent.CountDownLatch;
@Presubmit
@FlakyTest(detail = "Promote once confirmed non-flaky")
@@ -80,6 +84,8 @@
new DisplayCutout(
Insets.of(10, 10, 10, 10), rect, rect, rect, rect),
rect, rect, SOFT_INPUT_ADJUST_RESIZE);
+ mController.onFrameChanged(new Rect(0, 0, 100, 100));
+ mController.getState().setDisplayFrame(new Rect(0, 0, 100, 100));
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
@@ -101,6 +107,19 @@
}
@Test
+ public void testControlsRevoked_duringAnim() {
+ InsetsSourceControl control = new InsetsSourceControl(TYPE_TOP_BAR, mLeash, new Point());
+ mController.onControlsChanged(new InsetsSourceControl[] { control });
+
+ WindowInsetsAnimationControlListener mockListener =
+ mock(WindowInsetsAnimationControlListener.class);
+ mController.controlWindowInsetsAnimation(topBar(), mockListener);
+ verify(mockListener).onReady(any(), anyInt());
+ mController.onControlsChanged(new InsetsSourceControl[0]);
+ verify(mockListener).onCancelled();
+ }
+
+ @Test
public void testFrameDoesntMatchDisplay() {
mController.onFrameChanged(new Rect(0, 0, 100, 100));
mController.getState().setDisplayFrame(new Rect(0, 0, 200, 200));
@@ -119,24 +138,21 @@
InsetsSourceControl ime = controls[2];
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ // since there is no focused view, forcefully make IME visible.
+ mController.applyImeVisibility(true /* setVisible */);
mController.show(Type.all());
// quickly jump to final state by cancelling it.
mController.cancelExistingAnimation();
assertTrue(mController.getSourceConsumer(navBar.getType()).isVisible());
assertTrue(mController.getSourceConsumer(topBar.getType()).isVisible());
- // no focused view, no IME.
- assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
+ assertTrue(mController.getSourceConsumer(ime.getType()).isVisible());
+ mController.applyImeVisibility(false /* setVisible */);
mController.hide(Type.all());
mController.cancelExistingAnimation();
assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible());
assertFalse(mController.getSourceConsumer(topBar.getType()).isVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
-
- mController.show(Type.ime());
- mController.cancelExistingAnimation();
- // no focused view, no IME.
- assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
@@ -292,6 +308,35 @@
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
+ @Test
+ public void testAnimationEndState_controller() throws Exception {
+ InsetsSourceControl control = new InsetsSourceControl(TYPE_TOP_BAR, mLeash, new Point());
+ mController.onControlsChanged(new InsetsSourceControl[] { control });
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ WindowInsetsAnimationControlListener mockListener =
+ mock(WindowInsetsAnimationControlListener.class);
+ mController.controlWindowInsetsAnimation(topBar(), mockListener);
+
+ ArgumentCaptor<WindowInsetsAnimationController> controllerCaptor =
+ ArgumentCaptor.forClass(WindowInsetsAnimationController.class);
+ verify(mockListener).onReady(controllerCaptor.capture(), anyInt());
+ controllerCaptor.getValue().finish(0 /* shownTypes */);
+ });
+ waitUntilNextFrame();
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ assertFalse(mController.getSourceConsumer(TYPE_TOP_BAR).isVisible());
+ });
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ }
+
+ private void waitUntilNextFrame() throws Exception {
+ final CountDownLatch latch = new CountDownLatch(1);
+ Choreographer.getMainThreadInstance().postCallback(Choreographer.CALLBACK_COMMIT,
+ latch::countDown, null /* token */);
+ latch.await();
+ }
+
private InsetsSourceControl[] prepareControls() {
final InsetsSourceControl navBar = new InsetsSourceControl(TYPE_NAVIGATION_BAR, mLeash,
new Point());
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
index b6717e1..013408e 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
@@ -157,7 +157,7 @@
}
@Override
- public void internalNotifyViewHierarchyEvent(boolean started) {
+ public void internalNotifyViewTreeEvent(boolean started) {
throw new UnsupportedOperationException("should not have been called");
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/LabeledIntentTest.java b/core/tests/coretests/src/android/view/textclassifier/LabeledIntentTest.java
new file mode 100644
index 0000000..e4e9cde
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/LabeledIntentTest.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2019 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.view.textclassifier;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class LabeledIntentTest {
+ private static final String TITLE_WITHOUT_ENTITY = "Map";
+ private static final String TITLE_WITH_ENTITY = "Map NW14D1";
+ private static final String DESCRIPTION = "Check the map";
+ private static final Intent INTENT =
+ new Intent(Intent.ACTION_VIEW).setDataAndNormalize(Uri.parse("http://www.android.com"));
+ private static final int REQUEST_CODE = 42;
+ private Context mContext;
+
+ @Before
+ public void setup() {
+ mContext = InstrumentationRegistry.getTargetContext();
+ }
+
+ @Test
+ public void resolve_preferTitleWithEntity() {
+ LabeledIntent labeledIntent = new LabeledIntent(
+ TITLE_WITHOUT_ENTITY,
+ TITLE_WITH_ENTITY,
+ DESCRIPTION,
+ INTENT,
+ REQUEST_CODE
+ );
+
+ LabeledIntent.Result result =
+ labeledIntent.resolve(mContext, /*titleChooser*/ null);
+
+ assertThat(result).isNotNull();
+ assertThat(result.remoteAction.getTitle()).isEqualTo(TITLE_WITH_ENTITY);
+ assertThat(result.remoteAction.getContentDescription()).isEqualTo(DESCRIPTION);
+ Intent intent = result.resolvedIntent;
+ assertThat(intent.getAction()).isEqualTo(intent.getAction());
+ assertThat(intent.getComponent()).isNotNull();
+ }
+
+ @Test
+ public void resolve_useAvailableTitle() {
+ LabeledIntent labeledIntent = new LabeledIntent(
+ TITLE_WITHOUT_ENTITY,
+ null,
+ DESCRIPTION,
+ INTENT,
+ REQUEST_CODE
+ );
+
+ LabeledIntent.Result result =
+ labeledIntent.resolve(mContext, /*titleChooser*/ null);
+
+ assertThat(result).isNotNull();
+ assertThat(result.remoteAction.getTitle()).isEqualTo(TITLE_WITHOUT_ENTITY);
+ assertThat(result.remoteAction.getContentDescription()).isEqualTo(DESCRIPTION);
+ Intent intent = result.resolvedIntent;
+ assertThat(intent.getAction()).isEqualTo(intent.getAction());
+ assertThat(intent.getComponent()).isNotNull();
+ }
+
+ @Test
+ public void resolve_titleChooser() {
+ LabeledIntent labeledIntent = new LabeledIntent(
+ TITLE_WITHOUT_ENTITY,
+ null,
+ DESCRIPTION,
+ INTENT,
+ REQUEST_CODE
+ );
+
+ LabeledIntent.Result result =
+ labeledIntent.resolve(mContext, (labeledIntent1, resolveInfo) -> "chooser");
+
+ assertThat(result).isNotNull();
+ assertThat(result.remoteAction.getTitle()).isEqualTo("chooser");
+ assertThat(result.remoteAction.getContentDescription()).isEqualTo(DESCRIPTION);
+ Intent intent = result.resolvedIntent;
+ assertThat(intent.getAction()).isEqualTo(intent.getAction());
+ assertThat(intent.getComponent()).isNotNull();
+ }
+
+ @Test
+ public void resolve_titleChooserReturnsNull() {
+ LabeledIntent labeledIntent = new LabeledIntent(
+ TITLE_WITHOUT_ENTITY,
+ null,
+ DESCRIPTION,
+ INTENT,
+ REQUEST_CODE
+ );
+
+ LabeledIntent.Result result =
+ labeledIntent.resolve(mContext, (labeledIntent1, resolveInfo) -> null);
+
+ assertThat(result).isNotNull();
+ assertThat(result.remoteAction.getTitle()).isEqualTo(TITLE_WITHOUT_ENTITY);
+ assertThat(result.remoteAction.getContentDescription()).isEqualTo(DESCRIPTION);
+ Intent intent = result.resolvedIntent;
+ assertThat(intent.getAction()).isEqualTo(intent.getAction());
+ assertThat(intent.getComponent()).isNotNull();
+ }
+
+
+ @Test
+ public void resolve_missingTitle() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () ->
+ new LabeledIntent(
+ null,
+ null,
+ DESCRIPTION,
+ INTENT,
+ REQUEST_CODE
+ ));
+ }
+}
diff --git a/core/tests/coretests/src/android/view/textclassifier/LegacyIntentFactoryTest.java b/core/tests/coretests/src/android/view/textclassifier/LegacyIntentFactoryTest.java
index 73d3eec..743818c 100644
--- a/core/tests/coretests/src/android/view/textclassifier/LegacyIntentFactoryTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/LegacyIntentFactoryTest.java
@@ -58,9 +58,12 @@
null,
null,
null,
+ null,
+ null,
+ null,
null);
- List<TextClassifierImpl.LabeledIntent> intents = mLegacyIntentFactory.create(
+ List<LabeledIntent> intents = mLegacyIntentFactory.create(
InstrumentationRegistry.getContext(),
TEXT,
/* foreignText */ false,
@@ -68,8 +71,8 @@
classificationResult);
assertThat(intents).hasSize(1);
- TextClassifierImpl.LabeledIntent labeledIntent = intents.get(0);
- Intent intent = labeledIntent.getIntent();
+ LabeledIntent labeledIntent = intents.get(0);
+ Intent intent = labeledIntent.intent;
assertThat(intent.getAction()).isEqualTo(Intent.ACTION_DEFINE);
assertThat(intent.getStringExtra(Intent.EXTRA_TEXT)).isEqualTo(TEXT);
assertThat(
@@ -89,9 +92,12 @@
null,
null,
null,
+ null,
+ null,
+ null,
null);
- List<TextClassifierImpl.LabeledIntent> intents = mLegacyIntentFactory.create(
+ List<LabeledIntent> intents = mLegacyIntentFactory.create(
InstrumentationRegistry.getContext(),
TEXT,
/* foreignText */ true,
@@ -99,7 +105,7 @@
classificationResult);
assertThat(intents).hasSize(2);
- assertThat(intents.get(0).getIntent().getAction()).isEqualTo(Intent.ACTION_DEFINE);
- assertThat(intents.get(1).getIntent().getAction()).isEqualTo(Intent.ACTION_TRANSLATE);
+ assertThat(intents.get(0).intent.getAction()).isEqualTo(Intent.ACTION_DEFINE);
+ assertThat(intents.get(1).intent.getAction()).isEqualTo(Intent.ACTION_TRANSLATE);
}
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TemplateClassificationIntentFactoryTest.java b/core/tests/coretests/src/android/view/textclassifier/TemplateClassificationIntentFactoryTest.java
index d9dac31..9fd9e8e 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TemplateClassificationIntentFactoryTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TemplateClassificationIntentFactoryTest.java
@@ -40,7 +40,7 @@
public class TemplateClassificationIntentFactoryTest {
private static final String TEXT = "text";
- private static final String TITLE = "Map";
+ private static final String TITLE_WITHOUT_ENTITY = "Map";
private static final String DESCRIPTION = "Opens in Maps";
private static final String ACTION = Intent.ACTION_VIEW;
@@ -69,9 +69,12 @@
null,
null,
null,
+ null,
+ null,
+ null,
createRemoteActionTemplates());
- List<TextClassifierImpl.LabeledIntent> intents =
+ List<LabeledIntent> intents =
mTemplateClassificationIntentFactory.create(
InstrumentationRegistry.getContext(),
TEXT,
@@ -80,14 +83,14 @@
classificationResult);
assertThat(intents).hasSize(2);
- TextClassifierImpl.LabeledIntent labeledIntent = intents.get(0);
- assertThat(labeledIntent.getTitle()).isEqualTo(TITLE);
- Intent intent = labeledIntent.getIntent();
+ LabeledIntent labeledIntent = intents.get(0);
+ assertThat(labeledIntent.titleWithoutEntity).isEqualTo(TITLE_WITHOUT_ENTITY);
+ Intent intent = labeledIntent.intent;
assertThat(intent.getAction()).isEqualTo(ACTION);
assertThat(intent.hasExtra(TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER)).isTrue();
labeledIntent = intents.get(1);
- intent = labeledIntent.getIntent();
+ intent = labeledIntent.intent;
assertThat(intent.getAction()).isEqualTo(Intent.ACTION_TRANSLATE);
assertThat(intent.hasExtra(TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER)).isTrue();
}
@@ -105,9 +108,12 @@
null,
null,
null,
+ null,
+ null,
+ null,
createRemoteActionTemplates());
- List<TextClassifierImpl.LabeledIntent> intents =
+ List<LabeledIntent> intents =
mTemplateClassificationIntentFactory.create(
InstrumentationRegistry.getContext(),
TEXT,
@@ -116,9 +122,9 @@
classificationResult);
assertThat(intents).hasSize(1);
- TextClassifierImpl.LabeledIntent labeledIntent = intents.get(0);
- assertThat(labeledIntent.getTitle()).isEqualTo(TITLE);
- Intent intent = labeledIntent.getIntent();
+ LabeledIntent labeledIntent = intents.get(0);
+ assertThat(labeledIntent.titleWithoutEntity).isEqualTo(TITLE_WITHOUT_ENTITY);
+ Intent intent = labeledIntent.intent;
assertThat(intent.getAction()).isEqualTo(ACTION);
assertThat(intent.hasExtra(TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER)).isTrue();
}
@@ -126,7 +132,8 @@
private static RemoteActionTemplate[] createRemoteActionTemplates() {
return new RemoteActionTemplate[]{
new RemoteActionTemplate(
- TITLE,
+ TITLE_WITHOUT_ENTITY,
+ null,
DESCRIPTION,
ACTION,
null,
diff --git a/core/tests/coretests/src/android/view/textclassifier/TemplateIntentFactoryTest.java b/core/tests/coretests/src/android/view/textclassifier/TemplateIntentFactoryTest.java
index a1158a7..1860734 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TemplateIntentFactoryTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TemplateIntentFactoryTest.java
@@ -38,7 +38,8 @@
public class TemplateIntentFactoryTest {
private static final String TEXT = "text";
- private static final String TITLE = "Map";
+ private static final String TITLE_WITHOUT_ENTITY = "Map";
+ private static final String TITLE_WITH_ENTITY = "Map NW14D1";
private static final String DESCRIPTION = "Check the map";
private static final String ACTION = Intent.ACTION_VIEW;
private static final String DATA = Uri.parse("http://www.android.com").toString();
@@ -69,7 +70,8 @@
@Test
public void create_full() {
RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate(
- TITLE,
+ TITLE_WITHOUT_ENTITY,
+ TITLE_WITH_ENTITY,
DESCRIPTION,
ACTION,
DATA,
@@ -81,15 +83,16 @@
REQUEST_CODE
);
- List<TextClassifierImpl.LabeledIntent> intents =
+ List<LabeledIntent> intents =
mTemplateIntentFactory.create(new RemoteActionTemplate[]{remoteActionTemplate});
assertThat(intents).hasSize(1);
- TextClassifierImpl.LabeledIntent labeledIntent = intents.get(0);
- assertThat(labeledIntent.getTitle()).isEqualTo(TITLE);
- assertThat(labeledIntent.getDescription()).isEqualTo(DESCRIPTION);
- assertThat(labeledIntent.getRequestCode()).isEqualTo(REQUEST_CODE);
- Intent intent = labeledIntent.getIntent();
+ LabeledIntent labeledIntent = intents.get(0);
+ assertThat(labeledIntent.titleWithoutEntity).isEqualTo(TITLE_WITHOUT_ENTITY);
+ assertThat(labeledIntent.titleWithEntity).isEqualTo(TITLE_WITH_ENTITY);
+ assertThat(labeledIntent.description).isEqualTo(DESCRIPTION);
+ assertThat(labeledIntent.requestCode).isEqualTo(REQUEST_CODE);
+ Intent intent = labeledIntent.intent;
assertThat(intent.getAction()).isEqualTo(ACTION);
assertThat(intent.getData().toString()).isEqualTo(DATA);
assertThat(intent.getType()).isEqualTo(TYPE);
@@ -104,7 +107,8 @@
@Test
public void normalizesScheme() {
RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate(
- TITLE,
+ TITLE_WITHOUT_ENTITY,
+ TITLE_WITH_ENTITY,
DESCRIPTION,
ACTION,
"HTTp://www.android.com",
@@ -116,17 +120,18 @@
REQUEST_CODE
);
- List<TextClassifierImpl.LabeledIntent> intents =
+ List<LabeledIntent> intents =
mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate});
- String data = intents.get(0).getIntent().getData().toString();
+ String data = intents.get(0).intent.getData().toString();
assertThat(data).isEqualTo("http://www.android.com");
}
@Test
public void create_minimal() {
RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate(
- TITLE,
+ TITLE_WITHOUT_ENTITY,
+ null,
DESCRIPTION,
ACTION,
null,
@@ -138,16 +143,17 @@
null
);
- List<TextClassifierImpl.LabeledIntent> intents =
+ List<LabeledIntent> intents =
mTemplateIntentFactory.create(new RemoteActionTemplate[]{remoteActionTemplate});
assertThat(intents).hasSize(1);
- TextClassifierImpl.LabeledIntent labeledIntent = intents.get(0);
- assertThat(labeledIntent.getTitle()).isEqualTo(TITLE);
- assertThat(labeledIntent.getDescription()).isEqualTo(DESCRIPTION);
- assertThat(labeledIntent.getRequestCode()).isEqualTo(
- TextClassifierImpl.LabeledIntent.DEFAULT_REQUEST_CODE);
- Intent intent = labeledIntent.getIntent();
+ LabeledIntent labeledIntent = intents.get(0);
+ assertThat(labeledIntent.titleWithoutEntity).isEqualTo(TITLE_WITHOUT_ENTITY);
+ assertThat(labeledIntent.titleWithEntity).isNull();
+ assertThat(labeledIntent.description).isEqualTo(DESCRIPTION);
+ assertThat(labeledIntent.requestCode).isEqualTo(
+ LabeledIntent.DEFAULT_REQUEST_CODE);
+ Intent intent = labeledIntent.intent;
assertThat(intent.getAction()).isEqualTo(ACTION);
assertThat(intent.getData()).isNull();
assertThat(intent.getType()).isNull();
@@ -161,7 +167,7 @@
public void invalidTemplate_nullTemplate() {
RemoteActionTemplate remoteActionTemplate = null;
- List<TextClassifierImpl.LabeledIntent> intents =
+ List<LabeledIntent> intents =
mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate});
assertThat(intents).isEmpty();
@@ -170,7 +176,8 @@
@Test
public void invalidTemplate_nonEmptyPackageName() {
RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate(
- TITLE,
+ TITLE_WITHOUT_ENTITY,
+ TITLE_WITH_ENTITY,
DESCRIPTION,
ACTION,
DATA,
@@ -182,7 +189,7 @@
REQUEST_CODE
);
- List<TextClassifierImpl.LabeledIntent> intents =
+ List<LabeledIntent> intents =
mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate});
assertThat(intents).isEmpty();
@@ -192,6 +199,7 @@
public void invalidTemplate_emptyTitle() {
RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate(
null,
+ null,
DESCRIPTION,
ACTION,
null,
@@ -203,7 +211,7 @@
null
);
- List<TextClassifierImpl.LabeledIntent> intents =
+ List<LabeledIntent> intents =
mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate});
assertThat(intents).isEmpty();
@@ -212,7 +220,8 @@
@Test
public void invalidTemplate_emptyDescription() {
RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate(
- TITLE,
+ TITLE_WITHOUT_ENTITY,
+ TITLE_WITH_ENTITY,
null,
ACTION,
null,
@@ -224,7 +233,7 @@
null
);
- List<TextClassifierImpl.LabeledIntent> intents =
+ List<LabeledIntent> intents =
mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate});
assertThat(intents).isEmpty();
@@ -233,7 +242,8 @@
@Test
public void invalidTemplate_emptyIntentAction() {
RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate(
- TITLE,
+ TITLE_WITHOUT_ENTITY,
+ TITLE_WITH_ENTITY,
DESCRIPTION,
null,
null,
@@ -245,7 +255,7 @@
null
);
- List<TextClassifierImpl.LabeledIntent> intents =
+ List<LabeledIntent> intents =
mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate});
assertThat(intents).isEmpty();
diff --git a/core/tests/utillib/Android.bp b/core/tests/utillib/Android.bp
new file mode 100644
index 0000000..1f742c2
--- /dev/null
+++ b/core/tests/utillib/Android.bp
@@ -0,0 +1,22 @@
+// Copyright (C) 2010 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+java_library {
+ name: "frameworks-core-util-lib",
+
+ srcs: ["**/*.java"],
+
+ static_libs: ["junit"],
+ libs: ["android.test.base"],
+}
diff --git a/core/tests/utillib/Android.mk b/core/tests/utillib/Android.mk
deleted file mode 100644
index be1ab1f..0000000
--- a/core/tests/utillib/Android.mk
+++ /dev/null
@@ -1,29 +0,0 @@
-# Copyright (C) 2010 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_MODULE := frameworks-core-util-lib
-LOCAL_STATIC_JAVA_LIBRARIES := junit
-LOCAL_JAVA_LIBRARIES := android.test.base
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# Build the test APKs using their own makefiles
-include $(call all-makefiles-under,$(LOCAL_PATH))
-
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index aaf40b4..83c8b01 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -599,9 +599,6 @@
int colorOffset, @Nullable short[] indices, int indexOffset, int indexCount,
@NonNull Paint paint) {
checkRange(verts.length, vertOffset, vertexCount);
- if (isHardwareAccelerated()) {
- return;
- }
if (texs != null) {
checkRange(texs.length, texOffset, vertexCount);
}
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 8d3bac8..920fb4c 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -836,7 +836,7 @@
* @return A bitmap that represents the specified subset of source
* @throws IllegalArgumentException if the x, y, width, height values are
* outside of the dimensions of the source bitmap, or width is <= 0,
- * or height is <= 0
+ * or height is <= 0, or if the source bitmap has already been recycled
*/
public static Bitmap createBitmap(@NonNull Bitmap source, int x, int y, int width, int height,
@Nullable Matrix m, boolean filter) {
@@ -849,6 +849,9 @@
if (y + height > source.getHeight()) {
throw new IllegalArgumentException("y + height must be <= bitmap.height()");
}
+ if (source.isRecycled()) {
+ throw new IllegalArgumentException("cannot use a recycled source in createBitmap");
+ }
// check if we can just return our argument unchanged
if (!source.isMutable() && x == 0 && y == 0 && width == source.getWidth() &&
@@ -1270,7 +1273,7 @@
node.setLeftTopRightBottom(0, 0, width, height);
node.setClipToBounds(false);
node.setForceDarkAllowed(false);
- final RecordingCanvas canvas = node.startRecording(width, height);
+ final RecordingCanvas canvas = node.beginRecording(width, height);
if (source.getWidth() != width || source.getHeight() != height) {
canvas.scale(width / (float) source.getWidth(),
height / (float) source.getHeight());
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index b020556..e623354 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -258,7 +258,7 @@
* and the renderer will draw nothing.
*/
public void setContentRoot(@Nullable RenderNode content) {
- RecordingCanvas canvas = mRootNode.startRecording();
+ RecordingCanvas canvas = mRootNode.beginRecording();
if (content != null) {
canvas.drawRenderNode(content);
}
diff --git a/graphics/java/android/graphics/RecordingCanvas.java b/graphics/java/android/graphics/RecordingCanvas.java
index 515532f..30466e1 100644
--- a/graphics/java/android/graphics/RecordingCanvas.java
+++ b/graphics/java/android/graphics/RecordingCanvas.java
@@ -31,7 +31,7 @@
* Bitmap objects that it draws, preventing the backing memory of Bitmaps from being released while
* the RecordingCanvas is still holding a native reference to the memory.
*
- * This is obtained by calling {@link RenderNode#startRecording()} and is valid until the matching
+ * This is obtained by calling {@link RenderNode#beginRecording()} and is valid until the matching
* {@link RenderNode#endRecording()} is called. It must not be retained beyond that as it is
* internally reused.
*/
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index 09b18b7..e98879d 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -16,6 +16,9 @@
package android.graphics;
+import android.annotation.BytesLong;
+import android.annotation.ColorInt;
+import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -59,7 +62,7 @@
* <pre class="prettyprint">
* RenderNode renderNode = RenderNode.create("myRenderNode");
* renderNode.setLeftTopRightBottom(0, 0, 50, 50); // Set the size to 50x50
- * RecordingCanvas canvas = renderNode.startRecording();
+ * RecordingCanvas canvas = renderNode.beginRecording();
* try {
* // Draw with the canvas
* canvas.drawRect(...);
@@ -71,14 +74,13 @@
* <h3>Drawing a RenderNode in a View</h3>
* <pre class="prettyprint">
* protected void onDraw(Canvas canvas) {
- * if (canvas instanceof RecordingCanvas) {
- * RecordingCanvas recordingCanvas = (RecordingCanvas) canvas;
+ * if (canvas.isHardwareAccelerated()) {
* // Check that the RenderNode has a display list, re-recording it if it does not.
* if (!myRenderNode.hasDisplayList()) {
* updateDisplayList(myRenderNode);
* }
* // Draw the RenderNode into this canvas.
- * recordingCanvas.drawRenderNode(myRenderNode);
+ * canvas.drawRenderNode(myRenderNode);
* }
* }
* </pre>
@@ -95,7 +97,7 @@
*
* <h3>Properties</h3>
* <p>In addition, a RenderNode offers several properties, such as
- * {@link #setScaleX(float)} or {@link #setLeft(int)}, that can be used to affect all
+ * {@link #setScaleX(float)} or {@link #setTranslationX(float)}, that can be used to affect all
* the drawing commands recorded within. For instance, these properties can be used
* to move around a large number of images without re-issuing all the individual
* <code>canvas.drawBitmap()</code> calls.</p>
@@ -104,7 +106,7 @@
* private void createDisplayList() {
* mRenderNode = RenderNode.create("MyRenderNode");
* mRenderNode.setLeftTopRightBottom(0, 0, width, height);
- * RecordingCanvas canvas = mRenderNode.startRecording();
+ * RecordingCanvas canvas = mRenderNode.beginRecording();
* try {
* for (Bitmap b : mBitmaps) {
* canvas.drawBitmap(b, 0.0f, 0.0f, null);
@@ -116,9 +118,8 @@
* }
*
* protected void onDraw(Canvas canvas) {
- * if (canvas instanceof RecordingCanvas) {
- * RecordingCanvas recordingCanvas = (RecordingCanvas) canvas;
- * recordingCanvas.drawRenderNode(mRenderNode);
+ * if (canvas.isHardwareAccelerated())
+ * canvas.drawRenderNode(mRenderNode);
* }
* }
*
@@ -263,19 +264,20 @@
* stored in this display list.
*
* {@link #endRecording()} must be called when the recording is finished in order to apply
- * the updated display list.
+ * the updated display list. Failing to call {@link #endRecording()} will result in an
+ * {@link IllegalStateException} if {@link #beginRecording(int, int)} is called again.
*
* @param width The width of the recording viewport. This will not alter the width of the
- * RenderNode itself, that must be set with {@link #setLeft(int)} and
- * {@link #setRight(int)}
+ * RenderNode itself, that must be set with {@link #setPosition(Rect)}.
* @param height The height of the recording viewport. This will not alter the height of the
- * RenderNode itself, that must be set with {@link #setTop(int)} and
- * {@link #setBottom(int)}.
+ * RenderNode itself, that must be set with {@link #setPosition(Rect)}.
* @return A canvas to record drawing operations.
+ * @throws IllegalStateException If a recording is already in progress. That is, the previous
+ * call to {@link #beginRecording(int, int)} did not call {@link #endRecording()}.
* @see #endRecording()
* @see #hasDisplayList()
*/
- public RecordingCanvas startRecording(int width, int height) {
+ public RecordingCanvas beginRecording(int width, int height) {
if (mCurrentRecordingCanvas != null) {
throw new IllegalStateException(
"Recording currently in progress - missing #endRecording() call?");
@@ -285,21 +287,18 @@
}
/**
- * Same as {@link #startRecording(int, int)} with the width & height set
+ * Same as {@link #beginRecording(int, int)} with the width & height set
* to the RenderNode's own width & height. The RenderNode's width & height may be set
- * with {@link #setLeftTopRightBottom(int, int, int, int)}.
+ * with {@link #setPosition(int, int, int, int)}.
+ *
+ * @return A canvas to record drawing operations.
+ * @throws IllegalStateException If a recording is already in progress. That is, the previous
+ * call to {@link #beginRecording(int, int)} did not call {@link #endRecording()}.
+ * @see #endRecording()
+ * @see #hasDisplayList()
*/
- public RecordingCanvas startRecording() {
- return startRecording(nGetWidth(mNativeRenderNode), nGetHeight(mNativeRenderNode));
- }
-
- /**
- * @hide
- * @deprecated use {@link #startRecording(int, int)} instead
- */
- @Deprecated
- public RecordingCanvas start(int width, int height) {
- return startRecording(width, height);
+ public RecordingCanvas beginRecording() {
+ return beginRecording(nGetWidth(mNativeRenderNode), nGetHeight(mNativeRenderNode));
}
/**
@@ -307,13 +306,13 @@
* Ends the recording for this display list. Calling this method marks
* the display list valid and {@link #hasDisplayList()} will return true.
*
- * @see #startRecording(int, int)
+ * @see #beginRecording(int, int)
* @see #hasDisplayList()
*/
public void endRecording() {
if (mCurrentRecordingCanvas == null) {
throw new IllegalStateException(
- "No recording in progress, forgot to call #startRecording()?");
+ "No recording in progress, forgot to call #beginRecording()?");
}
RecordingCanvas canvas = mCurrentRecordingCanvas;
mCurrentRecordingCanvas = null;
@@ -324,13 +323,21 @@
/**
* @hide
+ * @deprecated use {@link #beginRecording(int, int)} instead
+ */
+ @Deprecated
+ public RecordingCanvas start(int width, int height) {
+ return beginRecording(width, height);
+ }
+
+ /**
+ * @hide
* @deprecated use {@link #endRecording()} instead
*/
@Deprecated
public void end(RecordingCanvas canvas) {
- if (mCurrentRecordingCanvas != canvas) {
- throw new IllegalArgumentException(
- "Canvas given isn't the one that was returned from #startRecording");
+ if (canvas != mCurrentRecordingCanvas) {
+ throw new IllegalArgumentException("Wrong canvas");
}
endRecording();
}
@@ -346,7 +353,7 @@
/**
* Returns whether the RenderNode has a display list. If this returns false, the RenderNode
- * should be re-recorded with {@link #startRecording()} and {@link #endRecording()}.
+ * should be re-recorded with {@link #beginRecording()} and {@link #endRecording()}.
*
* A RenderNode without a display list may still be drawn, however it will have no impact
* on the rendering content until its display list is updated.
@@ -425,18 +432,21 @@
* for performance or required for the current combination of {@link #setAlpha(float)} and
* {@link #setHasOverlappingRendering(boolean)}.
*
- * The usage of this is instead to allow for either overriding of the internal behavior
+ * <p>The usage of this is instead to allow for either overriding of the internal behavior
* if it's measured to be necessary for the particular rendering content in question or, more
* usefully, to add a composition effect to the RenderNode via the optional paint parameter.
*
- * Note: When a RenderNode is using a compositing layer it will also result in
+ * <p>Note: When a RenderNode is using a compositing layer it will also result in
* clipToBounds=true behavior.
*
* @param forceToLayer if true this forces the RenderNode to use an intermediate buffer.
* Default & generally recommended value is false.
* @param paint The blend mode, alpha, and ColorFilter to apply to the compositing layer.
- * Only applies if forceToLayer is true.
- * @return true if anything changed, false otherwise
+ * Only applies if forceToLayer is true. The paint's alpha is multiplied
+ * with {@link #getAlpha()} to resolve the final alpha of the RenderNode.
+ * If null then no additional composition effects are applied on top of the
+ * composition layer.
+ * @return True if the value changed, false if the new value was the same as the previous value.
*/
public boolean setUseCompositingLayer(boolean forceToLayer, @Nullable Paint paint) {
boolean didChange = nSetLayerType(mNativeRenderNode, forceToLayer ? 2 : 0);
@@ -457,15 +467,22 @@
}
/**
- * Sets the clip bounds of the RenderNode. If null, the clip bounds is removed from the
- * RenderNode. If non-null, the RenderNode will be clipped to this rect. If
+ * Sets an additional clip on the RenderNode. If null, the extra clip is removed from the
+ * RenderNode. If non-null, the RenderNode will be clipped to this rect. In addition if
* {@link #setClipToBounds(boolean)} is true, then the RenderNode will be clipped to the
- * intersection of this rectangle and the bounds of the render node.
+ * intersection of this rectangle and the bounds of the render node, which is set with
+ * {@link #setPosition(Rect)}.
*
- * @param rect the bounds to clip to. If null, the clip bounds are reset
- * @return True if the clip bounds changed, false otherwise
+ * <p>This is equivalent to do a {@link Canvas#clipRect(Rect)} at the start of this
+ * RenderNode's display list. However, as this is a property of the RenderNode instead
+ * of part of the display list it can be more easily animated for transient additional
+ * clipping. An example usage of this would be the {@link android.transition.ChangeBounds}
+ * transition animation with the resizeClip=true option.
+ *
+ * @param rect the bounds to clip to. If null, the additional clip is removed.
+ * @return True if the value changed, false if the new value was the same as the previous value.
*/
- public boolean setClipBounds(@Nullable Rect rect) {
+ public boolean setClipRect(@Nullable Rect rect) {
if (rect == null) {
return nSetClipBoundsEmpty(mNativeRenderNode);
} else {
@@ -477,11 +494,12 @@
* Set whether the Render node should clip itself to its bounds. This defaults to true,
* and is useful to the renderer in enable quick-rejection of chunks of the tree as well as
* better partial invalidation support. Clipping can be further restricted or controlled
- * through the combination of this property as well as {@link #setClipBounds(Rect)}, which
+ * through the combination of this property as well as {@link #setClipRect(Rect)}, which
* allows for a different clipping rectangle to be used in addition to or instead of the
- * {@link #setLeftTopRightBottom(int, int, int, int)} or the RenderNode.
+ * {@link #setPosition(int, int, int, int)} or the RenderNode.
*
* @param clipToBounds true if the display list should clip to its bounds, false otherwise.
+ * @return True if the value changed, false if the new value was the same as the previous value.
*/
public boolean setClipToBounds(boolean clipToBounds) {
return nSetClipToBounds(mNativeRenderNode, clipToBounds);
@@ -489,7 +507,7 @@
/**
* Returns whether or not the RenderNode is clipping to its bounds. See
- * {@link #setClipToBounds(boolean)} and {@link #setLeftTopRightBottom(int, int, int, int)}
+ * {@link #setClipToBounds(boolean)} and {@link #setPosition(int, int, int, int)}
*
* @return true if the render node clips to its bounds, false otherwise.
*/
@@ -498,20 +516,58 @@
}
/**
- * Sets whether the RenderNode should be drawn immediately after the
+ * <p>Sets whether the RenderNode should be drawn immediately after the
* closest ancestor RenderNode containing a projection receiver.
*
+ * <p>The default is false, and the rendering of this node happens in the typical draw order.
+ *
+ * <p>If true, then at rendering time this rendernode will not be drawn in order with the
+ * {@link Canvas#drawRenderNode(RenderNode)} command that drew this RenderNode, but instead
+ * it will be re-positioned in the RenderNode tree to be drawn on the closet ancestor with a
+ * child rendernode that has {@link #setProjectionReceiver(boolean)} as true.
+ *
+ * <p>The typical usage of this is to allow a child RenderNode to draw on a parent's background,
+ * such as the platform's usage with {@link android.graphics.drawable.RippleDrawable}. Consider
+ * the following structure, built out of which RenderNode called drawRenderNode on a different
+ * RenderNode:
+ *
+ * <pre>
+ * +-------------+
+ * |RenderNode: P|
+ * +-+----------++
+ * | |
+ * v v
+ * +-------+-----+ +-+--------------+
+ * |RenderNode: C| |RenderNode: P'BG|
+ * +-------+-----+ +----------------+
+ * |
+ * |
+ * +--------+-------+
+ * |RenderNode: C'BG|
+ * +----------------+
+ * </pre>
+ *
+ * If P'BG is a projection receiver, and C'BG is set to project backwards then C'BG will
+ * behave as if it was drawn directly by P'BG instead of by C. This includes inheriting P'BG's
+ * clip instead of C's clip.
+ *
* @param shouldProject true if the display list should be projected onto a
- * containing volume.
+ * containing volume. Default is false.
+ * @return True if the value changed, false if the new value was the same as the previous value.
*/
public boolean setProjectBackwards(boolean shouldProject) {
return nSetProjectBackwards(mNativeRenderNode, shouldProject);
}
/**
- * Sets whether the RenderNode is a projection receiver - that its parent
- * RenderNode should draw any descendent RenderNodes with
- * ProjectBackwards=true directly on top of it. Default value is false.
+ * Sets whether the RenderNode is a projection receiver. If true then this RenderNode's parent
+ * should draw any descendant RenderNodes with ProjectBackwards=true directly on top of it.
+ * Default value is false. See
+ * {@link #setProjectBackwards(boolean)} for a description of what this entails.
+ *
+ * @param shouldRecieve True if this RenderNode is a projection receiver, false otherwise.
+ * Default is false.
+ * @return True if the value changed, false if the new value was the same as the previous value.
*/
public boolean setProjectionReceiver(boolean shouldRecieve) {
return nSetProjectionReceiver(mNativeRenderNode, shouldRecieve);
@@ -526,6 +582,7 @@
* outline for those changes to be applied.
*
* @param outline The outline to use for this RenderNode.
+ * @return True if the value changed, false if the new value was the same as the previous value.
*/
public boolean setOutline(@Nullable Outline outline) {
if (outline == null) {
@@ -572,8 +629,9 @@
* {@link android.R.attr#spotShadowAlpha} theme attribute
*
* @param color The color this RenderNode will cast for its elevation spot shadow.
+ * @return True if the value changed, false if the new value was the same as the previous value.
*/
- public boolean setSpotShadowColor(int color) {
+ public boolean setSpotShadowColor(@ColorInt int color) {
return nSetSpotShadowColor(mNativeRenderNode, color);
}
@@ -581,7 +639,7 @@
* @return The shadow color set by {@link #setSpotShadowColor(int)}, or black if nothing
* was set
*/
- public int getSpotShadowColor() {
+ public @ColorInt int getSpotShadowColor() {
return nGetSpotShadowColor(mNativeRenderNode);
}
@@ -597,8 +655,9 @@
* {@link android.R.attr#ambientShadowAlpha} theme attribute.
*
* @param color The color this RenderNode will cast for its elevation shadow.
+ * @return True if the value changed, false if the new value was the same as the previous value.
*/
- public boolean setAmbientShadowColor(int color) {
+ public boolean setAmbientShadowColor(@ColorInt int color) {
return nSetAmbientShadowColor(mNativeRenderNode, color);
}
@@ -606,7 +665,7 @@
* @return The shadow color set by {@link #setAmbientShadowColor(int)}, or black if
* nothing was set
*/
- public int getAmbientShadowColor() {
+ public @ColorInt int getAmbientShadowColor() {
return nGetAmbientShadowColor(mNativeRenderNode);
}
@@ -614,6 +673,8 @@
* Enables or disables clipping to the outline.
*
* @param clipToOutline true if clipping to the outline.
+ * @return True if the clipToOutline value changed, false if previous value matched the new
+ * value.
*/
public boolean setClipToOutline(boolean clipToOutline) {
return nSetClipToOutline(mNativeRenderNode, clipToOutline);
@@ -640,7 +701,7 @@
/**
* Set the static matrix on the display list. The specified matrix is combined with other
- * transforms (such as {@link #setScaleX(float)}, {@link #setRotation(float)}, etc.)
+ * transforms (such as {@link #setScaleX(float)}, {@link #setRotationZ(float)}, etc.)
*
* @param matrix A transform matrix to apply to this display list
* @hide TODO Do we want this?
@@ -669,6 +730,7 @@
* @param alpha The translucency of the display list, must be a value between 0.0f and 1.0f
* @see View#setAlpha(float)
* @see #getAlpha()
+ * @return True if the value changed, false if the new value was the same as the previous value.
*/
public boolean setAlpha(float alpha) {
return nSetAlpha(mNativeRenderNode, alpha);
@@ -742,7 +804,7 @@
* Sets the base elevation of this RenderNode in pixels
*
* @param lift the elevation in pixels
- * @return true if the elevation changed, false if it was the same
+ * @return True if the value changed, false if the new value was the same as the previous value.
*/
public boolean setElevation(float lift) {
return nSetElevation(mNativeRenderNode, lift);
@@ -763,6 +825,7 @@
* @param translationX The X axis translation value of the display list, in pixels
* @see View#setTranslationX(float)
* @see #getTranslationX()
+ * @return True if the value changed, false if the new value was the same as the previous value.
*/
public boolean setTranslationX(float translationX) {
return nSetTranslationX(mNativeRenderNode, translationX);
@@ -783,6 +846,7 @@
* @param translationY The Y axis translation value of the display list, in pixels
* @see View#setTranslationY(float)
* @see #getTranslationY()
+ * @return True if the value changed, false if the new value was the same as the previous value.
*/
public boolean setTranslationY(float translationY) {
return nSetTranslationY(mNativeRenderNode, translationY);
@@ -802,6 +866,7 @@
*
* @see View#setTranslationZ(float)
* @see #getTranslationZ()
+ * @return True if the value changed, false if the new value was the same as the previous value.
*/
public boolean setTranslationZ(float translationZ) {
return nSetTranslationZ(mNativeRenderNode, translationZ);
@@ -820,19 +885,20 @@
* Sets the rotation value for the display list around the Z axis.
*
* @param rotation The rotation value of the display list, in degrees
- * @see View#setRotation(float)
- * @see #getRotation()
+ * @see View#setRotationZ(float)
+ * @see #getRotationZ()
+ * @return True if the value changed, false if the new value was the same as the previous value.
*/
- public boolean setRotation(float rotation) {
+ public boolean setRotationZ(float rotation) {
return nSetRotation(mNativeRenderNode, rotation);
}
/**
* Returns the rotation value for this display list around the Z axis, in degrees.
*
- * @see #setRotation(float)
+ * @see #setRotationZ(float)
*/
- public float getRotation() {
+ public float getRotationZ() {
return nGetRotation(mNativeRenderNode);
}
@@ -842,6 +908,7 @@
* @param rotationX The rotation value of the display list, in degrees
* @see View#setRotationX(float)
* @see #getRotationX()
+ * @return True if the value changed, false if the new value was the same as the previous value.
*/
public boolean setRotationX(float rotationX) {
return nSetRotationX(mNativeRenderNode, rotationX);
@@ -862,6 +929,7 @@
* @param rotationY The rotation value of the display list, in degrees
* @see View#setRotationY(float)
* @see #getRotationY()
+ * @return True if the value changed, false if the new value was the same as the previous value.
*/
public boolean setRotationY(float rotationY) {
return nSetRotationY(mNativeRenderNode, rotationY);
@@ -882,6 +950,7 @@
* @param scaleX The scale value of the display list
* @see View#setScaleX(float)
* @see #getScaleX()
+ * @return True if the value changed, false if the new value was the same as the previous value.
*/
public boolean setScaleX(float scaleX) {
return nSetScaleX(mNativeRenderNode, scaleX);
@@ -902,6 +971,7 @@
* @param scaleY The scale value of the display list
* @see View#setScaleY(float)
* @see #getScaleY()
+ * @return True if the value changed, false if the new value was the same as the previous value.
*/
public boolean setScaleY(float scaleY) {
return nSetScaleY(mNativeRenderNode, scaleY);
@@ -922,6 +992,7 @@
* @param pivotX The pivot value of the display list on the X axis, in pixels
* @see View#setPivotX(float)
* @see #getPivotX()
+ * @return True if the value changed, false if the new value was the same as the previous value.
*/
public boolean setPivotX(float pivotX) {
return nSetPivotX(mNativeRenderNode, pivotX);
@@ -942,6 +1013,7 @@
* @param pivotY The pivot value of the display list on the Y axis, in pixels
* @see View#setPivotY(float)
* @see #getPivotY()
+ * @return True if the value changed, false if the new value was the same as the previous value.
*/
public boolean setPivotY(float pivotY) {
return nSetPivotY(mNativeRenderNode, pivotY);
@@ -969,6 +1041,8 @@
* Clears any pivot previously set by a call to {@link #setPivotX(float)} or
* {@link #setPivotY(float)}. After calling this {@link #isPivotExplicitlySet()} will be false
* and the pivot used for rotation will return to default of being centered on the view.
+ *
+ * @return True if the value changed, false if the new value was the same as the previous value.
*/
public boolean resetPivot() {
return nResetPivot(mNativeRenderNode);
@@ -997,8 +1071,10 @@
* @param distance The distance in pixels, must always be positive
* @see #setRotationX(float)
* @see #setRotationY(float)
+ * @return True if the value changed, false if the new value was the same as the previous value.
*/
- public boolean setCameraDistance(float distance) {
+ public boolean setCameraDistance(
+ @FloatRange(from = 0.0f, to = Float.MAX_VALUE) float distance) {
if (!Float.isFinite(distance) || distance < 0.0f) {
throw new IllegalArgumentException("distance must be finite & positive, given="
+ distance);
@@ -1013,7 +1089,7 @@
* @return the distance along the Z axis in pixels.
* @see #setCameraDistance(float)
*/
- public float getCameraDistance() {
+ public @FloatRange(from = 0.0f, to = Float.MAX_VALUE) float getCameraDistance() {
return -nGetCameraDistance(mNativeRenderNode);
}
@@ -1022,6 +1098,7 @@
*
* @param left The left position, in pixels, of the RenderNode
* @return true if the value changed, false otherwise
+ * @hide
*/
public boolean setLeft(int left) {
return nSetLeft(mNativeRenderNode, left);
@@ -1032,6 +1109,7 @@
*
* @param top The top position, in pixels, of the RenderNode
* @return true if the value changed, false otherwise.
+ * @hide
*/
public boolean setTop(int top) {
return nSetTop(mNativeRenderNode, top);
@@ -1042,6 +1120,7 @@
*
* @param right The right position, in pixels, of the RenderNode
* @return true if the value changed, false otherwise.
+ * @hide
*/
public boolean setRight(int right) {
return nSetRight(mNativeRenderNode, right);
@@ -1052,6 +1131,7 @@
*
* @param bottom The bottom position, in pixels, of the RenderNode
* @return true if the value changed, false otherwise.
+ * @hide
*/
public boolean setBottom(int bottom) {
return nSetBottom(mNativeRenderNode, bottom);
@@ -1060,8 +1140,6 @@
/**
* Gets the left position for the RenderNode.
*
- * See {@link #setLeft(int)}
- *
* @return the left position in pixels
*/
public int getLeft() {
@@ -1071,8 +1149,6 @@
/**
* Gets the top position for the RenderNode.
*
- * See {@link #setTop(int)}
- *
* @return the top position in pixels
*/
public int getTop() {
@@ -1082,8 +1158,6 @@
/**
* Gets the right position for the RenderNode.
*
- * See {@link #setRight(int)}
- *
* @return the right position in pixels
*/
public int getRight() {
@@ -1093,8 +1167,6 @@
/**
* Gets the bottom position for the RenderNode.
*
- * See {@link #setBottom(int)}
- *
* @return the bottom position in pixels
*/
public int getBottom() {
@@ -1127,20 +1199,41 @@
* @param right The right position of the RenderNode, in pixels
* @param bottom The bottom position of the RenderNode, in pixels
* @return true if any values changed, false otherwise.
- * @see #setLeft(int)
- * @see #setTop(int)
- * @see #setRight(int)
- * @see #setBottom(int)
+ * @hide
*/
public boolean setLeftTopRightBottom(int left, int top, int right, int bottom) {
return nSetLeftTopRightBottom(mNativeRenderNode, left, top, right, bottom);
}
/**
+ * Sets the position of the RenderNode.
+ *
+ * @param left The left position of the RenderNode, in pixels
+ * @param top The top position of the RenderNode, in pixels
+ * @param right The right position of the RenderNode, in pixels
+ * @param bottom The bottom position of the RenderNode, in pixels
+ * @return True if the value changed, false if the new value was the same as the previous value.
+ */
+ public boolean setPosition(int left, int top, int right, int bottom) {
+ return nSetLeftTopRightBottom(mNativeRenderNode, left, top, right, bottom);
+ }
+
+ /**
+ * Sets the position of the RenderNode.
+ *
+ * @param position The position rectangle in pixels
+ * @return True if the value changed, false if the new value was the same as the previous value.
+ */
+ public boolean setPosition(Rect position) {
+ return nSetLeftTopRightBottom(mNativeRenderNode,
+ position.left, position.top, position.right, position.bottom);
+ }
+
+ /**
* Offsets the left and right positions for the RenderNode
*
* @param offset The amount that the left and right positions are offset in pixels
- * @return true if any values changed, false otherwise.
+ * @return True if the value changed, false if the new value was the same as the previous value.
*/
public boolean offsetLeftAndRight(int offset) {
return nOffsetLeftAndRight(mNativeRenderNode, offset);
@@ -1150,7 +1243,7 @@
* Offsets the top and bottom values for the RenderNode
*
* @param offset The amount that the left and right positions are offset in pixels
- * @return true if any values changed, false otherwise.
+ * @return True if the value changed, false if the new value was the same as the previous value.
*/
public boolean offsetTopAndBottom(int offset) {
return nOffsetTopAndBottom(mNativeRenderNode, offset);
@@ -1170,8 +1263,10 @@
* Gets the approximate memory usage of the RenderNode for debug purposes. Does not include
* the memory usage of any child RenderNodes nor any bitmaps, only the memory usage of
* this RenderNode and any data it owns.
+ *
+ * @return Approximate memory usage in bytes.
*/
- public int computeApproximateMemoryUsage() {
+ public @BytesLong long computeApproximateMemoryUsage() {
return nGetDebugSize(mNativeRenderNode);
}
@@ -1186,7 +1281,7 @@
* it prevent any 'false' in any of its children.
*
* @param allow Whether or not to allow force dark.
- * @return true If the value has changed, false otherwise.
+ * @return True if the value changed, false if the new value was the same as the previous value.
*/
public boolean setForceDarkAllowed(boolean allow) {
return nSetAllowForceDark(mNativeRenderNode, allow);
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 66d8542..d7b84ff 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -356,6 +356,7 @@
ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override,
bool /*stop_at_first_match*/,
+ bool ignore_configuration,
FindEntryResult* out_entry) const {
// Might use this if density_override != 0.
ResTable_config density_override_config;
@@ -399,7 +400,7 @@
// If desired_config is the same as the set configuration, then we can use our filtered list
// and we don't need to match the configurations, since they already matched.
- const bool use_fast_path = desired_config == &configuration_;
+ const bool use_fast_path = !ignore_configuration && desired_config == &configuration_;
for (size_t pi = 0; pi < package_count; pi++) {
const ConfiguredPackage& loaded_package_impl = package_group.packages_[pi];
@@ -475,21 +476,23 @@
// ResTable_config, we must copy it.
const auto iter_end = type_spec->types + type_spec->type_count;
for (auto iter = type_spec->types; iter != iter_end; ++iter) {
- ResTable_config this_config;
- this_config.copyFromDtoH((*iter)->config);
+ ResTable_config this_config{};
- if (!this_config.match(*desired_config)) {
- continue;
- }
+ if (!ignore_configuration) {
+ this_config.copyFromDtoH((*iter)->config);
+ if (!this_config.match(*desired_config)) {
+ continue;
+ }
- if (best_config == nullptr) {
- resolution_type = Resolution::Step::Type::INITIAL;
- } else if (this_config.isBetterThan(*best_config, desired_config)) {
- resolution_type = Resolution::Step::Type::BETTER_MATCH;
- } else if (package_is_overlay && this_config.compare(*best_config) == 0) {
- resolution_type = Resolution::Step::Type::OVERLAID;
- } else {
- continue;
+ if (best_config == nullptr) {
+ resolution_type = Resolution::Step::Type::INITIAL;
+ } else if (this_config.isBetterThan(*best_config, desired_config)) {
+ resolution_type = Resolution::Step::Type::BETTER_MATCH;
+ } else if (package_is_overlay && this_config.compare(*best_config) == 0) {
+ resolution_type = Resolution::Step::Type::OVERLAID;
+ } else {
+ continue;
+ }
}
// The configuration matches and is better than the previous selection.
@@ -506,6 +509,11 @@
best_config = &best_config_copy;
best_offset = offset;
+ if (ignore_configuration) {
+ // Any configuration will suffice, so break.
+ break;
+ }
+
if (resource_resolution_logging_enabled_) {
resolution_steps.push_back(Resolution::Step{resolution_type,
this_config.toString(),
@@ -622,8 +630,9 @@
bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) const {
FindEntryResult entry;
- ApkAssetsCookie cookie =
- FindEntry(resid, 0u /* density_override */, true /* stop_at_first_match */, &entry);
+ ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */,
+ true /* stop_at_first_match */,
+ true /* ignore_configuration */, &entry);
if (cookie == kInvalidCookie) {
return false;
}
@@ -652,13 +661,14 @@
bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) const {
FindEntryResult entry;
- ApkAssetsCookie cookie =
- FindEntry(resid, 0u /* density_override */, false /* stop_at_first_match */, &entry);
+ ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */,
+ false /* stop_at_first_match */,
+ true /* ignore_configuration */, &entry);
if (cookie != kInvalidCookie) {
*out_flags = entry.type_flags;
- return cookie;
+ return true;
}
- return kInvalidCookie;
+ return false;
}
ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag,
@@ -666,8 +676,8 @@
ResTable_config* out_selected_config,
uint32_t* out_flags) const {
FindEntryResult entry;
- ApkAssetsCookie cookie =
- FindEntry(resid, density_override, false /* stop_at_first_match */, &entry);
+ ApkAssetsCookie cookie = FindEntry(resid, density_override, false /* stop_at_first_match */,
+ false /* ignore_configuration */, &entry);
if (cookie == kInvalidCookie) {
return kInvalidCookie;
}
@@ -759,8 +769,10 @@
}
FindEntryResult entry;
- ApkAssetsCookie cookie =
- FindEntry(resid, 0u /* density_override */, false /* stop_at_first_match */, &entry);
+ ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */,
+ false /* stop_at_first_match */,
+ false /* ignore_configuration */,
+ &entry);
if (cookie == kInvalidCookie) {
return nullptr;
}
@@ -1387,7 +1399,9 @@
// Find the cookie of the attribute resource id
FindEntryResult attribute_entry_result;
ApkAssetsCookie attribute_cookie =
- o.asset_manager_->FindEntry(make_resid(p, t, e), 0 /* density_override */ , false,
+ o.asset_manager_->FindEntry(make_resid(p, t, e), 0 /* density_override */ ,
+ true /* stop_at_first_match */,
+ true /* ignore_configuration */,
&attribute_entry_result);
// Determine the package id of the attribute in the destination AssetManager
diff --git a/libs/androidfw/DisplayEventDispatcher.cpp b/libs/androidfw/DisplayEventDispatcher.cpp
index 3b9a348..6606148 100644
--- a/libs/androidfw/DisplayEventDispatcher.cpp
+++ b/libs/androidfw/DisplayEventDispatcher.cpp
@@ -135,6 +135,9 @@
case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
dispatchHotplug(ev.header.timestamp, ev.header.displayId, ev.hotplug.connected);
break;
+ case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
+ dispatchConfigChanged(ev.header.timestamp, ev.header.displayId, ev.config.configId);
+ break;
default:
ALOGW("dispatcher %p ~ ignoring unknown event type %#x", this, ev.header.type);
break;
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index fc5aa9c7..1e2b36c 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -257,11 +257,12 @@
// Creates a new Theme from this AssetManager.
std::unique_ptr<Theme> NewTheme();
- template <typename Func>
- void ForEachPackage(Func func) const {
+ void ForEachPackage(const std::function<bool(const std::string&, uint8_t)> func) const {
for (const PackageGroup& package_group : package_groups_) {
- func(package_group.packages_.front().loaded_package_->GetPackageName(),
- package_group.dynamic_ref_table.mAssignedPackageId);
+ if (!func(package_group.packages_.front().loaded_package_->GetPackageName(),
+ package_group.dynamic_ref_table.mAssignedPackageId)) {
+ return;
+ }
}
}
@@ -282,10 +283,13 @@
// care about the value. In this case, the value of `FindEntryResult::type_flags` is incomplete
// and should not be used.
//
+ // When `ignore_configuration` is true, FindEntry will return always select the first entry in
+ // for the type seen regardless of its configuration.
+ //
// NOTE: FindEntry takes care of ensuring that structs within FindEntryResult have been properly
// bounds-checked. Callers of FindEntry are free to trust the data if this method succeeds.
ApkAssetsCookie FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match,
- FindEntryResult* out_entry) const;
+ bool ignore_configuration, FindEntryResult* out_entry) const;
// Assigns package IDs to all shared library ApkAssets.
// Should be called whenever the ApkAssets are changed.
diff --git a/libs/androidfw/include/androidfw/DisplayEventDispatcher.h b/libs/androidfw/include/androidfw/DisplayEventDispatcher.h
index d2addba..5381c01 100644
--- a/libs/androidfw/include/androidfw/DisplayEventDispatcher.h
+++ b/libs/androidfw/include/androidfw/DisplayEventDispatcher.h
@@ -40,6 +40,8 @@
virtual void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) = 0;
virtual void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId,
bool connected) = 0;
+ virtual void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
+ int32_t configId) = 0;
virtual int handleEvent(int receiveFd, int events, void* data);
bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId,
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index df1537e..1bd30eb 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -161,7 +161,7 @@
SkAutoCanvasRestore acr2(canvas, shouldClip);
canvas->setMatrix(mProjectedDisplayList->mParentMatrix);
if (shouldClip) {
- clipOutline(*mProjectedDisplayList->mProjectedOutline, canvas, nullptr);
+ canvas->clipPath(*mProjectedDisplayList->mProjectedOutline->getPath());
}
drawBackwardsProjectedNodes(canvas, *mProjectedDisplayList);
}
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 733b866..abf0837 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -759,11 +759,11 @@
void PointerController::loadResourcesLocked() REQUIRES(mLock) {
mPolicy->loadPointerResources(&mResources, mLocked.viewport.displayId);
+ mPolicy->loadPointerIcon(&mLocked.pointerIcon, mLocked.viewport.displayId);
+ mLocked.additionalMouseResources.clear();
+ mLocked.animationResources.clear();
if (mLocked.presentation == PRESENTATION_POINTER) {
- mLocked.additionalMouseResources.clear();
- mLocked.animationResources.clear();
- mPolicy->loadPointerIcon(&mLocked.pointerIcon, mLocked.viewport.displayId);
mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
&mLocked.animationResources, mLocked.viewport.displayId);
}
diff --git a/location/lib/api/current.txt b/location/lib/api/current.txt
index dbb581f..d1b39b3 100644
--- a/location/lib/api/current.txt
+++ b/location/lib/api/current.txt
@@ -11,7 +11,7 @@
method public android.os.IBinder getBinder();
method public boolean isEnabled();
method @Deprecated protected void onDisable();
- method protected void onDump(java.io.FileDescriptor, java.io.PrintWriter, String[]);
+ method @Deprecated protected void onDump(java.io.FileDescriptor, java.io.PrintWriter, String[]);
method @Deprecated protected void onEnable();
method @Deprecated protected int onGetStatus(android.os.Bundle);
method @Deprecated protected long onGetStatusUpdateTime();
diff --git a/location/lib/java/com/android/location/provider/LocationProviderBase.java b/location/lib/java/com/android/location/provider/LocationProviderBase.java
index 7cd7207..fa113a8 100644
--- a/location/lib/java/com/android/location/provider/LocationProviderBase.java
+++ b/location/lib/java/com/android/location/provider/LocationProviderBase.java
@@ -240,8 +240,10 @@
protected abstract void onSetRequest(ProviderRequestUnbundled request, WorkSource source);
/**
- * Dump debug information.
+ * @deprecated This callback will never be invoked on Android Q and above. This method may be
+ * removed in the future. Prefer to dump provider state via the containing service instead.
*/
+ @Deprecated
protected void onDump(FileDescriptor fd, PrintWriter pw, String[] args) {}
/**
@@ -336,10 +338,5 @@
public void sendExtraCommand(String command, Bundle extras) {
onSendExtraCommand(command, extras);
}
-
- @Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- onDump(fd, pw, args);
- }
}
}
diff --git a/media/apex/java/android/media/MediaConstants.java b/media/apex/java/android/media/MediaConstants.java
index 65b6f55..776c1ba 100644
--- a/media/apex/java/android/media/MediaConstants.java
+++ b/media/apex/java/android/media/MediaConstants.java
@@ -27,6 +27,7 @@
static final String KEY_SESSION2LINK = "android.media.key.SESSION2LINK";
static final String KEY_ALLOWED_COMMANDS = "android.media.key.ALLOWED_COMMANDS";
static final String KEY_PLAYBACK_ACTIVE = "android.media.key.PLAYBACK_ACTIVE";
+ static final String KEY_TOKEN_EXTRAS = "android.media.key.TOKEN_EXTRAS";
private MediaConstants() {
}
diff --git a/media/apex/java/android/media/MediaController2.java b/media/apex/java/android/media/MediaController2.java
index 4ea384a..2709df0 100644
--- a/media/apex/java/android/media/MediaController2.java
+++ b/media/apex/java/android/media/MediaController2.java
@@ -21,6 +21,7 @@
import static android.media.MediaConstants.KEY_PID;
import static android.media.MediaConstants.KEY_PLAYBACK_ACTIVE;
import static android.media.MediaConstants.KEY_SESSION2LINK;
+import static android.media.MediaConstants.KEY_TOKEN_EXTRAS;
import static android.media.Session2Command.RESULT_ERROR_UNKNOWN_ERROR;
import static android.media.Session2Command.RESULT_INFO_SKIPPED;
import static android.media.Session2Token.TYPE_SESSION;
@@ -264,6 +265,7 @@
Session2CommandGroup allowedCommands =
connectionResult.getParcelable(KEY_ALLOWED_COMMANDS);
boolean playbackActive = connectionResult.getBoolean(KEY_PLAYBACK_ACTIVE);
+ Bundle tokenExtras = connectionResult.getBundle(KEY_TOKEN_EXTRAS);
if (DEBUG) {
Log.d(TAG, "notifyConnected sessionBinder=" + sessionBinder
+ ", allowedCommands=" + allowedCommands);
@@ -282,7 +284,7 @@
// so can be used without worrying about deadlock.
sessionBinder.linkToDeath(mDeathRecipient, 0);
mConnectedToken = new Session2Token(mSessionToken.getUid(), TYPE_SESSION,
- mSessionToken.getPackageName(), sessionBinder);
+ mSessionToken.getPackageName(), sessionBinder, tokenExtras);
}
mCallbackExecutor.execute(() -> {
mCallback.onConnected(MediaController2.this, allowedCommands);
diff --git a/media/apex/java/android/media/MediaSession2.java b/media/apex/java/android/media/MediaSession2.java
index 4c6945a..148e16c 100644
--- a/media/apex/java/android/media/MediaSession2.java
+++ b/media/apex/java/android/media/MediaSession2.java
@@ -21,6 +21,7 @@
import static android.media.MediaConstants.KEY_PID;
import static android.media.MediaConstants.KEY_PLAYBACK_ACTIVE;
import static android.media.MediaConstants.KEY_SESSION2LINK;
+import static android.media.MediaConstants.KEY_TOKEN_EXTRAS;
import static android.media.Session2Command.RESULT_ERROR_UNKNOWN_ERROR;
import static android.media.Session2Command.RESULT_INFO_SKIPPED;
import static android.media.Session2Token.TYPE_SESSION;
@@ -94,7 +95,8 @@
private ForegroundServiceEventCallback mForegroundServiceEventCallback;
MediaSession2(@NonNull Context context, @NonNull String id, PendingIntent sessionActivity,
- @NonNull Executor callbackExecutor, @NonNull SessionCallback callback) {
+ @NonNull Executor callbackExecutor, @NonNull SessionCallback callback,
+ Bundle tokenExtras) {
synchronized (MediaSession2.class) {
if (SESSION_ID_LIST.contains(id)) {
throw new IllegalStateException("Session ID must be unique. ID=" + id);
@@ -109,7 +111,7 @@
mCallback = callback;
mSessionStub = new Session2Link(this);
mSessionToken = new Session2Token(Process.myUid(), TYPE_SESSION, context.getPackageName(),
- mSessionStub);
+ mSessionStub, tokenExtras);
mSessionManager = (MediaSessionManager) mContext.getSystemService(
Context.MEDIA_SESSION_SERVICE);
// NOTE: mResultHandler uses main looper, so this MUST NOT be blocked.
@@ -339,6 +341,7 @@
connectionResult.putParcelable(KEY_ALLOWED_COMMANDS,
controllerInfo.mAllowedCommands);
connectionResult.putBoolean(KEY_PLAYBACK_ACTIVE, isPlaybackActive());
+ connectionResult.putBundle(KEY_TOKEN_EXTRAS, mSessionToken.getExtras());
// Double check if session is still there, because close() can be called in
// another thread.
@@ -444,6 +447,7 @@
private PendingIntent mSessionActivity;
private Executor mCallbackExecutor;
private SessionCallback mCallback;
+ private Bundle mExtras;
/**
* Creates a builder for {@link MediaSession2}.
@@ -507,6 +511,18 @@
}
/**
+ * Set extras for the session token.
+ *
+ * @return The Builder to allow chaining
+ * @see Session2Token#getExtras()
+ */
+ @NonNull
+ public Builder setExtras(@Nullable Bundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
+ /**
* Build {@link MediaSession2}.
*
* @return a new session
@@ -525,7 +541,7 @@
mId = "";
}
MediaSession2 session2 = new MediaSession2(mContext, mId, mSessionActivity,
- mCallbackExecutor, mCallback);
+ mCallbackExecutor, mCallback, mExtras);
// Notify framework about the newly create session after the constructor is finished.
// Otherwise, framework may access the session before the initialization is finished.
diff --git a/media/apex/java/android/media/Session2Token.java b/media/apex/java/android/media/Session2Token.java
index 238cc2b..6680e40 100644
--- a/media/apex/java/android/media/Session2Token.java
+++ b/media/apex/java/android/media/Session2Token.java
@@ -24,6 +24,7 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -88,6 +89,7 @@
private final String mServiceName;
private final Session2Link mSessionLink;
private final ComponentName mComponentName;
+ private final Bundle mExtras;
/**
* Constructor for the token with type {@link #TYPE_SESSION_SERVICE}.
@@ -116,15 +118,18 @@
mUid = uid;
mType = TYPE_SESSION_SERVICE;
mSessionLink = null;
+ mExtras = null;
}
- Session2Token(int uid, int type, String packageName, Session2Link sessionLink) {
+ Session2Token(int uid, int type, String packageName, Session2Link sessionLink,
+ Bundle tokenExtras) {
mUid = uid;
mType = type;
mPackageName = packageName;
mServiceName = null;
mComponentName = null;
mSessionLink = sessionLink;
+ mExtras = tokenExtras;
}
Session2Token(Parcel in) {
@@ -134,6 +139,7 @@
mServiceName = in.readString();
mSessionLink = in.readParcelable(null);
mComponentName = ComponentName.unflattenFromString(in.readString());
+ mExtras = in.readBundle();
}
@Override
@@ -144,6 +150,7 @@
dest.writeString(mServiceName);
dest.writeParcelable(mSessionLink, flags);
dest.writeString(mComponentName == null ? "" : mComponentName.flattenToString());
+ dest.writeBundle(mExtras);
}
@Override
@@ -207,6 +214,14 @@
return mType;
}
+ /**
+ * @return extras of the token
+ */
+ @Nullable
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
Session2Link getSessionLink() {
return mSessionLink;
}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index bd828ee..3fb2365 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -36,6 +36,7 @@
import android.content.Intent;
import android.media.audiopolicy.AudioPolicy;
import android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener;
+import android.media.audiopolicy.AudioProductStrategies;
import android.media.projection.MediaProjection;
import android.media.session.MediaController;
import android.media.session.MediaSession;
@@ -5221,6 +5222,30 @@
return AudioSystem.isHapticPlaybackSupported();
}
+ /**
+ * @hide
+ * Introspection API to retrieve audio product strategies.
+ * When implementing {Car|Oem}AudioManager, use this method to retrieve the collection of
+ * audio product strategies, which is indexed by a weakly typed index in order to be extended
+ * by OEM without any needs of AOSP patches.
+ * The {Car|Oem}AudioManager can expose API to build {@link AudioAttributes} for a given product
+ * strategy refered either by its index or human readable string. It will allow clients
+ * application to start streaming data using these {@link AudioAttributes} on the selected
+ * device by Audio Policy Engine.
+ * @return a (possibly zero-length) array of
+ * {@see android.media.audiopolicy.AudioProductStrategy} objects.
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public @NonNull AudioProductStrategies getAudioProductStrategies() {
+ final IAudioService service = getService();
+ try {
+ return service.getAudioProductStrategies();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
//---------------------------------------------------------
// Inner classes
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 571e67e..abdc3c9 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -33,6 +33,7 @@
import android.media.PlayerBase;
import android.media.VolumePolicy;
import android.media.audiopolicy.AudioPolicyConfig;
+import android.media.audiopolicy.AudioProductStrategies;
import android.media.audiopolicy.IAudioPolicyCallback;
import android.media.projection.IMediaProjection;
@@ -82,6 +83,8 @@
int getLastAudibleStreamVolume(int streamType);
+ AudioProductStrategies getAudioProductStrategies();
+
void setMicrophoneMute(boolean on, String callingPackage, int userId);
void setRingerModeExternal(int ringerMode, String caller);
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index e4d356b..08ce9fc 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -2645,6 +2645,7 @@
*/
private synchronized void setSubtitleAnchor() {
if ((mSubtitleController == null) && (ActivityThread.currentApplication() != null)) {
+ getMediaTimeProvider();
final HandlerThread thread = new HandlerThread("SetSubtitleAnchorThread");
thread.start();
Handler handler = new Handler(thread.getLooper());
@@ -2660,7 +2661,7 @@
@Override
public Looper getSubtitleLooper() {
- return Looper.getMainLooper();
+ return mTimeProvider.mEventHandler.getLooper();
}
});
thread.getLooper().quitSafely();
diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java
index 445edd1..5f65d58 100644
--- a/media/java/android/media/audiopolicy/AudioPolicy.java
+++ b/media/java/android/media/audiopolicy/AudioPolicy.java
@@ -364,11 +364,10 @@
* played.
* @param uid UID of the application to affect.
* @param devices list of devices to which the audio stream of the application may be routed.
- * @return {@link AudioManager#SUCCESS} if the change was successful, {@link AudioManager#ERROR}
- * otherwise.
+ * @return true if the change was successful, false otherwise.
*/
@SystemApi
- public int setUidDeviceAffinity(int uid, @NonNull List<AudioDeviceInfo> devices) {
+ public boolean setUidDeviceAffinity(int uid, @NonNull List<AudioDeviceInfo> devices) {
if (devices == null) {
throw new IllegalArgumentException("Illegal null list of audio devices");
}
@@ -393,10 +392,10 @@
try {
final int status = service.setUidDeviceAffinity(this.cb(),
uid, deviceTypes, deviceAdresses);
- return status;
+ return (status == AudioManager.SUCCESS);
} catch (RemoteException e) {
Log.e(TAG, "Dead object in setUidDeviceAffinity", e);
- return AudioManager.ERROR;
+ return false;
}
}
}
@@ -406,11 +405,10 @@
* Removes audio device affinity previously set by
* {@link #setUidDeviceAffinity(int, java.util.List)}.
* @param uid UID of the application affected.
- * @return {@link AudioManager#SUCCESS} if the change was successful, {@link AudioManager#ERROR}
- * otherwise.
+ * @return true if the change was successful, false otherwise.
*/
@SystemApi
- public int removeUidDeviceAffinity(int uid) {
+ public boolean removeUidDeviceAffinity(int uid) {
synchronized (mLock) {
if (mStatus != POLICY_STATUS_REGISTERED) {
throw new IllegalStateException("Cannot use unregistered AudioPolicy");
@@ -418,10 +416,10 @@
final IAudioService service = getService();
try {
final int status = service.removeUidDeviceAffinity(this.cb(), uid);
- return status;
+ return (status == AudioManager.SUCCESS);
} catch (RemoteException e) {
Log.e(TAG, "Dead object in removeUidDeviceAffinity", e);
- return AudioManager.ERROR;
+ return false;
}
}
}
@@ -682,7 +680,6 @@
*
*/
public static abstract class AudioPolicyVolumeCallback {
- /** @hide */
public AudioPolicyVolumeCallback() {}
/**
* Called when volume key-related changes are triggered, on the key down event.
diff --git a/media/java/android/media/audiopolicy/AudioProductStrategies.java b/media/java/android/media/audiopolicy/AudioProductStrategies.java
index b836409..6a2375f 100644
--- a/media/java/android/media/audiopolicy/AudioProductStrategies.java
+++ b/media/java/android/media/audiopolicy/AudioProductStrategies.java
@@ -144,8 +144,10 @@
* @hide
* @param aa the {@link AudioAttributes} for which stream type is requested
* @return the legacy stream type relevant for the given {@link AudioAttributes}.
- * If the product strategy is not associated to any stream, it returns STREAM_MUSIC.
- * If no product strategy supports the stream type, it returns STREAM_MUSIC.
+ * If the product strategy is not associated to any stream, it returns
+ * {@link AudioSystem#STREAM_MUSIC}.
+ * If no product strategy supports the stream type, it returns
+ * {@link AudioSystem#STREAM_MUSIC}.
*/
@SystemApi
public int getLegacyStreamTypeForAudioAttributes(@NonNull AudioAttributes aa) {
@@ -165,6 +167,19 @@
return AudioSystem.STREAM_MUSIC;
}
+ /**
+ * @hide
+ * @param aa the {@link AudioAttributes} to be considered
+ * @return {@link AudioProductStrategy} supporting the given {@link AudioAttributes}.
+ * null is returned if no match with given attributes.
+ */
+ @SystemApi
+ @Nullable
+ public AudioProductStrategy getProductStrategyForAudioAttributes(@NonNull AudioAttributes aa) {
+ Preconditions.checkNotNull(aa, "attributes must not be null");
+ return getById(native_get_product_strategies_from_audio_attributes(aa));
+ }
+
@Override
public int describeContents() {
return 0;
@@ -198,4 +213,7 @@
private static native int native_list_audio_product_strategies(
ArrayList<AudioProductStrategy> strategies);
+
+ private static native int native_get_product_strategies_from_audio_attributes(
+ AudioAttributes attributes);
}
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 8ed265d..65b58ab 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -20,8 +20,8 @@
"android_media_MediaScanner.cpp",
"android_media_MediaSync.cpp",
"android_media_ResampleInputStream.cpp",
+ "android_media_Streams.cpp",
"android_media_SyncParams.cpp",
- "android_media_Utils.cpp",
"android_mtp_MtpDatabase.cpp",
"android_mtp_MtpDevice.cpp",
"android_mtp_MtpServer.cpp",
@@ -34,6 +34,7 @@
"libutils",
"libbinder",
"libmedia",
+ "libmedia_jni_utils",
"libmedia_omx",
"libmediametrics",
"libmediadrm",
@@ -85,6 +86,36 @@
}
cc_library_shared {
+ name: "libmedia_jni_utils",
+ srcs: [
+ "android_media_Utils.cpp",
+ ],
+
+ shared_libs: [
+ "liblog",
+ "libmedia_omx",
+ "libnativewindow",
+ "libui",
+ "libutils",
+ "android.hidl.token@1.0-utils",
+ ],
+
+ include_dirs: [
+ "system/media/camera/include",
+ ],
+
+ export_include_dirs: ["."],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-error=deprecated-declarations",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+}
+
+cc_library_shared {
name: "libmedia2_jni",
srcs: [
@@ -125,7 +156,6 @@
"libcrypto",
"libcutils",
"libjsoncpp",
- "libmedia_helper",
"libmedia_player2_util",
"libmediaplayer2",
"libmediaplayer2-protos",
@@ -133,7 +163,6 @@
"libmediautils",
"libprocessgroup",
"libprotobuf-cpp-lite",
- "libstagefright",
"libstagefright_esds",
"libstagefright_foundation_without_imemory",
"libstagefright_httplive",
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index f07f1e8..150b6f9 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -23,7 +23,7 @@
#include "android_media_MediaCrypto.h"
#include "android_media_MediaDescrambler.h"
#include "android_media_MediaMetricsJNI.h"
-#include "android_media_Utils.h"
+#include "android_media_Streams.h"
#include "android_runtime/AndroidRuntime.h"
#include "android_runtime/android_view_Surface.h"
#include "android_util_Binder.h"
diff --git a/media/jni/android_media_MediaCodecList.cpp b/media/jni/android_media_MediaCodecList.cpp
index 6b8f745..923d1d2 100644
--- a/media/jni/android_media_MediaCodecList.cpp
+++ b/media/jni/android_media_MediaCodecList.cpp
@@ -31,7 +31,7 @@
#include "android_runtime/AndroidRuntime.h"
#include "jni.h"
#include <nativehelper/JNIHelp.h>
-#include "android_media_Utils.h"
+#include "android_media_Streams.h"
using namespace android;
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index c6b171b..f5ae9d0 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -22,7 +22,7 @@
#include "android_media_MediaDataSource.h"
#include "android_media_MediaExtractor.h"
#include "android_media_MediaMetricsJNI.h"
-#include "android_media_Utils.h"
+#include "android_media_Streams.h"
#include "android_os_HwRemoteBinder.h"
#include "android_runtime/AndroidRuntime.h"
#include "android_runtime/Log.h"
diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp
index c1226fa..a480784 100644
--- a/media/jni/android_media_MediaMetadataRetriever.cpp
+++ b/media/jni/android_media_MediaMetadataRetriever.cpp
@@ -18,6 +18,7 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "MediaMetadataRetrieverJNI"
+#include <cmath>
#include <assert.h>
#include <utils/Log.h>
#include <utils/threads.h>
@@ -32,7 +33,7 @@
#include <nativehelper/JNIHelp.h>
#include "android_runtime/AndroidRuntime.h"
#include "android_media_MediaDataSource.h"
-#include "android_media_Utils.h"
+#include "android_media_Streams.h"
#include "android_util_Binder.h"
#include "android/graphics/GraphicsJNI.h"
diff --git a/media/jni/android_media_MediaMuxer.cpp b/media/jni/android_media_MediaMuxer.cpp
index f11452a..f0aa4c3 100644
--- a/media/jni/android_media_MediaMuxer.cpp
+++ b/media/jni/android_media_MediaMuxer.cpp
@@ -18,7 +18,7 @@
#define LOG_TAG "MediaMuxer-JNI"
#include <utils/Log.h>
-#include "android_media_Utils.h"
+#include "android_media_Streams.h"
#include "android_runtime/AndroidRuntime.h"
#include "jni.h"
#include <nativehelper/JNIHelp.h>
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 35b1081..d24edc7 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -44,7 +44,7 @@
#include "android_media_PlaybackParams.h"
#include "android_media_SyncParams.h"
#include "android_media_VolumeShaper.h"
-#include "android_media_Utils.h"
+#include "android_media_Streams.h"
#include "android_os_Parcel.h"
#include "android_util_Binder.h"
diff --git a/media/jni/android_media_Streams.cpp b/media/jni/android_media_Streams.cpp
new file mode 100644
index 0000000..b7cbd97
--- /dev/null
+++ b/media/jni/android_media_Streams.cpp
@@ -0,0 +1,559 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "AndroidMediaStreams"
+
+#include <utils/Log.h>
+#include "android_media_Streams.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+#include <nativehelper/ScopedLocalRef.h>
+
+namespace android {
+
+AssetStream::AssetStream(SkStream* stream)
+ : mStream(stream), mPosition(0) {
+}
+
+AssetStream::~AssetStream() {
+}
+
+piex::Error AssetStream::GetData(
+ const size_t offset, const size_t length, std::uint8_t* data) {
+ // Seek first.
+ if (mPosition != offset) {
+ if (!mStream->seek(offset)) {
+ return piex::Error::kFail;
+ }
+ }
+
+ // Read bytes.
+ size_t size = mStream->read((void*)data, length);
+ mPosition = offset + size;
+
+ return size == length ? piex::Error::kOk : piex::Error::kFail;
+}
+
+BufferedStream::BufferedStream(SkStream* stream)
+ : mStream(stream) {
+}
+
+BufferedStream::~BufferedStream() {
+}
+
+piex::Error BufferedStream::GetData(
+ const size_t offset, const size_t length, std::uint8_t* data) {
+ // Seek first.
+ if (offset + length > mStreamBuffer.bytesWritten()) {
+ size_t sizeToRead = offset + length - mStreamBuffer.bytesWritten();
+ if (sizeToRead <= kMinSizeToRead) {
+ sizeToRead = kMinSizeToRead;
+ }
+
+ void* tempBuffer = malloc(sizeToRead);
+ if (tempBuffer == NULL) {
+ return piex::Error::kFail;
+ }
+
+ size_t bytesRead = mStream->read(tempBuffer, sizeToRead);
+ if (bytesRead != sizeToRead) {
+ free(tempBuffer);
+ return piex::Error::kFail;
+ }
+ mStreamBuffer.write(tempBuffer, bytesRead);
+ free(tempBuffer);
+ }
+
+ // Read bytes.
+ if (mStreamBuffer.read((void*)data, offset, length)) {
+ return piex::Error::kOk;
+ } else {
+ return piex::Error::kFail;
+ }
+}
+
+FileStream::FileStream(const int fd)
+ : mPosition(0) {
+ mFile = fdopen(fd, "r");
+ if (mFile == NULL) {
+ return;
+ }
+}
+
+FileStream::FileStream(const String8 filename)
+ : mPosition(0) {
+ mFile = fopen(filename.string(), "r");
+ if (mFile == NULL) {
+ return;
+ }
+}
+
+FileStream::~FileStream() {
+ if (mFile != NULL) {
+ fclose(mFile);
+ mFile = NULL;
+ }
+}
+
+piex::Error FileStream::GetData(
+ const size_t offset, const size_t length, std::uint8_t* data) {
+ if (mFile == NULL) {
+ return piex::Error::kFail;
+ }
+
+ // Seek first.
+ if (mPosition != offset) {
+ fseek(mFile, offset, SEEK_SET);
+ }
+
+ // Read bytes.
+ size_t size = fread((void*)data, sizeof(std::uint8_t), length, mFile);
+ mPosition += size;
+
+ // Handle errors and verify the size.
+ if (ferror(mFile) || size != length) {
+ ALOGV("GetData read failed: (offset: %zu, length: %zu)", offset, length);
+ return piex::Error::kFail;
+ }
+ return piex::Error::kOk;
+}
+
+bool FileStream::exists() const {
+ return mFile != NULL;
+}
+
+bool GetExifFromRawImage(
+ piex::StreamInterface* stream, const String8& filename,
+ piex::PreviewImageData& image_data) {
+ // Reset the PreviewImageData to its default.
+ image_data = piex::PreviewImageData();
+
+ if (!piex::IsRaw(stream)) {
+ // Format not supported.
+ ALOGV("Format not supported: %s", filename.string());
+ return false;
+ }
+
+ piex::Error err = piex::GetPreviewImageData(stream, &image_data);
+
+ if (err != piex::Error::kOk) {
+ // The input data seems to be broken.
+ ALOGV("Raw image not detected: %s (piex error code: %d)", filename.string(), (int32_t)err);
+ return false;
+ }
+
+ return true;
+}
+
+bool ConvertKeyValueArraysToKeyedVector(
+ JNIEnv *env, jobjectArray keys, jobjectArray values,
+ KeyedVector<String8, String8>* keyedVector) {
+
+ int nKeyValuePairs = 0;
+ bool failed = false;
+ if (keys != NULL && values != NULL) {
+ nKeyValuePairs = env->GetArrayLength(keys);
+ failed = (nKeyValuePairs != env->GetArrayLength(values));
+ }
+
+ if (!failed) {
+ failed = ((keys != NULL && values == NULL) ||
+ (keys == NULL && values != NULL));
+ }
+
+ if (failed) {
+ ALOGE("keys and values arrays have different length");
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+ return false;
+ }
+
+ for (int i = 0; i < nKeyValuePairs; ++i) {
+ // No need to check on the ArrayIndexOutOfBoundsException, since
+ // it won't happen here.
+ jstring key = (jstring) env->GetObjectArrayElement(keys, i);
+ jstring value = (jstring) env->GetObjectArrayElement(values, i);
+
+ const char* keyStr = env->GetStringUTFChars(key, NULL);
+ if (!keyStr) { // OutOfMemoryError
+ return false;
+ }
+
+ const char* valueStr = env->GetStringUTFChars(value, NULL);
+ if (!valueStr) { // OutOfMemoryError
+ env->ReleaseStringUTFChars(key, keyStr);
+ return false;
+ }
+
+ keyedVector->add(String8(keyStr), String8(valueStr));
+
+ env->ReleaseStringUTFChars(key, keyStr);
+ env->ReleaseStringUTFChars(value, valueStr);
+ env->DeleteLocalRef(key);
+ env->DeleteLocalRef(value);
+ }
+ return true;
+}
+
+static jobject makeIntegerObject(JNIEnv *env, int32_t value) {
+ ScopedLocalRef<jclass> clazz(env, env->FindClass("java/lang/Integer"));
+ CHECK(clazz.get() != NULL);
+
+ jmethodID integerConstructID =
+ env->GetMethodID(clazz.get(), "<init>", "(I)V");
+ CHECK(integerConstructID != NULL);
+
+ return env->NewObject(clazz.get(), integerConstructID, value);
+}
+
+static jobject makeLongObject(JNIEnv *env, int64_t value) {
+ ScopedLocalRef<jclass> clazz(env, env->FindClass("java/lang/Long"));
+ CHECK(clazz.get() != NULL);
+
+ jmethodID longConstructID = env->GetMethodID(clazz.get(), "<init>", "(J)V");
+ CHECK(longConstructID != NULL);
+
+ return env->NewObject(clazz.get(), longConstructID, value);
+}
+
+static jobject makeFloatObject(JNIEnv *env, float value) {
+ ScopedLocalRef<jclass> clazz(env, env->FindClass("java/lang/Float"));
+ CHECK(clazz.get() != NULL);
+
+ jmethodID floatConstructID =
+ env->GetMethodID(clazz.get(), "<init>", "(F)V");
+ CHECK(floatConstructID != NULL);
+
+ return env->NewObject(clazz.get(), floatConstructID, value);
+}
+
+static jobject makeByteBufferObject(
+ JNIEnv *env, const void *data, size_t size) {
+ jbyteArray byteArrayObj = env->NewByteArray(size);
+ env->SetByteArrayRegion(byteArrayObj, 0, size, (const jbyte *)data);
+
+ ScopedLocalRef<jclass> clazz(env, env->FindClass("java/nio/ByteBuffer"));
+ CHECK(clazz.get() != NULL);
+
+ jmethodID byteBufWrapID =
+ env->GetStaticMethodID(
+ clazz.get(), "wrap", "([B)Ljava/nio/ByteBuffer;");
+ CHECK(byteBufWrapID != NULL);
+
+ jobject byteBufObj = env->CallStaticObjectMethod(
+ clazz.get(), byteBufWrapID, byteArrayObj);
+
+ env->DeleteLocalRef(byteArrayObj); byteArrayObj = NULL;
+
+ return byteBufObj;
+}
+
+static void SetMapInt32(
+ JNIEnv *env, jobject hashMapObj, jmethodID hashMapPutID,
+ const char *key, int32_t value) {
+ jstring keyObj = env->NewStringUTF(key);
+ jobject valueObj = makeIntegerObject(env, value);
+
+ env->CallObjectMethod(hashMapObj, hashMapPutID, keyObj, valueObj);
+
+ env->DeleteLocalRef(valueObj); valueObj = NULL;
+ env->DeleteLocalRef(keyObj); keyObj = NULL;
+}
+
+status_t ConvertMessageToMap(
+ JNIEnv *env, const sp<AMessage> &msg, jobject *map) {
+ ScopedLocalRef<jclass> hashMapClazz(
+ env, env->FindClass("java/util/HashMap"));
+
+ if (hashMapClazz.get() == NULL) {
+ return -EINVAL;
+ }
+
+ jmethodID hashMapConstructID =
+ env->GetMethodID(hashMapClazz.get(), "<init>", "()V");
+
+ if (hashMapConstructID == NULL) {
+ return -EINVAL;
+ }
+
+ jmethodID hashMapPutID =
+ env->GetMethodID(
+ hashMapClazz.get(),
+ "put",
+ "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+
+ if (hashMapPutID == NULL) {
+ return -EINVAL;
+ }
+
+ jobject hashMap = env->NewObject(hashMapClazz.get(), hashMapConstructID);
+
+ for (size_t i = 0; i < msg->countEntries(); ++i) {
+ AMessage::Type valueType;
+ const char *key = msg->getEntryNameAt(i, &valueType);
+
+ if (!strncmp(key, "android._", 9)) {
+ // don't expose private keys (starting with android._)
+ continue;
+ }
+
+ jobject valueObj = NULL;
+
+ switch (valueType) {
+ case AMessage::kTypeInt32:
+ {
+ int32_t val;
+ CHECK(msg->findInt32(key, &val));
+
+ valueObj = makeIntegerObject(env, val);
+ break;
+ }
+
+ case AMessage::kTypeInt64:
+ {
+ int64_t val;
+ CHECK(msg->findInt64(key, &val));
+
+ valueObj = makeLongObject(env, val);
+ break;
+ }
+
+ case AMessage::kTypeFloat:
+ {
+ float val;
+ CHECK(msg->findFloat(key, &val));
+
+ valueObj = makeFloatObject(env, val);
+ break;
+ }
+
+ case AMessage::kTypeString:
+ {
+ AString val;
+ CHECK(msg->findString(key, &val));
+
+ valueObj = env->NewStringUTF(val.c_str());
+ break;
+ }
+
+ case AMessage::kTypeBuffer:
+ {
+ sp<ABuffer> buffer;
+ CHECK(msg->findBuffer(key, &buffer));
+
+ valueObj = makeByteBufferObject(
+ env, buffer->data(), buffer->size());
+ break;
+ }
+
+ case AMessage::kTypeRect:
+ {
+ int32_t left, top, right, bottom;
+ CHECK(msg->findRect(key, &left, &top, &right, &bottom));
+
+ SetMapInt32(
+ env,
+ hashMap,
+ hashMapPutID,
+ AStringPrintf("%s-left", key).c_str(),
+ left);
+
+ SetMapInt32(
+ env,
+ hashMap,
+ hashMapPutID,
+ AStringPrintf("%s-top", key).c_str(),
+ top);
+
+ SetMapInt32(
+ env,
+ hashMap,
+ hashMapPutID,
+ AStringPrintf("%s-right", key).c_str(),
+ right);
+
+ SetMapInt32(
+ env,
+ hashMap,
+ hashMapPutID,
+ AStringPrintf("%s-bottom", key).c_str(),
+ bottom);
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ if (valueObj != NULL) {
+ jstring keyObj = env->NewStringUTF(key);
+
+ env->CallObjectMethod(hashMap, hashMapPutID, keyObj, valueObj);
+
+ env->DeleteLocalRef(keyObj); keyObj = NULL;
+ env->DeleteLocalRef(valueObj); valueObj = NULL;
+ }
+ }
+
+ *map = hashMap;
+
+ return OK;
+}
+
+status_t ConvertKeyValueArraysToMessage(
+ JNIEnv *env, jobjectArray keys, jobjectArray values,
+ sp<AMessage> *out) {
+ ScopedLocalRef<jclass> stringClass(env, env->FindClass("java/lang/String"));
+ CHECK(stringClass.get() != NULL);
+ ScopedLocalRef<jclass> integerClass(env, env->FindClass("java/lang/Integer"));
+ CHECK(integerClass.get() != NULL);
+ ScopedLocalRef<jclass> longClass(env, env->FindClass("java/lang/Long"));
+ CHECK(longClass.get() != NULL);
+ ScopedLocalRef<jclass> floatClass(env, env->FindClass("java/lang/Float"));
+ CHECK(floatClass.get() != NULL);
+ ScopedLocalRef<jclass> byteBufClass(env, env->FindClass("java/nio/ByteBuffer"));
+ CHECK(byteBufClass.get() != NULL);
+
+ sp<AMessage> msg = new AMessage;
+
+ jsize numEntries = 0;
+
+ if (keys != NULL) {
+ if (values == NULL) {
+ return -EINVAL;
+ }
+
+ numEntries = env->GetArrayLength(keys);
+
+ if (numEntries != env->GetArrayLength(values)) {
+ return -EINVAL;
+ }
+ } else if (values != NULL) {
+ return -EINVAL;
+ }
+
+ for (jsize i = 0; i < numEntries; ++i) {
+ jobject keyObj = env->GetObjectArrayElement(keys, i);
+
+ if (!env->IsInstanceOf(keyObj, stringClass.get())) {
+ return -EINVAL;
+ }
+
+ const char *tmp = env->GetStringUTFChars((jstring)keyObj, NULL);
+
+ if (tmp == NULL) {
+ return -ENOMEM;
+ }
+
+ AString key = tmp;
+
+ env->ReleaseStringUTFChars((jstring)keyObj, tmp);
+ tmp = NULL;
+
+ if (key.startsWith("android._")) {
+ // don't propagate private keys (starting with android._)
+ continue;
+ }
+
+ jobject valueObj = env->GetObjectArrayElement(values, i);
+
+ if (env->IsInstanceOf(valueObj, stringClass.get())) {
+ const char *value = env->GetStringUTFChars((jstring)valueObj, NULL);
+
+ if (value == NULL) {
+ return -ENOMEM;
+ }
+
+ msg->setString(key.c_str(), value);
+
+ env->ReleaseStringUTFChars((jstring)valueObj, value);
+ value = NULL;
+ } else if (env->IsInstanceOf(valueObj, integerClass.get())) {
+ jmethodID intValueID =
+ env->GetMethodID(integerClass.get(), "intValue", "()I");
+ CHECK(intValueID != NULL);
+
+ jint value = env->CallIntMethod(valueObj, intValueID);
+
+ msg->setInt32(key.c_str(), value);
+ } else if (env->IsInstanceOf(valueObj, longClass.get())) {
+ jmethodID longValueID =
+ env->GetMethodID(longClass.get(), "longValue", "()J");
+ CHECK(longValueID != NULL);
+
+ jlong value = env->CallLongMethod(valueObj, longValueID);
+
+ msg->setInt64(key.c_str(), value);
+ } else if (env->IsInstanceOf(valueObj, floatClass.get())) {
+ jmethodID floatValueID =
+ env->GetMethodID(floatClass.get(), "floatValue", "()F");
+ CHECK(floatValueID != NULL);
+
+ jfloat value = env->CallFloatMethod(valueObj, floatValueID);
+
+ msg->setFloat(key.c_str(), value);
+ } else if (env->IsInstanceOf(valueObj, byteBufClass.get())) {
+ jmethodID positionID =
+ env->GetMethodID(byteBufClass.get(), "position", "()I");
+ CHECK(positionID != NULL);
+
+ jmethodID limitID =
+ env->GetMethodID(byteBufClass.get(), "limit", "()I");
+ CHECK(limitID != NULL);
+
+ jint position = env->CallIntMethod(valueObj, positionID);
+ jint limit = env->CallIntMethod(valueObj, limitID);
+
+ sp<ABuffer> buffer = new ABuffer(limit - position);
+
+ void *data = env->GetDirectBufferAddress(valueObj);
+
+ if (data != NULL) {
+ memcpy(buffer->data(),
+ (const uint8_t *)data + position,
+ buffer->size());
+ } else {
+ jmethodID arrayID =
+ env->GetMethodID(byteBufClass.get(), "array", "()[B");
+ CHECK(arrayID != NULL);
+
+ jbyteArray byteArray =
+ (jbyteArray)env->CallObjectMethod(valueObj, arrayID);
+ CHECK(byteArray != NULL);
+
+ env->GetByteArrayRegion(
+ byteArray,
+ position,
+ buffer->size(),
+ (jbyte *)buffer->data());
+
+ env->DeleteLocalRef(byteArray); byteArray = NULL;
+ }
+
+ msg->setBuffer(key.c_str(), buffer);
+ }
+ }
+
+ *out = msg;
+
+ return OK;
+}
+
+} // namespace android
+
diff --git a/media/jni/android_media_Streams.h b/media/jni/android_media_Streams.h
new file mode 100644
index 0000000..d174f9a
--- /dev/null
+++ b/media/jni/android_media_Streams.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2019, 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.
+ */
+
+#ifndef _ANDROID_MEDIA_STREAMS_H_
+#define _ANDROID_MEDIA_STREAMS_H_
+
+#include "src/piex_types.h"
+#include "src/piex.h"
+
+#include <jni.h>
+#include <nativehelper/JNIHelp.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/StrongPointer.h>
+#include <SkStream.h>
+
+
+namespace android {
+
+class AssetStream : public piex::StreamInterface {
+private:
+ SkStream *mStream;
+ size_t mPosition;
+
+public:
+ explicit AssetStream(SkStream* stream);
+ ~AssetStream();
+
+ // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer
+ // provided by the caller, guaranteed to be at least "length" bytes long.
+ // On 'kOk' the 'data' pointer contains 'length' valid bytes beginning at
+ // 'offset' bytes from the start of the stream.
+ // Returns 'kFail' if 'offset' + 'length' exceeds the stream and does not
+ // change the contents of 'data'.
+ piex::Error GetData(
+ const size_t offset, const size_t length, std::uint8_t* data) override;
+};
+
+class BufferedStream : public piex::StreamInterface {
+private:
+ SkStream *mStream;
+ // Growable memory stream
+ SkDynamicMemoryWStream mStreamBuffer;
+
+ // Minimum size to read on filling the buffer.
+ const size_t kMinSizeToRead = 8192;
+
+public:
+ explicit BufferedStream(SkStream* stream);
+ ~BufferedStream();
+
+ // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer
+ // provided by the caller, guaranteed to be at least "length" bytes long.
+ // On 'kOk' the 'data' pointer contains 'length' valid bytes beginning at
+ // 'offset' bytes from the start of the stream.
+ // Returns 'kFail' if 'offset' + 'length' exceeds the stream and does not
+ // change the contents of 'data'.
+ piex::Error GetData(
+ const size_t offset, const size_t length, std::uint8_t* data) override;
+};
+
+class FileStream : public piex::StreamInterface {
+private:
+ FILE *mFile;
+ size_t mPosition;
+
+public:
+ explicit FileStream(const int fd);
+ explicit FileStream(const String8 filename);
+ ~FileStream();
+
+ // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer
+ // provided by the caller, guaranteed to be at least "length" bytes long.
+ // On 'kOk' the 'data' pointer contains 'length' valid bytes beginning at
+ // 'offset' bytes from the start of the stream.
+ // Returns 'kFail' if 'offset' + 'length' exceeds the stream and does not
+ // change the contents of 'data'.
+ piex::Error GetData(
+ const size_t offset, const size_t length, std::uint8_t* data) override;
+ bool exists() const;
+};
+
+// Reads EXIF metadata from a given raw image via piex.
+// And returns true if the operation is successful; otherwise, false.
+bool GetExifFromRawImage(
+ piex::StreamInterface* stream, const String8& filename, piex::PreviewImageData& image_data);
+
+// Returns true if the conversion is successful; otherwise, false.
+bool ConvertKeyValueArraysToKeyedVector(
+ JNIEnv *env, jobjectArray keys, jobjectArray values,
+ KeyedVector<String8, String8>* vector);
+
+struct AMessage;
+status_t ConvertMessageToMap(
+ JNIEnv *env, const sp<AMessage> &msg, jobject *map);
+
+status_t ConvertKeyValueArraysToMessage(
+ JNIEnv *env, jobjectArray keys, jobjectArray values,
+ sp<AMessage> *msg);
+
+}; // namespace android
+
+#endif // _ANDROID_MEDIA_STREAMS_H_
diff --git a/media/jni/android_media_Utils.cpp b/media/jni/android_media_Utils.cpp
index 01baadb..2ef7b9e 100644
--- a/media/jni/android_media_Utils.cpp
+++ b/media/jni/android_media_Utils.cpp
@@ -21,12 +21,6 @@
#include <utils/Log.h>
#include "android_media_Utils.h"
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/ABuffer.h>
-#include <media/stagefright/foundation/AMessage.h>
-
-#include <nativehelper/ScopedLocalRef.h>
-
#define ALIGN(x, mask) ( ((x) + (mask) - 1) & ~((mask) - 1) )
// Must be in sync with the value in HeicCompositeStream.cpp
@@ -34,533 +28,6 @@
namespace android {
-AssetStream::AssetStream(SkStream* stream)
- : mStream(stream), mPosition(0) {
-}
-
-AssetStream::~AssetStream() {
-}
-
-piex::Error AssetStream::GetData(
- const size_t offset, const size_t length, std::uint8_t* data) {
- // Seek first.
- if (mPosition != offset) {
- if (!mStream->seek(offset)) {
- return piex::Error::kFail;
- }
- }
-
- // Read bytes.
- size_t size = mStream->read((void*)data, length);
- mPosition = offset + size;
-
- return size == length ? piex::Error::kOk : piex::Error::kFail;
-}
-
-BufferedStream::BufferedStream(SkStream* stream)
- : mStream(stream) {
-}
-
-BufferedStream::~BufferedStream() {
-}
-
-piex::Error BufferedStream::GetData(
- const size_t offset, const size_t length, std::uint8_t* data) {
- // Seek first.
- if (offset + length > mStreamBuffer.bytesWritten()) {
- size_t sizeToRead = offset + length - mStreamBuffer.bytesWritten();
- if (sizeToRead <= kMinSizeToRead) {
- sizeToRead = kMinSizeToRead;
- }
-
- void* tempBuffer = malloc(sizeToRead);
- if (tempBuffer == NULL) {
- return piex::Error::kFail;
- }
-
- size_t bytesRead = mStream->read(tempBuffer, sizeToRead);
- if (bytesRead != sizeToRead) {
- free(tempBuffer);
- return piex::Error::kFail;
- }
- mStreamBuffer.write(tempBuffer, bytesRead);
- free(tempBuffer);
- }
-
- // Read bytes.
- if (mStreamBuffer.read((void*)data, offset, length)) {
- return piex::Error::kOk;
- } else {
- return piex::Error::kFail;
- }
-}
-
-FileStream::FileStream(const int fd)
- : mPosition(0) {
- mFile = fdopen(fd, "r");
- if (mFile == NULL) {
- return;
- }
-}
-
-FileStream::FileStream(const String8 filename)
- : mPosition(0) {
- mFile = fopen(filename.string(), "r");
- if (mFile == NULL) {
- return;
- }
-}
-
-FileStream::~FileStream() {
- if (mFile != NULL) {
- fclose(mFile);
- mFile = NULL;
- }
-}
-
-piex::Error FileStream::GetData(
- const size_t offset, const size_t length, std::uint8_t* data) {
- if (mFile == NULL) {
- return piex::Error::kFail;
- }
-
- // Seek first.
- if (mPosition != offset) {
- fseek(mFile, offset, SEEK_SET);
- }
-
- // Read bytes.
- size_t size = fread((void*)data, sizeof(std::uint8_t), length, mFile);
- mPosition += size;
-
- // Handle errors and verify the size.
- if (ferror(mFile) || size != length) {
- ALOGV("GetData read failed: (offset: %zu, length: %zu)", offset, length);
- return piex::Error::kFail;
- }
- return piex::Error::kOk;
-}
-
-bool FileStream::exists() const {
- return mFile != NULL;
-}
-
-bool GetExifFromRawImage(
- piex::StreamInterface* stream, const String8& filename,
- piex::PreviewImageData& image_data) {
- // Reset the PreviewImageData to its default.
- image_data = piex::PreviewImageData();
-
- if (!piex::IsRaw(stream)) {
- // Format not supported.
- ALOGV("Format not supported: %s", filename.string());
- return false;
- }
-
- piex::Error err = piex::GetPreviewImageData(stream, &image_data);
-
- if (err != piex::Error::kOk) {
- // The input data seems to be broken.
- ALOGV("Raw image not detected: %s (piex error code: %d)", filename.string(), (int32_t)err);
- return false;
- }
-
- return true;
-}
-
-bool ConvertKeyValueArraysToKeyedVector(
- JNIEnv *env, jobjectArray keys, jobjectArray values,
- KeyedVector<String8, String8>* keyedVector) {
-
- int nKeyValuePairs = 0;
- bool failed = false;
- if (keys != NULL && values != NULL) {
- nKeyValuePairs = env->GetArrayLength(keys);
- failed = (nKeyValuePairs != env->GetArrayLength(values));
- }
-
- if (!failed) {
- failed = ((keys != NULL && values == NULL) ||
- (keys == NULL && values != NULL));
- }
-
- if (failed) {
- ALOGE("keys and values arrays have different length");
- jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
- return false;
- }
-
- for (int i = 0; i < nKeyValuePairs; ++i) {
- // No need to check on the ArrayIndexOutOfBoundsException, since
- // it won't happen here.
- jstring key = (jstring) env->GetObjectArrayElement(keys, i);
- jstring value = (jstring) env->GetObjectArrayElement(values, i);
-
- const char* keyStr = env->GetStringUTFChars(key, NULL);
- if (!keyStr) { // OutOfMemoryError
- return false;
- }
-
- const char* valueStr = env->GetStringUTFChars(value, NULL);
- if (!valueStr) { // OutOfMemoryError
- env->ReleaseStringUTFChars(key, keyStr);
- return false;
- }
-
- keyedVector->add(String8(keyStr), String8(valueStr));
-
- env->ReleaseStringUTFChars(key, keyStr);
- env->ReleaseStringUTFChars(value, valueStr);
- env->DeleteLocalRef(key);
- env->DeleteLocalRef(value);
- }
- return true;
-}
-
-static jobject makeIntegerObject(JNIEnv *env, int32_t value) {
- ScopedLocalRef<jclass> clazz(env, env->FindClass("java/lang/Integer"));
- CHECK(clazz.get() != NULL);
-
- jmethodID integerConstructID =
- env->GetMethodID(clazz.get(), "<init>", "(I)V");
- CHECK(integerConstructID != NULL);
-
- return env->NewObject(clazz.get(), integerConstructID, value);
-}
-
-static jobject makeLongObject(JNIEnv *env, int64_t value) {
- ScopedLocalRef<jclass> clazz(env, env->FindClass("java/lang/Long"));
- CHECK(clazz.get() != NULL);
-
- jmethodID longConstructID = env->GetMethodID(clazz.get(), "<init>", "(J)V");
- CHECK(longConstructID != NULL);
-
- return env->NewObject(clazz.get(), longConstructID, value);
-}
-
-static jobject makeFloatObject(JNIEnv *env, float value) {
- ScopedLocalRef<jclass> clazz(env, env->FindClass("java/lang/Float"));
- CHECK(clazz.get() != NULL);
-
- jmethodID floatConstructID =
- env->GetMethodID(clazz.get(), "<init>", "(F)V");
- CHECK(floatConstructID != NULL);
-
- return env->NewObject(clazz.get(), floatConstructID, value);
-}
-
-static jobject makeByteBufferObject(
- JNIEnv *env, const void *data, size_t size) {
- jbyteArray byteArrayObj = env->NewByteArray(size);
- env->SetByteArrayRegion(byteArrayObj, 0, size, (const jbyte *)data);
-
- ScopedLocalRef<jclass> clazz(env, env->FindClass("java/nio/ByteBuffer"));
- CHECK(clazz.get() != NULL);
-
- jmethodID byteBufWrapID =
- env->GetStaticMethodID(
- clazz.get(), "wrap", "([B)Ljava/nio/ByteBuffer;");
- CHECK(byteBufWrapID != NULL);
-
- jobject byteBufObj = env->CallStaticObjectMethod(
- clazz.get(), byteBufWrapID, byteArrayObj);
-
- env->DeleteLocalRef(byteArrayObj); byteArrayObj = NULL;
-
- return byteBufObj;
-}
-
-static void SetMapInt32(
- JNIEnv *env, jobject hashMapObj, jmethodID hashMapPutID,
- const char *key, int32_t value) {
- jstring keyObj = env->NewStringUTF(key);
- jobject valueObj = makeIntegerObject(env, value);
-
- env->CallObjectMethod(hashMapObj, hashMapPutID, keyObj, valueObj);
-
- env->DeleteLocalRef(valueObj); valueObj = NULL;
- env->DeleteLocalRef(keyObj); keyObj = NULL;
-}
-
-status_t ConvertMessageToMap(
- JNIEnv *env, const sp<AMessage> &msg, jobject *map) {
- ScopedLocalRef<jclass> hashMapClazz(
- env, env->FindClass("java/util/HashMap"));
-
- if (hashMapClazz.get() == NULL) {
- return -EINVAL;
- }
-
- jmethodID hashMapConstructID =
- env->GetMethodID(hashMapClazz.get(), "<init>", "()V");
-
- if (hashMapConstructID == NULL) {
- return -EINVAL;
- }
-
- jmethodID hashMapPutID =
- env->GetMethodID(
- hashMapClazz.get(),
- "put",
- "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
-
- if (hashMapPutID == NULL) {
- return -EINVAL;
- }
-
- jobject hashMap = env->NewObject(hashMapClazz.get(), hashMapConstructID);
-
- for (size_t i = 0; i < msg->countEntries(); ++i) {
- AMessage::Type valueType;
- const char *key = msg->getEntryNameAt(i, &valueType);
-
- if (!strncmp(key, "android._", 9)) {
- // don't expose private keys (starting with android._)
- continue;
- }
-
- jobject valueObj = NULL;
-
- switch (valueType) {
- case AMessage::kTypeInt32:
- {
- int32_t val;
- CHECK(msg->findInt32(key, &val));
-
- valueObj = makeIntegerObject(env, val);
- break;
- }
-
- case AMessage::kTypeInt64:
- {
- int64_t val;
- CHECK(msg->findInt64(key, &val));
-
- valueObj = makeLongObject(env, val);
- break;
- }
-
- case AMessage::kTypeFloat:
- {
- float val;
- CHECK(msg->findFloat(key, &val));
-
- valueObj = makeFloatObject(env, val);
- break;
- }
-
- case AMessage::kTypeString:
- {
- AString val;
- CHECK(msg->findString(key, &val));
-
- valueObj = env->NewStringUTF(val.c_str());
- break;
- }
-
- case AMessage::kTypeBuffer:
- {
- sp<ABuffer> buffer;
- CHECK(msg->findBuffer(key, &buffer));
-
- valueObj = makeByteBufferObject(
- env, buffer->data(), buffer->size());
- break;
- }
-
- case AMessage::kTypeRect:
- {
- int32_t left, top, right, bottom;
- CHECK(msg->findRect(key, &left, &top, &right, &bottom));
-
- SetMapInt32(
- env,
- hashMap,
- hashMapPutID,
- AStringPrintf("%s-left", key).c_str(),
- left);
-
- SetMapInt32(
- env,
- hashMap,
- hashMapPutID,
- AStringPrintf("%s-top", key).c_str(),
- top);
-
- SetMapInt32(
- env,
- hashMap,
- hashMapPutID,
- AStringPrintf("%s-right", key).c_str(),
- right);
-
- SetMapInt32(
- env,
- hashMap,
- hashMapPutID,
- AStringPrintf("%s-bottom", key).c_str(),
- bottom);
- break;
- }
-
- default:
- break;
- }
-
- if (valueObj != NULL) {
- jstring keyObj = env->NewStringUTF(key);
-
- env->CallObjectMethod(hashMap, hashMapPutID, keyObj, valueObj);
-
- env->DeleteLocalRef(keyObj); keyObj = NULL;
- env->DeleteLocalRef(valueObj); valueObj = NULL;
- }
- }
-
- *map = hashMap;
-
- return OK;
-}
-
-status_t ConvertKeyValueArraysToMessage(
- JNIEnv *env, jobjectArray keys, jobjectArray values,
- sp<AMessage> *out) {
- ScopedLocalRef<jclass> stringClass(env, env->FindClass("java/lang/String"));
- CHECK(stringClass.get() != NULL);
- ScopedLocalRef<jclass> integerClass(env, env->FindClass("java/lang/Integer"));
- CHECK(integerClass.get() != NULL);
- ScopedLocalRef<jclass> longClass(env, env->FindClass("java/lang/Long"));
- CHECK(longClass.get() != NULL);
- ScopedLocalRef<jclass> floatClass(env, env->FindClass("java/lang/Float"));
- CHECK(floatClass.get() != NULL);
- ScopedLocalRef<jclass> byteBufClass(env, env->FindClass("java/nio/ByteBuffer"));
- CHECK(byteBufClass.get() != NULL);
-
- sp<AMessage> msg = new AMessage;
-
- jsize numEntries = 0;
-
- if (keys != NULL) {
- if (values == NULL) {
- return -EINVAL;
- }
-
- numEntries = env->GetArrayLength(keys);
-
- if (numEntries != env->GetArrayLength(values)) {
- return -EINVAL;
- }
- } else if (values != NULL) {
- return -EINVAL;
- }
-
- for (jsize i = 0; i < numEntries; ++i) {
- jobject keyObj = env->GetObjectArrayElement(keys, i);
-
- if (!env->IsInstanceOf(keyObj, stringClass.get())) {
- return -EINVAL;
- }
-
- const char *tmp = env->GetStringUTFChars((jstring)keyObj, NULL);
-
- if (tmp == NULL) {
- return -ENOMEM;
- }
-
- AString key = tmp;
-
- env->ReleaseStringUTFChars((jstring)keyObj, tmp);
- tmp = NULL;
-
- if (key.startsWith("android._")) {
- // don't propagate private keys (starting with android._)
- continue;
- }
-
- jobject valueObj = env->GetObjectArrayElement(values, i);
-
- if (env->IsInstanceOf(valueObj, stringClass.get())) {
- const char *value = env->GetStringUTFChars((jstring)valueObj, NULL);
-
- if (value == NULL) {
- return -ENOMEM;
- }
-
- msg->setString(key.c_str(), value);
-
- env->ReleaseStringUTFChars((jstring)valueObj, value);
- value = NULL;
- } else if (env->IsInstanceOf(valueObj, integerClass.get())) {
- jmethodID intValueID =
- env->GetMethodID(integerClass.get(), "intValue", "()I");
- CHECK(intValueID != NULL);
-
- jint value = env->CallIntMethod(valueObj, intValueID);
-
- msg->setInt32(key.c_str(), value);
- } else if (env->IsInstanceOf(valueObj, longClass.get())) {
- jmethodID longValueID =
- env->GetMethodID(longClass.get(), "longValue", "()J");
- CHECK(longValueID != NULL);
-
- jlong value = env->CallLongMethod(valueObj, longValueID);
-
- msg->setInt64(key.c_str(), value);
- } else if (env->IsInstanceOf(valueObj, floatClass.get())) {
- jmethodID floatValueID =
- env->GetMethodID(floatClass.get(), "floatValue", "()F");
- CHECK(floatValueID != NULL);
-
- jfloat value = env->CallFloatMethod(valueObj, floatValueID);
-
- msg->setFloat(key.c_str(), value);
- } else if (env->IsInstanceOf(valueObj, byteBufClass.get())) {
- jmethodID positionID =
- env->GetMethodID(byteBufClass.get(), "position", "()I");
- CHECK(positionID != NULL);
-
- jmethodID limitID =
- env->GetMethodID(byteBufClass.get(), "limit", "()I");
- CHECK(limitID != NULL);
-
- jint position = env->CallIntMethod(valueObj, positionID);
- jint limit = env->CallIntMethod(valueObj, limitID);
-
- sp<ABuffer> buffer = new ABuffer(limit - position);
-
- void *data = env->GetDirectBufferAddress(valueObj);
-
- if (data != NULL) {
- memcpy(buffer->data(),
- (const uint8_t *)data + position,
- buffer->size());
- } else {
- jmethodID arrayID =
- env->GetMethodID(byteBufClass.get(), "array", "()[B");
- CHECK(arrayID != NULL);
-
- jbyteArray byteArray =
- (jbyteArray)env->CallObjectMethod(valueObj, arrayID);
- CHECK(byteArray != NULL);
-
- env->GetByteArrayRegion(
- byteArray,
- position,
- buffer->size(),
- (jbyte *)buffer->data());
-
- env->DeleteLocalRef(byteArray); byteArray = NULL;
- }
-
- msg->setBuffer(key.c_str(), buffer);
- }
- }
-
- *out = msg;
-
- return OK;
-}
-
// -----------Utility functions used by ImageReader/Writer JNI-----------------
enum {
diff --git a/media/jni/android_media_Utils.h b/media/jni/android_media_Utils.h
index 19c1b88..12841c0 100644
--- a/media/jni/android_media_Utils.h
+++ b/media/jni/android_media_Utils.h
@@ -17,100 +17,10 @@
#ifndef _ANDROID_MEDIA_UTILS_H_
#define _ANDROID_MEDIA_UTILS_H_
-#include "src/piex_types.h"
-#include "src/piex.h"
-
-#include <android_runtime/AndroidRuntime.h>
#include <gui/CpuConsumer.h>
-#include <jni.h>
-#include <nativehelper/JNIHelp.h>
-#include <utils/KeyedVector.h>
-#include <utils/String8.h>
-#include <SkStream.h>
namespace android {
-class AssetStream : public piex::StreamInterface {
-private:
- SkStream *mStream;
- size_t mPosition;
-
-public:
- explicit AssetStream(SkStream* stream);
- ~AssetStream();
-
- // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer
- // provided by the caller, guaranteed to be at least "length" bytes long.
- // On 'kOk' the 'data' pointer contains 'length' valid bytes beginning at
- // 'offset' bytes from the start of the stream.
- // Returns 'kFail' if 'offset' + 'length' exceeds the stream and does not
- // change the contents of 'data'.
- piex::Error GetData(
- const size_t offset, const size_t length, std::uint8_t* data) override;
-};
-
-class BufferedStream : public piex::StreamInterface {
-private:
- SkStream *mStream;
- // Growable memory stream
- SkDynamicMemoryWStream mStreamBuffer;
-
- // Minimum size to read on filling the buffer.
- const size_t kMinSizeToRead = 8192;
-
-public:
- explicit BufferedStream(SkStream* stream);
- ~BufferedStream();
-
- // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer
- // provided by the caller, guaranteed to be at least "length" bytes long.
- // On 'kOk' the 'data' pointer contains 'length' valid bytes beginning at
- // 'offset' bytes from the start of the stream.
- // Returns 'kFail' if 'offset' + 'length' exceeds the stream and does not
- // change the contents of 'data'.
- piex::Error GetData(
- const size_t offset, const size_t length, std::uint8_t* data) override;
-};
-
-class FileStream : public piex::StreamInterface {
-private:
- FILE *mFile;
- size_t mPosition;
-
-public:
- explicit FileStream(const int fd);
- explicit FileStream(const String8 filename);
- ~FileStream();
-
- // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer
- // provided by the caller, guaranteed to be at least "length" bytes long.
- // On 'kOk' the 'data' pointer contains 'length' valid bytes beginning at
- // 'offset' bytes from the start of the stream.
- // Returns 'kFail' if 'offset' + 'length' exceeds the stream and does not
- // change the contents of 'data'.
- piex::Error GetData(
- const size_t offset, const size_t length, std::uint8_t* data) override;
- bool exists() const;
-};
-
-// Reads EXIF metadata from a given raw image via piex.
-// And returns true if the operation is successful; otherwise, false.
-bool GetExifFromRawImage(
- piex::StreamInterface* stream, const String8& filename, piex::PreviewImageData& image_data);
-
-// Returns true if the conversion is successful; otherwise, false.
-bool ConvertKeyValueArraysToKeyedVector(
- JNIEnv *env, jobjectArray keys, jobjectArray values,
- KeyedVector<String8, String8>* vector);
-
-struct AMessage;
-status_t ConvertMessageToMap(
- JNIEnv *env, const sp<AMessage> &msg, jobject *map);
-
-status_t ConvertKeyValueArraysToMessage(
- JNIEnv *env, jobjectArray keys, jobjectArray values,
- sp<AMessage> *msg);
-
// -----------Utility functions used by ImageReader/Writer JNI-----------------
typedef CpuConsumer::LockedBuffer LockedImage;
diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp
index 06a7182..1f89d86 100644
--- a/media/jni/android_mtp_MtpDatabase.cpp
+++ b/media/jni/android_mtp_MtpDatabase.cpp
@@ -18,7 +18,7 @@
#include "utils/Log.h"
#include "utils/String8.h"
-#include "android_media_Utils.h"
+#include "android_media_Streams.h"
#include "mtp.h"
#include "IMtpDatabase.h"
#include "MtpDataPacket.h"
diff --git a/native/android/choreographer.cpp b/native/android/choreographer.cpp
index 2db575b..3fecd53 100644
--- a/native/android/choreographer.cpp
+++ b/native/android/choreographer.cpp
@@ -70,6 +70,8 @@
void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) override;
void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
+ void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
+ int32_t configId) override;
void scheduleCallbacks();
@@ -164,6 +166,13 @@
this, displayId, toString(connected));
}
+void Choreographer::dispatchConfigChanged(nsecs_t, PhysicalDisplayId displayId,
+ int32_t configId) {
+ ALOGV("choreographer %p ~ received config changed event (displayId=%"
+ ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", configId=%s), ignoring.",
+ this, displayId, toString(configId));
+}
+
void Choreographer::handleMessage(const Message& message) {
switch (message.what) {
case MSG_SCHEDULE_CALLBACKS:
diff --git a/packages/NetworkStack/Android.bp b/packages/NetworkStack/Android.bp
index b700bf3..5f1f26d 100644
--- a/packages/NetworkStack/Android.bp
+++ b/packages/NetworkStack/Android.bp
@@ -28,6 +28,7 @@
static_libs: [
"netd_aidl_interface-java",
"networkstack-aidl-interfaces-java",
+ "datastallprotosnano",
]
}
@@ -43,4 +44,4 @@
jarjar_rules: "jarjar-rules-shared.txt",
manifest: "AndroidManifest.xml",
required: ["NetworkStackPermissionStub"],
-}
\ No newline at end of file
+}
diff --git a/packages/NetworkStack/src/android/net/metrics/DataStallDetectionStats.java b/packages/NetworkStack/src/android/net/metrics/DataStallDetectionStats.java
new file mode 100644
index 0000000..225dc0f
--- /dev/null
+++ b/packages/NetworkStack/src/android/net/metrics/DataStallDetectionStats.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2019 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.metrics;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.util.NetworkStackUtils;
+import android.net.wifi.WifiInfo;
+
+import com.android.internal.util.HexDump;
+import com.android.server.connectivity.nano.CellularData;
+import com.android.server.connectivity.nano.DataStallEventProto;
+import com.android.server.connectivity.nano.DnsEvent;
+import com.android.server.connectivity.nano.WifiData;
+
+import com.google.protobuf.nano.MessageNano;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Class to record the stats of detection level information for data stall.
+ *
+ * @hide
+ */
+public final class DataStallDetectionStats {
+ private static final int UNKNOWN_SIGNAL_STRENGTH = -1;
+ @NonNull
+ final byte[] mCellularInfo;
+ @NonNull
+ final byte[] mWifiInfo;
+ @NonNull
+ final byte[] mDns;
+ final int mEvaluationType;
+ final int mNetworkType;
+
+ public DataStallDetectionStats(@Nullable byte[] cell, @Nullable byte[] wifi,
+ @NonNull int[] returnCode, @NonNull long[] dnsTime, int evalType, int netType) {
+ mCellularInfo = emptyCellDataIfNull(cell);
+ mWifiInfo = emptyWifiInfoIfNull(wifi);
+
+ DnsEvent dns = new DnsEvent();
+ dns.dnsReturnCode = returnCode;
+ dns.dnsTime = dnsTime;
+ mDns = MessageNano.toByteArray(dns);
+ mEvaluationType = evalType;
+ mNetworkType = netType;
+ }
+
+ private byte[] emptyCellDataIfNull(@Nullable byte[] cell) {
+ if (cell != null) return cell;
+
+ CellularData data = new CellularData();
+ data.ratType = DataStallEventProto.RADIO_TECHNOLOGY_UNKNOWN;
+ data.networkMccmnc = "";
+ data.simMccmnc = "";
+ data.signalStrength = UNKNOWN_SIGNAL_STRENGTH;
+ return MessageNano.toByteArray(data);
+ }
+
+ private byte[] emptyWifiInfoIfNull(@Nullable byte[] wifi) {
+ if (wifi != null) return wifi;
+
+ WifiData data = new WifiData();
+ data.wifiBand = DataStallEventProto.AP_BAND_UNKNOWN;
+ data.signalStrength = UNKNOWN_SIGNAL_STRENGTH;
+ return MessageNano.toByteArray(data);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("type: ").append(mNetworkType)
+ .append(", evaluation type: ")
+ .append(mEvaluationType)
+ .append(", wifi info: ")
+ .append(HexDump.toHexString(mWifiInfo))
+ .append(", cell info: ")
+ .append(HexDump.toHexString(mCellularInfo))
+ .append(", dns: ")
+ .append(HexDump.toHexString(mDns));
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(@Nullable final Object o) {
+ if (!(o instanceof DataStallDetectionStats)) return false;
+ final DataStallDetectionStats other = (DataStallDetectionStats) o;
+ return (mNetworkType == other.mNetworkType)
+ && (mEvaluationType == other.mEvaluationType)
+ && Arrays.equals(mWifiInfo, other.mWifiInfo)
+ && Arrays.equals(mCellularInfo, other.mCellularInfo)
+ && Arrays.equals(mDns, other.mDns);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mNetworkType, mEvaluationType, mWifiInfo, mCellularInfo, mDns);
+ }
+
+ /**
+ * Utility to create an instance of {@Link DataStallDetectionStats}
+ *
+ * @hide
+ */
+ public static class Builder {
+ @Nullable
+ private byte[] mCellularInfo;
+ @Nullable
+ private byte[] mWifiInfo;
+ @NonNull
+ private final List<Integer> mDnsReturnCode = new ArrayList<Integer>();
+ @NonNull
+ private final List<Long> mDnsTimeStamp = new ArrayList<Long>();
+ private int mEvaluationType;
+ private int mNetworkType;
+
+ /**
+ * Add a dns event into Builder.
+ *
+ * @param code the return code of the dns event.
+ * @param timeMs the elapsedRealtime in ms that the the dns event was received from netd.
+ * @return {@code this} {@link Builder} instance.
+ */
+ public Builder addDnsEvent(int code, long timeMs) {
+ mDnsReturnCode.add(code);
+ mDnsTimeStamp.add(timeMs);
+ return this;
+ }
+
+ /**
+ * Set the dns evaluation type into Builder.
+ *
+ * @param type the return code of the dns event.
+ * @return {@code this} {@link Builder} instance.
+ */
+ public Builder setEvaluationType(int type) {
+ mEvaluationType = type;
+ return this;
+ }
+
+ /**
+ * Set the network type into Builder.
+ *
+ * @param type the network type of the logged network.
+ * @return {@code this} {@link Builder} instance.
+ */
+ public Builder setNetworkType(int type) {
+ mNetworkType = type;
+ return this;
+ }
+
+ /**
+ * Set the wifi data into Builder.
+ *
+ * @param info a {@link WifiInfo} of the connected wifi network.
+ * @return {@code this} {@link Builder} instance.
+ */
+ public Builder setWiFiData(@Nullable final WifiInfo info) {
+ WifiData data = new WifiData();
+ data.wifiBand = getWifiBand(info);
+ data.signalStrength = (info != null) ? info.getRssi() : UNKNOWN_SIGNAL_STRENGTH;
+ mWifiInfo = MessageNano.toByteArray(data);
+ return this;
+ }
+
+ private static int getWifiBand(@Nullable final WifiInfo info) {
+ if (info == null) return DataStallEventProto.AP_BAND_UNKNOWN;
+
+ int freq = info.getFrequency();
+ // Refer to ScanResult.is5GHz() and ScanResult.is24GHz().
+ if (freq > 4900 && freq < 5900) {
+ return DataStallEventProto.AP_BAND_5GHZ;
+ } else if (freq > 2400 && freq < 2500) {
+ return DataStallEventProto.AP_BAND_2GHZ;
+ } else {
+ return DataStallEventProto.AP_BAND_UNKNOWN;
+ }
+ }
+
+ /**
+ * Set the cellular data into Builder.
+ *
+ * @param radioType the radio technology of the logged cellular network.
+ * @param roaming a boolean indicates if logged cellular network is roaming or not.
+ * @param networkMccmnc the mccmnc of the camped network.
+ * @param simMccmnc the mccmnc of the sim.
+ * @return {@code this} {@link Builder} instance.
+ */
+ public Builder setCellData(int radioType, boolean roaming,
+ @NonNull String networkMccmnc, @NonNull String simMccmnc, int ss) {
+ CellularData data = new CellularData();
+ data.ratType = radioType;
+ data.isRoaming = roaming;
+ data.networkMccmnc = networkMccmnc;
+ data.simMccmnc = simMccmnc;
+ data.signalStrength = ss;
+ mCellularInfo = MessageNano.toByteArray(data);
+ return this;
+ }
+
+ /**
+ * Create a new {@Link DataStallDetectionStats}.
+ */
+ public DataStallDetectionStats build() {
+ return new DataStallDetectionStats(mCellularInfo, mWifiInfo,
+ NetworkStackUtils.convertToIntArray(mDnsReturnCode),
+ NetworkStackUtils.convertToLongArray(mDnsTimeStamp),
+ mEvaluationType, mNetworkType);
+ }
+ }
+}
diff --git a/packages/NetworkStack/src/android/net/metrics/DataStallStatsUtils.java b/packages/NetworkStack/src/android/net/metrics/DataStallStatsUtils.java
new file mode 100644
index 0000000..17a36ad
--- /dev/null
+++ b/packages/NetworkStack/src/android/net/metrics/DataStallStatsUtils.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 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.metrics;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.captiveportal.CaptivePortalProbeResult;
+import android.util.Log;
+
+import com.android.internal.util.HexDump;
+import com.android.server.connectivity.nano.DataStallEventProto;
+
+/**
+ * Collection of utilities for data stall metrics.
+ *
+ * To see if the logs are properly sent to statsd, execute following command.
+ *
+ * $ adb shell cmd stats print-logs
+ * $ adb logcat | grep statsd OR $ adb logcat -b stats
+ *
+ * @hide
+ */
+public class DataStallStatsUtils {
+ private static final String TAG = DataStallStatsUtils.class.getSimpleName();
+ private static final boolean DBG = false;
+
+ private static int probeResultToEnum(@Nullable final CaptivePortalProbeResult result) {
+ if (result == null) return DataStallEventProto.INVALID;
+
+ // TODO: Add partial connectivity support.
+ if (result.isSuccessful()) {
+ return DataStallEventProto.VALID;
+ } else if (result.isPortal()) {
+ return DataStallEventProto.PORTAL;
+ } else {
+ return DataStallEventProto.INVALID;
+ }
+ }
+
+ /**
+ * Write the metric to {@link StatsLog}.
+ */
+ public static void write(@NonNull final DataStallDetectionStats stats,
+ @NonNull final CaptivePortalProbeResult result) {
+ int validationResult = probeResultToEnum(result);
+ if (DBG) {
+ Log.d(TAG, "write: " + stats + " with result: " + validationResult
+ + ", dns: " + HexDump.toHexString(stats.mDns));
+ }
+ // TODO(b/124613085): Send to Statsd once the public StatsLog API is ready.
+ }
+}
diff --git a/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java b/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java
index 98123a5..481dbda 100644
--- a/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java
+++ b/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java
@@ -16,8 +16,11 @@
package android.net.util;
+import android.annotation.NonNull;
+
import java.io.FileDescriptor;
import java.io.IOException;
+import java.util.List;
/**
* Collection of utilities for the network stack.
@@ -40,4 +43,26 @@
} catch (IOException ignored) {
}
}
+
+ /**
+ * Returns an int array from the given Integer list.
+ */
+ public static int[] convertToIntArray(@NonNull List<Integer> list) {
+ int[] array = new int[list.size()];
+ for (int i = 0; i < list.size(); i++) {
+ array[i] = list.get(i);
+ }
+ return array;
+ }
+
+ /**
+ * Returns a long array from the given long list.
+ */
+ public static long[] convertToLongArray(@NonNull List<Long> list) {
+ long[] array = new long[list.size()];
+ for (int i = 0; i < list.size(); i++) {
+ array[i] = list.get(i);
+ }
+ return array;
+ }
}
diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
index 4b846b0..e82a5d7 100644
--- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
+++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
@@ -33,6 +33,7 @@
import static android.net.metrics.ValidationProbeEvent.PROBE_PRIVDNS;
import static android.net.util.NetworkStackUtils.isEmpty;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
@@ -50,6 +51,8 @@
import android.net.Uri;
import android.net.captiveportal.CaptivePortalProbeResult;
import android.net.captiveportal.CaptivePortalProbeSpec;
+import android.net.metrics.DataStallDetectionStats;
+import android.net.metrics.DataStallStatsUtils;
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.NetworkEvent;
import android.net.metrics.ValidationProbeEvent;
@@ -66,8 +69,10 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.telephony.AccessNetworkConstants;
+import android.telephony.CellSignalStrength;
import android.telephony.NetworkRegistrationState;
import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
@@ -126,6 +131,9 @@
private static final int DATA_STALL_EVALUATION_TYPE_DNS = 1;
private static final int DEFAULT_DATA_STALL_EVALUATION_TYPES =
(1 << DATA_STALL_EVALUATION_TYPE_DNS);
+ // Reevaluate it as intending to increase the number. Larger log size may cause statsd
+ // log buffer bust and have stats log lost.
+ private static final int DEFAULT_DNS_LOG_SIZE = 20;
enum EvaluationResult {
VALIDATED(true),
@@ -244,6 +252,7 @@
private final ConnectivityManager mCm;
private final IpConnectivityLog mMetricsLog;
private final Dependencies mDependencies;
+ private final DataStallStatsUtils mDetectionStatsUtils;
// Configuration values for captive portal detection probes.
private final String mCaptivePortalUserAgent;
@@ -302,17 +311,19 @@
private final int mDataStallEvaluationType;
private final DnsStallDetector mDnsStallDetector;
private long mLastProbeTime;
+ // Set to true if data stall is suspected and reset to false after metrics are sent to statsd.
+ private boolean mCollectDataStallMetrics = false;
public NetworkMonitor(Context context, INetworkMonitorCallbacks cb, Network network,
SharedLog validationLog) {
this(context, cb, network, new IpConnectivityLog(), validationLog,
- Dependencies.DEFAULT);
+ Dependencies.DEFAULT, new DataStallStatsUtils());
}
@VisibleForTesting
protected NetworkMonitor(Context context, INetworkMonitorCallbacks cb, Network network,
IpConnectivityLog logger, SharedLog validationLogs,
- Dependencies deps) {
+ Dependencies deps, DataStallStatsUtils detectionStatsUtils) {
// Add suffix indicating which NetworkMonitor we're talking about.
super(TAG + "/" + network.toString());
@@ -325,6 +336,7 @@
mValidationLogs = validationLogs;
mCallback = cb;
mDependencies = deps;
+ mDetectionStatsUtils = detectionStatsUtils;
mNonPrivateDnsBypassNetwork = network;
mNetwork = deps.getPrivateDnsBypassNetwork(network);
mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
@@ -656,6 +668,7 @@
case EVENT_DNS_NOTIFICATION:
mDnsStallDetector.accumulateConsecutiveDnsTimeoutCount(message.arg1);
if (isDataStall()) {
+ mCollectDataStallMetrics = true;
validationLog("Suspecting data stall, reevaluate");
transitionTo(mEvaluatingState);
}
@@ -667,6 +680,66 @@
}
}
+ private void writeDataStallStats(@NonNull final CaptivePortalProbeResult result) {
+ /*
+ * Collect data stall detection level information for each transport type. Collect type
+ * specific information for cellular and wifi only currently. Generate
+ * DataStallDetectionStats for each transport type. E.g., if a network supports both
+ * TRANSPORT_WIFI and TRANSPORT_VPN, two DataStallDetectionStats will be generated.
+ */
+ final int[] transports = mNetworkCapabilities.getTransportTypes();
+
+ for (int i = 0; i < transports.length; i++) {
+ DataStallStatsUtils.write(buildDataStallDetectionStats(transports[i]), result);
+ }
+ mCollectDataStallMetrics = false;
+ }
+
+ @VisibleForTesting
+ protected DataStallDetectionStats buildDataStallDetectionStats(int transport) {
+ final DataStallDetectionStats.Builder stats = new DataStallDetectionStats.Builder();
+ if (VDBG_STALL) log("collectDataStallMetrics: type=" + transport);
+ stats.setEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS);
+ stats.setNetworkType(transport);
+ switch (transport) {
+ case NetworkCapabilities.TRANSPORT_WIFI:
+ // TODO: Update it if status query in dual wifi is supported.
+ final WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
+ stats.setWiFiData(wifiInfo);
+ break;
+ case NetworkCapabilities.TRANSPORT_CELLULAR:
+ final boolean isRoaming = !mNetworkCapabilities.hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
+ final SignalStrength ss = mTelephonyManager.getSignalStrength();
+ // TODO(b/120452078): Support multi-sim.
+ stats.setCellData(
+ mTelephonyManager.getDataNetworkType(),
+ isRoaming,
+ mTelephonyManager.getNetworkOperator(),
+ mTelephonyManager.getSimOperator(),
+ (ss != null)
+ ? ss.getLevel() : CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN);
+ break;
+ default:
+ // No transport type specific information for the other types.
+ break;
+ }
+ addDnsEvents(stats);
+
+ return stats.build();
+ }
+
+ @VisibleForTesting
+ protected void addDnsEvents(@NonNull final DataStallDetectionStats.Builder stats) {
+ final int size = mDnsStallDetector.mResultIndices.size();
+ for (int i = 1; i <= DEFAULT_DNS_LOG_SIZE && i <= size; i++) {
+ final int index = mDnsStallDetector.mResultIndices.indexOf(size - i);
+ stats.addDnsEvent(mDnsStallDetector.mDnsEvents[index].mReturnCode,
+ mDnsStallDetector.mDnsEvents[index].mTimeStamp);
+ }
+ }
+
+
// Being in the MaybeNotifyState State indicates the user may have been notified that sign-in
// is required. This State takes care to clear the notification upon exit from the State.
private class MaybeNotifyState extends State {
@@ -972,6 +1045,11 @@
final CaptivePortalProbeResult probeResult =
(CaptivePortalProbeResult) message.obj;
mLastProbeTime = SystemClock.elapsedRealtime();
+
+ if (mCollectDataStallMetrics) {
+ writeDataStallStats(probeResult);
+ }
+
if (probeResult.isSuccessful()) {
// Transit EvaluatingPrivateDnsState to get to Validated
// state (even if no Private DNS validation required).
@@ -1617,7 +1695,6 @@
*/
@VisibleForTesting
protected class DnsStallDetector {
- private static final int DEFAULT_DNS_LOG_SIZE = 50;
private int mConsecutiveTimeoutCount = 0;
private int mSize;
final DnsResult[] mDnsEvents;
diff --git a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
index 9a16bb7..ddb7030 100644
--- a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
+++ b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
@@ -39,6 +39,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.annotation.NonNull;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.INetworkMonitorCallbacks;
@@ -48,8 +49,11 @@
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.captiveportal.CaptivePortalProbeResult;
+import android.net.metrics.DataStallDetectionStats;
+import android.net.metrics.DataStallStatsUtils;
import android.net.metrics.IpConnectivityLog;
import android.net.util.SharedLog;
+import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.ConditionVariable;
@@ -58,6 +62,7 @@
import android.provider.Settings;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.telephony.CellSignalStrength;
import android.telephony.TelephonyManager;
import android.util.ArrayMap;
@@ -98,6 +103,8 @@
private @Mock NetworkMonitor.Dependencies mDependencies;
private @Mock INetworkMonitorCallbacks mCallbacks;
private @Spy Network mNetwork = new Network(TEST_NETID);
+ private @Mock DataStallStatsUtils mDataStallStatsUtils;
+ private @Mock WifiInfo mWifiInfo;
private static final int TEST_NETID = 4242;
@@ -105,10 +112,12 @@
private static final String TEST_HTTPS_URL = "https://www.google.com/gen_204";
private static final String TEST_FALLBACK_URL = "http://fallback.google.com/gen_204";
private static final String TEST_OTHER_FALLBACK_URL = "http://otherfallback.google.com/gen_204";
+ private static final String TEST_MCCMNC = "123456";
private static final int DATA_STALL_EVALUATION_TYPE_DNS = 1;
private static final int RETURN_CODE_DNS_SUCCESS = 0;
private static final int RETURN_CODE_DNS_TIMEOUT = 255;
+ private static final int DEFAULT_DNS_TIMEOUT_THRESHOLD = 5;
private static final int HANDLER_TIMEOUT_MS = 1000;
@@ -186,9 +195,9 @@
private long mProbeTime = 0;
WrappedNetworkMonitor(Context context, Network network, IpConnectivityLog logger,
- Dependencies deps) {
+ Dependencies deps, DataStallStatsUtils statsUtils) {
super(context, mCallbacks, network, logger,
- new SharedLog("test_nm"), deps);
+ new SharedLog("test_nm"), deps, statsUtils);
}
@Override
@@ -199,11 +208,16 @@
protected void setLastProbeTime(long time) {
mProbeTime = time;
}
+
+ @Override
+ protected void addDnsEvents(@NonNull final DataStallDetectionStats.Builder stats) {
+ generateTimeoutDnsEvent(stats, DEFAULT_DNS_TIMEOUT_THRESHOLD);
+ }
}
private WrappedNetworkMonitor makeMeteredWrappedNetworkMonitor() {
final WrappedNetworkMonitor nm = new WrappedNetworkMonitor(
- mContext, mNetwork, mLogger, mDependencies);
+ mContext, mNetwork, mLogger, mDependencies, mDataStallStatsUtils);
when(mCm.getNetworkCapabilities(any())).thenReturn(METERED_CAPABILITIES);
nm.start();
waitForIdle(nm.getHandler());
@@ -212,7 +226,7 @@
private WrappedNetworkMonitor makeNotMeteredWrappedNetworkMonitor() {
final WrappedNetworkMonitor nm = new WrappedNetworkMonitor(
- mContext, mNetwork, mLogger, mDependencies);
+ mContext, mNetwork, mLogger, mDependencies, mDataStallStatsUtils);
when(mCm.getNetworkCapabilities(any())).thenReturn(NOT_METERED_CAPABILITIES);
nm.start();
waitForIdle(nm.getHandler());
@@ -222,7 +236,7 @@
private NetworkMonitor makeMonitor() {
final NetworkMonitor nm = new NetworkMonitor(
mContext, mCallbacks, mNetwork, mLogger, mValidationLogger,
- mDependencies);
+ mDependencies, mDataStallStatsUtils);
nm.start();
waitForIdle(nm.getHandler());
return nm;
@@ -384,7 +398,7 @@
public void testIsDataStall_EvaluationDnsOnNotMeteredNetwork() {
WrappedNetworkMonitor wrappedMonitor = makeNotMeteredWrappedNetworkMonitor();
wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
- makeDnsTimeoutEvent(wrappedMonitor, 5);
+ makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD);
assertTrue(wrappedMonitor.isDataStall());
}
@@ -395,7 +409,7 @@
assertFalse(wrappedMonitor.isDataStall());
wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
- makeDnsTimeoutEvent(wrappedMonitor, 5);
+ makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD);
assertTrue(wrappedMonitor.isDataStall());
}
@@ -429,7 +443,7 @@
// Test dns events happened in valid dns time threshold.
WrappedNetworkMonitor wrappedMonitor = makeMeteredWrappedNetworkMonitor();
wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
- makeDnsTimeoutEvent(wrappedMonitor, 5);
+ makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD);
assertFalse(wrappedMonitor.isDataStall());
wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
assertTrue(wrappedMonitor.isDataStall());
@@ -438,7 +452,7 @@
setValidDataStallDnsTimeThreshold(0);
wrappedMonitor = makeMeteredWrappedNetworkMonitor();
wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
- makeDnsTimeoutEvent(wrappedMonitor, 5);
+ makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD);
assertFalse(wrappedMonitor.isDataStall());
wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
assertFalse(wrappedMonitor.isDataStall());
@@ -505,6 +519,59 @@
.notifyNetworkTested(NETWORK_TEST_RESULT_VALID, null);
}
+ @Test
+ public void testDataStall_StallSuspectedAndSendMetrics() throws IOException {
+ WrappedNetworkMonitor wrappedMonitor = makeNotMeteredWrappedNetworkMonitor();
+ wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
+ makeDnsTimeoutEvent(wrappedMonitor, 5);
+ assertTrue(wrappedMonitor.isDataStall());
+ verify(mDataStallStatsUtils, times(1)).write(any(), any());
+ }
+
+ @Test
+ public void testDataStall_NoStallSuspectedAndSendMetrics() throws IOException {
+ WrappedNetworkMonitor wrappedMonitor = makeNotMeteredWrappedNetworkMonitor();
+ wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
+ makeDnsTimeoutEvent(wrappedMonitor, 3);
+ assertFalse(wrappedMonitor.isDataStall());
+ verify(mDataStallStatsUtils, never()).write(any(), any());
+ }
+
+ @Test
+ public void testCollectDataStallMetrics() {
+ WrappedNetworkMonitor wrappedMonitor = makeNotMeteredWrappedNetworkMonitor();
+
+ when(mTelephony.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_LTE);
+ when(mTelephony.getNetworkOperator()).thenReturn(TEST_MCCMNC);
+ when(mTelephony.getSimOperator()).thenReturn(TEST_MCCMNC);
+
+ DataStallDetectionStats.Builder stats =
+ new DataStallDetectionStats.Builder()
+ .setEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS)
+ .setNetworkType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .setCellData(TelephonyManager.NETWORK_TYPE_LTE /* radioType */,
+ true /* roaming */,
+ TEST_MCCMNC /* networkMccmnc */,
+ TEST_MCCMNC /* simMccmnc */,
+ CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN /* signalStrength */);
+ generateTimeoutDnsEvent(stats, DEFAULT_DNS_TIMEOUT_THRESHOLD);
+
+ assertEquals(wrappedMonitor.buildDataStallDetectionStats(
+ NetworkCapabilities.TRANSPORT_CELLULAR), stats.build());
+
+ when(mWifi.getConnectionInfo()).thenReturn(mWifiInfo);
+
+ stats = new DataStallDetectionStats.Builder()
+ .setEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS)
+ .setNetworkType(NetworkCapabilities.TRANSPORT_WIFI)
+ .setWiFiData(mWifiInfo);
+ generateTimeoutDnsEvent(stats, DEFAULT_DNS_TIMEOUT_THRESHOLD);
+
+ assertEquals(
+ wrappedMonitor.buildDataStallDetectionStats(NetworkCapabilities.TRANSPORT_WIFI),
+ stats.build());
+ }
+
private void makeDnsTimeoutEvent(WrappedNetworkMonitor wrappedMonitor, int count) {
for (int i = 0; i < count; i++) {
wrappedMonitor.getDnsStallDetector().accumulateConsecutiveDnsTimeoutCount(
@@ -594,5 +661,11 @@
private void setStatus(HttpURLConnection connection, int status) throws IOException {
doReturn(status).when(connection).getResponseCode();
}
+
+ private void generateTimeoutDnsEvent(DataStallDetectionStats.Builder stats, int num) {
+ for (int i = 0; i < num; i++) {
+ stats.addDnsEvent(RETURN_CODE_DNS_TIMEOUT, 123456789 /* timeMs */);
+ }
+ }
}
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index caa928f..730e9e1 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -29,7 +29,7 @@
resource_dirs: ["res"],
- srcs: ["src/**/*.java"],
+ srcs: ["src/**/*.java", "src/**/*.kt"],
min_sdk_version: "21",
diff --git a/packages/SettingsLib/BarChartPreference/res/layout/settings_bar_view.xml b/packages/SettingsLib/BarChartPreference/res/layout/settings_bar_view.xml
index b053317..4bc68b3 100644
--- a/packages/SettingsLib/BarChartPreference/res/layout/settings_bar_view.xml
+++ b/packages/SettingsLib/BarChartPreference/res/layout/settings_bar_view.xml
@@ -25,8 +25,7 @@
<View
android:id="@+id/bar_view"
android:layout_width="8dp"
- android:layout_height="wrap_content"
- android:background="?android:attr/colorAccent"/>
+ android:layout_height="wrap_content"/>
<ImageView
android:id="@+id/icon_view"
diff --git a/packages/SettingsLib/BarChartPreference/res/values/styles.xml b/packages/SettingsLib/BarChartPreference/res/values/styles.xml
index 647d080..4876cb6 100644
--- a/packages/SettingsLib/BarChartPreference/res/values/styles.xml
+++ b/packages/SettingsLib/BarChartPreference/res/values/styles.xml
@@ -18,7 +18,7 @@
<resources>
<style name="BarViewStyle">
<item name="android:layout_width">0dp</item>
- <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_height">226dp</item>
<item name="android:layout_weight">1</item>
<item name="android:layout_marginStart">8dp</item>
<item name="android:layout_marginEnd">8dp</item>
diff --git a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java
index 3b87fca..1003c97 100644
--- a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java
+++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java
@@ -160,8 +160,11 @@
// If the state is loading, we just show a blank view.
if (mIsLoading) {
+ holder.itemView.setVisibility(View.INVISIBLE);
return;
}
+ holder.itemView.setVisibility(View.VISIBLE);
+
// We must show title of bar chart.
bindChartTitleView(holder);
diff --git a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java
index 6bf61ae..3ef0235 100644
--- a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java
+++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java
@@ -89,7 +89,7 @@
private void init() {
LayoutInflater.from(getContext()).inflate(R.layout.settings_bar_view, this);
setOrientation(LinearLayout.VERTICAL);
- setGravity(Gravity.CENTER);
+ setGravity(Gravity.CENTER | Gravity.BOTTOM);
mBarView = findViewById(R.id.bar_view);
mIcon = findViewById(R.id.icon_view);
diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt b/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt
new file mode 100644
index 0000000..337106b
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt
@@ -0,0 +1,404 @@
+/*
+ * Copyright (C) 2019 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.graph
+
+import android.content.Context
+import android.graphics.BlendMode
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.ColorFilter
+import android.graphics.Matrix
+import android.graphics.Paint
+import android.graphics.Path
+import android.graphics.PixelFormat
+import android.graphics.Rect
+import android.graphics.RectF
+import android.graphics.drawable.Drawable
+import android.util.PathParser
+import android.util.TypedValue
+
+import com.android.settingslib.R
+import com.android.settingslib.Utils
+
+/**
+ * A battery meter drawable that respects paths configured in
+ * frameworks/base/core/res/res/values/config.xml to allow for an easily overrideable battery icon
+ */
+open class ThemedBatteryDrawable(private val context: Context, frameColor: Int) : Drawable() {
+
+ // Need to load:
+ // 1. perimeter shape
+ // 2. fill mask (if smaller than perimeter, this would create a fill that
+ // doesn't touch the walls
+ private val perimeterPath = Path()
+ private val scaledPerimeter = Path()
+ // Fill will cover the whole bounding rect of the fillMask, and be masked by the path
+ private val fillMask = Path()
+ private val scaledFill = Path()
+ // Based off of the mask, the fill will interpolate across this space
+ private val fillRect = RectF()
+ // Top of this rect changes based on level, 100% == fillRect
+ private val levelRect = RectF()
+ private val levelPath = Path()
+ // Updates the transform of the paths when our bounds change
+ private val scaleMatrix = Matrix()
+ private val padding = Rect()
+ // The net result of fill + perimeter paths
+ private val unifiedPath = Path()
+
+ // Bolt path (used while charging)
+ private val boltPath = Path()
+ private val scaledBolt = Path()
+
+ // Plus sign (used for power save mode)
+ private val plusPath = Path()
+ private val scaledPlus = Path()
+
+ private var intrinsicHeight: Int
+ private var intrinsicWidth: Int
+
+ // To implement hysteresis, keep track of the need to invert the interior icon of the battery
+ private var invertFillIcon = false
+
+ // Colors can be configured based on battery level (see res/values/arrays.xml)
+ private var colorLevels: IntArray
+
+ private var fillColor: Int = Color.MAGENTA
+ private var backgroundColor: Int = Color.MAGENTA
+ // updated whenever level changes
+ private var levelColor: Int = Color.MAGENTA
+
+ // Dual tone implies that battery level is a clipped overlay over top of the whole shape
+ private var dualTone = false
+
+ private val invalidateRunnable: () -> Unit = {
+ invalidateSelf()
+ }
+
+ open var criticalLevel: Int = 0
+
+ var charging = false
+ set(value) {
+ field = value
+ postInvalidate()
+ }
+
+ var powerSaveEnabled = false
+ set(value) {
+ field = value
+ postInvalidate()
+ }
+
+ private val fillColorStrokePaint: Paint by lazy {
+ val p = Paint(Paint.ANTI_ALIAS_FLAG)
+ p.color = frameColor
+ p.isDither = true
+ p.strokeWidth = 5f
+ p.style = Paint.Style.STROKE
+ p.blendMode = BlendMode.SRC
+ p.strokeMiter = 5f
+ p
+ }
+
+ private val fillColorStrokeProtection: Paint by lazy {
+ val p = Paint(Paint.ANTI_ALIAS_FLAG)
+ p.isDither = true
+ p.strokeWidth = 5f
+ p.style = Paint.Style.STROKE
+ p.blendMode = BlendMode.CLEAR
+ p.strokeMiter = 5f
+ p
+ }
+
+ private val fillPaint: Paint by lazy {
+ val p = Paint(Paint.ANTI_ALIAS_FLAG)
+ p.color = frameColor
+ p.alpha = 255
+ p.isDither = true
+ p.strokeWidth = 0f
+ p.style = Paint.Style.FILL_AND_STROKE
+ p
+ }
+
+ // Only used if dualTone is set to true
+ private val dualToneBackgroundFill: Paint by lazy {
+ val p = Paint(Paint.ANTI_ALIAS_FLAG)
+ p.color = frameColor
+ p.alpha = 255
+ p.isDither = true
+ p.strokeWidth = 0f
+ p.style = Paint.Style.FILL_AND_STROKE
+ p
+ }
+
+ init {
+ val density = context.resources.displayMetrics.density
+ intrinsicHeight = (Companion.HEIGHT * density).toInt()
+ intrinsicWidth = (Companion.WIDTH * density).toInt()
+
+ val res = context.resources
+ val levels = res.obtainTypedArray(R.array.batterymeter_color_levels)
+ val colors = res.obtainTypedArray(R.array.batterymeter_color_values)
+ val N = levels.length()
+ colorLevels = IntArray(2 * N)
+ for (i in 0 until N) {
+ colorLevels[2 * i] = levels.getInt(i, 0)
+ if (colors.getType(i) == TypedValue.TYPE_ATTRIBUTE) {
+ colorLevels[2 * i + 1] = Utils.getColorAttrDefaultColor(context,
+ colors.getThemeAttributeId(i, 0))
+ } else {
+ colorLevels[2 * i + 1] = colors.getColor(i, 0)
+ }
+ }
+ levels.recycle()
+ colors.recycle()
+
+ criticalLevel = context.resources.getInteger(
+ com.android.internal.R.integer.config_criticalBatteryWarningLevel)
+
+ loadPaths()
+ }
+
+ override fun draw(c: Canvas) {
+ unifiedPath.reset()
+ levelPath.reset()
+ levelRect.set(fillRect)
+ val fillFraction = level / 100f
+ val fillTop =
+ if (level >= 95)
+ fillRect.top
+ else
+ fillRect.top + (fillRect.height() * (1 - fillFraction))
+
+ levelRect.top = Math.floor(fillTop.toDouble()).toFloat()
+ levelPath.addRect(levelRect, Path.Direction.CCW)
+
+ // The perimeter should never change
+ unifiedPath.addPath(scaledPerimeter)
+ // IF drawing dual tone, the level is used only to clip the whole drawable path
+ if (!dualTone) {
+ unifiedPath.op(levelPath, Path.Op.UNION)
+ }
+
+ fillPaint.color = levelColor
+
+ // Deal with unifiedPath clipping before it draws
+ if (charging) {
+ // Clip out the bolt shape
+ unifiedPath.op(scaledBolt, Path.Op.DIFFERENCE)
+ if (!invertFillIcon) {
+ c.drawPath(scaledBolt, fillPaint)
+ }
+ } else if (powerSaveEnabled) {
+ // Clip out the plus shape
+ unifiedPath.op(scaledPlus, Path.Op.DIFFERENCE)
+ if (!invertFillIcon) {
+ c.drawPath(scaledPlus, fillPaint)
+ }
+ }
+
+ if (dualTone) {
+ // Dual tone means we draw the shape again, clipped to the charge level
+ c.drawPath(unifiedPath, dualToneBackgroundFill)
+ c.save()
+ c.clipRect(0f,
+ bounds.bottom - bounds.height() * fillFraction,
+ bounds.right.toFloat(),
+ bounds.bottom.toFloat())
+ c.drawPath(unifiedPath, fillPaint)
+ c.restore()
+ } else {
+ // Non dual-tone means we draw the perimeter (with the level fill), and potentially
+ // draw the fill again with a critical color
+ fillPaint.color = fillColor
+ c.drawPath(unifiedPath, fillPaint)
+ fillPaint.color = levelColor
+
+ // Show colorError below this level
+ if (level <= Companion.CRITICAL_LEVEL && !charging) {
+ c.save()
+ c.clipPath(scaledFill)
+ c.drawPath(levelPath, fillPaint)
+ c.restore()
+ }
+ }
+
+ if (charging) {
+ c.clipOutPath(scaledBolt)
+ if (invertFillIcon) {
+ c.drawPath(scaledBolt, fillColorStrokePaint)
+ } else {
+ c.drawPath(scaledBolt, fillColorStrokeProtection)
+ }
+ } else if (powerSaveEnabled) {
+ c.clipOutPath(scaledPlus)
+ if (invertFillIcon) {
+ c.drawPath(scaledPlus, fillColorStrokePaint)
+ } else {
+ c.drawPath(scaledPlus, fillColorStrokeProtection)
+ }
+ }
+ }
+
+ private fun batteryColorForLevel(level: Int): Int {
+ return when {
+ charging || powerSaveEnabled -> fillPaint.color
+ else -> getColorForLevel(level)
+ }
+ }
+
+ private fun getColorForLevel(level: Int): Int {
+ var thresh: Int
+ var color = 0
+ var i = 0
+ while (i < colorLevels.size) {
+ thresh = colorLevels[i]
+ color = colorLevels[i + 1]
+ if (level <= thresh) {
+
+ // Respect tinting for "normal" level
+ return if (i == colorLevels.size - 2) {
+ fillColor
+ } else {
+ color
+ }
+ }
+ i += 2
+ }
+ return color
+ }
+
+ /**
+ * Alpha is unused internally, and should be defined in the colors passed to {@link setColors}.
+ * Further, setting an alpha for a dual tone battery meter doesn't make sense without bounds
+ * defining the minimum background fill alpha. This is because fill + background must be equal
+ * to the net alpha passed in here.
+ */
+ override fun setAlpha(alpha: Int) {
+ }
+
+ override fun setColorFilter(colorFilter: ColorFilter?) {
+ fillPaint.colorFilter = colorFilter
+ fillColorStrokePaint.colorFilter = colorFilter
+ dualToneBackgroundFill.colorFilter = colorFilter
+ }
+
+ /**
+ * Deprecated, but required by Drawable
+ */
+ override fun getOpacity(): Int {
+ return PixelFormat.OPAQUE
+ }
+
+ override fun getIntrinsicHeight(): Int {
+ return intrinsicHeight
+ }
+
+ override fun getIntrinsicWidth(): Int {
+ return intrinsicWidth
+ }
+
+ /**
+ * Set the fill level
+ */
+ public open fun setBatteryLevel(l: Int) {
+ invertFillIcon = if (l >= 67) true else if (l <= 33) false else invertFillIcon
+ level = l
+ levelColor = batteryColorForLevel(level)
+ invalidateSelf()
+ }
+
+ public fun getBatteryLevel(): Int {
+ return level
+ }
+
+ override fun onBoundsChange(bounds: Rect?) {
+ super.onBoundsChange(bounds)
+ updateSize()
+ }
+
+ fun setPadding(left: Int, top: Int, right: Int, bottom: Int) {
+ padding.left = left
+ padding.top = top
+ padding.right = right
+ padding.bottom = bottom
+
+ updateSize()
+ }
+
+ fun setColors(fgColor: Int, bgColor: Int, singleToneColor: Int) {
+ fillColor = if (dualTone) fgColor else singleToneColor
+
+ fillPaint.color = fillColor
+ fillColorStrokePaint.color = fillColor
+
+ backgroundColor = bgColor
+ dualToneBackgroundFill.color = bgColor
+
+ invalidateSelf()
+ }
+
+ private fun postInvalidate() {
+ unscheduleSelf(invalidateRunnable)
+ scheduleSelf(invalidateRunnable, 0)
+ }
+
+ private fun updateSize() {
+ val b = bounds
+ if (b.isEmpty) {
+ scaleMatrix.setScale(1f, 1f)
+ } else {
+ scaleMatrix.setScale((b.right / Companion.WIDTH), (b.bottom / Companion.HEIGHT))
+ }
+
+ perimeterPath.transform(scaleMatrix, scaledPerimeter)
+ fillMask.transform(scaleMatrix, scaledFill)
+ scaledFill.computeBounds(fillRect, true)
+ boltPath.transform(scaleMatrix, scaledBolt)
+ plusPath.transform(scaleMatrix, scaledPlus)
+ }
+
+ private fun loadPaths() {
+ val pathString = context.resources.getString(
+ com.android.internal.R.string.config_batterymeterPerimeterPath)
+ perimeterPath.set(PathParser.createPathFromPathData(pathString))
+ val b = RectF()
+ perimeterPath.computeBounds(b, true)
+
+ val fillMaskString = context.resources.getString(
+ com.android.internal.R.string.config_batterymeterFillMask)
+ fillMask.set(PathParser.createPathFromPathData(fillMaskString))
+ // Set the fill rect so we can calculate the fill properly
+ fillMask.computeBounds(fillRect, true)
+
+ val boltPathString = context.resources.getString(
+ com.android.internal.R.string.config_batterymeterBoltPath)
+ boltPath.set(PathParser.createPathFromPathData(boltPathString))
+
+ val plusPathString = context.resources.getString(
+ com.android.internal.R.string.config_batterymeterPowersavePath)
+ plusPath.set(PathParser.createPathFromPathData(plusPathString))
+
+ dualTone = context.resources.getBoolean(
+ com.android.internal.R.bool.config_batterymeterDualTone)
+ }
+
+ companion object {
+ private const val TAG = "ThemedBatteryDrawable"
+ private const val WIDTH = 12f
+ private const val HEIGHT = 20f
+ private const val CRITICAL_LEVEL = 15
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreference.java b/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreference.java
index 2a12810..a31b71e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreference.java
@@ -59,6 +59,5 @@
setIcon(R.drawable.ic_info_outline_24);
setKey(KEY_FOOTER);
setOrder(ORDER_FOOTER);
- setSelectable(false);
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java
index 1080cf4..3acca2a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java
@@ -305,7 +305,7 @@
}
@Test
- public void onBindViewHolder_loadingStateIsTrue_shouldNotInitAnyView() {
+ public void onBindViewHolder_loadingStateIsTrue_shouldHideAllViews() {
final BarViewInfo viewInfo = new BarViewInfo(mIcon, 30 /* barNumber */, R.string.debug_app);
viewInfo.setClickListener(v -> {
});
@@ -317,8 +317,7 @@
mPreference.onBindViewHolder(mHolder);
- assertThat(TextUtils.isEmpty(mTitleView.getText())).isTrue();
- assertThat(TextUtils.isEmpty(mDetailsView.getText())).isTrue();
+ assertThat(mHolder.itemView.getVisibility()).isEqualTo(View.INVISIBLE);
}
@Test
@@ -334,6 +333,7 @@
mPreference.onBindViewHolder(mHolder);
+ assertThat(mHolder.itemView.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(TextUtils.isEmpty(mTitleView.getText())).isFalse();
assertThat(TextUtils.isEmpty(mDetailsView.getText())).isFalse();
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index a7ad223..ceafbfa 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1379,8 +1379,8 @@
}
}
if (enableOverride) {
- if (Secure.LOCATION_PROVIDERS_ALLOWED.equals(name)) {
- final Setting overridden = getLocationProvidersAllowedSetting(owningUserId);
+ if (Secure.LOCATION_MODE.equals(name)) {
+ final Setting overridden = getLocationModeSetting(owningUserId);
if (overridden != null) {
return overridden;
}
@@ -1475,7 +1475,7 @@
return null;
}
- private Setting getLocationProvidersAllowedSetting(int owningUserId) {
+ private Setting getLocationModeSetting(int owningUserId) {
synchronized (mLock) {
final Setting setting = getGlobalSetting(
Global.LOCATION_GLOBAL_KILL_SWITCH);
@@ -1486,7 +1486,7 @@
final SettingsState settingsState = mSettingsRegistry.getSettingsLocked(
SETTINGS_TYPE_SECURE, owningUserId);
return settingsState.new Setting(
- Secure.LOCATION_PROVIDERS_ALLOWED,
+ Secure.LOCATION_MODE,
"", // value
"", // tag
"", // default value
@@ -1497,7 +1497,7 @@
@Override
public boolean update(String value, boolean setDefault, String packageName,
String tag, boolean forceNonSystemPackage) {
- Slog.wtf(LOG_TAG, "update shoudln't be called on this instance.");
+ Slog.wtf(LOG_TAG, "update shouldn't be called on this instance.");
return false;
}
};
@@ -3115,7 +3115,7 @@
final int key = makeKey(SETTINGS_TYPE_SECURE, userId);
mGenerationRegistry.incrementGeneration(key);
- final Uri uri = getNotificationUriFor(key, Secure.LOCATION_PROVIDERS_ALLOWED);
+ final Uri uri = getNotificationUriFor(key, Secure.LOCATION_MODE);
mHandler.obtainMessage(MyHandler.MSG_NOTIFY_URI_CHANGED,
userId, 0, uri).sendToTarget();
}
diff --git a/packages/SystemUI/res/drawable/bubble_expanded_header_bg.xml b/packages/SystemUI/res/drawable/bubble_expanded_header_bg.xml
deleted file mode 100644
index a76b7b1..0000000
--- a/packages/SystemUI/res/drawable/bubble_expanded_header_bg.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2018 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
- <item>
- <shape android:shape="rectangle">
- <solid android:color="?android:attr/colorBackgroundFloating"/>
- <corners
- android:topLeftRadius="@dimen/corner_size"
- android:topRightRadius="@dimen/corner_size"/>
- </shape>
- </item>
-</layer-list>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_tune_black_16dp.xml b/packages/SystemUI/res/drawable/ic_tune_black_16dp.xml
new file mode 100644
index 0000000..339cb70
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_tune_black_16dp.xml
@@ -0,0 +1,25 @@
+<!--
+ Copyright (C) 2019 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="16dp"
+ android:height="16dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,17v2h6v-2L3,17zM3,5v2h10L13,5L3,5zM13,21v-2h8v-2h-8v-2h-2v6h2zM7,9v2L3,11v2h4v2h2L9,9L7,9zM21,13v-2L11,11v2h10zM15,9h2L17,7h4L21,5h-4L17,3h-2v6z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_customize_tile_decoration.xml b/packages/SystemUI/res/drawable/qs_customize_tile_decoration.xml
new file mode 100644
index 0000000..f086cec
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_customize_tile_decoration.xml
@@ -0,0 +1,17 @@
+<!--
+ Copyright (C) 2019 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.
+-->
+<color xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/qs_customize_decoration"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_customizer_background_primary.xml b/packages/SystemUI/res/drawable/qs_customizer_background_primary.xml
index abe1429..215dee4 100644
--- a/packages/SystemUI/res/drawable/qs_customizer_background_primary.xml
+++ b/packages/SystemUI/res/drawable/qs_customizer_background_primary.xml
@@ -15,7 +15,7 @@
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android">
<shape>
- <solid android:color="?android:attr/colorPrimary"/>
+ <solid android:color="@color/qs_customize_background"/>
<corners android:radius="?android:attr/dialogCornerRadius" />
</shape>
</inset>
diff --git a/packages/SystemUI/res/drawable/qs_customizer_toolbar.xml b/packages/SystemUI/res/drawable/qs_customizer_toolbar.xml
index 557cae1..648d45b 100644
--- a/packages/SystemUI/res/drawable/qs_customizer_toolbar.xml
+++ b/packages/SystemUI/res/drawable/qs_customizer_toolbar.xml
@@ -15,7 +15,7 @@
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android">
<shape>
- <solid android:color="?android:attr/colorSecondary"/>
+ <solid android:color="@color/qs_customize_background"/>
<corners
android:topLeftRadius="?android:attr/dialogCornerRadius"
android:topRightRadius="?android:attr/dialogCornerRadius" />
diff --git a/packages/SystemUI/res/layout/bubble_expanded_view.xml b/packages/SystemUI/res/layout/bubble_expanded_view.xml
index a8b1b2e..56a3cd5 100644
--- a/packages/SystemUI/res/layout/bubble_expanded_view.xml
+++ b/packages/SystemUI/res/layout/bubble_expanded_view.xml
@@ -29,10 +29,9 @@
<FrameLayout
android:id="@+id/header_permission_wrapper"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:animateLayoutChanges="true"
- android:background="@drawable/bubble_expanded_header_bg">
+ android:animateLayoutChanges="true">
<LinearLayout
android:id="@+id/header_layout"
diff --git a/packages/SystemUI/res/layout/keyguard_status_bar.xml b/packages/SystemUI/res/layout/keyguard_status_bar.xml
index 5b9816d..66c0c5c 100644
--- a/packages/SystemUI/res/layout/keyguard_status_bar.xml
+++ b/packages/SystemUI/res/layout/keyguard_status_bar.xml
@@ -71,7 +71,7 @@
android:gravity="center_vertical"
android:ellipsize="marquee"
android:textDirection="locale"
- android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textAppearance="@style/TextAppearance.StatusBar.Clock"
android:textColor="?attr/wallpaperTextColorSecondary"
android:singleLine="true"
systemui:showMissingSim="true"
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml b/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml
index bc15f2c4..665fc3f 100644
--- a/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml
+++ b/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml
@@ -54,9 +54,6 @@
android:orientation="vertical"
android:gravity="start"
/>
-
- <include android:id="@+id/overflow" layout="@layout/ongoing_privacy_dialog_item"
- android:visibility="gone" />
</LinearLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/qs_customize_divider.xml b/packages/SystemUI/res/layout/qs_customize_divider.xml
index 6fcfa8d..d6664fe 100644
--- a/packages/SystemUI/res/layout/qs_customize_divider.xml
+++ b/packages/SystemUI/res/layout/qs_customize_divider.xml
@@ -24,5 +24,4 @@
android:paddingTop="20dp"
android:paddingBottom="13dp"
android:textAppearance="@style/TextAppearance.QSEdit.Headers"
- android:textColor="?android:attr/colorAccent"
android:text="@string/drag_to_add_tiles" />
diff --git a/packages/SystemUI/res/layout/qs_customize_header.xml b/packages/SystemUI/res/layout/qs_customize_header.xml
index d54dfee..58066a3 100644
--- a/packages/SystemUI/res/layout/qs_customize_header.xml
+++ b/packages/SystemUI/res/layout/qs_customize_header.xml
@@ -24,5 +24,4 @@
android:gravity="center"
android:minHeight="@dimen/qs_customize_header_min_height"
android:textAppearance="@style/TextAppearance.QSEdit.Headers"
- android:textColor="?android:attr/colorAccent"
android:text="@string/drag_to_rearrange_tiles" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_customize_panel_content.xml b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
index 87b4551..09f512f 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel_content.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
@@ -37,7 +37,8 @@
android:layout_height="wrap_content"
android:background="@drawable/qs_customizer_toolbar"
android:navigationContentDescription="@*android:string/action_bar_up_description"
- style="?android:attr/toolbarStyle" />
+ style="@style/QSCustomizeToolbar"
+ />
<androidx.recyclerview.widget.RecyclerView
android:id="@android:id/list"
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index e67bb60..ca34c23 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -87,7 +87,7 @@
android:background="@drawable/rounded_bg_bottom_background">
<com.android.keyguard.AlphaOptimizedImageButton
android:id="@+id/settings"
- android:src="@drawable/ic_settings_16dp"
+ android:src="@drawable/ic_tune_black_16dp"
android:layout_width="@dimen/volume_dialog_tap_target_size"
android:layout_height="@dimen/volume_dialog_tap_target_size"
android:layout_gravity="center"
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index 2c5120d..5803bf1 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -41,4 +41,10 @@
<!-- The color of the text inside a notification -->
<color name="notification_primary_text_color">@*android:color/notification_primary_text_color_dark</color>
+
+ <!-- The color of the background in the top part of QSCustomizer -->
+ <color name="qs_customize_background">@color/GM2_grey_900</color>
+
+ <!-- The color of the background in the bottom part of QSCustomizer -->
+ <color name="qs_customize_decoration">@color/GM2_grey_800</color>
</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index d5f29ba..0e4ffee 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -35,6 +35,8 @@
<color name="status_bar_clock_color">#FFFFFFFF</color>
<color name="qs_user_detail_icon_muted">#FFFFFFFF</color> <!-- not so muted after all -->
<color name="qs_tile_disabled_color">#9E9E9E</color> <!-- 38% black -->
+ <color name="qs_customize_background">@color/GM2_grey_50</color>
+ <color name="qs_customize_decoration">@color/GM2_grey_100</color>
<!-- Tint color for the content on the notification overflow card. -->
<color name="keyguard_overflow_content_color">#ff686868</color>
@@ -72,7 +74,7 @@
<color name="notification_primary_text_color">@*android:color/notification_primary_text_color_light</color>
<!-- The "inside" of a notification, reached via longpress -->
- <color name="notification_guts_bg_color">#f8f9fa</color>
+ <color name="notification_guts_bg_color">@color/GM2_grey_50</color>
<color name="assist_orb_color">#ffffff</color>
@@ -123,7 +125,7 @@
<color name="zen_introduction">#ffffffff</color>
- <color name="smart_reply_button_text">#5F6368</color>
+ <color name="smart_reply_button_text">@color/GM2_grey_700</color>
<color name="smart_reply_button_text_dark_bg">@*android:color/notification_primary_text_color_dark</color>
<color name="smart_reply_button_background">#ffffffff</color>
<color name="smart_reply_button_stroke">#ffdadce0</color>
@@ -134,4 +136,17 @@
<!-- Logout button -->
<color name="logout_button_bg_color">#ccffffff</color>
+
+ <!-- GM2 colors -->
+ <color name="GM2_grey_50">#F8F9FA</color>
+ <color name="GM2_grey_100">#F1F3F4</color>
+ <color name="GM2_grey_200">#E8EAED</color>
+ <color name="GM2_grey_300">#DADCE0</color>
+ <color name="GM2_grey_400">#BDC1C6</color>
+ <color name="GM2_grey_500">#9AA0A6</color>
+ <color name="GM2_grey_600">#80868B</color>
+ <color name="GM2_grey_650">#70757A</color>
+ <color name="GM2_grey_700">#5F6368</color>
+ <color name="GM2_grey_800">#3C4043</color>
+ <color name="GM2_grey_900">#202124</color>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 536bc4e..50cf37b 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -44,8 +44,9 @@
<!-- Height of the battery icon in the status bar. -->
<dimen name="status_bar_battery_icon_height">13.0dp</dimen>
- <!-- Width of the battery icon in the status bar. -->
- <dimen name="status_bar_battery_icon_width">8.5dp</dimen>
+ <!-- Width of the battery icon in the status bar. The battery drawable assumes a 12x20 canvas,
+ so the width of the icon should be 13.0dp * (12.0 / 20.0) -->
+ <dimen name="status_bar_battery_icon_width">7.8dp</dimen>
<!-- The font size for the clock in the status bar. -->
<dimen name="status_bar_clock_size">14sp</dimen>
@@ -1049,4 +1050,7 @@
<dimen name="bubble_pointer_margin">8dp</dimen>
<!-- Height of the permission prompt shown with bubbles -->
<dimen name="bubble_permission_height">120dp</dimen>
+
+ <!-- Size of the RAT type for CellularTile -->
+ <dimen name="celltile_rat_type_size">10sp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 96cdbd3..30dbc8b 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -216,6 +216,12 @@
<item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
</style>
+ <!-- This is hard coded to be sans-serif-condensed to match the icons -->
+ <style name="TextAppearance.RATBadge" parent="@style/TextAppearance.QS.TileLabel.Secondary">
+ <item name="android:fontFamily">sans-serif-condensed</item>
+ <item name="android:textSize">@dimen/celltile_rat_type_size</item>
+ </style>
+
<style name="TextAppearance.QS.CarrierInfo">
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
<item name="android:textSize">@dimen/qs_carrier_info_text_size</item>
@@ -458,8 +464,15 @@
</style>
<style name="TextAppearance.QSEdit.Headers"
- parent="@*android:style/TextAppearance.Material.Body2">
- <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ parent="@*android:style/TextAppearance.DeviceDefault.Body2">
+ <item name="android:textSize">11sp</item>
+ <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:textAllCaps">true</item>
+ </style>
+
+ <style name="QSCustomizeToolbar" parent="@*android:style/Widget.DeviceDefault.Toolbar">
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:elevation">10dp</item>
</style>
<style name="edit_theme" parent="qs_theme">
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/StatsLogCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/StatsLogCompat.java
index 5bc1f25..d17725f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/StatsLogCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/StatsLogCompat.java
@@ -32,4 +32,17 @@
StatsLog.write(19, action, srcState, dstState, extension,
swipeUpEnabled);
}
+
+ /**
+ * StatsLog.write(StatsLog.STYLE_EVENT, action, colorPackageHash,
+ * fontPackageHash, shapePackageHash, clockPackageHash,
+ * launcherGrid, wallpaperCategoryHash, wallpaperIdHash);
+ */
+ public static void write(int action, int colorPackageHash,
+ int fontPackageHash, int shapePackageHash, int clockPackageHash,
+ int launcherGrid, int wallpaperCategoryHash, int wallpaperIdHash) {
+ StatsLog.write(179, action, colorPackageHash,
+ fontPackageHash, shapePackageHash, clockPackageHash,
+ launcherGrid, wallpaperCategoryHash, wallpaperIdHash);
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
index a055950..4cb8d90 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
@@ -34,6 +34,8 @@
import com.android.internal.widget.LockPatternChecker;
import com.android.internal.widget.LockPatternUtils;
+import java.util.Arrays;
+
/**
* Base class for PIN and password unlock screens.
*/
@@ -124,18 +126,19 @@
protected void verifyPasswordAndUnlock() {
if (mDismissing) return; // already verified but haven't been dismissed; don't do it again.
- final String entry = getPasswordText();
+ final byte[] entry = getPasswordText();
setPasswordEntryInputEnabled(false);
if (mPendingLockCheck != null) {
mPendingLockCheck.cancel(false);
}
final int userId = KeyguardUpdateMonitor.getCurrentUser();
- if (entry.length() <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) {
+ if (entry.length <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) {
// to avoid accidental lockout, only count attempts that are long enough to be a
// real password. This may require some tweaking.
setPasswordEntryInputEnabled(true);
onPasswordChecked(userId, false /* matched */, 0, false /* not valid - too short */);
+ Arrays.fill(entry, (byte) 0);
return;
}
@@ -157,6 +160,7 @@
}
onPasswordChecked(userId, true /* matched */, 0 /* timeoutMs */,
true /* isValidPassword */);
+ Arrays.fill(entry, (byte) 0);
}
@Override
@@ -171,6 +175,7 @@
onPasswordChecked(userId, false /* matched */, timeoutMs,
true /* isValidPassword */);
}
+ Arrays.fill(entry, (byte) 0);
}
@Override
@@ -181,6 +186,7 @@
LatencyTracker.getInstance(mContext).onActionEnd(
ACTION_CHECK_CREDENTIAL_UNLOCKED);
}
+ Arrays.fill(entry, (byte) 0);
}
});
}
@@ -211,7 +217,7 @@
}
protected abstract void resetPasswordText(boolean animate, boolean announce);
- protected abstract String getPasswordText();
+ protected abstract byte[] getPasswordText();
protected abstract void setPasswordEntryEnabled(boolean enabled);
protected abstract void setPasswordEntryInputEnabled(boolean enabled);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 583dac7..9dd9717 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -17,7 +17,6 @@
import static android.view.Display.DEFAULT_DISPLAY;
-import android.annotation.Nullable;
import android.app.Presentation;
import android.content.Context;
import android.graphics.Point;
@@ -32,15 +31,11 @@
import android.view.View;
import android.view.WindowManager;
-import java.util.function.BooleanSupplier;
-
// TODO(multi-display): Support multiple external displays
public class KeyguardDisplayManager {
protected static final String TAG = "KeyguardDisplayManager";
private static boolean DEBUG = KeyguardConstants.DEBUG;
- private final ViewMediatorCallback mCallback;
-
private final MediaRouter mMediaRouter;
private final DisplayManager mDisplayService;
private final Context mContext;
@@ -57,7 +52,7 @@
public void onDisplayAdded(int displayId) {
final Display display = mDisplayService.getDisplay(displayId);
if (mShowing) {
- notifyIfChanged(() -> showPresentation(display));
+ showPresentation(display);
}
}
@@ -76,13 +71,12 @@
@Override
public void onDisplayRemoved(int displayId) {
- notifyIfChanged(() -> hidePresentation(displayId));
+ hidePresentation(displayId);
}
};
- public KeyguardDisplayManager(Context context, ViewMediatorCallback callback) {
+ public KeyguardDisplayManager(Context context) {
mContext = context;
- mCallback = callback;
mMediaRouter = mContext.getSystemService(MediaRouter.class);
mDisplayService = mContext.getSystemService(DisplayManager.class);
mDisplayService.registerDisplayListener(mDisplayListener, null /* handler */);
@@ -138,42 +132,13 @@
/**
* @param displayId The id of the display to hide the presentation off.
- * @return {@code true} if the a presentation was removed.
- * {@code false} if the presentation was not added before.
*/
- private boolean hidePresentation(int displayId) {
+ private void hidePresentation(int displayId) {
final Presentation presentation = mPresentations.get(displayId);
if (presentation != null) {
presentation.dismiss();
mPresentations.remove(displayId);
- return true;
}
- return false;
- }
-
- private void notifyIfChanged(BooleanSupplier updateMethod) {
- if (updateMethod.getAsBoolean()) {
- final int[] displayList = getPresentationDisplayIds();
- mCallback.onSecondaryDisplayShowingChanged(displayList);
- }
- }
-
- /**
- * @return An array of displayId's on which a {@link KeyguardPresentation} is showing on.
- */
- @Nullable
- private int[] getPresentationDisplayIds() {
- final int size = mPresentations.size();
- if (size == 0) return null;
-
- final int[] displayIds = new int[size];
- for (int i = mPresentations.size() - 1; i >= 0; i--) {
- final Presentation presentation = mPresentations.valueAt(i);
- if (presentation != null) {
- displayIds[i] = presentation.getDisplay().getDisplayId();
- }
- }
- return displayIds;
}
public void show() {
@@ -181,7 +146,7 @@
if (DEBUG) Log.v(TAG, "show");
mMediaRouter.addCallback(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY,
mMediaRouterCallback, MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY);
- notifyIfChanged(() -> updateDisplays(true /* showing */));
+ updateDisplays(true /* showing */);
}
mShowing = true;
}
@@ -190,7 +155,7 @@
if (mShowing) {
if (DEBUG) Log.v(TAG, "hide");
mMediaRouter.removeCallback(mMediaRouterCallback);
- notifyIfChanged(() -> updateDisplays(false /* showing */));
+ updateDisplays(false /* showing */);
}
mShowing = false;
}
@@ -200,19 +165,19 @@
@Override
public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
if (DEBUG) Log.d(TAG, "onRouteSelected: type=" + type + ", info=" + info);
- notifyIfChanged(() -> updateDisplays(mShowing));
+ updateDisplays(mShowing);
}
@Override
public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
if (DEBUG) Log.d(TAG, "onRouteUnselected: type=" + type + ", info=" + info);
- notifyIfChanged(() -> updateDisplays(mShowing));
+ updateDisplays(mShowing);
}
@Override
public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo info) {
if (DEBUG) Log.d(TAG, "onRoutePresentationDisplayChanged: info=" + info);
- notifyIfChanged(() -> updateDisplays(mShowing));
+ updateDisplays(mShowing);
}
};
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index 261f391..185edbf 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -241,8 +241,8 @@
}
@Override
- protected String getPasswordText() {
- return mPasswordEntry.getText().toString();
+ protected byte[] getPasswordText() {
+ return charSequenceToByteArray(mPasswordEntry.getText());
}
@Override
@@ -377,4 +377,18 @@
return getContext().getString(
com.android.internal.R.string.keyguard_accessibility_password_unlock);
}
+
+ /*
+ * This method avoids creating a new string when getting a byte array from EditView#getText().
+ */
+ private static byte[] charSequenceToByteArray(CharSequence chars) {
+ if (chars == null) {
+ return null;
+ }
+ byte[] bytes = new byte[chars.length()];
+ for (int i = 0; i < chars.length(); i++) {
+ bytes[i] = (byte) chars.charAt(i);
+ }
+ return bytes;
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
index 3cc18dd..ecafc34 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -165,8 +165,8 @@
}
@Override
- protected String getPasswordText() {
- return mPasswordEntry.getText();
+ protected byte[] getPasswordText() {
+ return charSequenceToByteArray(mPasswordEntry.getText());
}
@Override
@@ -264,4 +264,18 @@
return getContext().getString(
com.android.internal.R.string.keyguard_accessibility_pin_unlock);
}
+
+ /*
+ * This method avoids creating a new string when getting a byte array from EditView#getText().
+ */
+ private static byte[] charSequenceToByteArray(CharSequence chars) {
+ if (chars == null) {
+ return null;
+ }
+ byte[] bytes = new byte[chars.length()];
+ for (int i = 0; i < chars.length(); i++) {
+ bytes[i] = (byte) chars.charAt(i);
+ }
+ return bytes;
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java b/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java
index a07c5cb..6e06130 100644
--- a/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java
@@ -96,11 +96,6 @@
int getBouncerPromptReason();
/**
- * Invoked when the secondary displays showing a keyguard window changes.
- */
- void onSecondaryDisplayShowingChanged(int[] displayId);
-
- /**
* Consumes a message that was enqueued to be displayed on the next time the bouncer shows up.
* @return Message that should be displayed above the challenge.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 2006794..592b603 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -46,7 +46,7 @@
import android.widget.TextView;
import com.android.settingslib.Utils;
-import com.android.settingslib.graph.BatteryMeterDrawableBase;
+import com.android.settingslib.graph.ThemedBatteryDrawable;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.settings.CurrentUserTracker;
@@ -76,7 +76,7 @@
public static final int MODE_OFF = 2;
public static final int MODE_ESTIMATE = 3;
- private final BatteryMeterDrawableBase mDrawable;
+ private final ThemedBatteryDrawable mDrawable;
private final String mSlotBattery;
private final ImageView mBatteryIconView;
private final CurrentUserTracker mUserTracker;
@@ -94,9 +94,11 @@
private boolean mIsSubscribedForTunerUpdates;
private boolean mCharging;
+ private int mDarkModeSingleToneColor;
private int mDarkModeBackgroundColor;
private int mDarkModeFillColor;
+ private int mLightModeSingleToneColor;
private int mLightModeBackgroundColor;
private int mLightModeFillColor;
private int mUser;
@@ -106,6 +108,7 @@
*/
private boolean mUseWallpaperTextColors;
+ private int mNonAdaptedSingleToneColor;
private int mNonAdaptedForegroundColor;
private int mNonAdaptedBackgroundColor;
@@ -127,7 +130,7 @@
defStyle, 0);
final int frameColor = atts.getColor(R.styleable.BatteryMeterView_frameColor,
context.getColor(R.color.meter_background_color));
- mDrawable = new BatteryMeterDrawableBase(context, frameColor);
+ mDrawable = new ThemedBatteryDrawable(context, frameColor);
atts.recycle();
mSettingObserver = new SettingObserver(new Handler(context.getMainLooper()));
@@ -169,6 +172,10 @@
setClipChildren(false);
setClipToPadding(false);
Dependency.get(ConfigurationController.class).observe(viewAttachLifecycle(this), this);
+
+ // Needed for PorderDuff.Mode.CLEAR operations to work properly, but redraws don't happen
+ // enough to justify a hardware layer.
+ setLayerType(LAYER_TYPE_SOFTWARE, null);
}
public void setForceShowPercent(boolean show) {
@@ -243,9 +250,11 @@
if (mUseWallpaperTextColors) {
updateColors(
Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor),
- Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColorSecondary));
+ Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColorSecondary),
+ Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor));
} else {
- updateColors(mNonAdaptedForegroundColor, mNonAdaptedBackgroundColor);
+ updateColors(mNonAdaptedForegroundColor, mNonAdaptedBackgroundColor,
+ mNonAdaptedSingleToneColor);
}
}
@@ -258,10 +267,14 @@
Utils.getThemeAttr(context, R.attr.darkIconTheme));
Context dualToneLightTheme = new ContextThemeWrapper(context,
Utils.getThemeAttr(context, R.attr.lightIconTheme));
+ mDarkModeSingleToneColor = Utils.getColorAttrDefaultColor(dualToneDarkTheme,
+ R.attr.singleToneColor);
mDarkModeBackgroundColor = Utils.getColorAttrDefaultColor(dualToneDarkTheme,
R.attr.backgroundColor);
mDarkModeFillColor = Utils.getColorAttrDefaultColor(dualToneDarkTheme,
R.attr.fillColor);
+ mLightModeSingleToneColor = Utils.getColorAttrDefaultColor(dualToneLightTheme,
+ R.attr.singleToneColor);
mLightModeBackgroundColor = Utils.getColorAttrDefaultColor(dualToneLightTheme,
R.attr.backgroundColor);
mLightModeFillColor = Utils.getColorAttrDefaultColor(dualToneLightTheme, R.attr.fillColor);
@@ -303,8 +316,8 @@
@Override
public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
- mDrawable.setBatteryLevel(level);
mDrawable.setCharging(pluggedIn);
+ mDrawable.setBatteryLevel(level);
mCharging = pluggedIn;
mLevel = level;
updatePercentText();
@@ -315,7 +328,7 @@
@Override
public void onPowerSaveChanged(boolean isPowerSave) {
- mDrawable.setPowerSave(isPowerSave);
+ mDrawable.setPowerSaveEnabled(isPowerSave);
}
private TextView loadPercentView() {
@@ -406,21 +419,24 @@
@Override
public void onDarkChanged(Rect area, float darkIntensity, int tint) {
float intensity = DarkIconDispatcher.isInArea(area, this) ? darkIntensity : 0;
+ mNonAdaptedSingleToneColor = getColorForDarkIntensity(
+ intensity, mLightModeSingleToneColor, mDarkModeSingleToneColor);
mNonAdaptedForegroundColor = getColorForDarkIntensity(
intensity, mLightModeFillColor, mDarkModeFillColor);
mNonAdaptedBackgroundColor = getColorForDarkIntensity(
intensity, mLightModeBackgroundColor,mDarkModeBackgroundColor);
if (!mUseWallpaperTextColors) {
- updateColors(mNonAdaptedForegroundColor, mNonAdaptedBackgroundColor);
+ updateColors(mNonAdaptedForegroundColor, mNonAdaptedBackgroundColor,
+ mNonAdaptedSingleToneColor);
}
}
- private void updateColors(int foregroundColor, int backgroundColor) {
- mDrawable.setColors(foregroundColor, backgroundColor);
- mTextColor = foregroundColor;
+ private void updateColors(int foregroundColor, int backgroundColor, int singleToneColor) {
+ mDrawable.setColors(foregroundColor, backgroundColor, singleToneColor);
+ mTextColor = singleToneColor;
if (mBatteryPercentView != null) {
- mBatteryPercentView.setTextColor(foregroundColor);
+ mBatteryPercentView.setTextColor(singleToneColor);
}
}
@@ -429,7 +445,7 @@
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- String powerSave = mDrawable == null ? null : mDrawable.getPowerSave() + "";
+ String powerSave = mDrawable == null ? null : mDrawable.getPowerSaveEnabled() + "";
CharSequence percent = mBatteryPercentView == null ? null : mBatteryPercentView.getText();
pw.println(" BatteryMeterView:");
pw.println(" mDrawable.getPowerSave: " + powerSave);
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 5672073..87f004f 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -61,7 +61,6 @@
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationFilter;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
-import com.android.systemui.statusbar.notification.NotificationRowBinder;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
@@ -266,7 +265,6 @@
@Inject Lazy<NotificationListener> mNotificationListener;
@Inject Lazy<NotificationLogger> mNotificationLogger;
@Inject Lazy<NotificationViewHierarchyManager> mNotificationViewHierarchyManager;
- @Inject Lazy<NotificationRowBinder> mNotificationRowBinder;
@Inject Lazy<NotificationFilter> mNotificationFilter;
@Inject Lazy<NotificationInterruptionStateProvider> mNotificationInterruptionStateProvider;
@Inject Lazy<KeyguardDismissUtil> mKeyguardDismissUtil;
@@ -440,7 +438,6 @@
mProviders.put(NotificationLogger.class, mNotificationLogger::get);
mProviders.put(NotificationViewHierarchyManager.class,
mNotificationViewHierarchyManager::get);
- mProviders.put(NotificationRowBinder.class, mNotificationRowBinder::get);
mProviders.put(NotificationFilter.class, mNotificationFilter::get);
mProviders.put(NotificationInterruptionStateProvider.class,
mNotificationInterruptionStateProvider::get);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index b635033..603b3b9 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -39,6 +39,7 @@
import android.graphics.Insets;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.ShapeDrawable;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -48,7 +49,6 @@
import android.util.Log;
import android.util.StatsLog;
import android.view.View;
-import android.view.ViewGroup;
import android.view.WindowInsets;
import android.widget.FrameLayout;
import android.widget.ImageButton;
@@ -189,6 +189,8 @@
mPointerView.setBackground(triangleDrawable);
FrameLayout viewWrapper = findViewById(R.id.header_permission_wrapper);
+ viewWrapper.setBackground(createHeaderPermissionBackground(bgColor));
+
LayoutTransition transition = new LayoutTransition();
transition.setDuration(200);
@@ -204,6 +206,7 @@
viewWrapper.setLayoutTransition(transition);
viewWrapper.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
+
mHeaderHeight = getContext().getResources().getDimensionPixelSize(
R.dimen.bubble_expanded_header_height);
mPermissionHeight = getContext().getResources().getDimensionPixelSize(
@@ -243,7 +246,10 @@
if (!mShowOnTop) {
removeView(mPointerView);
if (mUseFooter) {
+ View divider = findViewById(R.id.divider);
+ viewWrapper.removeView(divider);
removeView(viewWrapper);
+ addView(divider);
addView(viewWrapper);
}
addView(mPointerView);
@@ -251,6 +257,25 @@
}
/**
+ * Creates a background with corners rounded based on how the view is configured to display
+ */
+ private Drawable createHeaderPermissionBackground(int bgColor) {
+ TypedArray ta2 = getContext().obtainStyledAttributes(
+ new int[] {android.R.attr.dialogCornerRadius});
+ final float cr = ta2.getDimension(0, 0f);
+ ta2.recycle();
+
+ float[] radii = mUseFooter
+ ? new float[] {0, 0, 0, 0, cr, cr, cr, cr}
+ : new float[] {cr, cr, cr, cr, 0, 0, 0, 0};
+ GradientDrawable chromeBackground = new GradientDrawable();
+ chromeBackground.setShape(GradientDrawable.RECTANGLE);
+ chromeBackground.setCornerRadii(radii);
+ chromeBackground.setColor(bgColor);
+ return chromeBackground;
+ }
+
+ /**
* Sets the listener to notify when a bubble has been blocked.
*/
public void setOnBlockedListener(OnBubbleBlockedListener listener) {
@@ -491,23 +516,14 @@
/**
* Removes and releases an ActivityView if one was previously created for this bubble.
*/
- public void destroyActivityView(ViewGroup tmpParent) {
+ public void destroyActivityView() {
if (mActivityView == null) {
return;
}
- if (!mActivityViewReady) {
- // release not needed, never initialized?
- mActivityView = null;
- return;
+ if (mActivityViewReady) {
+ mActivityView.release();
}
- // HACK: release() will crash if the view is not attached.
- if (!isAttachedToWindow()) {
- mActivityView.setVisibility(View.GONE);
- tmpParent.addView(this);
- }
-
- mActivityView.release();
- ((ViewGroup) getParent()).removeView(this);
+ removeView(mActivityView);
mActivityView = null;
mActivityViewReady = false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 167bf47..8235d8d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -348,7 +348,7 @@
// Remove it from the views
int removedIndex = mBubbleContainer.indexOfChild(b.iconView);
- b.expandedView.destroyActivityView(this /* tmpParent */);
+ b.expandedView.destroyActivityView();
mBubbleContainer.removeView(b.iconView);
int bubbleCount = mBubbleContainer.getChildCount();
@@ -377,7 +377,7 @@
public void stackDismissed() {
for (Bubble bubble : mBubbleData.getBubbles()) {
bubble.entry.setBubbleDismissed(true);
- bubble.expandedView.destroyActivityView(this /* tmpParent */);
+ bubble.expandedView.destroyActivityView();
}
mBubbleData.clear();
collapseStack();
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java b/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
index 5353ee6..06dbdbf 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
@@ -17,11 +17,11 @@
package com.android.systemui.doze;
import android.content.Context;
+import android.hardware.display.AmbientDisplayConfiguration;
import android.os.Handler;
import android.os.UserHandle;
import android.util.Log;
-import com.android.internal.hardware.AmbientDisplayConfiguration;
import com.android.systemui.dock.DockManager;
import com.android.systemui.doze.DozeMachine.State;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
index 3250182..36e28dc 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
@@ -21,9 +21,9 @@
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorManager;
+import android.hardware.display.AmbientDisplayConfiguration;
import android.os.Handler;
-import com.android.internal.hardware.AmbientDisplayConfiguration;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Dependency;
import com.android.systemui.R;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index 4d89a39..6c4be06 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -17,12 +17,12 @@
package com.android.systemui.doze;
import android.annotation.MainThread;
+import android.hardware.display.AmbientDisplayConfiguration;
import android.os.Trace;
import android.os.UserHandle;
import android.util.Log;
import android.view.Display;
-import com.android.internal.hardware.AmbientDisplayConfiguration;
import com.android.internal.util.Preconditions;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.Assert;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 2d1dba6..c5799cc 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -30,6 +30,7 @@
import android.hardware.SensorManager;
import android.hardware.TriggerEvent;
import android.hardware.TriggerEventListener;
+import android.hardware.display.AmbientDisplayConfiguration;
import android.net.Uri;
import android.os.Handler;
import android.os.SystemClock;
@@ -38,7 +39,8 @@
import android.text.TextUtils;
import android.util.Log;
-import com.android.internal.hardware.AmbientDisplayConfiguration;
+import androidx.annotation.VisibleForTesting;
+
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.systemui.plugins.SensorManagerPlugin;
@@ -60,7 +62,6 @@
private final Context mContext;
private final AlarmManager mAlarmManager;
private final SensorManager mSensorManager;
- private final TriggerSensor[] mSensors;
private final ContentResolver mResolver;
private final TriggerSensor mPickupSensor;
private final DozeParameters mDozeParameters;
@@ -68,10 +69,12 @@
private final WakeLock mWakeLock;
private final Consumer<Boolean> mProxCallback;
private final Callback mCallback;
+ @VisibleForTesting
+ protected final TriggerSensor[] mSensors;
private final Handler mHandler = new Handler();
private final ProxSensor mProxSensor;
-
+ private long mDebounceFrom;
public DozeSensors(Context context, AlarmManager alarmManager, SensorManager sensorManager,
DozeParameters dozeParameters, AmbientDisplayConfiguration config, WakeLock wakeLock,
@@ -134,13 +137,21 @@
mConfig.wakeScreenGestureAvailable() && alwaysOn,
DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN,
false /* reports touch coordinates */,
- false /* touchscreen */),
+ false /* touchscreen */, mConfig.getWakeLockScreenDebounce()),
};
mProxSensor = new ProxSensor(policy);
mCallback = callback;
}
+ /**
+ * Temporarily disable some sensors to avoid turning on the device while the user is
+ * turning it off.
+ */
+ public void requestTemporaryDisable() {
+ mDebounceFrom = SystemClock.uptimeMillis();
+ }
+
private Sensor findSensorWithType(String type) {
return findSensorWithType(mSensorManager, type);
}
@@ -320,7 +331,8 @@
}
}
- private class TriggerSensor extends TriggerEventListener {
+ @VisibleForTesting
+ class TriggerSensor extends TriggerEventListener {
final Sensor mSensor;
final boolean mConfigured;
final int mPulseReason;
@@ -467,23 +479,25 @@
/**
* A Sensor that is injected via plugin.
*/
- private class PluginSensor extends TriggerSensor {
+ @VisibleForTesting
+ class PluginSensor extends TriggerSensor implements SensorManagerPlugin.SensorEventListener {
- private final SensorManagerPlugin.Sensor mPluginSensor;
- private final SensorManagerPlugin.SensorEventListener mTriggerEventListener = (event) -> {
- DozeLog.traceSensor(mContext, mPulseReason);
- mHandler.post(mWakeLock.wrap(() -> {
- if (DEBUG) Log.d(TAG, "onSensorEvent: " + triggerEventToString(event));
- mCallback.onSensorPulse(mPulseReason, true /* sensorPerformsProxCheck */, -1, -1,
- event.getValues());
- }));
- };
+ final SensorManagerPlugin.Sensor mPluginSensor;
+ private long mDebounce;
PluginSensor(SensorManagerPlugin.Sensor sensor, String setting, boolean configured,
int pulseReason, boolean reportsTouchCoordinates, boolean requiresTouchscreen) {
+ this(sensor, setting, configured, pulseReason, reportsTouchCoordinates,
+ requiresTouchscreen, 0L /* debounce */);
+ }
+
+ PluginSensor(SensorManagerPlugin.Sensor sensor, String setting, boolean configured,
+ int pulseReason, boolean reportsTouchCoordinates, boolean requiresTouchscreen,
+ long debounce) {
super(null, setting, configured, pulseReason, reportsTouchCoordinates,
requiresTouchscreen);
mPluginSensor = sensor;
+ mDebounce = debounce;
}
@Override
@@ -492,11 +506,11 @@
AsyncSensorManager asyncSensorManager = (AsyncSensorManager) mSensorManager;
if (mRequested && !mDisabled && (enabledBySetting() || mIgnoresSetting)
&& !mRegistered) {
- asyncSensorManager.registerPluginListener(mPluginSensor, mTriggerEventListener);
+ asyncSensorManager.registerPluginListener(mPluginSensor, this);
mRegistered = true;
if (DEBUG) Log.d(TAG, "registerPluginListener");
} else if (mRegistered) {
- asyncSensorManager.unregisterPluginListener(mPluginSensor, mTriggerEventListener);
+ asyncSensorManager.unregisterPluginListener(mPluginSensor, this);
mRegistered = false;
if (DEBUG) Log.d(TAG, "unregisterPluginListener");
}
@@ -524,6 +538,21 @@
}
return sb.append(']').toString();
}
+
+ @Override
+ public void onSensorChanged(SensorManagerPlugin.SensorEvent event) {
+ DozeLog.traceSensor(mContext, mPulseReason);
+ mHandler.post(mWakeLock.wrap(() -> {
+ final long now = SystemClock.uptimeMillis();
+ if (now < mDebounceFrom + mDebounce) {
+ if (DEBUG) Log.d(TAG, "onSensorEvent dropped: " + triggerEventToString(event));
+ return;
+ }
+ if (DEBUG) Log.d(TAG, "onSensorEvent: " + triggerEventToString(event));
+ mCallback.onSensorPulse(mPulseReason, true /* sensorPerformsProxCheck */, -1, -1,
+ event.getValues());
+ }));
+ }
}
public interface Callback {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 70bf903..3654aac 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -27,6 +27,7 @@
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
+import android.hardware.display.AmbientDisplayConfiguration;
import android.os.Handler;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -34,7 +35,6 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.hardware.AmbientDisplayConfiguration;
import com.android.internal.util.Preconditions;
import com.android.systemui.dock.DockManager;
import com.android.systemui.statusbar.phone.DozeParameters;
@@ -143,8 +143,12 @@
if (isWakeDisplay) {
onWakeScreen(wakeEvent, mMachine.getState());
- } else if (isLongPress || isWakeLockScreen) {
+ } else if (isLongPress) {
requestPulse(pulseReason, sensorPerformedProxCheck);
+ } else if (isWakeLockScreen) {
+ if (wakeEvent) {
+ requestPulse(pulseReason, sensorPerformedProxCheck);
+ }
} else {
proximityCheckThenCall((result) -> {
if (result == ProximityCheck.RESULT_NEAR) {
@@ -228,6 +232,7 @@
if (mDockManager != null) {
mDockManager.addListener(mDockEventListener);
}
+ mDozeSensors.requestTemporaryDisable();
checkTriggersAtInit();
break;
case DOZE:
@@ -250,6 +255,9 @@
mDozeSensors.setTouchscreenSensorsListening(false);
mDozeSensors.setProxListening(true);
break;
+ case DOZE_PULSE_DONE:
+ mDozeSensors.requestTemporaryDisable();
+ break;
case FINISH:
mBroadcastReceiver.unregister(mContext);
mDozeHost.removeCallback(mHostCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 66cfadf..172746e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -21,7 +21,6 @@
import static com.android.internal.telephony.IccCardConstants.State.ABSENT;
import static com.android.internal.telephony.IccCardConstants.State.PIN_REQUIRED;
import static com.android.internal.telephony.IccCardConstants.State.PUK_REQUIRED;
-import static com.android.internal.telephony.IccCardConstants.State.READY;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
@@ -95,7 +94,6 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Arrays;
/**
* Mediates requests related to the keyguard. This includes queries about the
@@ -247,9 +245,6 @@
// AOD is enabled and status bar is in AOD state.
private boolean mAodShowing;
- // display ids of the external display on which we have put a keyguard window
- private int[] mSecondaryDisplaysShowing;
-
/** Cached value of #isInputRestricted */
private boolean mInputRestricted;
@@ -687,13 +682,6 @@
mCustomMessage = null;
return message;
}
-
- @Override
- public void onSecondaryDisplayShowingChanged(int[] displayIds) {
- synchronized (KeyguardViewMediator.this) {
- setShowingLocked(mShowing, mAodShowing, displayIds, false);
- }
- }
};
public void userActivity() {
@@ -722,7 +710,7 @@
mContext.registerReceiver(mDelayedLockBroadcastReceiver, delayedActionFilter,
SYSTEMUI_PERMISSION, null /* scheduler */);
- mKeyguardDisplayManager = new KeyguardDisplayManager(mContext, mViewMediatorCallback);
+ mKeyguardDisplayManager = new KeyguardDisplayManager(mContext);
mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
@@ -738,10 +726,10 @@
setShowingLocked(!shouldWaitForProvisioning()
&& !mLockPatternUtils.isLockScreenDisabled(
KeyguardUpdateMonitor.getCurrentUser()),
- mAodShowing, mSecondaryDisplaysShowing, true /* forceCallbacks */);
+ mAodShowing, true /* forceCallbacks */);
} else {
// The system's keyguard is disabled or missing.
- setShowingLocked(false, mAodShowing, mSecondaryDisplaysShowing, true);
+ setShowingLocked(false, mAodShowing, true);
}
mStatusBarKeyguardViewManager =
@@ -1764,12 +1752,10 @@
playSound(mTrustedSoundId);
}
- private void updateActivityLockScreenState(boolean showing, boolean aodShowing,
- int[] secondaryDisplaysShowing) {
+ private void updateActivityLockScreenState(boolean showing, boolean aodShowing) {
mUiOffloadThread.submit(() -> {
try {
- ActivityTaskManager.getService().setLockScreenShown(showing, aodShowing,
- secondaryDisplaysShowing);
+ ActivityTaskManager.getService().setLockScreenShown(showing, aodShowing);
} catch (RemoteException e) {
}
});
@@ -1892,8 +1878,7 @@
if (!mHiding) {
// Tell ActivityManager that we canceled the keyguardExitAnimation.
- setShowingLocked(mShowing, mAodShowing, mSecondaryDisplaysShowing,
- true /* force */);
+ setShowingLocked(mShowing, mAodShowing, true /* force */);
return;
}
mHiding = false;
@@ -2163,23 +2148,19 @@
}
private void setShowingLocked(boolean showing, boolean aodShowing) {
- setShowingLocked(showing, aodShowing, mSecondaryDisplaysShowing,
- false /* forceCallbacks */);
+ setShowingLocked(showing, aodShowing, false /* forceCallbacks */);
}
- private void setShowingLocked(boolean showing, boolean aodShowing,
- int[] secondaryDisplaysShowing, boolean forceCallbacks) {
+ private void setShowingLocked(boolean showing, boolean aodShowing, boolean forceCallbacks) {
final boolean notifyDefaultDisplayCallbacks = showing != mShowing
|| aodShowing != mAodShowing || forceCallbacks;
- if (notifyDefaultDisplayCallbacks
- || !Arrays.equals(secondaryDisplaysShowing, mSecondaryDisplaysShowing)) {
+ if (notifyDefaultDisplayCallbacks) {
mShowing = showing;
mAodShowing = aodShowing;
- mSecondaryDisplaysShowing = secondaryDisplaysShowing;
if (notifyDefaultDisplayCallbacks) {
notifyDefaultDisplayCallbacks(showing);
}
- updateActivityLockScreenState(showing, aodShowing, secondaryDisplaysShowing);
+ updateActivityLockScreenState(showing, aodShowing);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
index 75b8a05..84a3446 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
@@ -42,14 +42,10 @@
private val iconSize = context.resources.getDimensionPixelSize(
R.dimen.ongoing_appops_dialog_icon_size)
- private val plusSize = context.resources.getDimensionPixelSize(
- R.dimen.ongoing_appops_dialog_app_plus_size)
private val iconColor = context.resources.getColor(
com.android.internal.R.color.text_color_primary, context.theme)
- private val plusColor: Int
private val iconMargin = context.resources.getDimensionPixelSize(
R.dimen.ongoing_appops_dialog_icon_margin)
- private val MAX_ITEMS = context.resources.getInteger(R.integer.ongoing_appops_dialog_max_apps)
private val iconFactory = IconDrawableFactory.newInstance(context, true)
private var dismissDialog: (() -> Unit)? = null
private val appsAndTypes = dialogBuilder.appsAndTypes
@@ -57,13 +53,6 @@
{ it.second.min() },
{ it.first }))
- init {
- val a = context.theme.obtainStyledAttributes(
- intArrayOf(com.android.internal.R.attr.colorAccent))
- plusColor = a.getColor(0, 0)
- a.recycle()
- }
-
fun createDialog(): Dialog {
val builder = AlertDialog.Builder(context).apply {
setPositiveButton(R.string.ongoing_privacy_dialog_ok, null)
@@ -96,33 +85,9 @@
val numItems = appsAndTypes.size
for (i in 0..(numItems - 1)) {
- if (i >= MAX_ITEMS) break
val item = appsAndTypes[i]
addAppItem(appsList, item.first, item.second, dialogBuilder.types.size > 1)
}
-
- if (numItems > MAX_ITEMS) {
- val overflow = contentView.findViewById(R.id.overflow) as LinearLayout
- overflow.visibility = View.VISIBLE
- val overflowText = overflow.findViewById(R.id.app_name) as TextView
- overflowText.text = context.resources.getQuantityString(
- R.plurals.ongoing_privacy_dialog_overflow_text,
- numItems - MAX_ITEMS,
- numItems - MAX_ITEMS
- )
- val overflowPlus = overflow.findViewById(R.id.app_icon) as ImageView
- val lp = overflowPlus.layoutParams.apply {
- height = plusSize
- width = plusSize
- }
- overflowPlus.layoutParams = lp
- overflowPlus.apply {
- val plus = context.getDrawable(R.drawable.plus)
- imageTintList = ColorStateList.valueOf(plusColor)
- setImageDrawable(plus)
- }
- }
-
return contentView
}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
index a6e48f8..3f581c4d 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
@@ -18,6 +18,8 @@
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
+import android.os.UserHandle
+import android.util.IconDrawableFactory
import com.android.systemui.R
typealias Privacy = PrivacyType
@@ -46,14 +48,21 @@
private val applicationInfo: ApplicationInfo? by lazy {
try {
- context.packageManager.getApplicationInfo(packageName, 0)
+ val userHandle = UserHandle.getUserHandleForUid(uid)
+ context.createPackageContextAsUser(packageName, 0, userHandle).getPackageManager()
+ .getApplicationInfo(packageName, 0)
} catch (_: PackageManager.NameNotFoundException) {
null
}
}
val icon: Drawable by lazy {
applicationInfo?.let {
- context.packageManager.getApplicationIcon(it)
+ try {
+ val iconFactory = IconDrawableFactory.newInstance(context, true)
+ iconFactory.getBadgedIcon(it, UserHandle.getUserId(uid))
+ } catch (_: Exception) {
+ null
+ }
} ?: context.getDrawable(android.R.drawable.sym_def_app_icon)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index 64ad95c..c209b31 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -38,7 +38,6 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
-import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.plugins.qs.QS;
@@ -103,10 +102,6 @@
mToolbar.getMenu().add(Menu.NONE, MENU_RESET, 0,
mContext.getString(com.android.internal.R.string.reset));
mToolbar.setTitle(R.string.qs_edit);
- int accentColor = Utils.getColorAttrDefaultColor(context, android.R.attr.colorAccent);
- mToolbar.setTitleTextColor(accentColor);
- mToolbar.getNavigationIcon().setTint(accentColor);
- mToolbar.getOverflowIcon().setTint(accentColor);
mRecyclerView = findViewById(android.R.id.list);
mTransparentView = findViewById(R.id.customizer_transparent_view);
mTileAdapter = new TileAdapter(getContext());
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index a29e93a..608f646 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -19,9 +19,8 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
-import android.content.res.TypedArray;
import android.graphics.Canvas;
-import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.view.LayoutInflater;
import android.view.View;
@@ -505,13 +504,10 @@
};
private class TileItemDecoration extends ItemDecoration {
- private final ColorDrawable mDrawable;
+ private final Drawable mDrawable;
private TileItemDecoration(Context context) {
- TypedArray ta =
- context.obtainStyledAttributes(new int[]{android.R.attr.colorSecondary});
- mDrawable = new ColorDrawable(ta.getColor(0, 0));
- ta.recycle();
+ mDrawable = context.getDrawable(R.drawable.qs_customize_tile_decoration);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
index 7f76900..c664a20 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
@@ -15,14 +15,11 @@
*/
package com.android.systemui.qs.tiles;
-import android.content.Context;
import android.content.Intent;
-import android.graphics.drawable.Drawable;
import android.service.quicksettings.Tile;
import android.widget.Switch;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.settingslib.graph.BatteryMeterDrawableBase;
import com.android.systemui.R;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.qs.QSHost;
@@ -41,6 +38,8 @@
private boolean mCharging;
private boolean mPluggedIn;
+ private Icon mIcon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_battery_saver);
+
@Inject
public BatterySaverTile(QSHost host, BatteryController batteryController) {
super(host);
@@ -84,9 +83,7 @@
protected void handleUpdateState(BooleanState state, Object arg) {
state.state = mPluggedIn ? Tile.STATE_UNAVAILABLE
: mPowerSave ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
- BatterySaverIcon bsi = new BatterySaverIcon();
- bsi.mState = state.state;
- state.icon = bsi;
+ state.icon = mIcon;
state.label = mContext.getString(R.string.battery_detail_switch_title);
state.contentDescription = state.label;
state.value = mPowerSave;
@@ -106,48 +103,4 @@
mPowerSave = isPowerSave;
refreshState(null);
}
-
- public static class BatterySaverIcon extends Icon {
- private int mState;
-
- @Override
- public Drawable getDrawable(Context context) {
- BatterySaverDrawable b =
- new BatterySaverDrawable(context, QSTileImpl.getColorForState(context, mState));
- b.mState = mState;
- final int pad = context.getResources()
- .getDimensionPixelSize(R.dimen.qs_tile_divider_height);
- b.setPadding(pad, pad, pad, pad);
- return b;
- }
- }
-
- private static class BatterySaverDrawable extends BatteryMeterDrawableBase {
- private int mState;
- private static final int MAX_BATTERY = 100;
-
- BatterySaverDrawable(Context context, int frameColor) {
- super(context, frameColor);
- // Show as full so it's always uniform color
- super.setBatteryLevel(MAX_BATTERY);
- setPowerSave(true);
- setCharging(false);
- setPowerSaveAsColorError(false);
- mPowerSaveAsColorError = true;
- mFramePaint.setColor(0);
- mPowersavePaint.setColor(frameColor);
- mFramePaint.setStrokeWidth(mPowersavePaint.getStrokeWidth());
- mPlusPaint.setColor(frameColor);
- }
-
- @Override
- protected int batteryColorForLevel(int level) {
- return 0;
- }
-
- @Override
- public void setBatteryLevel(int val) {
- // Don't change the actual level, otherwise this won't draw correctly
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index e1becdb..c587a39 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -25,7 +25,11 @@
import android.content.res.Resources;
import android.provider.Settings;
import android.service.quicksettings.Tile;
+import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
import android.text.TextUtils;
+import android.text.style.TextAppearanceSpan;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -188,7 +192,8 @@
state.secondaryLabel = r.getString(R.string.status_bar_airplane);
} else if (mobileDataEnabled) {
state.state = Tile.STATE_ACTIVE;
- state.secondaryLabel = getMobileDataSubscriptionName(cb);
+ state.secondaryLabel = appendMobileDataType(getMobileDataSubscriptionName(cb),
+ cb.dataContentDescription);
} else {
state.state = Tile.STATE_INACTIVE;
state.secondaryLabel = r.getString(R.string.cell_data_off);
@@ -207,6 +212,18 @@
state.contentDescription = state.label + ", " + contentDescriptionSuffix;
}
+ private CharSequence appendMobileDataType(CharSequence current, CharSequence dataType) {
+ if (TextUtils.isEmpty(dataType)) {
+ return current;
+ }
+ SpannableString type = new SpannableString(dataType);
+ SpannableStringBuilder builder = new SpannableStringBuilder(current);
+ builder.append(" ");
+ builder.append(type, new TextAppearanceSpan(mContext, R.style.TextAppearance_RATBadge),
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ return builder;
+ }
+
private CharSequence getMobileDataSubscriptionName(CallbackInfo cb) {
if (cb.roaming && !TextUtils.isEmpty(cb.dataSubscriptionName)) {
String roaming = mContext.getString(R.string.data_connection_roaming);
@@ -232,6 +249,7 @@
private static final class CallbackInfo {
boolean airplaneModeEnabled;
CharSequence dataSubscriptionName;
+ CharSequence dataContentDescription;
boolean activityIn;
boolean activityOut;
boolean noSim;
@@ -250,6 +268,7 @@
return;
}
mInfo.dataSubscriptionName = mController.getMobileDataNetworkName();
+ mInfo.dataContentDescription = (description != null) ? typeContentDescription : null;
mInfo.activityIn = activityIn;
mInfo.activityOut = activityOut;
mInfo.roaming = roaming;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 52a8814..3e40cfc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -22,6 +22,7 @@
import android.content.res.Resources;
import android.provider.Settings;
import android.service.quicksettings.Tile;
+import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -208,6 +209,9 @@
if (wifiConnected) {
minimalContentDescription.append(cb.wifiSignalContentDescription).append(",");
minimalContentDescription.append(removeDoubleQuotes(cb.ssid));
+ if (!TextUtils.isEmpty(state.secondaryLabel)) {
+ minimalContentDescription.append(",").append(state.secondaryLabel);
+ }
}
}
state.contentDescription = minimalContentDescription.toString();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
index 22d1d5b..d427260 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
@@ -32,6 +32,8 @@
*/
public class FlingAnimationUtils {
+ private static final String TAG = "FlingAnimationUtils";
+
private static final float LINEAR_OUT_SLOW_IN_X2 = 0.35f;
private static final float LINEAR_OUT_SLOW_IN_X2_MAX = 0.68f;
private static final float LINEAR_OUT_FASTER_IN_X2 = 0.5f;
@@ -195,6 +197,10 @@
}
private Interpolator getInterpolator(float startGradient, float velocityFactor) {
+ if (Float.isNaN(velocityFactor)) {
+ Log.e(TAG, "Invalid velocity factor", new Throwable());
+ return Interpolators.LINEAR_OUT_SLOW_IN;
+ }
if (startGradient != mCachedStartGradient
|| velocityFactor != mCachedVelocityFactor) {
float speedup = mSpeedUpFactor * (1.0f - velocityFactor);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
index c945afd..f34b912 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
@@ -15,7 +15,6 @@
*/
package com.android.systemui.statusbar;
-import com.android.systemui.statusbar.notification.NotificationRowBinder;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -27,8 +26,7 @@
* want to perform some action before doing so).
*/
public interface NotificationPresenter extends ExpandableNotificationRow.OnExpandClickListener,
- ActivatableNotificationView.OnActivatedListener,
- NotificationRowBinder.BindRowCallback {
+ ActivatableNotificationView.OnActivatedListener {
/**
* Returns true if the presenter is not visible. For example, it may not be necessary to do
* animations if this returns true.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 3fbc641..4ed9ae4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -35,6 +35,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationRowBinder;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
@@ -124,16 +125,7 @@
return mRemoteInputManager;
}
- private NotificationRowBinder getRowBinder() {
- if (mNotificationRowBinder == null) {
- mNotificationRowBinder = Dependency.get(NotificationRowBinder.class);
- }
- return mNotificationRowBinder;
- }
-
- // TODO: Remove this once we can always use a mocked row binder in our tests
- @VisibleForTesting
- void setRowBinder(NotificationRowBinder notificationRowBinder) {
+ public void setRowBinder(NotificationRowBinder notificationRowBinder) {
mNotificationRowBinder = notificationRowBinder;
}
@@ -345,7 +337,7 @@
Dependency.get(LeakDetector.class).trackInstance(entry);
// Construct the expanded view.
- getRowBinder().inflateViews(entry, () -> performRemoveNotification(notification));
+ requireBinder().inflateViews(entry, () -> performRemoveNotification(notification));
abortExistingInflation(key);
@@ -386,7 +378,7 @@
listener.onPreEntryUpdated(entry);
}
- getRowBinder().inflateViews(entry, () -> performRemoveNotification(notification));
+ requireBinder().inflateViews(entry, () -> performRemoveNotification(notification));
updateNotifications();
if (DEBUG) {
@@ -440,7 +432,7 @@
// By comparing the old and new UI adjustments, reinflate the view accordingly.
for (NotificationEntry entry : entries) {
- getRowBinder().onNotificationRankingUpdated(
+ requireBinder().onNotificationRankingUpdated(
entry,
oldImportances.get(entry.key),
oldAdjustments.get(entry.key),
@@ -486,4 +478,12 @@
activeExtender.setShouldManageLifetime(entry, false);
}
}
+
+ private NotificationRowBinder requireBinder() {
+ if (mNotificationRowBinder == null) {
+ throw new RuntimeException("You must initialize NotificationEntryManager by calling"
+ + "setRowBinder() before using.");
+ }
+ return mNotificationRowBinder;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinder.java
new file mode 100644
index 0000000..7504e86
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinder.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection;
+
+import android.annotation.Nullable;
+
+import com.android.systemui.statusbar.NotificationUiAdjustment;
+import com.android.systemui.statusbar.notification.InflationException;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+
+/**
+ * Used by the {@link NotificationEntryManager}. When notifications are added or updated, the binder
+ * is asked to (re)inflate and prepare their views. This inflation must occur off the main thread.
+ */
+public interface NotificationRowBinder {
+ /**
+ * Called when a notification has been added or updated. The binder must asynchronously inflate
+ * and bind the views associated with the notification.
+ *
+ * TODO: The caller is notified when the inflation completes, but this is currently a very
+ * roundabout business. Add an explicit completion/failure callback to this method.
+ */
+ void inflateViews(
+ NotificationEntry entry,
+ Runnable onDismissRunnable)
+ throws InflationException;
+
+ /**
+ * Called when the ranking has been updated (but not add or remove has been done). The binder
+ * should inspect the old and new adjustments and re-inflate the entry's views if necessary
+ * (e.g. if something important changed).
+ */
+ void onNotificationRankingUpdated(
+ NotificationEntry entry,
+ @Nullable Integer oldImportance,
+ NotificationUiAdjustment oldAdjustment,
+ NotificationUiAdjustment newAdjustment);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
index 6f5baf9..b91cdaf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification;
+package com.android.systemui.statusbar.notification.collection;
import static com.android.internal.util.Preconditions.checkNotNull;
-import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_AMBIENT;
import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP;
@@ -39,7 +38,9 @@
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationUiAdjustment;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.InflationException;
+import com.android.systemui.statusbar.notification.NotificationClicker;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
@@ -50,13 +51,8 @@
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.HeadsUpManager;
-import javax.inject.Inject;
-import javax.inject.Named;
-import javax.inject.Singleton;
-
/** Handles inflating and updating views for notifications. */
-@Singleton
-public class NotificationRowBinder {
+public class NotificationRowBinderImpl implements NotificationRowBinder {
private static final String TAG = "NotificationViewManager";
@@ -84,9 +80,7 @@
private NotificationClicker mNotificationClicker;
private final NotificationLogger mNotificationLogger = Dependency.get(NotificationLogger.class);
- @Inject
- public NotificationRowBinder(Context context,
- @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress) {
+ public NotificationRowBinderImpl(Context context, boolean allowLongPress) {
mContext = context;
mMessagingUtil = new NotificationMessagingUtil(context);
mAllowLongPress = allowLongPress;
@@ -122,6 +116,7 @@
/**
* Inflates the views for the given entry (possibly asynchronously).
*/
+ @Override
public void inflateViews(
NotificationEntry entry,
Runnable onDismissRunnable)
@@ -192,6 +187,7 @@
* Updates the views bound to an entry when the entry's ranking changes, either in-place or by
* reinflating them.
*/
+ @Override
public void onNotificationRankingUpdated(
NotificationEntry entry,
@Nullable Integer oldImportance,
@@ -264,7 +260,7 @@
}
private void logNotificationExpansion(String key, boolean userAction, boolean expanded) {
- mNotificationLogger.onExpansionChanged(key, userAction, expanded);
+ mNotificationLogger.onExpansionChanged(key, userAction, expanded);
}
/** Callback for when a row is bound to an entry. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index 1049773..a17e042 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone;
import android.content.Context;
+import android.hardware.display.AmbientDisplayConfiguration;
import android.os.PowerManager;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -26,7 +27,6 @@
import android.util.SparseBooleanArray;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.hardware.AmbientDisplayConfiguration;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.doze.AlwaysOnDisplayPolicy;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 13d4489..41580f6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -195,9 +195,9 @@
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.NotificationListController;
-import com.android.systemui.statusbar.notification.NotificationRowBinder;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -390,7 +390,6 @@
protected NotificationEntryManager mEntryManager;
private NotificationListController mNotificationListController;
private NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
- private NotificationRowBinder mNotificationRowBinder;
protected NotificationViewHierarchyManager mViewHierarchyManager;
protected ForegroundServiceController mForegroundServiceController;
protected AppOpsController mAppOpsController;
@@ -620,7 +619,6 @@
mEntryManager = Dependency.get(NotificationEntryManager.class);
mNotificationInterruptionStateProvider =
Dependency.get(NotificationInterruptionStateProvider.class);
- mNotificationRowBinder = Dependency.get(NotificationRowBinder.class);
mViewHierarchyManager = Dependency.get(NotificationViewHierarchyManager.class);
mForegroundServiceController = Dependency.get(ForegroundServiceController.class);
mAppOpsController = Dependency.get(AppOpsController.class);
@@ -1032,10 +1030,15 @@
mStatusBarWindow, this, mNotificationPanel,
(NotificationListContainer) mStackScroller);
+ final NotificationRowBinderImpl rowBinder =
+ new NotificationRowBinderImpl(
+ mContext,
+ SystemUIFactory.getInstance().provideAllowNotificationLongPress());
+
mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanel,
mHeadsUpManager, mStatusBarWindow, mStackScroller, mDozeScrimController,
mScrimController, mActivityLaunchAnimator, mStatusBarKeyguardViewManager,
- mNotificationAlertingManager);
+ mNotificationAlertingManager, rowBinder);
mNotificationListController =
new NotificationListController(
@@ -1051,7 +1054,9 @@
mNotificationActivityStarter = new StatusBarNotificationActivityStarter(
mContext, mNotificationPanel, mPresenter, mHeadsUpManager, mActivityLaunchAnimator);
mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
- mNotificationRowBinder.setNotificationClicker(new NotificationClicker(
+
+ mEntryManager.setRowBinder(rowBinder);
+ rowBinder.setNotificationClicker(new NotificationClicker(
this, Dependency.get(BubbleController.class), mNotificationActivityStarter));
mGroupAlertTransferHelper.bind(mEntryManager, mGroupManager);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 3ce66c5..6fe8964 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -64,9 +64,9 @@
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
-import com.android.systemui.statusbar.notification.NotificationRowBinder;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -79,7 +79,8 @@
import java.util.ArrayList;
public class StatusBarNotificationPresenter implements NotificationPresenter,
- ConfigurationController.ConfigurationListener {
+ ConfigurationController.ConfigurationListener,
+ NotificationRowBinderImpl.BindRowCallback {
private final LockscreenGestureLogger mLockscreenGestureLogger =
Dependency.get(LockscreenGestureLogger.class);
@@ -97,8 +98,6 @@
(SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class);
private final NotificationEntryManager mEntryManager =
Dependency.get(NotificationEntryManager.class);
- private final NotificationRowBinder mNotificationRowBinder =
- Dependency.get(NotificationRowBinder.class);
private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider =
Dependency.get(NotificationInterruptionStateProvider.class);
private final NotificationMediaManager mMediaManager =
@@ -140,7 +139,8 @@
ScrimController scrimController,
ActivityLaunchAnimator activityLaunchAnimator,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
- NotificationAlertingManager notificationAlertingManager) {
+ NotificationAlertingManager notificationAlertingManager,
+ NotificationRowBinderImpl notificationRowBinder) {
mContext = context;
mNotificationPanel = panel;
mHeadsUpManager = headsUp;
@@ -217,7 +217,7 @@
mEntryManager.addNotificationLifetimeExtender(mGutsManager);
mEntryManager.addNotificationLifetimeExtenders(
remoteInputManager.getLifetimeExtenders());
- mNotificationRowBinder.setUpWithPresenter(this, notifListContainer, mHeadsUpManager,
+ notificationRowBinder.setUpWithPresenter(this, notifListContainer, mHeadsUpManager,
mEntryManager, this);
mNotificationInterruptionStateProvider.setUpWithPresenter(
this, mHeadsUpManager, this::canHeadsUp);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index ad4ba75..50c4fac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -29,14 +29,18 @@
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.hardware.display.AmbientDisplayConfiguration;
import android.media.AudioManager;
import android.media.session.MediaSessionLegacyHelper;
import android.net.Uri;
import android.os.Bundle;
import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.Settings;
import android.util.AttributeSet;
import android.view.ActionMode;
import android.view.DisplayCutout;
+import android.view.GestureDetector;
import android.view.InputDevice;
import android.view.InputQueue;
import android.view.KeyEvent;
@@ -63,16 +67,23 @@
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.tuner.TunerService;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+/**
+ * Combined status bar and notification panel view. Also holding backdrop and scrims.
+ */
public class StatusBarWindowView extends FrameLayout {
public static final String TAG = "StatusBarWindowView";
public static final boolean DEBUG = StatusBar.DEBUG;
+ private final GestureDetector mGestureDetector;
+ private final StatusBarStateController mStatusBarStateController;
+ private boolean mDoubleTapEnabled;
+ private boolean mSingleTapEnabled;
private DragDownHelper mDragDownHelper;
- private DoubleTapHelper mDoubleTapHelper;
private NotificationStackScrollLayout mStackScrollLayout;
private NotificationPanelView mNotificationPanel;
private View mBrightnessMirror;
@@ -95,8 +106,37 @@
private boolean mTouchActive;
private boolean mExpandAnimationRunning;
private boolean mExpandAnimationPending;
- private final StatusBarStateController
- mStatusBarStateController = Dependency.get(StatusBarStateController.class);
+
+ private final GestureDetector.SimpleOnGestureListener mGestureListener =
+ new GestureDetector.SimpleOnGestureListener() {
+ @Override
+ public boolean onSingleTapConfirmed(MotionEvent e) {
+ if (mSingleTapEnabled) {
+ mService.wakeUpIfDozing(SystemClock.uptimeMillis(), StatusBarWindowView.this,
+ "SINGLE_TAP");
+ }
+ return mSingleTapEnabled;
+ }
+
+ @Override
+ public boolean onDoubleTap(MotionEvent e) {
+ if (mDoubleTapEnabled) {
+ mService.wakeUpIfDozing(SystemClock.uptimeMillis(), StatusBarWindowView.this,
+ "DOUBLE_TAP");
+ }
+ return mDoubleTapEnabled;
+ }
+ };
+ private final TunerService.Tunable mTunable = (key, newValue) -> {
+ AmbientDisplayConfiguration configuration = new AmbientDisplayConfiguration(mContext);
+ switch (key) {
+ case Settings.Secure.DOZE_DOUBLE_TAP_GESTURE:
+ mDoubleTapEnabled = configuration.doubleTapGestureEnabled(UserHandle.USER_CURRENT);
+ break;
+ case Settings.Secure.DOZE_TAP_SCREEN_GESTURE:
+ mSingleTapEnabled = configuration.tapGestureEnabled(UserHandle.USER_CURRENT);
+ }
+ };
/**
* If set to true, the current gesture started below the notch and we need to dispatch touch
@@ -110,10 +150,11 @@
mTransparentSrcPaint.setColor(0);
mTransparentSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
mFalsingManager = FalsingManager.getInstance(context);
- mDoubleTapHelper = new DoubleTapHelper(this, active -> {}, () -> {
- mService.wakeUpIfDozing(SystemClock.uptimeMillis(), this, "DOUBLE_TAP");
- return true;
- }, null, null);
+ mGestureDetector = new GestureDetector(context, mGestureListener);
+ mStatusBarStateController = Dependency.get(StatusBarStateController.class);
+ Dependency.get(TunerService.class).addTunable(mTunable,
+ Settings.Secure.DOZE_DOUBLE_TAP_GESTURE,
+ Settings.Secure.DOZE_TAP_SCREEN_GESTURE);
}
@Override
@@ -306,6 +347,7 @@
return false;
}
mFalsingManager.onTouchEvent(ev, getWidth(), getHeight());
+ mGestureDetector.onTouchEvent(ev);
if (mBrightnessMirror != null && mBrightnessMirror.getVisibility() == VISIBLE) {
// Disallow new pointers while the brightness mirror is visible. This is so that you
// can't touch anything other than the brightness slider while the mirror is showing
@@ -366,7 +408,6 @@
public boolean onTouchEvent(MotionEvent ev) {
boolean handled = false;
if (mService.isDozing()) {
- mDoubleTapHelper.onTouchEvent(ev);
handled = !mService.isPulsing();
}
if ((mStatusBarStateController.getState() == StatusBarState.KEYGUARD && !handled)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
index b22150b..8b81585 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
@@ -57,13 +57,17 @@
private final int mDefaultMinNumSystemGeneratedReplies;
private final int mDefaultMaxNumActions;
- private boolean mEnabled;
- private boolean mRequiresTargetingP;
- private int mMaxSqueezeRemeasureAttempts;
- private boolean mEditChoicesBeforeSending;
- private boolean mShowInHeadsUp;
- private int mMinNumSystemGeneratedReplies;
- private int mMaxNumActions;
+ // These fields are updated on the UI thread but can be accessed on both the UI thread and
+ // background threads. We use the volatile keyword here instead of synchronization blocks since
+ // we only care about variable updates here being visible to other threads (and not for example
+ // whether the variables we are reading were updated in the same go).
+ private volatile boolean mEnabled;
+ private volatile boolean mRequiresTargetingP;
+ private volatile int mMaxSqueezeRemeasureAttempts;
+ private volatile boolean mEditChoicesBeforeSending;
+ private volatile boolean mShowInHeadsUp;
+ private volatile int mMinNumSystemGeneratedReplies;
+ private volatile int mMaxNumActions;
private final Context mContext;
private final KeyValueListParser mParser = new KeyValueListParser(',');
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
index ecf1784..0070dcf 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
@@ -19,6 +19,7 @@
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.DialogInterface;
+import android.hardware.display.AmbientDisplayConfiguration;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
@@ -29,7 +30,6 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceFragment;
-import com.android.internal.hardware.AmbientDisplayConfiguration;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 617b191..ffd8206 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -25,6 +25,7 @@
import static android.media.AudioManager.STREAM_MUSIC;
import static android.media.AudioManager.STREAM_RING;
import static android.media.AudioManager.STREAM_VOICE_CALL;
+import static android.view.View.ACCESSIBILITY_LIVE_REGION_POLITE;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
@@ -431,8 +432,7 @@
if (mSettingsIcon != null) {
mSettingsIcon.setOnClickListener(v -> {
Events.writeEvent(mContext, Events.EVENT_SETTINGS_CLICK);
- Intent intent = new Intent(Settings.ACTION_SOUND_SETTINGS);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ Intent intent = new Intent(Settings.Panel.ACTION_VOLUME);
dismissH(DISMISS_REASON_SETTINGS_CLICKED);
Dependency.get(ActivityStarter.class).startActivity(intent,
true /* dismissShade */);
@@ -442,6 +442,7 @@
public void initRingerH() {
if (mRingerIcon != null) {
+ mRingerIcon.setAccessibilityLiveRegion(ACCESSIBILITY_LIVE_REGION_POLITE);
mRingerIcon.setOnClickListener(v -> {
Prefs.putBoolean(mContext, Prefs.Key.TOUCHED_RINGER_TOGGLE, true);
final StreamState ss = mState.states.get(AudioManager.STREAM_RING);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index e91a7e9..0114075 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -29,6 +29,7 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -82,6 +83,9 @@
mTestableLooper = TestableLooper.get(this);
mDependency.injectTestDependency(Dependency.MAIN_HANDLER,
new Handler(mTestableLooper.getLooper()));
+ mTunablePaddingService = mDependency.injectMockDependency(TunablePaddingService.class);
+ mTunerService = mDependency.injectMockDependency(TunerService.class);
+ mFragmentService = mDependency.injectMockDependency(FragmentService.class);
mStatusBar = mock(StatusBar.class);
mWindowManager = mock(WindowManager.class);
@@ -93,12 +97,9 @@
when(mWindowManager.getDefaultDisplay()).thenReturn(display);
mContext.addMockSystemService(WindowManager.class, mWindowManager);
- mFragmentService = mDependency.injectMockDependency(FragmentService.class);
mFragmentHostManager = mock(FragmentHostManager.class);
when(mFragmentService.getFragmentHostManager(any())).thenReturn(mFragmentHostManager);
- mTunerService = mDependency.injectMockDependency(TunerService.class);
-
mScreenDecorations = new ScreenDecorations() {
@Override
@@ -126,8 +127,7 @@
};
mScreenDecorations.mContext = mContext;
mScreenDecorations.mComponents = mContext.getComponents();
-
- mTunablePaddingService = mDependency.injectMockDependency(TunablePaddingService.class);
+ reset(mTunerService);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationTest.java
index 45342d4..6807c22 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationTest.java
@@ -18,12 +18,12 @@
import static junit.framework.TestCase.assertEquals;
+import android.hardware.display.AmbientDisplayConfiguration;
import android.os.UserHandle;
import android.provider.Settings;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
-import com.android.internal.hardware.AmbientDisplayConfiguration;
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
index e4558df..9438cbb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
@@ -16,12 +16,13 @@
package com.android.systemui.doze;
+import android.hardware.display.AmbientDisplayConfiguration;
+
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.withSettings;
-import com.android.internal.hardware.AmbientDisplayConfiguration;
import com.android.systemui.statusbar.phone.DozeParameters;
import org.mockito.Answers;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
index 0fc0953..23ff3d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
@@ -27,6 +27,7 @@
import static org.mockito.Mockito.when;
import android.app.Instrumentation;
+import android.hardware.display.AmbientDisplayConfiguration;
import android.os.Handler;
import android.os.Looper;
import android.support.test.InstrumentationRegistry;
@@ -35,7 +36,6 @@
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
-import com.android.internal.hardware.AmbientDisplayConfiguration;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerFake;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
index 5a6200f..f972508 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
@@ -38,11 +38,11 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.hardware.display.AmbientDisplayConfiguration;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.UiThreadTest;
-import com.android.internal.hardware.AmbientDisplayConfiguration;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.util.wakelock.WakeLockFake;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
new file mode 100644
index 0000000..463a6e6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.doze;
+
+import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_LOCK_SCREEN;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.AlarmManager;
+import android.hardware.display.AmbientDisplayConfiguration;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.SensorManagerPlugin;
+import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.util.AsyncSensorManager;
+import com.android.systemui.util.wakelock.WakeLock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.function.Consumer;
+
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+@SmallTest
+public class DozeSensorsTest extends SysuiTestCase {
+
+ @Mock
+ private AlarmManager mAlarmManager;
+ @Mock
+ private AsyncSensorManager mSensorManager;
+ @Mock
+ private DozeParameters mDozeParameters;
+ @Mock
+ private AmbientDisplayConfiguration mAmbientDisplayConfiguration;
+ @Mock
+ private WakeLock mWakeLock;
+ @Mock
+ private DozeSensors.Callback mCallback;
+ @Mock
+ private Consumer<Boolean> mProxCallback;
+ @Mock
+ private AlwaysOnDisplayPolicy mAlwaysOnDisplayPolicy;
+ private SensorManagerPlugin.SensorEventListener mWakeLockScreenListener;
+ private TestableLooper mTestableLooper;
+ private DozeSensors mDozeSensors;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mTestableLooper = TestableLooper.get(this);
+ when(mAmbientDisplayConfiguration.getWakeLockScreenDebounce()).thenReturn(5000L);
+ when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true);
+ doAnswer(invocation -> {
+ ((Runnable) invocation.getArgument(0)).run();
+ return null;
+ }).when(mWakeLock).wrap(any(Runnable.class));
+ mDozeSensors = new TestableDozeSensors();
+ }
+
+ @Test
+ public void testSensorDebounce() {
+ mDozeSensors.setListening(true);
+
+ mWakeLockScreenListener.onSensorChanged(mock(SensorManagerPlugin.SensorEvent.class));
+ mTestableLooper.processAllMessages();
+ verify(mCallback).onSensorPulse(eq(DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN),
+ anyBoolean(), anyFloat(), anyFloat(), eq(null));
+
+ mDozeSensors.requestTemporaryDisable();
+ reset(mCallback);
+ mWakeLockScreenListener.onSensorChanged(mock(SensorManagerPlugin.SensorEvent.class));
+ mTestableLooper.processAllMessages();
+ verify(mCallback, never()).onSensorPulse(eq(DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN),
+ anyBoolean(), anyFloat(), anyFloat(), eq(null));
+ }
+
+ private class TestableDozeSensors extends DozeSensors {
+
+ TestableDozeSensors() {
+ super(getContext(), mAlarmManager, mSensorManager, mDozeParameters,
+ mAmbientDisplayConfiguration, mWakeLock, mCallback, mProxCallback,
+ mAlwaysOnDisplayPolicy);
+ for (TriggerSensor sensor : mSensors) {
+ if (sensor instanceof PluginSensor
+ && ((PluginSensor) sensor).mPluginSensor.getType()
+ == TYPE_WAKE_LOCK_SCREEN) {
+ mWakeLockScreenListener = (PluginSensor) sensor;
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index cdac7c97..ca37347 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -27,6 +27,7 @@
import android.app.AlarmManager;
import android.app.Instrumentation;
+import android.hardware.display.AmbientDisplayConfiguration;
import android.os.Handler;
import android.os.Looper;
import android.support.test.InstrumentationRegistry;
@@ -34,7 +35,6 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
-import com.android.internal.hardware.AmbientDisplayConfiguration;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerFake;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 04e7cab..0d4328f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -67,6 +67,8 @@
import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationRowBinder;
+import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -102,10 +104,8 @@
@Mock private KeyguardEnvironment mEnvironment;
@Mock private ExpandableNotificationRow mRow;
@Mock private NotificationListContainer mListContainer;
- @Mock
- private NotificationEntryListener mEntryListener;
- @Mock
- private NotificationRowBinder.BindRowCallback mBindCallback;
+ @Mock private NotificationEntryListener mEntryListener;
+ @Mock private NotificationRowBinderImpl.BindRowCallback mBindCallback;
@Mock private HeadsUpManager mHeadsUpManager;
@Mock private NotificationListenerService.RankingMap mRankingMap;
@Mock private RemoteInputController mRemoteInputController;
@@ -235,10 +235,12 @@
mEntryManager.setUpWithPresenter(mPresenter, mListContainer, mHeadsUpManager);
mEntryManager.addNotificationEntryListener(mEntryListener);
- NotificationRowBinder notificationRowBinder = Dependency.get(NotificationRowBinder.class);
+ NotificationRowBinderImpl notificationRowBinder =
+ new NotificationRowBinderImpl(mContext, true /* allowLongPress */);
notificationRowBinder.setUpWithPresenter(
mPresenter, mListContainer, mHeadsUpManager, mEntryManager, mBindCallback);
notificationRowBinder.setNotificationClicker(mock(NotificationClicker.class));
+ mEntryManager.setRowBinder(notificationRowBinder);
setUserSentiment(mEntry.key, NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index 12cb9957..d2b0f7f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -41,6 +41,7 @@
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.NotificationAlertingManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -74,7 +75,8 @@
statusBarWindowView, mock(NotificationListContainerViewGroup.class),
mock(DozeScrimController.class), mock(ScrimController.class),
mock(ActivityLaunchAnimator.class), mock(StatusBarKeyguardViewManager.class),
- mock(NotificationAlertingManager.class));
+ mock(NotificationAlertingManager.class),
+ mock(NotificationRowBinderImpl.class));
}
@Test
diff --git a/proto/Android.bp b/proto/Android.bp
index 7f826b3..7b119a7 100644
--- a/proto/Android.bp
+++ b/proto/Android.bp
@@ -28,14 +28,4 @@
srcs: ["src/metrics_constants/metrics_constants.proto"],
no_framework_libs: true,
sdk_version: "system_current",
- // Pin java_version until jarjar is certified to support later versions. http://b/72703434
- java_version: "1.8",
- target: {
- android: {
- jarjar_rules: "jarjar-rules.txt",
- },
- host: {
- static_libs: ["libprotobuf-java-nano"],
- },
- },
}
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index b3e5f69..8929e82 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -7017,6 +7017,54 @@
// OS: Q
ACTION_SWITCH_SHARE_PROFILE = 1661;
+ // ACTION: Show Contextual homepage, log latency in loading cards
+ ACTION_CONTEXTUAL_HOME_SHOW = 1662;
+
+ // ACTION: Contextual card displays
+ ACTION_CONTEXTUAL_CARD_SHOW = 1663;
+
+ // ACTION: Contextual cards are eligible to be shown, but don't rank high
+ ACTION_CONTEXTUAL_CARD_NOT_SHOW = 1664;
+
+ // ACTION: Settings > long press a card, and click dismiss
+ // Contextual card is dismissed
+ ACTION_CONTEXTUAL_CARD_DISMISS = 1665;
+
+ // ACTION: Settings > click a card
+ // Contextual card is clicked
+ ACTION_CONTEXTUAL_CARD_CLICK = 1666;
+
+ // Mapping: go/at-mapping
+ PAGE_ATSSI = 1667;
+
+ PAGE_ATSII = 1668;
+
+ PAGE_ATUS = 1669;
+
+ PAGE_ATSSP = 1670;
+
+ PAGE_ATSAP = 1671;
+
+ PAGE_ATSCP = 1672;
+
+ PAGE_ATHNP = 1673;
+
+ ACTION_ATSG = 1674;
+
+ ACTION_ATPG = 1675;
+
+ ACTION_ATCLPB = 1676;
+
+ ACTION_ATCGIB = 1677;
+
+ ACTION_ATCPAB = 1678;
+
+ ACTION_ATCSAUC = 1679;
+
+ ACTION_ATCSCUC = 1680;
+
+ ACTION_ATCHNUC = 1681;
+
// ---- End Q Constants, all Q constants go above this line ----
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index ee3777d..670e794 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -521,6 +521,9 @@
// Total number of saved networks with mac randomization enabled.
optional int32 num_saved_networks_with_mac_randomization = 138;
+
+ // Link Probe metrics
+ optional LinkProbeStats link_probe_stats = 139;
}
// Information that gets logged for every WiFi connection.
@@ -1959,7 +1962,6 @@
// The total duration elapsed while in this mobility state, in ms
optional int64 total_duration_ms = 3;
-
// the total duration elapsed while in this mobility state with PNO scans running, in ms
optional int64 pno_duration_ms = 4;
}
@@ -2098,6 +2100,8 @@
// Easy Connect (DPP)
message WifiDppLog {
+ reserved 6;
+
// Number of Configurator-Initiator requests
optional int32 num_dpp_configurator_initiator_requests = 1;
@@ -2114,7 +2118,7 @@
repeated DppFailureStatusHistogramBucket dpp_failure_code = 5;
// Easy Connect (DPP) operation time bucket
- repeated HistogramBucket dpp_operation_time = 6;
+ repeated HistogramBucketInt32 dpp_operation_time = 7;
// Histogram bucket for Wi-Fi DPP configurator success
message DppConfiguratorSuccessStatusHistogramBucket {
@@ -2134,18 +2138,6 @@
optional int32 count = 2;
}
- // Histogram bucket for Wi-Fi DPP logs. Range is [start, end)
- message HistogramBucket {
- // lower range of the bucket (inclusive)
- optional int32 start = 1;
-
- // upper range of the bucket (exclusive)
- optional int32 end = 2;
-
- // number of samples in the bucket
- optional int32 count = 3;
- }
-
enum DppConfiguratorSuccessCode {
// Unknown success code
EASY_CONNECT_EVENT_SUCCESS_UNKNOWN = 0;
@@ -2211,3 +2203,76 @@
optional int32 count = 3;
}
}
+
+// Histogram bucket counting with int32. Range is [start, end)
+message HistogramBucketInt32 {
+ // lower range of the bucket (inclusive)
+ optional int32 start = 1;
+
+ // upper range of the bucket (exclusive)
+ optional int32 end = 2;
+
+ // number of samples in the bucket
+ optional int32 count = 3;
+}
+
+// Single entry in a map from int32 => int32
+message MapEntryInt32Int32 {
+ // the key
+ optional int32 key = 1;
+
+ // the value
+ optional int32 value = 2;
+}
+
+message LinkProbeStats {
+ enum LinkProbeFailureReason {
+ // unknown reason
+ LINK_PROBE_FAILURE_REASON_UNKNOWN = 0;
+
+ // Specified MCS rate when it is unsupported by the driver.
+ LINK_PROBE_FAILURE_REASON_MCS_UNSUPPORTED = 1;
+
+ // Driver reported that no ACK was received for the transmitted probe.
+ LINK_PROBE_FAILURE_REASON_NO_ACK = 2;
+
+ // Driver failed to report on the status of the transmitted probe within the timeout.
+ LINK_PROBE_FAILURE_REASON_TIMEOUT = 3;
+
+ // An existing link probe is in progress.
+ LINK_PROBE_FAILURE_REASON_ALREADY_STARTED = 4;
+ }
+
+ // Counts the number of failures for each failure reason.
+ message LinkProbeFailureReasonCount {
+ // The failure reason.
+ optional LinkProbeFailureReason failure_reason = 1;
+
+ // The number of occurrences for this failure reason.
+ optional int32 count = 2;
+ }
+
+ // Counts the occurrences of RSSI values when a link probe succeeds.
+ repeated MapEntryInt32Int32 success_rssi_counts = 1;
+
+ // Counts the occurrences of RSSI values when a link probe fails.
+ repeated MapEntryInt32Int32 failure_rssi_counts = 2;
+
+ // Counts the occurrences of Link Speed values when a link probe succeeds.
+ repeated MapEntryInt32Int32 success_link_speed_counts = 3;
+
+ // Counts the occurrences of Link Speed values when a link probe fails.
+ repeated MapEntryInt32Int32 failure_link_speed_counts = 4;
+
+ // Histogram for the number of seconds since the last TX success when a link probe succeeds.
+ repeated HistogramBucketInt32 success_seconds_since_last_tx_success_histogram = 5;
+
+ // Histogram for the number of seconds since the last TX success when a link probe fails.
+ repeated HistogramBucketInt32 failure_seconds_since_last_tx_success_histogram = 6;
+
+ // Histogram for the elapsed time of successful link probes, in ms.
+ repeated HistogramBucketInt32 success_elapsed_time_ms_histogram = 7;
+
+ // Counts the occurrences of error codes for failed link probes.
+ repeated LinkProbeFailureReasonCount failure_reason_counts = 8;
+}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 1cca813..245e2c9 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -70,6 +70,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.infra.AbstractRemoteService;
import com.android.internal.os.IResultReceiver;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
@@ -109,6 +110,8 @@
private static final char COMPAT_PACKAGE_URL_IDS_BLOCK_BEGIN = '[';
private static final char COMPAT_PACKAGE_URL_IDS_BLOCK_END = ']';
+ private static final int DEFAULT_AUGMENTED_AUTOFILL_REQUEST_TIMEOUT_MILLIS = 5_000;
+
/**
* Maximum number of partitions that can be allowed in a session.
*
@@ -162,6 +165,11 @@
@GuardedBy("mLock")
private int mSupportedSmartSuggestionModes;
+ @GuardedBy("mLock")
+ int mAugmentedServiceIdleUnbindTimeoutMs;
+ @GuardedBy("mLock")
+ int mAugmentedServiceRequestTimeoutMs;
+
public AutofillManagerService(Context context) {
super(context,
new SecureSettingsServiceNameResolver(context, Settings.Secure.AUTOFILL_SERVICE),
@@ -171,12 +179,12 @@
DeviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_AUTOFILL,
ActivityThread.currentApplication().getMainExecutor(),
- (namespace, name, value) -> setSmartSuggestionModesFromDeviceConfig(value));
+ (namespace, key, value) -> onDeviceConfigChange(key, value));
setLogLevelFromSettings();
setMaxPartitionsFromSettings();
setMaxVisibleDatasetsFromSettings();
- setSmartSuggestionModesFromDeviceConfig();
+ setDeviceConfigProperties();
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
@@ -227,6 +235,18 @@
}
}
+ private void onDeviceConfigChange(@NonNull String key, @Nullable String value) {
+ switch (key) {
+ case AutofillManager.DEVICE_CONFIG_AUTOFILL_SMART_SUGGESTION_SUPPORTED_MODES:
+ case AutofillManager.DEVICE_CONFIG_AUGMENTED_SERVICE_IDLE_UNBIND_TIMEOUT:
+ case AutofillManager.DEVICE_CONFIG_AUGMENTED_SERVICE_REQUEST_TIMEOUT:
+ setDeviceConfigProperties();
+ break;
+ default:
+ Slog.i(mTag, "Ignoring change on " + key);
+ }
+ }
+
@Override // from AbstractMasterSystemService
protected AutofillManagerServiceImpl newServiceLocked(@UserIdInt int resolvedUserId,
boolean disabled) {
@@ -457,27 +477,24 @@
}
}
- private void setSmartSuggestionModesFromDeviceConfig() {
- final String value = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_AUTOFILL,
- AutofillManager.DEVICE_CONFIG_AUTOFILL_SMART_SUGGESTION_SUPPORTED_MODES);
- setSmartSuggestionModesFromDeviceConfig(value);
- }
-
- private void setSmartSuggestionModesFromDeviceConfig(@Nullable String value) {
- if (sDebug) Slog.d(TAG, "setSmartSuggestionEmulationFromDeviceConfig(): value=" + value);
- final int flags;
- if (value == null) {
- flags = AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM;
- } else {
- try {
- flags = Integer.parseInt(value);
- } catch (Exception e) {
- Slog.w(TAG, "setSmartSuggestionEmulationFromDeviceConfig(): NAN:" + value);
- return;
- }
- }
+ private void setDeviceConfigProperties() {
synchronized (mLock) {
- mSupportedSmartSuggestionModes = flags;
+ mAugmentedServiceIdleUnbindTimeoutMs = Helper.getIntDeviceConfigProperty(
+ AutofillManager.DEVICE_CONFIG_AUGMENTED_SERVICE_IDLE_UNBIND_TIMEOUT,
+ (int) AbstractRemoteService.PERMANENT_BOUND_TIMEOUT_MS);
+ mAugmentedServiceRequestTimeoutMs = Helper.getIntDeviceConfigProperty(
+ AutofillManager.DEVICE_CONFIG_AUGMENTED_SERVICE_REQUEST_TIMEOUT,
+ DEFAULT_AUGMENTED_AUTOFILL_REQUEST_TIMEOUT_MILLIS);
+ mSupportedSmartSuggestionModes = Helper.getIntDeviceConfigProperty(
+ AutofillManager.DEVICE_CONFIG_AUTOFILL_SMART_SUGGESTION_SUPPORTED_MODES,
+ AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM);
+ if (verbose) {
+ Slog.v(mTag, "setDeviceConfigProperties(): "
+ + "augmentedIdleTimeout=" + mAugmentedServiceIdleUnbindTimeoutMs
+ + ", augmentedRequestTimeout=" + mAugmentedServiceRequestTimeoutMs
+ + ", smartSuggestionMode="
+ + getSmartSuggestionModeToString(mSupportedSmartSuggestionModes));
+ }
}
}
@@ -1280,6 +1297,10 @@
pw.print("Smart Suggestion modes: ");
pw.println(getSmartSuggestionModeToString(mSupportedSmartSuggestionModes));
}
+ pw.print("Augmented Service Idle Unbind Timeout: ");
+ pw.println(mAugmentedServiceIdleUnbindTimeoutMs);
+ pw.print("Augmented Service Request Timeout: ");
+ pw.println(mAugmentedServiceRequestTimeoutMs);
if (showHistory) {
pw.println(); pw.println("Requests history:"); pw.println();
mRequestsHistory.reverseDump(fd, pw, args);
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 2e99654..3f33813 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -98,7 +98,7 @@
private static final int MAX_SESSION_ID_CREATE_TRIES = 2048;
/** Minimum interval to prune abandoned sessions */
- private static final int MAX_ABANDONED_SESSION_MILLIS = 30000;
+ private static final int MAX_ABANDONED_SESSION_MILLIS = 30_000;
private final AutoFillUI mUi;
private final MetricsLogger mMetricsLogger = new MetricsLogger();
@@ -1087,7 +1087,9 @@
}
mRemoteAugmentedAutofillService = null;
}
- }, mMaster.isInstantServiceAllowed(), mMaster.verbose);
+ }, mMaster.isInstantServiceAllowed(), mMaster.verbose,
+ mMaster.mAugmentedServiceIdleUnbindTimeoutMs,
+ mMaster.mAugmentedServiceRequestTimeoutMs);
}
return mRemoteAugmentedAutofillService;
@@ -1097,14 +1099,17 @@
* Called when the {@link #mAugmentedAutofillResolver} changed (among other places).
*/
private void updateRemoteAugmentedAutofillService(@Nullable String serviceName) {
- if (serviceName == null) {
- if (sVerbose) Slog.v(TAG, "updateRemoteAugmentedAutofillService(): time's up!");
- synchronized (mLock) {
- if (mRemoteAugmentedAutofillService != null) {
- mRemoteAugmentedAutofillService.destroy();
- mRemoteAugmentedAutofillService = null;
+ synchronized (mLock) {
+ if (mRemoteAugmentedAutofillService != null) {
+ if (sVerbose) {
+ Slog.v(TAG, "updateRemoteAugmentedAutofillService(): "
+ + "destroying old remote service");
}
+ mRemoteAugmentedAutofillService.destroy();
+ mRemoteAugmentedAutofillService = null;
}
+
+ mRemoteAugmentedAutofillService = getRemoteAugmentedAutofillServiceLocked();
}
}
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index 3c0da7d..d300bf2 100644
--- a/services/autofill/java/com/android/server/autofill/Helper.java
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -22,9 +22,11 @@
import android.app.assist.AssistStructure.ViewNode;
import android.content.ComponentName;
import android.metrics.LogMaker;
+import android.provider.DeviceConfig;
import android.service.autofill.Dataset;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Log;
import android.util.Slog;
import android.view.WindowManager;
import android.view.autofill.AutofillId;
@@ -205,6 +207,21 @@
}
}
+ /**
+ * Gets the value of a device config property from the Autofill namespace.
+ */
+ static int getIntDeviceConfigProperty(@NonNull String key, int defaultValue) {
+ final String value = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_AUTOFILL, key);
+ if (value == null) return defaultValue;
+
+ try {
+ return Integer.parseInt(value);
+ } catch (Exception e) {
+ Log.w(TAG, "error parsing value (" + value + ") of property " + key + ": " + e);
+ return defaultValue;
+ }
+ }
+
private interface ViewNodeFilter {
boolean matches(ViewNode node);
}
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index 88228fb..a38c3cf 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -31,7 +31,6 @@
import android.service.autofill.augmented.AugmentedAutofillService;
import android.service.autofill.augmented.IAugmentedAutofillService;
import android.service.autofill.augmented.IFillCallback;
-import android.text.format.DateUtils;
import android.util.Pair;
import android.util.Slog;
import android.view.autofill.AutofillId;
@@ -48,13 +47,17 @@
private static final String TAG = RemoteAugmentedAutofillService.class.getSimpleName();
- private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
+ private final int mIdleUnbindTimeoutMs;
+ private final int mRequestTimeoutMs;
RemoteAugmentedAutofillService(Context context, ComponentName serviceName,
int userId, RemoteAugmentedAutofillServiceCallbacks callbacks,
- boolean bindInstantServiceAllowed, boolean verbose) {
+ boolean bindInstantServiceAllowed, boolean verbose, int idleUnbindTimeoutMs,
+ int requestTimeoutMs) {
super(context, AugmentedAutofillService.SERVICE_INTERFACE, serviceName, userId, callbacks,
bindInstantServiceAllowed, verbose);
+ mIdleUnbindTimeoutMs = idleUnbindTimeoutMs;
+ mRequestTimeoutMs = requestTimeoutMs;
// Bind right away.
scheduleBind();
@@ -108,12 +111,12 @@
@Override // from AbstractRemoteService
protected long getTimeoutIdleBindMillis() {
- return PERMANENT_BOUND_TIMEOUT_MS;
+ return mIdleUnbindTimeoutMs;
}
@Override // from AbstractRemoteService
protected long getRemoteRequestMillis() {
- return TIMEOUT_REMOTE_REQUEST_MILLIS;
+ return mRequestTimeoutMs;
}
/**
@@ -209,7 +212,7 @@
protected void onTimeout(RemoteAugmentedAutofillService remoteService) {
// TODO(b/122858578): must update the logged AUTOFILL_AUGMENTED_REQUEST with the
// timeout
- Slog.w(TAG, "PendingAutofillRequest timed out (" + TIMEOUT_REMOTE_REQUEST_MILLIS
+ Slog.w(TAG, "PendingAutofillRequest timed out (" + remoteService.mRequestTimeoutMs
+ "ms) for " + remoteService);
// NOTE: so far we don't need notify RemoteAugmentedAutofillServiceCallbacks
finish();
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 848f249..2b94d10d6 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -17,6 +17,7 @@
package com.android.server.autofill;
import static android.service.autofill.AutofillFieldClassificationService.EXTRA_SCORES;
+import static android.service.autofill.FillRequest.FLAG_AUGMENTED_AUTOFILL_REQUEST;
import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
import static android.service.autofill.FillRequest.INVALID_REQUEST_ID;
import static android.view.autofill.AutofillManager.ACTION_START_SESSION;
@@ -527,6 +528,16 @@
*/
@GuardedBy("mLock")
private void requestNewFillResponseLocked(int flags) {
+
+ if ((flags & FLAG_AUGMENTED_AUTOFILL_REQUEST) != 0) {
+ // TODO(b/122858578): log metrics
+ if (sVerbose) {
+ Slog.v(TAG, "requestNewFillResponse(): triggering augmented autofill instead");
+ }
+ triggerAugmentedAutofillLocked();
+ return;
+ }
+
int requestId;
do {
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index d5713a1..9f7a940 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -52,6 +52,7 @@
import android.view.contentcapture.UserDataRemovalRequest;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.infra.AbstractRemoteService;
import com.android.internal.os.IResultReceiver;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
@@ -103,11 +104,12 @@
private boolean mDisabledByDeviceConfig;
// Device-config settings that are cached and passed back to apps
- public int mDevCfgLoggingLevel;
- public int mDevCfgMaxBufferSize;
- public int mDevCfgIdleFlushingFrequencyMs;
- public int mDevCfgTextChangeFlushingFrequencyMs;
- public int mDevCfgLogHistorySize;
+ @GuardedBy("mLock") int mDevCfgLoggingLevel;
+ @GuardedBy("mLock") int mDevCfgMaxBufferSize;
+ @GuardedBy("mLock") int mDevCfgIdleFlushingFrequencyMs;
+ @GuardedBy("mLock") int mDevCfgTextChangeFlushingFrequencyMs;
+ @GuardedBy("mLock") int mDevCfgLogHistorySize;
+ @GuardedBy("mLock") int mDevCfgIdleUnbindTimeoutMs;
public ContentCaptureManagerService(@NonNull Context context) {
super(context, new FrameworkResourcesServiceNameResolver(context,
@@ -238,6 +240,7 @@
case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY:
case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE:
case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_TEXT_CHANGE_FLUSH_FREQUENCY:
+ case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_UNBIND_TIMEOUT:
setFineTuneParamsFromDeviceConfig();
return;
default:
@@ -246,22 +249,29 @@
}
private void setFineTuneParamsFromDeviceConfig() {
- mDevCfgMaxBufferSize = ContentCaptureHelper.getIntDeviceConfigProperty(
- ContentCaptureManager.DEVICE_CONFIG_PROPERTY_MAX_BUFFER_SIZE,
- ContentCaptureManager.DEFAULT_MAX_BUFFER_SIZE);
- mDevCfgIdleFlushingFrequencyMs = ContentCaptureHelper.getIntDeviceConfigProperty(
- ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY,
- ContentCaptureManager.DEFAULT_IDLE_FLUSHING_FREQUENCY_MS);
- mDevCfgTextChangeFlushingFrequencyMs = ContentCaptureHelper.getIntDeviceConfigProperty(
- ContentCaptureManager.DEVICE_CONFIG_PROPERTY_TEXT_CHANGE_FLUSH_FREQUENCY,
- ContentCaptureManager.DEFAULT_TEXT_CHANGE_FLUSHING_FREQUENCY_MS);
- mDevCfgLogHistorySize = ContentCaptureHelper.getIntDeviceConfigProperty(
- ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE, 20);
- if (verbose) {
- Slog.v(mTag, "setFineTuneParamsFromDeviceConfig(): bufferSize=" + mDevCfgMaxBufferSize
- + ", idleFlush=" + mDevCfgIdleFlushingFrequencyMs
- + ", textFluxh=" + mDevCfgTextChangeFlushingFrequencyMs
- + ", logHistory=" + mDevCfgLogHistorySize);
+ synchronized (mLock) {
+ mDevCfgMaxBufferSize = ContentCaptureHelper.getIntDeviceConfigProperty(
+ ContentCaptureManager.DEVICE_CONFIG_PROPERTY_MAX_BUFFER_SIZE,
+ ContentCaptureManager.DEFAULT_MAX_BUFFER_SIZE);
+ mDevCfgIdleFlushingFrequencyMs = ContentCaptureHelper.getIntDeviceConfigProperty(
+ ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY,
+ ContentCaptureManager.DEFAULT_IDLE_FLUSHING_FREQUENCY_MS);
+ mDevCfgTextChangeFlushingFrequencyMs = ContentCaptureHelper.getIntDeviceConfigProperty(
+ ContentCaptureManager.DEVICE_CONFIG_PROPERTY_TEXT_CHANGE_FLUSH_FREQUENCY,
+ ContentCaptureManager.DEFAULT_TEXT_CHANGE_FLUSHING_FREQUENCY_MS);
+ mDevCfgLogHistorySize = ContentCaptureHelper.getIntDeviceConfigProperty(
+ ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE, 20);
+ mDevCfgIdleUnbindTimeoutMs = ContentCaptureHelper.getIntDeviceConfigProperty(
+ ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_UNBIND_TIMEOUT,
+ (int) AbstractRemoteService.PERMANENT_BOUND_TIMEOUT_MS);
+ if (verbose) {
+ Slog.v(mTag, "setFineTuneParamsFromDeviceConfig(): "
+ + "bufferSize=" + mDevCfgMaxBufferSize
+ + ", idleFlush=" + mDevCfgIdleFlushingFrequencyMs
+ + ", textFluxh=" + mDevCfgTextChangeFlushingFrequencyMs
+ + ", logHistory=" + mDevCfgLogHistorySize
+ + ", idleUnbindTimeoutMs=" + mDevCfgIdleUnbindTimeoutMs);
+ }
}
}
@@ -472,6 +482,8 @@
pw.print(prefix2); pw.print("textChangeFlushingFrequencyMs: ");
pw.println(mDevCfgTextChangeFlushingFrequencyMs);
pw.print(prefix2); pw.print("logHistorySize: "); pw.println(mDevCfgLogHistorySize);
+ pw.print(prefix2); pw.print("idleUnbindTimeoutMs: ");
+ pw.println(mDevCfgIdleUnbindTimeoutMs);
}
final class ContentCaptureManagerServiceStub extends IContentCaptureManager.Stub {
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index 5b48046..b33259d 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -126,7 +126,7 @@
mRemoteService = new RemoteContentCaptureService(mMaster.getContext(),
ContentCaptureService.SERVICE_INTERFACE, serviceComponentName,
mRemoteServiceCallback, mUserId, this, mMaster.isBindInstantServiceAllowed(),
- mMaster.verbose);
+ mMaster.verbose, mMaster.mDevCfgIdleUnbindTimeoutMs);
}
}
diff --git a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
index f9fda64..dc07c0a 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
@@ -38,14 +38,16 @@
IContentCaptureService> {
private final IBinder mServerCallback;
+ private final int mIdleUnbindTimeoutMs;
RemoteContentCaptureService(Context context, String serviceInterface,
ComponentName serviceComponentName, IContentCaptureServiceCallback callback, int userId,
ContentCaptureServiceCallbacks callbacks, boolean bindInstantServiceAllowed,
- boolean verbose) {
+ boolean verbose, int idleUnbindTimeoutMs) {
super(context, serviceInterface, serviceComponentName, userId, callbacks,
bindInstantServiceAllowed, verbose, /* initialCapacity= */ 2);
mServerCallback = callback.asBinder();
+ mIdleUnbindTimeoutMs = idleUnbindTimeoutMs;
// Bind right away, which will trigger a onConnected() on service's
scheduleBind();
@@ -58,8 +60,7 @@
@Override // from AbstractRemoteService
protected long getTimeoutIdleBindMillis() {
- // TODO(b/111276913): read from Settings so it can be changed in the field
- return PERMANENT_BOUND_TIMEOUT_MS;
+ return mIdleUnbindTimeoutMs;
}
@Override // from RemoteService
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index ed459db..c37a805 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -2375,6 +2375,11 @@
pw.decreaseIndent();
}
+
+ pw.println();
+ pw.println("NetworkStackClient logs:");
+ pw.increaseIndent();
+ NetworkStackClient.getInstance().dump(pw);
}
private void dumpNetworks(IndentingPrintWriter pw) {
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 5989a46..d8b96e4 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -1972,7 +1972,7 @@
continue;
}
- // requests that ignore location settings will never provider notifications
+ // requests that ignore location settings will never provide notifications
if (isSettingsExemptLocked(record)) {
continue;
}
@@ -2010,19 +2010,22 @@
WorkSource worksource = new WorkSource();
ProviderRequest providerRequest = new ProviderRequest();
- long backgroundThrottleInterval;
-
- long identity = Binder.clearCallingIdentity();
- try {
- backgroundThrottleInterval = Settings.Global.getLong(
- mContext.getContentResolver(),
- Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
- DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
-
if (records != null && !records.isEmpty()) {
+ long backgroundThrottleInterval;
+
+ long identity = Binder.clearCallingIdentity();
+ try {
+ backgroundThrottleInterval = Settings.Global.getLong(
+ mContext.getContentResolver(),
+ Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
+ DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+
+ final boolean isForegroundOnlyMode =
+ mPowerManager.getLocationPowerSaveMode()
+ == PowerManager.LOCATION_MODE_FOREGROUND_ONLY;
// initialize the low power mode to true and set to false if any of the records requires
providerRequest.lowPowerMode = true;
for (UpdateRecord record : records) {
@@ -2037,7 +2040,9 @@
record.mReceiver.mAllowedResolutionLevel)) {
continue;
}
- if (!provider.isUseableLocked()) {
+ final boolean isBatterySaverDisablingLocation =
+ isForegroundOnlyMode && !record.mIsForegroundUid;
+ if (!provider.isUseableLocked() || isBatterySaverDisablingLocation) {
if (isSettingsExemptLocked(record)) {
providerRequest.locationSettingsIgnored = true;
providerRequest.lowPowerMode = false;
diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java
index 526aebe..097a7d6 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -42,6 +42,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.MediaStore;
@@ -235,9 +236,17 @@
* Handler for on start pinning message
*/
private void handlePinOnStart() {
- // Files to pin come from the overlay and can be specified per-device config
- String[] filesToPin = mContext.getResources().getStringArray(
- com.android.internal.R.array.config_defaultPinnerServiceFiles);
+ final String bootImage = SystemProperties.get("dalvik.vm.boot-image", "");
+ String[] filesToPin = null;
+ if (bootImage.endsWith("apex.art")) {
+ // Use the files listed for that specific boot image
+ filesToPin = mContext.getResources().getStringArray(
+ com.android.internal.R.array.config_apexBootImagePinnerServiceFiles);
+ } else {
+ // Files to pin come from the overlay and can be specified per-device config
+ filesToPin = mContext.getResources().getStringArray(
+ com.android.internal.R.array.config_defaultPinnerServiceFiles);
+ }
// Continue trying to pin each file even if we fail to pin some of them
for (String fileToPin : filesToPin) {
PinnedFile pf = pinFile(fileToPin,
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index a5eab85..ada3947 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -347,12 +347,6 @@
@GuardedBy("mPackagesLock")
private final SparseArray<ArraySet<String>> mPackages = new SparseArray<>();
- /**
- * List of volumes visible to any user.
- * TODO: may be have a map of userId -> volumes?
- */
- private final CopyOnWriteArrayList<VolumeInfo> mVisibleVols = new CopyOnWriteArrayList<>();
-
private volatile int mCurrentUserId = UserHandle.USER_SYSTEM;
/** Holding lock for AppFuse business */
@@ -956,8 +950,6 @@
addInternalVolumeLocked();
}
- mVisibleVols.clear();
-
try {
mVold.reset();
@@ -1895,9 +1887,6 @@
private void mount(VolumeInfo vol) {
try {
mVold.mount(vol.id, vol.mountFlags, vol.mountUserId);
- if ((vol.mountFlags & VolumeInfo.MOUNT_FLAG_VISIBLE) != 0) {
- mVisibleVols.add(vol);
- }
} catch (Exception e) {
Slog.wtf(TAG, e);
}
@@ -1914,9 +1903,6 @@
private void unmount(VolumeInfo vol) {
try {
mVold.unmount(vol.id);
- if ((vol.mountFlags & VolumeInfo.MOUNT_FLAG_VISIBLE) != 0) {
- mVisibleVols.remove(vol);
- }
} catch (Exception e) {
Slog.wtf(TAG, e);
}
@@ -3851,14 +3837,6 @@
pw.decreaseIndent();
pw.println();
- pw.println("mVisibleVols:");
- pw.increaseIndent();
- for (int i = 0; i < mVisibleVols.size(); i++) {
- mVisibleVols.get(i).dump(pw);
- }
- pw.decreaseIndent();
-
- pw.println();
pw.println("Primary storage UUID: " + mPrimaryStorageUuid);
pw.println();
@@ -4056,38 +4034,9 @@
}
@Override
- public String[] getVisibleVolumesForUser(int userId) {
- synchronized (mLock) {
- if (!ArrayUtils.contains(mSystemUnlockedUsers, userId)) {
- return EmptyArray.STRING;
- }
- }
- final ArrayList<String> visibleVolsForUser = new ArrayList<>();
- for (int i = mVisibleVols.size() - 1; i >= 0; --i) {
- final VolumeInfo vol = mVisibleVols.get(i);
- if (vol.isVisibleForUser(userId)) {
- visibleVolsForUser.add(getVolumeLabel(vol));
- }
- }
- return visibleVolsForUser.toArray(new String[visibleVolsForUser.size()]);
- }
-
- @Override
public String getSandboxId(String packageName) {
return StorageManagerService.this.getSandboxId(packageName,
mPmInternal.getSharedUserIdForPackage(packageName));
}
-
- private String getVolumeLabel(VolumeInfo vol) {
- // STOPSHIP: Label needs to part of VolumeInfo and need to be passed on from vold
- switch (vol.getType()) {
- case VolumeInfo.TYPE_EMULATED:
- return "emulated";
- case VolumeInfo.TYPE_PUBLIC:
- return vol.fsUuid == null ? vol.id : vol.fsUuid;
- default:
- return null;
- }
- }
}
}
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index fd946cd..5633082c 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -177,8 +177,6 @@
private ServiceState[] mServiceState;
- private int[] mNetworkType;
-
private int[] mVoiceActivationState;
private int[] mDataActivationState;
@@ -213,6 +211,9 @@
private CallAttributes mCallAttributes = new CallAttributes(new PreciseCallState(),
TelephonyManager.NETWORK_TYPE_UNKNOWN, new CallQuality());
+ // network type of the call associated with the mCallAttributes and mCallQuality
+ private int mCallNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+
private int[] mSrvccState;
private int mDefaultSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -375,7 +376,6 @@
mDataConnectionNetworkType = new int[numPhones];
mCallIncomingNumber = new String[numPhones];
mServiceState = new ServiceState[numPhones];
- mNetworkType = new int[numPhones];
mVoiceActivationState = new int[numPhones];
mDataActivationState = new int[numPhones];
mUserMobileDataState = new boolean[numPhones];
@@ -396,7 +396,6 @@
mDataActivationState[i] = TelephonyManager.SIM_ACTIVATION_STATE_UNKNOWN;
mCallIncomingNumber[i] = "";
mServiceState[i] = new ServiceState();
- mNetworkType[i] = mServiceState[i].getVoiceNetworkType();
mSignalStrength[i] = new SignalStrength();
mUserMobileDataState[i] = false;
mMessageWaiting[i] = false;
@@ -998,21 +997,6 @@
if (validatePhoneId(phoneId)) {
mServiceState[phoneId] = state;
- boolean notifyCallAttributes = true;
- if (mNetworkType[phoneId] != mServiceState[phoneId].getVoiceNetworkType()) {
- mNetworkType[phoneId] = state.getVoiceNetworkType();
- mCallAttributes = new CallAttributes(mPreciseCallState, mNetworkType[phoneId],
- mCallQuality);
- } else {
- // No change to network type, so no need to notify call attributes
- notifyCallAttributes = false;
- }
-
- if (mCallQuality == null) {
- // No call quality reported yet, so no need to notify call attributes
- notifyCallAttributes = false;
- }
-
for (Record r : mRecords) {
if (VDBG) {
log("notifyServiceStateForSubscriber: r=" + r + " subId=" + subId
@@ -1040,14 +1024,6 @@
mRemoveList.add(r.binder);
}
}
- if (notifyCallAttributes && r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED)) {
- try {
- r.callback.onCallAttributesChanged(mCallAttributes);
- } catch (RemoteException ex) {
- mRemoveList.add(r.binder);
- }
- }
}
} else {
log("notifyServiceStateForSubscriber: INVALID phoneId=" + phoneId);
@@ -1574,7 +1550,7 @@
log("notifyPreciseCallState: mCallQuality is null, skipping call attributes");
notifyCallAttributes = false;
} else {
- mCallAttributes = new CallAttributes(mPreciseCallState, mNetworkType[phoneId],
+ mCallAttributes = new CallAttributes(mPreciseCallState, mCallNetworkType,
mCallQuality);
}
@@ -1840,16 +1816,16 @@
}
@Override
- public void notifyCallQualityChanged(CallQuality callQuality, int phoneId) {
+ public void notifyCallQualityChanged(CallQuality callQuality, int phoneId,
+ int callNetworkType) {
if (!checkNotifyPermission("notifyCallQualityChanged()")) {
return;
}
// merge CallQuality with PreciseCallState and network type
mCallQuality = callQuality;
- mCallAttributes = new CallAttributes(mPreciseCallState,
- mNetworkType[phoneId],
- callQuality);
+ mCallNetworkType = callNetworkType;
+ mCallAttributes = new CallAttributes(mPreciseCallState, callNetworkType, callQuality);
synchronized (mRecords) {
TelephonyManager tm = (TelephonyManager) mContext.getSystemService(
@@ -1886,7 +1862,6 @@
pw.println("mCallState=" + mCallState[i]);
pw.println("mCallIncomingNumber=" + mCallIncomingNumber[i]);
pw.println("mServiceState=" + mServiceState[i]);
- pw.println("mNetworkType=" + mNetworkType[i]);
pw.println("mVoiceActivationState= " + mVoiceActivationState[i]);
pw.println("mDataActivationState= " + mDataActivationState[i]);
pw.println("mUserMobileDataState= " + mUserMobileDataState[i]);
@@ -1900,6 +1875,7 @@
pw.println("mImsCallDisconnectCause=" + mImsReasonInfo.get(i).toString());
pw.decreaseIndent();
}
+ pw.println("mCallNetworkType=" + mCallNetworkType);
pw.println("mPreciseDataConnectionState=" + mPreciseDataConnectionState);
pw.println("mPreciseCallState=" + mPreciseCallState);
pw.println("mCallDisconnectCause=" + mCallDisconnectCause);
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index d025d73..de41152 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -402,7 +402,7 @@
private void updateActivityStartsLoggingEnabled() {
mFlagActivityStartsLoggingEnabled = Settings.Global.getInt(mResolver,
- Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED, 0) == 1;
+ Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED, 1) == 1;
}
private void updateBackgroundActivityStartsEnabled() {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 375c5c4..f2902b1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1861,7 +1861,7 @@
ProcessRecord proc;
int procState;
int statType;
- int pid;
+ int pid = -1;
long lastPssTime;
synchronized (ActivityManagerService.this) {
if (mPendingPssProcesses.size() <= 0) {
@@ -15039,7 +15039,7 @@
oldQueue.performReceiveLocked(oldRecord.callerApp, oldRecord.resultTo,
oldRecord.intent,
Activity.RESULT_CANCELED, null, null,
- false, false, oldRecord.userId, oldRecord);
+ false, false, oldRecord.userId);
} catch (RemoteException e) {
Slog.w(TAG, "Failure ["
+ queue.mQueueName + "] sending broadcast result of "
@@ -17110,6 +17110,13 @@
}
@Override
+ public boolean startUserInForegroundWithListener(final int userId,
+ @Nullable IProgressListener unlockListener) {
+ // Permission check done inside UserController.
+ return mUserController.startUser(userId, /* foreground */ true, unlockListener);
+ }
+
+ @Override
public boolean unlockUser(int userId, byte[] token, byte[] secret, IProgressListener listener) {
return mUserController.unlockUser(userId, token, secret, listener);
}
diff --git a/services/core/java/com/android/server/am/BroadcastConstants.java b/services/core/java/com/android/server/am/BroadcastConstants.java
index 820caf1..be17b1b 100644
--- a/services/core/java/com/android/server/am/BroadcastConstants.java
+++ b/services/core/java/com/android/server/am/BroadcastConstants.java
@@ -38,6 +38,8 @@
static final String KEY_DEFERRAL = "bcast_deferral";
static final String KEY_DEFERRAL_DECAY_FACTOR = "bcast_deferral_decay_factor";
static final String KEY_DEFERRAL_FLOOR = "bcast_deferral_floor";
+ static final String KEY_ALLOW_BG_ACTIVITY_START_TIMEOUT =
+ "bcast_allow_bg_activity_start_timeout";
// All time intervals are in milliseconds
private static final long DEFAULT_TIMEOUT = 10_000;
@@ -45,6 +47,7 @@
private static final long DEFAULT_DEFERRAL = 5_000;
private static final float DEFAULT_DEFERRAL_DECAY_FACTOR = 0.75f;
private static final long DEFAULT_DEFERRAL_FLOOR = 0;
+ private static final long DEFAULT_ALLOW_BG_ACTIVITY_START_TIMEOUT = 10_000;
// All time constants are in milliseconds
@@ -59,6 +62,8 @@
public float DEFERRAL_DECAY_FACTOR = DEFAULT_DEFERRAL_DECAY_FACTOR;
// Minimum that the deferral time can decay to until the backlog fully clears
public long DEFERRAL_FLOOR = DEFAULT_DEFERRAL_FLOOR;
+ // For how long after a whitelisted receiver's start its process can start a background activity
+ public long ALLOW_BG_ACTIVITY_START_TIMEOUT = DEFAULT_ALLOW_BG_ACTIVITY_START_TIMEOUT;
// Settings override tracking for this instance
private String mSettingsKey;
@@ -113,6 +118,8 @@
DEFERRAL_DECAY_FACTOR = mParser.getFloat(KEY_DEFERRAL_DECAY_FACTOR,
DEFERRAL_DECAY_FACTOR);
DEFERRAL_FLOOR = mParser.getLong(KEY_DEFERRAL_FLOOR, DEFERRAL_FLOOR);
+ ALLOW_BG_ACTIVITY_START_TIMEOUT = mParser.getLong(KEY_ALLOW_BG_ACTIVITY_START_TIMEOUT,
+ ALLOW_BG_ACTIVITY_START_TIMEOUT);
}
}
@@ -145,6 +152,9 @@
pw.print(" "); pw.print(KEY_DEFERRAL_FLOOR); pw.print(" = ");
TimeUtils.formatDuration(DEFERRAL_FLOOR, pw);
+
+ pw.print(" "); pw.print(KEY_ALLOW_BG_ACTIVITY_START_TIMEOUT); pw.print(" = ");
+ TimeUtils.formatDuration(ALLOW_BG_ACTIVITY_START_TIMEOUT, pw);
pw.println();
}
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index d9ea1da..efb1c44 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -74,9 +74,6 @@
static final int MAX_BROADCAST_SUMMARY_HISTORY
= ActivityManager.isLowRamDeviceStatic() ? 25 : 300;
- // For how long after a whitelisted receiver's start its process can start a background activity
- private static final int RECEIVER_BG_ACTIVITY_START_TIMEOUT_MS = 10_000;
-
final ActivityManagerService mService;
/**
@@ -310,9 +307,6 @@
r.curApp = app;
app.curReceivers.add(r);
app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
- if (r.allowBackgroundActivityStarts) {
- app.addAllowBackgroundActivityStartsToken(r);
- }
mService.mProcessList.updateLruProcessLocked(app, false, null);
if (!skipOomAdj) {
mService.updateOomAdjLocked();
@@ -454,8 +448,25 @@
Slog.w(TAG, "finishReceiver [" + mQueueName + "] called but state is IDLE");
}
if (r.allowBackgroundActivityStarts && r.curApp != null) {
- r.curApp.removeAllowBackgroundActivityStartsToken(r);
- }
+ if (elapsed > mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT) {
+ // if the receiver has run for more than allowed bg activity start timeout,
+ // just remove the token for this process now and we're done
+ r.curApp.removeAllowBackgroundActivityStartsToken(r);
+ } else {
+ // the receiver had run for less than allowed bg activity start timeout,
+ // so allow the process to still start activities from bg for some more time
+ String msgToken = (r.curApp.toShortString() + r.toString()).intern();
+ // first, if there exists a past scheduled request to remove this token, drop
+ // that request - we don't want the token to be swept from under our feet...
+ mHandler.removeCallbacksAndMessages(msgToken);
+ // ...then schedule the removal of the token after the extended timeout
+ mHandler.postAtTime(() -> {
+ if (r.curApp != null) {
+ r.curApp.removeAllowBackgroundActivityStartsToken(r);
+ }
+ }, msgToken, (r.receiverTime + mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT));
+ }
+ }
// If we're abandoning this broadcast before any receivers were actually spun up,
// nextReceiver is zero; in which case time-to-process bookkeeping doesn't apply.
if (r.nextReceiver > 0) {
@@ -554,7 +565,7 @@
void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
Intent intent, int resultCode, String data, Bundle extras,
- boolean ordered, boolean sticky, int sendingUser, BroadcastRecord br)
+ boolean ordered, boolean sticky, int sendingUser)
throws RemoteException {
// Send the intent to the receiver asynchronously using one-way binder calls.
if (app != null) {
@@ -562,15 +573,6 @@
// If we have an app thread, do the call through that so it is
// correctly ordered with other one-way calls.
try {
- if (br.allowBackgroundActivityStarts) {
- app.addAllowBackgroundActivityStartsToken(br);
- // schedule removal of the whitelisting token after the timeout
- mHandler.postDelayed(() -> {
- if (app != null) {
- app.removeAllowBackgroundActivityStartsToken(br);
- }
- }, RECEIVER_BG_ACTIVITY_START_TIMEOUT_MS);
- }
app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
data, extras, ordered, sticky, sendingUser, app.getReportedProcState());
// TODO: Uncomment this when (b/28322359) is fixed and we aren't getting
@@ -794,9 +796,13 @@
skipReceiverLocked(r);
}
} else {
+ if (r.receiverTime == 0) {
+ r.receiverTime = SystemClock.uptimeMillis();
+ }
+ maybeAddAllowBackgroundActivityStartsToken(filter.receiverList.app, r);
performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
new Intent(r.intent), r.resultCode, r.resultData,
- r.resultExtras, r.ordered, r.initialSticky, r.userId, r);
+ r.resultExtras, r.ordered, r.initialSticky, r.userId);
}
if (ordered) {
r.state = BroadcastRecord.CALL_DONE_RECEIVE;
@@ -1100,7 +1106,7 @@
}
performReceiveLocked(r.callerApp, r.resultTo,
new Intent(r.intent), r.resultCode,
- r.resultData, r.resultExtras, false, false, r.userId, r);
+ r.resultData, r.resultExtras, false, false, r.userId);
// Set this to null so that the reference
// (local and remote) isn't kept in the mBroadcastHistory.
r.resultTo = null;
@@ -1255,6 +1261,9 @@
r.state = BroadcastRecord.IDLE;
scheduleBroadcastsLocked();
} else {
+ if (filter.receiverList != null) {
+ maybeAddAllowBackgroundActivityStartsToken(filter.receiverList.app, r);
+ }
if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) {
scheduleTempWhitelistLocked(filter.owningUid,
brOptions.getTemporaryAppWhitelistDuration(), r);
@@ -1561,6 +1570,7 @@
try {
app.addPackage(info.activityInfo.packageName,
info.activityInfo.applicationInfo.versionCode, mService.mProcessStats);
+ maybeAddAllowBackgroundActivityStartsToken(app, r);
processCurBroadcastLocked(r, app, skipOomAdj);
return;
} catch (RemoteException e) {
@@ -1611,10 +1621,23 @@
return;
}
+ maybeAddAllowBackgroundActivityStartsToken(r.curApp, r);
mPendingBroadcast = r;
mPendingBroadcastRecvIndex = recIdx;
}
+ private void maybeAddAllowBackgroundActivityStartsToken(ProcessRecord proc, BroadcastRecord r) {
+ if (r == null || proc == null || !r.allowBackgroundActivityStarts) {
+ return;
+ }
+ String msgToken = (proc.toShortString() + r.toString()).intern();
+ // first, if there exists a past scheduled request to remove this token, drop
+ // that request - we don't want the token to be swept from under our feet...
+ mHandler.removeCallbacksAndMessages(msgToken);
+ // ...then add the token
+ proc.addAllowBackgroundActivityStartsToken(r);
+ }
+
final void setBroadcastTimeoutLocked(long timeoutTime) {
if (! mPendingBroadcastTimeoutMessage) {
Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 3a61dd9..30798a8 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -1764,8 +1764,6 @@
.getPackagesForUid(uid);
final StorageManagerInternal storageManagerInternal =
LocalServices.getService(StorageManagerInternal.class);
- final String[] visibleVolIds = storageManagerInternal
- .getVisibleVolumesForUser(UserHandle.getUserId(uid));
final String sandboxId = storageManagerInternal.getSandboxId(app.info.packageName);
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
app.processName);
@@ -1776,7 +1774,7 @@
app.processName, uid, uid, gids, runtimeFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, null, app.info.packageName,
- packageNames, visibleVolIds, sandboxId,
+ packageNames, sandboxId,
new String[] {PROC_START_SEQ_IDENT + app.startSeq});
} else if (hostingType.equals("app_zygote")) {
final AppZygote appZygote = createAppZygoteForProcessIfNeeded(app);
@@ -1785,14 +1783,14 @@
app.processName, uid, uid, gids, runtimeFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, null, app.info.packageName,
- packageNames, visibleVolIds, sandboxId, /*useBlastulaPool=*/ false,
+ packageNames, sandboxId, /*useBlastulaPool=*/ false,
new String[] {PROC_START_SEQ_IDENT + app.startSeq});
} else {
startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, runtimeFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, invokeWith, app.info.packageName,
- packageNames, visibleVolIds, sandboxId,
+ packageNames, sandboxId,
new String[] {PROC_START_SEQ_IDENT + app.startSeq});
}
checkSlow(startTime, "startProcess: returned from zygote!");
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index cbbbe47..894a704 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -38,8 +38,9 @@
/**
* Maps system settings to system properties.
* <p>The properties are dynamically updated when settings change.
+ * @hide
*/
-class SettingsToPropertiesMapper {
+public class SettingsToPropertiesMapper {
private static final String TAG = "SettingsToPropertiesMapper";
@@ -156,8 +157,8 @@
* during current device booting.
* @return
*/
- public boolean isNativeFlagsResetPerformed() {
- String value = systemPropertiesGet(RESET_PERFORMED_PROPERTY);
+ public static boolean isNativeFlagsResetPerformed() {
+ String value = SystemProperties.get(RESET_PERFORMED_PROPERTY);
return "true".equals(value);
}
@@ -166,7 +167,7 @@
* booting.
* @return
*/
- public String[] getResetNativeCategories() {
+ public static String[] getResetNativeCategories() {
if (!isNativeFlagsResetPerformed()) {
return new String[0];
}
@@ -214,7 +215,7 @@
if (value == null) {
// It's impossible to remove system property, therefore we check previous value to
// avoid setting an empty string if the property wasn't set.
- if (TextUtils.isEmpty(systemPropertiesGet(key))) {
+ if (TextUtils.isEmpty(SystemProperties.get(key))) {
return;
}
value = "";
@@ -224,7 +225,7 @@
}
try {
- systemPropertiesSet(key, value);
+ SystemProperties.set(key, value);
} catch (Exception e) {
// Failure to set a property can be caused by SELinux denial. This usually indicates
// that the property wasn't whitelisted in sepolicy.
@@ -250,17 +251,7 @@
}
@VisibleForTesting
- protected String systemPropertiesGet(String key) {
- return SystemProperties.get(key);
- }
-
- @VisibleForTesting
- protected void systemPropertiesSet(String key, String value) {
- SystemProperties.set(key, value);
- }
-
- @VisibleForTesting
- protected String getResetFlagsFileContent() {
+ static String getResetFlagsFileContent() {
String content = null;
try {
File reset_flag_file = new File(RESET_RECORD_FILE_PATH);
@@ -279,4 +270,4 @@
String settingValue = Settings.Global.getString(mContentResolver, settingName);
setProperty(propName, settingValue);
}
-}
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index ac20f6c..1e406c0 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -2214,7 +2214,11 @@
void startUserWidgets(int userId) {
AppWidgetManagerInternal awm = LocalServices.getService(AppWidgetManagerInternal.class);
if (awm != null) {
- awm.unlockUser(userId);
+ // Out of band, because this is called during a sequence with
+ // sensitive cross-service lock management
+ FgThread.getHandler().post(() -> {
+ awm.unlockUser(userId);
+ });
}
}
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index 708de73..4485a54 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -144,7 +144,7 @@
* Whether history is enabled.
*/
@GuardedBy("mInMemoryLock")
- private int mMode = AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE;
+ private int mMode = AppOpsManager.HISTORICAL_MODE_DISABLED;
/**
* This granularity has been chosen to allow clean delineation for intervals
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index afdfbe3..a2d6508 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -88,6 +88,7 @@
import android.media.audiopolicy.AudioMix;
import android.media.audiopolicy.AudioPolicy;
import android.media.audiopolicy.AudioPolicyConfig;
+import android.media.audiopolicy.AudioProductStrategies;
import android.media.audiopolicy.IAudioPolicyCallback;
import android.media.projection.IMediaProjection;
import android.media.projection.IMediaProjectionManager;
@@ -274,6 +275,9 @@
private SettingsObserver mSettingsObserver;
+ /** @see AudioProductStrategies */
+ private static AudioProductStrategies sAudioProductStrategies;
+
private int mMode = AudioSystem.MODE_NORMAL;
// protects mRingerMode
private final Object mSettingsLock = new Object();
@@ -624,6 +628,8 @@
mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
mHasVibrator = mVibrator == null ? false : mVibrator.hasVibrator();
+ sAudioProductStrategies = new AudioProductStrategies();
+
// Initialize volume
int maxCallVolume = SystemProperties.getInt("ro.config.vc_call_vol_steps", -1);
if (maxCallVolume != -1) {
@@ -988,6 +994,14 @@
}
}
+ /**
+ * @return the {@link android.media.audiopolicy.AudioProductStrategies} discovered from the
+ * platform configuration file.
+ */
+ public @NonNull AudioProductStrategies getAudioProductStrategies() {
+ return sAudioProductStrategies;
+ }
+
private void checkAllAliasStreamVolumes() {
synchronized (mSettingsLock) {
synchronized (VolumeStreamState.class) {
diff --git a/services/core/java/com/android/server/biometrics/ClientMonitor.java b/services/core/java/com/android/server/biometrics/ClientMonitor.java
index e80b39b..89fa2de 100644
--- a/services/core/java/com/android/server/biometrics/ClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/ClientMonitor.java
@@ -209,11 +209,7 @@
public void binderDied() {
// If the current client dies we should cancel the current operation.
Slog.e(getLogTag(), "Binder died, cancelling client");
- try {
- getDaemonWrapper().cancel();
- } catch (RemoteException e) {
- Slog.e(getLogTag(), "Remote exception", e);
- }
+ stop(false /* initiatedByClient */);
mToken = null;
mListener = null;
}
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index 420b23e..d84a4d2 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -19,10 +19,11 @@
import static android.Manifest.permission.CHANGE_NETWORK_STATE;
import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
+import static android.Manifest.permission.INTERNET;
import static android.Manifest.permission.NETWORK_STACK;
-import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
-import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
+import static android.Manifest.permission.UPDATE_DEVICE_STATS;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
+import static android.content.pm.PackageManager.MATCH_ANY_USER;
import static android.os.Process.INVALID_UID;
import static android.os.Process.SYSTEM_UID;
@@ -32,23 +33,31 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.UserInfo;
-import android.net.Uri;
+import android.net.INetd;
+import android.net.util.NetdService;
import android.os.Build;
import android.os.INetworkManagementService;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
+import android.util.Slog;
+import android.util.SparseIntArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+import com.android.server.LocalServices;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
-import java.util.Map.Entry;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
/**
@@ -75,6 +84,59 @@
// Keys are App IDs. Values are true for SYSTEM permission and false for NETWORK permission.
private final Map<Integer, Boolean> mApps = new HashMap<>();
+ // Keys are App packageNames, Values are app uids. . We need to keep track of this information
+ // because PackageListObserver#onPackageRemoved does not pass the UID.
+ @GuardedBy("mPackageNameUidMap")
+ private final Map<String, Integer> mPackageNameUidMap = new HashMap<>();
+
+ private class PackageListObserver implements PackageManagerInternal.PackageListObserver {
+ @Override
+ public void onPackageAdded(String packageName) {
+ final PackageInfo app = getPackageInfo(packageName);
+ if (app == null) {
+ Slog.wtf(TAG, "Failed to get information of installed package: " + packageName);
+ return;
+ }
+ int uid = (app.applicationInfo != null) ? app.applicationInfo.uid : INVALID_UID;
+ if (uid == INVALID_UID) {
+ Slog.wtf(TAG, "Failed to get the uid of installed package: " + packageName
+ + "uid: " + uid);
+ return;
+ }
+ if (app.requestedPermissions == null) {
+ return;
+ }
+ sendPackagePermissionsForUid(uid,
+ filterPermission(Arrays.asList(app.requestedPermissions)));
+ synchronized (mPackageNameUidMap) {
+ mPackageNameUidMap.put(packageName, uid);
+ }
+ }
+
+ @Override
+ public void onPackageRemoved(String packageName) {
+ int uid;
+ synchronized (mPackageNameUidMap) {
+ if (!mPackageNameUidMap.containsKey(packageName)) {
+ return;
+ }
+ uid = mPackageNameUidMap.get(packageName);
+ mPackageNameUidMap.remove(packageName);
+ }
+ int permission = 0;
+ String[] packages = mPackageManager.getPackagesForUid(uid);
+ if (packages != null && packages.length > 0) {
+ for (String name : packages) {
+ final PackageInfo app = getPackageInfo(name);
+ if (app != null && app.requestedPermissions != null) {
+ permission |= filterPermission(Arrays.asList(app.requestedPermissions));
+ }
+ }
+ }
+ sendPackagePermissionsForUid(uid, permission);
+ }
+ }
+
public PermissionMonitor(Context context, INetworkManagementService netd) {
mContext = context;
mPackageManager = context.getPackageManager();
@@ -87,12 +149,21 @@
public synchronized void startMonitoring() {
log("Monitoring");
- List<PackageInfo> apps = mPackageManager.getInstalledPackages(GET_PERMISSIONS);
+ PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
+ if (pmi != null) {
+ pmi.getPackageList(new PackageListObserver());
+ } else {
+ loge("failed to get the PackageManagerInternal service");
+ }
+ List<PackageInfo> apps = mPackageManager.getInstalledPackages(GET_PERMISSIONS
+ | MATCH_ANY_USER);
if (apps == null) {
loge("No apps");
return;
}
+ SparseIntArray netdPermsUids = new SparseIntArray();
+
for (PackageInfo app : apps) {
int uid = app.applicationInfo != null ? app.applicationInfo.uid : INVALID_UID;
if (uid < 0) {
@@ -110,6 +181,17 @@
mApps.put(uid, hasRestrictedPermission);
}
}
+
+ //TODO: unify the management of the permissions into one codepath.
+ if (app.requestedPermissions != null) {
+ int otherNetdPerms = filterPermission(Arrays.asList(app.requestedPermissions));
+ if (otherNetdPerms != 0) {
+ netdPermsUids.put(uid, netdPermsUids.get(uid) | otherNetdPerms);
+ synchronized (mPackageNameUidMap) {
+ mPackageNameUidMap.put(app.applicationInfo.packageName, uid);
+ }
+ }
+ }
}
List<UserInfo> users = mUserManager.getUsers(true); // exclude dying users
@@ -121,6 +203,7 @@
log("Users: " + mUsers.size() + ", Apps: " + mApps.size());
update(mUsers, mApps, true);
+ sendPackagePermissionsToNetd(netdPermsUids);
}
@VisibleForTesting
@@ -339,6 +422,107 @@
}
}
+ private static int filterPermission(List<String> requestedPermissions) {
+ int permissions = 0;
+ if (requestedPermissions.contains(INTERNET)) {
+ permissions |= INetd.PERMISSION_INTERNET;
+ }
+ if (requestedPermissions.contains(UPDATE_DEVICE_STATS)) {
+ permissions |= INetd.PERMISSION_UPDATE_DEVICE_STATS;
+ }
+ return permissions;
+ }
+
+ private PackageInfo getPackageInfo(String packageName) {
+ try {
+ PackageInfo app = mPackageManager.getPackageInfo(packageName, GET_PERMISSIONS
+ | MATCH_ANY_USER);
+ return app;
+ } catch (NameNotFoundException e) {
+ // App not found.
+ loge("NameNotFoundException " + packageName);
+ return null;
+ }
+ }
+
+ /**
+ * Called by PackageListObserver when a package is installed/uninstalled. Send the updated
+ * permission information to netd.
+ *
+ * @param uid the app uid of the package installed
+ * @param permissions the permissions the app requested and netd cares about.
+ *
+ * @hide
+ */
+ private void sendPackagePermissionsForUid(int uid, int permissions) {
+ SparseIntArray netdPermissionsAppIds = new SparseIntArray();
+ netdPermissionsAppIds.put(uid, permissions);
+ sendPackagePermissionsToNetd(netdPermissionsAppIds);
+ }
+
+ /**
+ * Called by packageManagerService to send IPC to netd. Grant or revoke the INTERNET
+ * and/or UPDATE_DEVICE_STATS permission of the uids in array.
+ *
+ * @param netdPermissionsAppIds integer pairs of uids and the permission granted to it. If the
+ * permission is 0, revoke all permissions of that uid.
+ *
+ * @hide
+ */
+ private void sendPackagePermissionsToNetd(SparseIntArray netdPermissionsAppIds) {
+ INetd netdService = NetdService.getInstance();
+ if (netdService == null) {
+ Log.e(TAG, "Failed to get the netd service");
+ return;
+ }
+ ArrayList<Integer> allPermissionAppIds = new ArrayList<>();
+ ArrayList<Integer> internetPermissionAppIds = new ArrayList<>();
+ ArrayList<Integer> updateStatsPermissionAppIds = new ArrayList<>();
+ ArrayList<Integer> uninstalledAppIds = new ArrayList<>();
+ for (int i = 0; i < netdPermissionsAppIds.size(); i++) {
+ int permissions = netdPermissionsAppIds.valueAt(i);
+ switch(permissions) {
+ case (INetd.PERMISSION_INTERNET | INetd.PERMISSION_UPDATE_DEVICE_STATS):
+ allPermissionAppIds.add(netdPermissionsAppIds.keyAt(i));
+ break;
+ case INetd.PERMISSION_INTERNET:
+ internetPermissionAppIds.add(netdPermissionsAppIds.keyAt(i));
+ break;
+ case INetd.PERMISSION_UPDATE_DEVICE_STATS:
+ updateStatsPermissionAppIds.add(netdPermissionsAppIds.keyAt(i));
+ break;
+ case INetd.NO_PERMISSIONS:
+ uninstalledAppIds.add(netdPermissionsAppIds.keyAt(i));
+ break;
+ default:
+ Log.e(TAG, "unknown permission type: " + permissions + "for uid: "
+ + netdPermissionsAppIds.keyAt(i));
+ }
+ }
+ try {
+ // TODO: add a lock inside netd to protect IPC trafficSetNetPermForUids()
+ if (allPermissionAppIds.size() != 0) {
+ netdService.trafficSetNetPermForUids(
+ INetd.PERMISSION_INTERNET | INetd.PERMISSION_UPDATE_DEVICE_STATS,
+ ArrayUtils.convertToIntArray(allPermissionAppIds));
+ }
+ if (internetPermissionAppIds.size() != 0) {
+ netdService.trafficSetNetPermForUids(INetd.PERMISSION_INTERNET,
+ ArrayUtils.convertToIntArray(internetPermissionAppIds));
+ }
+ if (updateStatsPermissionAppIds.size() != 0) {
+ netdService.trafficSetNetPermForUids(INetd.PERMISSION_UPDATE_DEVICE_STATS,
+ ArrayUtils.convertToIntArray(updateStatsPermissionAppIds));
+ }
+ if (uninstalledAppIds.size() != 0) {
+ netdService.trafficSetNetPermForUids(INetd.NO_PERMISSIONS,
+ ArrayUtils.convertToIntArray(uninstalledAppIds));
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Pass appId list of special permission failed." + e);
+ }
+ }
+
private static void log(String s) {
if (DBG) {
Log.d(TAG, s);
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index b020997..01d19c0 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -37,7 +37,9 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.ColorSpace;
+import android.graphics.GraphicBuffer;
import android.graphics.Point;
+import android.graphics.Rect;
import android.hardware.SensorManager;
import android.hardware.display.AmbientBrightnessDayStats;
import android.hardware.display.BrightnessChangeEvent;
@@ -1277,7 +1279,14 @@
if (token == null) {
return false;
}
- SurfaceControl.screenshot(token, outSurface);
+ final GraphicBuffer gb = SurfaceControl.screenshotToBufferWithSecureLayersUnsafe(
+ token, new Rect(), 0 /* width */, 0 /* height */, false /* useIdentityTransform */,
+ 0 /* rotation */);
+ try {
+ outSurface.attachAndQueueBuffer(gb);
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Failed to take screenshot - " + e.getMessage());
+ }
return true;
}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 4891947..77df10b 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -65,7 +65,8 @@
new LongSparseArray<LocalDisplayDevice>();
@SuppressWarnings("unused") // Becomes active at instantiation time.
- private HotplugDisplayEventReceiver mHotplugReceiver;
+ private PhysicalDisplayEventReceiver mPhysicalDisplayEventReceiver;
+
// Called with SyncRoot lock held.
public LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
@@ -77,7 +78,7 @@
public void registerLocked() {
super.registerLocked();
- mHotplugReceiver = new HotplugDisplayEventReceiver(getHandler().getLooper());
+ mPhysicalDisplayEventReceiver = new PhysicalDisplayEventReceiver(getHandler().getLooper());
for (long physicalDisplayId : SurfaceControl.getPhysicalDisplayIds()) {
tryConnectDisplayLocked(physicalDisplayId);
@@ -727,8 +728,8 @@
}
}
- private final class HotplugDisplayEventReceiver extends DisplayEventReceiver {
- public HotplugDisplayEventReceiver(Looper looper) {
+ private final class PhysicalDisplayEventReceiver extends DisplayEventReceiver {
+ PhysicalDisplayEventReceiver(Looper looper) {
super(looper, VSYNC_SOURCE_APP);
}
@@ -742,5 +743,15 @@
}
}
}
+
+ @Override
+ public void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId) {
+ if (DEBUG) {
+ Slog.d(TAG, "onConfigChanged("
+ + "timestampNanos=" + timestampNanos
+ + ", builtInDisplayId=" + physicalDisplayId
+ + ", configId=" + configId + ")");
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index bffa8f4..3052e3c 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -28,6 +28,7 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ServiceInfo;
import android.database.ContentObserver;
+import android.hardware.display.AmbientDisplayConfiguration;
import android.hardware.input.InputManagerInternal;
import android.os.Binder;
import android.os.Build;
@@ -46,7 +47,6 @@
import android.util.Slog;
import android.view.Display;
-import com.android.internal.hardware.AmbientDisplayConfiguration;
import com.android.internal.util.DumpUtils;
import com.android.server.FgThread;
import com.android.server.LocalServices;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 6710986..2026957 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -31,6 +31,8 @@
import android.media.AudioManager;
import android.media.AudioSystem;
import android.media.tv.TvContract;
+import android.media.tv.TvInputInfo;
+import android.media.tv.TvInputManager.TvInputCallback;
import android.os.SystemProperties;
import android.provider.Settings.Global;
import android.util.Slog;
@@ -84,10 +86,13 @@
.get(Constants.PROPERTY_SYSTEM_AUDIO_DEVICE_ARC_PORT, "0").contains("tvinput");
// Keeps the mapping (HDMI port ID to TV input URI) to keep track of the TV inputs ready to
- // accept input switching request from HDMI devices. Requests for which the corresponding
- // input ID is not yet registered by TV input framework need to be buffered for delayed
- // processing.
- private final HashMap<Integer, String> mTvInputs = new HashMap<>();
+ // accept input switching request from HDMI devices.
+ @GuardedBy("mLock")
+ private final HashMap<Integer, String> mPortIdToTvInputs = new HashMap<>();
+
+ // A map from TV input id to HDMI device info.
+ @GuardedBy("mLock")
+ private final HashMap<String, HdmiDeviceInfo> mTvInputsToDeviceInfo = new HashMap<>();
// Copy of mDeviceInfos to guarantee thread-safety.
@GuardedBy("mLock")
@@ -103,14 +108,57 @@
mService.readBooleanSetting(Global.HDMI_CEC_SWITCH_ENABLED, false);
mSystemAudioControlFeatureEnabled =
mService.readBooleanSetting(Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, true);
- // TODO(amyjojo): Maintain a portId to TvinputId map.
- mTvInputs.put(2, "com.droidlogic.tvinput/.services.Hdmi1InputService/HW5");
- mTvInputs.put(4, "com.droidlogic.tvinput/.services.Hdmi2InputService/HW6");
- mTvInputs.put(1, "com.droidlogic.tvinput/.services.Hdmi3InputService/HW7");
}
private static final String SHORT_AUDIO_DESCRIPTOR_CONFIG_PATH = "/vendor/etc/sadConfig.xml";
+ private final TvInputCallback mTvInputCallback = new TvInputCallback() {
+ @Override
+ public void onInputAdded(String inputId) {
+ addOrUpdateTvInput(inputId);
+ }
+
+ @Override
+ public void onInputRemoved(String inputId) {
+ removeTvInput(inputId);
+ }
+
+ @Override
+ public void onInputUpdated(String inputId) {
+ addOrUpdateTvInput(inputId);
+ }
+ };
+
+ @ServiceThreadOnly
+ private void addOrUpdateTvInput(String inputId) {
+ assertRunOnServiceThread();
+ synchronized (mLock) {
+ TvInputInfo tvInfo = mService.getTvInputManager().getTvInputInfo(inputId);
+ if (tvInfo == null) {
+ return;
+ }
+ HdmiDeviceInfo info = tvInfo.getHdmiDeviceInfo();
+ if (info == null) {
+ return;
+ }
+ mPortIdToTvInputs.put(info.getPortId(), inputId);
+ mTvInputsToDeviceInfo.put(inputId, info);
+ }
+ }
+
+ @ServiceThreadOnly
+ private void removeTvInput(String inputId) {
+ assertRunOnServiceThread();
+ synchronized (mLock) {
+ if (mTvInputsToDeviceInfo.get(inputId) == null) {
+ return;
+ }
+ int portId = mTvInputsToDeviceInfo.get(inputId).getPortId();
+ mPortIdToTvInputs.remove(portId);
+ mTvInputsToDeviceInfo.remove(inputId);
+ }
+ }
+
/**
* Called when a device is newly added or a new device is detected or
* an existing device is updated.
@@ -248,17 +296,29 @@
}
if (mService.getPortInfo(portId).getType() == HdmiPortInfo.PORT_OUTPUT) {
mCecMessageCache.flushAll();
- } else {
- if (connected) {
- launchDeviceDiscovery();
- } else {
- // TODO(amyjojo): remove device from mDeviceInfo
+ } else if (!connected && mPortIdToTvInputs.get(portId) != null) {
+ String tvInputId = mPortIdToTvInputs.get(portId);
+ HdmiDeviceInfo info = mTvInputsToDeviceInfo.get(tvInputId);
+ if (info == null) {
+ return;
}
+ // Update with TIF on the device removal. TIF callback will update
+ // mPortIdToTvInputs and mPortIdToTvInputs.
+ removeCecDevice(info.getLogicalAddress());
}
}
@Override
@ServiceThreadOnly
+ protected void disableDevice(boolean initiatedByCec, PendingActionClearedCallback callback) {
+ super.disableDevice(initiatedByCec, callback);
+ assertRunOnServiceThread();
+ mService.unregisterTvInputCallback(mTvInputCallback);
+ // TODO(amyjojo): check disableDevice and onStandby behaviors per spec
+ }
+
+ @Override
+ @ServiceThreadOnly
protected void onStandby(boolean initiatedByCec, int standbyAction) {
assertRunOnServiceThread();
mTvSystemAudioModeSupport = false;
@@ -284,6 +344,7 @@
mAddress, mService.getPhysicalAddress(), mDeviceType));
mService.sendCecCommand(
HdmiCecMessageBuilder.buildDeviceVendorIdCommand(mAddress, mService.getVendorId()));
+ mService.registerTvInputCallback(mTvInputCallback);
int systemAudioControlOnPowerOnProp =
SystemProperties.getInt(
PROPERTY_SYSTEM_AUDIO_CONTROL_ON_POWER_ON,
@@ -996,6 +1057,17 @@
}
// Wake up device
mService.wakeUp();
+ // If Audio device is the active source or is on the active path,
+ // enable system audio mode without querying TV's support on sam.
+ // This is per HDMI spec 1.4b CEC 13.15.4.2.
+ if (mService.pathToPortId(getActiveSource().physicalAddress)
+ != Constants.INVALID_PORT_ID) {
+ setSystemAudioMode(true);
+ mService.sendCecCommand(
+ HdmiCecMessageBuilder.buildSetSystemAudioMode(
+ mAddress, Constants.ADDR_BROADCAST, true));
+ return;
+ }
// Check if TV supports System Audio Control.
// Handle broadcasting setSystemAudioMode on or aborting message on callback.
queryTvSystemAudioModeSupport(new TvSystemAudioModeSupportedCallback() {
@@ -1064,9 +1136,9 @@
setLocalActivePort(portId);
return;
} else {
- String uri = mTvInputs.get(portId);
+ String uri = mPortIdToTvInputs.get(portId);
if (uri != null) {
- switchToTvInput(mTvInputs.get(portId));
+ switchToTvInput(uri);
} else {
HdmiLogger.debug("Port number does not match any Tv Input.");
return;
@@ -1215,7 +1287,8 @@
pw.println("mArcIntentUsed: " + mArcIntentUsed);
pw.println("mRoutingPort: " + getRoutingPort());
pw.println("mLocalActivePort: " + getLocalActivePort());
- HdmiUtils.dumpMap(pw, "mTvInputs:", mTvInputs);
+ HdmiUtils.dumpMap(pw, "mPortIdToTvInputs:", mPortIdToTvInputs);
+ HdmiUtils.dumpMap(pw, "mTvInputsToDeviceInfo:", mTvInputsToDeviceInfo);
HdmiUtils.dumpSparseArray(pw, "mDeviceInfos:", mDeviceInfos);
pw.decreaseIndent();
super.dump(pw);
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 072238e..f5adb01 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -576,7 +576,8 @@
Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED,
Global.MHL_INPUT_SWITCHING_ENABLED,
Global.MHL_POWER_CHARGE_ENABLED,
- Global.HDMI_CEC_SWITCH_ENABLED
+ Global.HDMI_CEC_SWITCH_ENABLED,
+ Global.DEVICE_NAME
};
for (String s : settings) {
resolver.registerContentObserver(Global.getUriFor(s), false, mSettingsObserver,
@@ -642,6 +643,10 @@
case Global.MHL_POWER_CHARGE_ENABLED:
mMhlController.setOption(OPTION_MHL_POWER_CHARGE, toInt(enabled));
break;
+ case Global.DEVICE_NAME:
+ String deviceName = readStringSetting(option, Build.MODEL);
+ setDisplayName(deviceName);
+ break;
}
}
}
@@ -670,6 +675,15 @@
return SystemProperties.getBoolean(key, defVal);
}
+ String readStringSetting(String key, String defVal) {
+ ContentResolver cr = getContext().getContentResolver();
+ String content = Global.getString(cr, key);
+ if (TextUtils.isEmpty(content)) {
+ return defVal;
+ }
+ return content;
+ }
+
private void initializeCec(int initiatedBy) {
mAddressAllocated = false;
mCecController.setOption(OptionKey.SYSTEM_CEC_CONTROL, true);
@@ -1178,13 +1192,29 @@
}
private HdmiDeviceInfo createDeviceInfo(int logicalAddress, int deviceType, int powerStatus) {
- // TODO: find better name instead of model name.
- String displayName = Build.MODEL;
+ String displayName = readStringSetting(Global.DEVICE_NAME, Build.MODEL);
return new HdmiDeviceInfo(logicalAddress,
getPhysicalAddress(), pathToPortId(getPhysicalAddress()), deviceType,
getVendorId(), displayName, powerStatus);
}
+ // Set the display name in HdmiDeviceInfo of the current devices to content provided by
+ // Global.DEVICE_NAME. Only set and broadcast if the new name is different.
+ private void setDisplayName(String newDisplayName) {
+ for (HdmiCecLocalDevice device : getAllLocalDevices()) {
+ HdmiDeviceInfo deviceInfo = device.getDeviceInfo();
+ if (deviceInfo.getDisplayName().equals(newDisplayName)) {
+ continue;
+ }
+ device.setDeviceInfo(new HdmiDeviceInfo(
+ deviceInfo.getLogicalAddress(), deviceInfo.getPhysicalAddress(),
+ deviceInfo.getPortId(), deviceInfo.getDeviceType(), deviceInfo.getVendorId(),
+ newDisplayName, deviceInfo.getDevicePowerStatus()));
+ sendCecCommand(HdmiCecMessageBuilder.buildSetOsdNameCommand(
+ device.mAddress, Constants.ADDR_TV, newDisplayName));
+ }
+ }
+
@ServiceThreadOnly
void handleMhlHotplugEvent(int portId, boolean connected) {
assertRunOnServiceThread();
diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
index 2bfb31f..d2ccfcc 100644
--- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
@@ -133,7 +133,8 @@
* {@link com.android.internal.infra.AbstractRemoteService} instances, or
* {@code null} when the service doesn't bind to remote services.
* @param disallowProperty when not {@code null}, defines a {@link UserManager} restriction that
- * disables the service.
+ * disables the service. <b>NOTE: </b> you'll also need to add it to
+ * {@code UserRestrictionsUtils.USER_RESTRICTIONS}.
*/
protected AbstractMasterSystemService(@NonNull Context context,
@Nullable ServiceNameResolver serviceNameResolver,
diff --git a/services/core/java/com/android/server/job/controllers/StorageController.java b/services/core/java/com/android/server/job/controllers/StorageController.java
index c2ae53f..51187df 100644
--- a/services/core/java/com/android/server/job/controllers/StorageController.java
+++ b/services/core/java/com/android/server/job/controllers/StorageController.java
@@ -81,20 +81,16 @@
synchronized (mLock) {
for (int i = mTrackedTasks.size() - 1; i >= 0; i--) {
final JobStatus ts = mTrackedTasks.valueAt(i);
- boolean previous = ts.setStorageNotLowConstraintSatisfied(storageNotLow);
- if (previous != storageNotLow) {
- reportChange = true;
- }
+ reportChange |= ts.setStorageNotLowConstraintSatisfied(storageNotLow);
}
}
- // Let the scheduler know that state has changed. This may or may not result in an
- // execution.
- if (reportChange) {
- mStateChangedListener.onControllerStateChanged();
- }
- // Also tell the scheduler that any ready jobs should be flushed.
if (storageNotLow) {
+ // Tell the scheduler that any ready jobs should be flushed.
mStateChangedListener.onRunJobNow(null);
+ } else if (reportChange) {
+ // Let the scheduler know that state has changed. This may or may not result in an
+ // execution.
+ mStateChangedListener.onControllerStateChanged();
}
}
@@ -143,9 +139,10 @@
+ sElapsedRealtimeClock.millis());
}
mStorageLow = true;
+ maybeReportNewStorageState();
} else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(action)) {
if (DEBUG) {
- Slog.d(TAG, "Available stoage high enough to do work. @ "
+ Slog.d(TAG, "Available storage high enough to do work. @ "
+ sElapsedRealtimeClock.millis());
}
mStorageLow = false;
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 243b6fe..5156300 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -140,6 +140,9 @@
private static final int LOCATION_HAS_SPEED_ACCURACY = 64;
private static final int LOCATION_HAS_BEARING_ACCURACY = 128;
+ // these need to match ElapsedRealtimeFlags enum in types.hal
+ private static final int ELAPSED_REALTIME_HAS_TIMESTAMP_NS = 1;
+
// IMPORTANT - the GPS_DELETE_* symbols here must match GnssAidingData enum in IGnss.hal
private static final int GPS_DELETE_EPHEMERIS = 0x0001;
private static final int GPS_DELETE_ALMANAC = 0x0002;
@@ -527,6 +530,7 @@
mPowerManager.getPowerSaveState(ServiceType.LOCATION);
switch (result.locationMode) {
case PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF:
+ case PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF:
// If we are in battery saver mode and the screen is off, disable GPS.
disableGps |= result.batterySaverEnabled && !mPowerManager.isInteractive();
break;
@@ -756,10 +760,16 @@
float speedAccuracyMetersPerSecond = location.getSpeedAccuracyMetersPerSecond();
float bearingAccuracyDegrees = location.getBearingAccuracyDegrees();
long timestamp = location.getTime();
- native_inject_best_location(gnssLocationFlags, latitudeDegrees, longitudeDegrees,
- altitudeMeters, speedMetersPerSec, bearingDegrees, horizontalAccuracyMeters,
- verticalAccuracyMeters, speedAccuracyMetersPerSecond, bearingAccuracyDegrees,
- timestamp);
+
+ int elapsedRealtimeFlags = ELAPSED_REALTIME_HAS_TIMESTAMP_NS;
+ long elapsedRealtimeNanos = location.getElapsedRealtimeNanos();
+
+ native_inject_best_location(
+ gnssLocationFlags, latitudeDegrees, longitudeDegrees,
+ altitudeMeters, speedMetersPerSec, bearingDegrees,
+ horizontalAccuracyMeters, verticalAccuracyMeters,
+ speedAccuracyMetersPerSecond, bearingAccuracyDegrees, timestamp,
+ elapsedRealtimeFlags, elapsedRealtimeNanos);
}
/** Returns true if the location request is too frequent. */
@@ -1259,9 +1269,6 @@
if (VERBOSE) Log.v(TAG, "reportLocation " + location.toString());
- // It would be nice to push the elapsed real-time timestamp
- // further down the stack, but this is still useful
- location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
location.setExtras(mLocationExtras.getBundle());
reportLocation(location);
@@ -2150,17 +2157,11 @@
private native int native_read_nmea(byte[] buffer, int bufferSize);
private native void native_inject_best_location(
- int gnssLocationFlags,
- double latitudeDegrees,
- double longitudeDegrees,
- double altitudeMeters,
- float speedMetersPerSec,
- float bearingDegrees,
- float horizontalAccuracyMeters,
- float verticalAccuracyMeters,
- float speedAccuracyMetersPerSecond,
- float bearingAccuracyDegrees,
- long timestamp);
+ int gnssLocationFlags, double latitudeDegrees, double longitudeDegrees,
+ double altitudeMeters, float speedMetersPerSec, float bearingDegrees,
+ float horizontalAccuracyMeters, float verticalAccuracyMeters,
+ float speedAccuracyMetersPerSecond, float bearingAccuracyDegrees,
+ long timestamp, int elapsedRealtimeFlags, long elapsedRealtimeNanos);
private native void native_inject_location(double latitude, double longitude, float accuracy);
@@ -2187,4 +2188,4 @@
int lac, int cid);
private native void native_agps_set_id(int type, String setid);
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java
index afe3473..00e1ffa 100644
--- a/services/core/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/core/java/com/android/server/location/LocationProviderProxy.java
@@ -34,13 +34,11 @@
import com.android.internal.location.ILocationProviderManager;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
-import com.android.internal.os.TransferPipe;
import com.android.server.FgThread;
import com.android.server.LocationManagerService;
import com.android.server.ServiceWatcher;
import java.io.FileDescriptor;
-import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
@@ -189,14 +187,6 @@
pw.println(" additional packages=" + mProviderPackages);
}
}
- mServiceWatcher.runOnBinderBlocking(binder -> {
- try {
- TransferPipe.dumpAsync(binder, fd, args);
- } catch (IOException | RemoteException e) {
- pw.println(" <failed to dump location provider: " + e + ">");
- }
- return null;
- }, null);
}
@Override
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index aa85c83..ee968c8 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -123,7 +123,6 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
-import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyStoreException;
@@ -277,7 +276,7 @@
* @param managedUserPassword Managed profile original password (when it has separated lock).
* NULL when it does not have a separated lock before.
*/
- public void tieManagedProfileLockIfNecessary(int managedUserId, String managedUserPassword) {
+ public void tieManagedProfileLockIfNecessary(int managedUserId, byte[] managedUserPassword) {
if (DEBUG) Slog.v(TAG, "Check child profile lock for user: " + managedUserId);
// Only for managed profile
if (!mUserManager.getUserInfo(managedUserId).isManagedProfile()) {
@@ -312,7 +311,12 @@
byte[] randomLockSeed = new byte[] {};
try {
randomLockSeed = SecureRandom.getInstance("SHA1PRNG").generateSeed(40);
- String newPassword = String.valueOf(HexEncoding.encode(randomLockSeed));
+ char[] newPasswordChars = HexEncoding.encode(randomLockSeed);
+ byte[] newPassword = new byte[newPasswordChars.length];
+ for (int i = 0; i < newPasswordChars.length; i++) {
+ newPassword[i] = (byte) newPasswordChars[i];
+ }
+ Arrays.fill(newPasswordChars, '\u0000');
final int quality = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
setLockCredentialInternal(newPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
managedUserPassword, quality, managedUserId);
@@ -321,6 +325,7 @@
// password directly, so we always store a password.
setLong(LockPatternUtils.PASSWORD_TYPE_KEY, quality, managedUserId);
tieProfileLockToParent(managedUserId, newPassword);
+ Arrays.fill(newPassword, (byte) 0);
} catch (NoSuchAlgorithmException | RemoteException e) {
Slog.e(TAG, "Fail to tie managed profile", e);
// Nothing client can do to fix this issue, so we do not throw exception out
@@ -618,7 +623,7 @@
try {
final long handle = getSyntheticPasswordHandleLocked(userId);
- final String noCredential = null;
+ final byte[] noCredential = null;
AuthenticationResult result =
mSpManager.unwrapPasswordBasedSyntheticPassword(
getGateKeeperService(), handle, noCredential, userId, null);
@@ -956,7 +961,7 @@
@Override
public void setSeparateProfileChallengeEnabled(int userId, boolean enabled,
- String managedUserPassword) {
+ byte[] managedUserPassword) {
checkWritePermission(userId);
if (!mLockPatternUtils.hasSecureLockScreen()) {
throw new UnsupportedOperationException(
@@ -969,8 +974,8 @@
}
@GuardedBy("mSeparateChallengeLock")
- private void setSeparateProfileChallengeEnabledLocked(@UserIdInt int userId, boolean enabled,
- String managedUserPassword) {
+ private void setSeparateProfileChallengeEnabledLocked(@UserIdInt int userId,
+ boolean enabled, byte[] managedUserPassword) {
final boolean old = getBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, false, userId);
setBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, enabled, userId);
try {
@@ -1113,24 +1118,28 @@
return mStorage.hasCredential(userId);
}
- private void setKeystorePassword(String password, int userHandle) {
+ private void setKeystorePassword(byte[] password, int userHandle) {
final KeyStore ks = KeyStore.getInstance();
- ks.onUserPasswordChanged(userHandle, password);
+ // TODO(b/120484642): Update keystore to accept byte[] passwords
+ String passwordString = password == null ? null : new String(password);
+ ks.onUserPasswordChanged(userHandle, passwordString);
}
- private void unlockKeystore(String password, int userHandle) {
+ private void unlockKeystore(byte[] password, int userHandle) {
if (DEBUG) Slog.v(TAG, "Unlock keystore for user: " + userHandle);
+ // TODO(b/120484642): Update keystore to accept byte[] passwords
+ String passwordString = password == null ? null : new String(password);
final KeyStore ks = KeyStore.getInstance();
- ks.unlock(userHandle, password);
+ ks.unlock(userHandle, passwordString);
}
@VisibleForTesting
- protected String getDecryptedPasswordForTiedProfile(int userId)
+ protected byte[] getDecryptedPasswordForTiedProfile(int userId)
throws KeyStoreException, UnrecoverableKeyException,
NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException,
CertificateException, IOException {
- if (DEBUG) Slog.v(TAG, "Get child profile decrytped key");
+ if (DEBUG) Slog.v(TAG, "Get child profile decrypted key");
byte[] storedData = mStorage.readChildProfileLock(userId);
if (storedData == null) {
throw new FileNotFoundException("Child profile lock file not found");
@@ -1149,7 +1158,7 @@
cipher.init(Cipher.DECRYPT_MODE, decryptionKey, new GCMParameterSpec(128, iv));
decryptionResult = cipher.doFinal(encryptedPassword);
- return new String(decryptionResult, StandardCharsets.UTF_8);
+ return decryptionResult;
}
private void unlockChildProfile(int profileHandle, boolean ignoreUserNotAuthenticated)
@@ -1226,11 +1235,11 @@
&& mUserManager.isUserRunning(userInfo.id);
}
- private Map<Integer, String> getDecryptedPasswordsForAllTiedProfiles(int userId) {
+ private Map<Integer, byte[]> getDecryptedPasswordsForAllTiedProfiles(int userId) {
if (mUserManager.getUserInfo(userId).isManagedProfile()) {
return null;
}
- Map<Integer, String> result = new ArrayMap<Integer, String>();
+ Map<Integer, byte[]> result = new ArrayMap<Integer, byte[]>();
final List<UserInfo> profiles = mUserManager.getProfiles(userId);
final int size = profiles.size();
for (int i = 0; i < size; i++) {
@@ -1268,7 +1277,7 @@
* terminates when the user is a managed profile.
*/
private void synchronizeUnifiedWorkChallengeForProfiles(int userId,
- Map<Integer, String> profilePasswordMap) throws RemoteException {
+ Map<Integer, byte[]> profilePasswordMap) throws RemoteException {
if (mUserManager.getUserInfo(userId).isManagedProfile()) {
return;
}
@@ -1317,9 +1326,10 @@
// This method should be called by LockPatternUtil only, all internal methods in this class
// should call setLockCredentialInternal.
@Override
- public void setLockCredential(String credential, int type, String savedCredential,
- int requestedQuality, int userId)
+ public void setLockCredential(byte[] credential, int type,
+ byte[] savedCredential, int requestedQuality, int userId)
throws RemoteException {
+
if (!mLockPatternUtils.hasSecureLockScreen()) {
throw new UnsupportedOperationException(
"This operation requires secure lock screen feature");
@@ -1333,14 +1343,14 @@
notifySeparateProfileChallengeChanged(userId);
}
- private void setLockCredentialInternal(String credential, int credentialType,
- String savedCredential, int requestedQuality, int userId) throws RemoteException {
+ private void setLockCredentialInternal(byte[] credential, int credentialType,
+ byte[] savedCredential, int requestedQuality, int userId) throws RemoteException {
// Normalize savedCredential and credential such that empty string is always represented
// as null.
- if (TextUtils.isEmpty(savedCredential)) {
+ if (savedCredential == null || savedCredential.length == 0) {
savedCredential = null;
}
- if (TextUtils.isEmpty(credential)) {
+ if (credential == null || credential.length == 0) {
credential = null;
}
synchronized (mSpManager) {
@@ -1409,7 +1419,7 @@
mStorage.writeCredentialHash(willStore, userId);
// push new secret and auth token to vold
GateKeeperResponse gkResponse = getGateKeeperService()
- .verifyChallenge(userId, 0, willStore.hash, credential.getBytes());
+ .verifyChallenge(userId, 0, willStore.hash, credential);
setUserKeyProtection(userId, credential, convertResponse(gkResponse));
fixateNewestUserKeyAuth(userId);
// Refresh the auth token
@@ -1429,9 +1439,8 @@
}
@VisibleForTesting
- protected void tieProfileLockToParent(int userId, String password) {
+ protected void tieProfileLockToParent(int userId, byte[] password) {
if (DEBUG) Slog.v(TAG, "tieProfileLockToParent for user: " + userId);
- byte[] randomLockSeed = password.getBytes(StandardCharsets.UTF_8);
byte[] encryptionResult;
byte[] iv;
try {
@@ -1465,7 +1474,7 @@
KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_GCM + "/"
+ KeyProperties.ENCRYPTION_PADDING_NONE);
cipher.init(Cipher.ENCRYPT_MODE, keyStoreEncryptionKey);
- encryptionResult = cipher.doFinal(randomLockSeed);
+ encryptionResult = cipher.doFinal(password);
iv = cipher.getIV();
} finally {
// The original key can now be discarded.
@@ -1490,17 +1499,11 @@
}
private byte[] enrollCredential(byte[] enrolledHandle,
- String enrolledCredential, String toEnroll, int userId)
+ byte[] enrolledCredential, byte[] toEnroll, int userId)
throws RemoteException {
checkWritePermission(userId);
- byte[] enrolledCredentialBytes = enrolledCredential == null
- ? null
- : enrolledCredential.getBytes();
- byte[] toEnrollBytes = toEnroll == null
- ? null
- : toEnroll.getBytes();
GateKeeperResponse response = getGateKeeperService().enroll(userId, enrolledHandle,
- enrolledCredentialBytes, toEnrollBytes);
+ enrolledCredential, toEnroll);
if (response == null) {
return null;
@@ -1521,7 +1524,7 @@
addUserKeyAuth(userId, null, key);
}
- private void setUserKeyProtection(int userId, String credential, VerifyCredentialResponse vcr)
+ private void setUserKeyProtection(int userId, byte[] credential, VerifyCredentialResponse vcr)
throws RemoteException {
if (DEBUG) Slog.d(TAG, "setUserKeyProtection: user=" + userId);
if (vcr == null) {
@@ -1543,16 +1546,15 @@
addUserKeyAuth(userId, null, null);
}
- private static byte[] secretFromCredential(String credential) throws RemoteException {
+ private static byte[] secretFromCredential(byte[] credential) throws RemoteException {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-512");
// Personalize the hash
- byte[] personalization = "Android FBE credential hash"
- .getBytes(StandardCharsets.UTF_8);
+ byte[] personalization = "Android FBE credential hash".getBytes();
// Pad it to the block size of the hash function
personalization = Arrays.copyOf(personalization, 128);
digest.update(personalization);
- digest.update(credential.getBytes(StandardCharsets.UTF_8));
+ digest.update(credential);
return digest.digest();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("NoSuchAlgorithmException for SHA-512");
@@ -1588,7 +1590,7 @@
checkWritePermission(userId);
if (DEBUG) Slog.v(TAG, "Reset keystore for user: " + userId);
int managedUserId = -1;
- String managedUserDecryptedPassword = null;
+ byte[] managedUserDecryptedPassword = null;
final List<UserInfo> profiles = mUserManager.getProfiles(userId);
for (UserInfo pi : profiles) {
// Unlock managed profile with unified lock
@@ -1625,17 +1627,20 @@
tieProfileLockToParent(managedUserId, managedUserDecryptedPassword);
}
}
+ if (managedUserDecryptedPassword != null && managedUserDecryptedPassword.length > 0) {
+ Arrays.fill(managedUserDecryptedPassword, (byte) 0);
+ }
}
@Override
- public VerifyCredentialResponse checkCredential(String credential, int type, int userId,
+ public VerifyCredentialResponse checkCredential(byte[] credential, int type, int userId,
ICheckCredentialProgressCallback progressCallback) throws RemoteException {
checkPasswordReadPermission(userId);
return doVerifyCredential(credential, type, false, 0, userId, progressCallback);
}
@Override
- public VerifyCredentialResponse verifyCredential(String credential, int type, long challenge,
+ public VerifyCredentialResponse verifyCredential(byte[] credential, int type, long challenge,
int userId) throws RemoteException {
checkPasswordReadPermission(userId);
return doVerifyCredential(credential, type, true, challenge, userId,
@@ -1646,10 +1651,10 @@
* Verify user credential and unlock the user. Fix pattern bug by deprecating the old base zero
* format.
*/
- private VerifyCredentialResponse doVerifyCredential(String credential, int credentialType,
+ private VerifyCredentialResponse doVerifyCredential(byte[] credential, int credentialType,
boolean hasChallenge, long challenge, int userId,
ICheckCredentialProgressCallback progressCallback) throws RemoteException {
- if (TextUtils.isEmpty(credential)) {
+ if (credential == null || credential.length == 0) {
throw new IllegalArgumentException("Credential can't be null or empty");
}
if (userId == USER_FRP && Settings.Global.getInt(mContext.getContentResolver(),
@@ -1684,9 +1689,9 @@
boolean shouldReEnrollBaseZero = storedHash.type == LockPatternUtils.CREDENTIAL_TYPE_PATTERN
&& storedHash.isBaseZeroPattern;
- String credentialToVerify;
+ byte[] credentialToVerify;
if (shouldReEnrollBaseZero) {
- credentialToVerify = LockPatternUtils.patternStringToBaseZero(credential);
+ credentialToVerify = LockPatternUtils.patternByteArrayToBaseZero(credential);
} else {
credentialToVerify = credential;
}
@@ -1706,7 +1711,7 @@
}
@Override
- public VerifyCredentialResponse verifyTiedProfileChallenge(String credential, int type,
+ public VerifyCredentialResponse verifyTiedProfileChallenge(byte[] credential, int type,
long challenge, int userId) throws RemoteException {
checkPasswordReadPermission(userId);
if (!isManagedProfileWithUnifiedLock(userId)) {
@@ -1748,14 +1753,15 @@
* hash to GK.
*/
private VerifyCredentialResponse verifyCredential(int userId, CredentialHash storedHash,
- String credential, boolean hasChallenge, long challenge,
+ byte[] credential, boolean hasChallenge, long challenge,
ICheckCredentialProgressCallback progressCallback) throws RemoteException {
- if ((storedHash == null || storedHash.hash.length == 0) && TextUtils.isEmpty(credential)) {
+ if ((storedHash == null || storedHash.hash.length == 0)
+ && (credential == null || credential.length == 0)) {
// don't need to pass empty credentials to GateKeeper
return VerifyCredentialResponse.OK;
}
- if (storedHash == null || TextUtils.isEmpty(credential)) {
+ if (storedHash == null || credential == null || credential.length == 0) {
return VerifyCredentialResponse.ERROR;
}
@@ -1766,14 +1772,14 @@
if (storedHash.version == CredentialHash.VERSION_LEGACY) {
final byte[] hash;
if (storedHash.type == LockPatternUtils.CREDENTIAL_TYPE_PATTERN) {
- hash = LockPatternUtils.patternToHash(LockPatternUtils.stringToPattern(credential));
+ hash = LockPatternUtils.patternToHash(
+ LockPatternUtils.byteArrayToPattern(credential));
} else {
- hash = mLockPatternUtils.legacyPasswordToHash(credential, userId)
- .getBytes(StandardCharsets.UTF_8);
+ hash = mLockPatternUtils.legacyPasswordToHash(credential, userId).getBytes();
}
if (Arrays.equals(hash, storedHash.hash)) {
if (storedHash.type == LockPatternUtils.CREDENTIAL_TYPE_PATTERN) {
- unlockKeystore(LockPatternUtils.patternStringToBaseZero(credential), userId);
+ unlockKeystore(LockPatternUtils.patternByteArrayToBaseZero(credential), userId);
} else {
unlockKeystore(credential, userId);
}
@@ -1804,7 +1810,7 @@
}
}
GateKeeperResponse gateKeeperResponse = getGateKeeperService()
- .verifyChallenge(userId, challenge, storedHash.hash, credential.getBytes());
+ .verifyChallenge(userId, challenge, storedHash.hash, credential);
VerifyCredentialResponse response = convertResponse(gateKeeperResponse);
boolean shouldReEnroll = gateKeeperResponse.getShouldReEnroll();
@@ -1863,7 +1869,7 @@
* Call this method to notify DPMS regarding the latest password metric. This should be called
* when the user is authenticating or when a new password is being set.
*/
- private void notifyActivePasswordMetricsAvailable(String password, @UserIdInt int userId) {
+ private void notifyActivePasswordMetricsAvailable(byte[] password, @UserIdInt int userId) {
final PasswordMetrics metrics;
if (password == null) {
metrics = new PasswordMetrics();
@@ -1912,6 +1918,7 @@
// service can't connect to vold, it restarts, and then the new instance
// does successfully connect.
final IStorageManager service = mInjector.getStorageManager();
+ // TODO(b/120484642): Update vold to return a password as a byte array
String password;
long identity = Binder.clearCallingIdentity();
try {
@@ -1926,8 +1933,8 @@
try {
if (mLockPatternUtils.isLockPatternEnabled(userId)) {
- if (checkCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, userId,
- null /* progressCallback */)
+ if (checkCredential(password.getBytes(), LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
+ userId, null /* progressCallback */)
.getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
return true;
}
@@ -1937,8 +1944,8 @@
try {
if (mLockPatternUtils.isLockPasswordEnabled(userId)) {
- if (checkCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, userId,
- null /* progressCallback */)
+ if (checkCredential(password.getBytes(), LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
+ userId, null /* progressCallback */)
.getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
return true;
}
@@ -1966,7 +1973,8 @@
} catch (RemoteException ex) {
Slog.w(TAG, "unable to clear GK secure user id");
}
- if (unknownUser || mUserManager.getUserInfo(userId).isManagedProfile()) {
+ UserInfo userInfo = mUserManager.getUserInfo(userId);
+ if (unknownUser || userInfo == null || userInfo.isManagedProfile()) {
removeKeystoreProfileKey(userId);
}
}
@@ -2322,7 +2330,7 @@
@GuardedBy("mSpManager")
@VisibleForTesting
protected AuthenticationToken initializeSyntheticPasswordLocked(byte[] credentialHash,
- String credential, int credentialType, int requestedQuality,
+ byte[] credential, int credentialType, int requestedQuality,
int userId) throws RemoteException {
Slog.i(TAG, "Initialize SyntheticPassword for user: " + userId);
final AuthenticationToken auth = mSpManager.newSyntheticPasswordAndSid(
@@ -2383,7 +2391,7 @@
setLong(SYNTHETIC_PASSWORD_ENABLED_KEY, 1, UserHandle.USER_SYSTEM);
}
- private VerifyCredentialResponse spBasedDoVerifyCredential(String userCredential, int
+ private VerifyCredentialResponse spBasedDoVerifyCredential(byte[] userCredential, int
credentialType, boolean hasChallenge, long challenge, int userId,
ICheckCredentialProgressCallback progressCallback) throws RemoteException {
if (DEBUG) Slog.d(TAG, "spBasedDoVerifyCredential: user=" + userId);
@@ -2482,12 +2490,12 @@
* added back when new password is set in future.
*/
@GuardedBy("mSpManager")
- private long setLockCredentialWithAuthTokenLocked(String credential, int credentialType,
+ private long setLockCredentialWithAuthTokenLocked(byte[] credential, int credentialType,
AuthenticationToken auth, int requestedQuality, int userId) throws RemoteException {
if (DEBUG) Slog.d(TAG, "setLockCredentialWithAuthTokenLocked: user=" + userId);
long newHandle = mSpManager.createPasswordBasedSyntheticPassword(getGateKeeperService(),
credential, credentialType, auth, requestedQuality, userId);
- final Map<Integer, String> profilePasswords;
+ final Map<Integer, byte[]> profilePasswords;
if (credential != null) {
// // not needed by synchronizeUnifiedWorkChallengeForProfiles()
profilePasswords = null;
@@ -2526,12 +2534,19 @@
synchronizeUnifiedWorkChallengeForProfiles(userId, profilePasswords);
notifyActivePasswordMetricsAvailable(credential, userId);
+
+ if (profilePasswords != null) {
+ for (Map.Entry<Integer, byte[]> entry : profilePasswords.entrySet()) {
+ Arrays.fill(entry.getValue(), (byte) 0);
+ }
+ }
+
return newHandle;
}
@GuardedBy("mSpManager")
- private void spBasedSetLockCredentialInternalLocked(String credential, int credentialType,
- String savedCredential, int requestedQuality, int userId) throws RemoteException {
+ private void spBasedSetLockCredentialInternalLocked(byte[] credential, int credentialType,
+ byte[] savedCredential, int requestedQuality, int userId) throws RemoteException {
if (DEBUG) Slog.d(TAG, "spBasedSetLockCredentialInternalLocked: user=" + userId);
if (isManagedProfileWithUnifiedLock(userId)) {
// get credential from keystore when managed profile has unified lock
@@ -2604,9 +2619,9 @@
* If user is a managed profile with unified challenge, currentCredential is ignored.
*/
@Override
- public byte[] getHashFactor(String currentCredential, int userId) throws RemoteException {
+ public byte[] getHashFactor(byte[] currentCredential, int userId) throws RemoteException {
checkPasswordReadPermission(userId);
- if (TextUtils.isEmpty(currentCredential)) {
+ if (currentCredential == null || currentCredential.length == 0) {
currentCredential = null;
}
if (isManagedProfileWithUnifiedLock(userId)) {
@@ -2700,7 +2715,7 @@
}
}
- private boolean setLockCredentialWithToken(String credential, int type, long tokenHandle,
+ private boolean setLockCredentialWithToken(byte[] credential, int type, long tokenHandle,
byte[] token, int requestedQuality, int userId) throws RemoteException {
boolean result;
synchronized (mSpManager) {
@@ -2720,7 +2735,7 @@
return result;
}
- private boolean setLockCredentialWithTokenInternal(String credential, int type,
+ private boolean setLockCredentialWithTokenInternal(byte[] credential, int type,
long tokenHandle, byte[] token, int requestedQuality, int userId) throws RemoteException {
final AuthenticationResult result;
synchronized (mSpManager) {
@@ -2947,8 +2962,8 @@
}
@Override
- public boolean setLockCredentialWithToken(String credential, int type, long tokenHandle,
- byte[] token, int requestedQuality, int userId) {
+ public boolean setLockCredentialWithToken(byte[] credential, int type,
+ long tokenHandle, byte[] token, int requestedQuality, int userId) {
if (!mLockPatternUtils.hasSecureLockScreen()) {
throw new UnsupportedOperationException(
"This operation requires secure lock screen feature.");
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
index 6163077..ee22264 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
@@ -190,22 +190,30 @@
}
private void runSetPattern() {
- mLockPatternUtils.saveLockPattern(stringToPattern(mNew), mOld, mCurrentUserId);
+ byte[] oldBytes = mOld != null ? mOld.getBytes() : null;
+ mLockPatternUtils.saveLockPattern(stringToPattern(mNew), oldBytes, mCurrentUserId);
getOutPrintWriter().println("Pattern set to '" + mNew + "'");
}
private void runSetPassword() {
- mLockPatternUtils.saveLockPassword(mNew, mOld, PASSWORD_QUALITY_ALPHABETIC, mCurrentUserId);
+ byte[] newBytes = mNew != null ? mNew.getBytes() : null;
+ byte[] oldBytes = mOld != null ? mOld.getBytes() : null;
+ mLockPatternUtils.saveLockPassword(newBytes, oldBytes, PASSWORD_QUALITY_ALPHABETIC,
+ mCurrentUserId);
getOutPrintWriter().println("Password set to '" + mNew + "'");
}
private void runSetPin() {
- mLockPatternUtils.saveLockPassword(mNew, mOld, PASSWORD_QUALITY_NUMERIC, mCurrentUserId);
+ byte[] newBytes = mNew != null ? mNew.getBytes() : null;
+ byte[] oldBytes = mOld != null ? mOld.getBytes() : null;
+ mLockPatternUtils.saveLockPassword(newBytes, oldBytes, PASSWORD_QUALITY_NUMERIC,
+ mCurrentUserId);
getOutPrintWriter().println("Pin set to '" + mNew + "'");
}
private void runClear() {
- mLockPatternUtils.clearLock(mOld, mCurrentUserId);
+ byte[] oldBytes = mOld != null ? mOld.getBytes() : null;
+ mLockPatternUtils.clearLock(oldBytes, mCurrentUserId);
getOutPrintWriter().println("Lock credential cleared");
}
@@ -232,7 +240,8 @@
try {
final boolean result;
if (havePassword) {
- result = mLockPatternUtils.checkPassword(mOld, mCurrentUserId);
+ byte[] passwordBytes = mOld != null ? mOld.getBytes() : null;
+ result = mLockPatternUtils.checkPassword(passwordBytes, mCurrentUserId);
} else {
result = mLockPatternUtils.checkPattern(stringToPattern(mOld), mCurrentUserId);
}
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index 0e195bc..ea39dff 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -97,7 +97,7 @@
private static final String WEAVER_SLOT_NAME = "weaver";
public static final long DEFAULT_HANDLE = 0L;
- private static final String DEFAULT_PASSWORD = "default-password";
+ private static final byte[] DEFAULT_PASSWORD = "default-password".getBytes();
private static final byte WEAVER_VERSION = 1;
private static final int INVALID_WEAVER_SLOT = -1;
@@ -165,7 +165,7 @@
}
}
- public String deriveKeyStorePassword() {
+ public byte[] deriveKeyStorePassword() {
return bytesToHex(derivePassword(PERSONALIZATION_KEY_STORE_PASSWORD));
}
@@ -454,11 +454,11 @@
*
*/
public AuthenticationToken newSyntheticPasswordAndSid(IGateKeeperService gatekeeper,
- byte[] hash, String credential, int userId) throws RemoteException {
+ byte[] hash, byte[] credential, int userId) throws RemoteException {
AuthenticationToken result = AuthenticationToken.create();
GateKeeperResponse response;
if (hash != null) {
- response = gatekeeper.enroll(userId, hash, credential.getBytes(),
+ response = gatekeeper.enroll(userId, hash, credential,
result.deriveGkPassword());
if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
Log.w(TAG, "Fail to migrate SID, assuming no SID, user " + userId);
@@ -616,7 +616,7 @@
* @see #clearSidForUser
*/
public long createPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper,
- String credential, int credentialType, AuthenticationToken authToken,
+ byte[] credential, int credentialType, AuthenticationToken authToken,
int requestedQuality, int userId)
throws RemoteException {
if (credential == null || credentialType == LockPatternUtils.CREDENTIAL_TYPE_NONE) {
@@ -670,7 +670,7 @@
}
public VerifyCredentialResponse verifyFrpCredential(IGateKeeperService gatekeeper,
- String userCredential, int credentialType,
+ byte[] userCredential, int credentialType,
ICheckCredentialProgressCallback progressCallback) throws RemoteException {
PersistentData persistentData = mStorage.readPersistentDataBlock();
if (persistentData.type == PersistentData.TYPE_SP) {
@@ -839,7 +839,7 @@
* unknown. Caller might choose to validate it by examining AuthenticationResult.credentialType
*/
public AuthenticationResult unwrapPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper,
- long handle, String credential, int userId,
+ long handle, byte[] credential, int userId,
ICheckCredentialProgressCallback progressCallback) throws RemoteException {
if (credential == null) {
credential = DEFAULT_PASSWORD;
@@ -1152,7 +1152,7 @@
return String.format("%s%x", LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX, handle);
}
- private byte[] computePasswordToken(String password, PasswordData data) {
+ private byte[] computePasswordToken(byte[] password, PasswordData data) {
return scrypt(password, data.salt, 1 << data.scryptN, 1 << data.scryptR, 1 << data.scryptP,
PASSWORD_TOKEN_LENGTH);
}
@@ -1173,8 +1173,8 @@
return nativeSidFromPasswordHandle(handle);
}
- protected byte[] scrypt(String password, byte[] salt, int N, int r, int p, int outLen) {
- return new Scrypt().scrypt(password.getBytes(), salt, N, r, p, outLen);
+ protected byte[] scrypt(byte[] password, byte[] salt, int n, int r, int p, int outLen) {
+ return new Scrypt().scrypt(password, salt, n, r, p, outLen);
}
native long nativeSidFromPasswordHandle(byte[] handle);
@@ -1195,17 +1195,17 @@
return result;
}
- final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
- public static String bytesToHex(byte[] bytes) {
+ protected static final byte[] HEX_ARRAY = "0123456789ABCDEF".getBytes();
+ private static byte[] bytesToHex(byte[] bytes) {
if (bytes == null) {
- return "null";
+ return "null".getBytes();
}
- char[] hexChars = new char[bytes.length * 2];
+ byte[] hexBytes = new byte[bytes.length * 2];
for ( int j = 0; j < bytes.length; j++ ) {
int v = bytes[j] & 0xFF;
- hexChars[j * 2] = hexArray[v >>> 4];
- hexChars[j * 2 + 1] = hexArray[v & 0x0F];
+ hexBytes[j * 2] = HEX_ARRAY[v >>> 4];
+ hexBytes[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
}
- return new String(hexChars);
+ return hexBytes;
}
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
index e7a71b9..5676da2 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
@@ -38,7 +38,6 @@
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
-import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
@@ -51,6 +50,7 @@
import java.security.cert.CertPath;
import java.security.cert.CertificateException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
@@ -85,7 +85,7 @@
private final RecoverableKeyStoreDb mRecoverableKeyStoreDb;
private final int mUserId;
private final int mCredentialType;
- private final String mCredential;
+ private final byte[] mCredential;
private final boolean mCredentialUpdated;
private final PlatformKeyManager mPlatformKeyManager;
private final RecoverySnapshotStorage mRecoverySnapshotStorage;
@@ -100,7 +100,7 @@
RecoverySnapshotListenersStorage recoverySnapshotListenersStorage,
int userId,
int credentialType,
- String credential,
+ byte[] credential,
boolean credentialUpdated
) throws NoSuchAlgorithmException, KeyStoreException, InsecureUserException {
return new KeySyncTask(
@@ -134,7 +134,7 @@
RecoverySnapshotListenersStorage recoverySnapshotListenersStorage,
int userId,
int credentialType,
- String credential,
+ byte[] credential,
boolean credentialUpdated,
PlatformKeyManager platformKeyManager,
TestOnlyInsecureCertificateHelper testOnlyInsecureCertificateHelper,
@@ -450,7 +450,7 @@
*/
@VisibleForTesting
@KeyChainProtectionParams.LockScreenUiFormat static int getUiFormat(
- int credentialType, String credential) {
+ int credentialType, byte[] credential) {
if (credentialType == LockPatternUtils.CREDENTIAL_TYPE_PATTERN) {
return KeyChainProtectionParams.UI_FORMAT_PATTERN;
} else if (isPin(credential)) {
@@ -475,13 +475,13 @@
* Returns {@code true} if {@code credential} looks like a pin.
*/
@VisibleForTesting
- static boolean isPin(@Nullable String credential) {
+ static boolean isPin(@Nullable byte[] credential) {
if (credential == null) {
return false;
}
- int length = credential.length();
+ int length = credential.length;
for (int i = 0; i < length; i++) {
- if (!Character.isDigit(credential.charAt(i))) {
+ if (!Character.isDigit((char) credential[i])) {
return false;
}
}
@@ -494,8 +494,7 @@
* @return The SHA-256 hash.
*/
@VisibleForTesting
- static byte[] hashCredentialsBySaltedSha256(byte[] salt, String credentials) {
- byte[] credentialsBytes = credentials.getBytes(StandardCharsets.UTF_8);
+ static byte[] hashCredentialsBySaltedSha256(byte[] salt, byte[] credentialsBytes) {
ByteBuffer byteBuffer = ByteBuffer.allocate(
salt.length + credentialsBytes.length + LENGTH_PREFIX_BYTES * 2);
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
@@ -506,17 +505,19 @@
byte[] bytes = byteBuffer.array();
try {
- return MessageDigest.getInstance(LOCK_SCREEN_HASH_ALGORITHM).digest(bytes);
+ byte[] hash = MessageDigest.getInstance(LOCK_SCREEN_HASH_ALGORITHM).digest(bytes);
+ Arrays.fill(bytes, (byte) 0);
+ return hash;
} catch (NoSuchAlgorithmException e) {
// Impossible, SHA-256 must be supported on Android.
throw new RuntimeException(e);
}
}
- private byte[] hashCredentialsByScrypt(byte[] salt, String credentials) {
+ private byte[] hashCredentialsByScrypt(byte[] salt, byte[] credentials) {
return mScrypt.scrypt(
- credentials.getBytes(StandardCharsets.UTF_8), salt,
- SCRYPT_PARAM_N, SCRYPT_PARAM_R, SCRYPT_PARAM_P, SCRYPT_PARAM_OUTLEN_BYTES);
+ credentials, salt, SCRYPT_PARAM_N, SCRYPT_PARAM_R, SCRYPT_PARAM_P,
+ SCRYPT_PARAM_OUTLEN_BYTES);
}
private static SecretKey generateRecoveryKey() throws NoSuchAlgorithmException {
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index ed864c0..47b9c27 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -892,13 +892,13 @@
* This function can only be used inside LockSettingsService.
*
* @param storedHashType from {@code CredentialHash}
- * @param credential - unencrypted String. Password length should be at most 16 symbols {@code
- * mPasswordMaxLength}
+ * @param credential - unencrypted byte array. Password length should be at most 16 symbols
+ * {@code mPasswordMaxLength}
* @param userId for user who just unlocked the device.
* @hide
*/
public void lockScreenSecretAvailable(
- int storedHashType, @NonNull String credential, int userId) {
+ int storedHashType, @NonNull byte[] credential, int userId) {
// So as not to block the critical path unlocking the phone, defer to another thread.
try {
mExecutorService.execute(KeySyncTask.newInstance(
@@ -923,13 +923,13 @@
* This function can only be used inside LockSettingsService.
*
* @param storedHashType from {@code CredentialHash}
- * @param credential - unencrypted String
+ * @param credential - unencrypted byte array
* @param userId for the user whose lock screen credentials were changed.
* @hide
*/
public void lockScreenSecretChanged(
int storedHashType,
- @Nullable String credential,
+ @Nullable byte[] credential,
int userId) {
// So as not to block the critical path unlocking the phone, defer to another thread.
try {
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java
index 057429c..90a36723 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java
@@ -87,10 +87,30 @@
|| isTestOnlyCertificateAlias(rootCertificateAlias);
}
- public boolean doesCredentialSupportInsecureMode(int credentialType, String credential) {
- return (credentialType == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD)
- && (credential != null)
- && credential.startsWith(TrustedRootCertificates.INSECURE_PASSWORD_PREFIX);
+ /**
+ * Checks whether a password is in "Insecure mode"
+ * @param credentialType the type of credential, e.g. pattern and password
+ * @param credential the pattern or password
+ * @return true, if the credential is in "Insecure mode"
+ */
+ public boolean doesCredentialSupportInsecureMode(int credentialType, byte[] credential) {
+ if (credential == null) {
+ return false;
+ }
+ if (credentialType != LockPatternUtils.CREDENTIAL_TYPE_PASSWORD) {
+ return false;
+ }
+ byte[] insecurePasswordPrefixBytes =
+ TrustedRootCertificates.INSECURE_PASSWORD_PREFIX.getBytes();
+ if (credential.length < insecurePasswordPrefixBytes.length) {
+ return false;
+ }
+ for (int i = 0; i < insecurePasswordPrefixBytes.length; i++) {
+ if (credential[i] != insecurePasswordPrefixBytes[i]) {
+ return false;
+ }
+ }
+ return true;
}
public Map<String, Pair<SecretKey, byte[]>> keepOnlyWhitelistedInsecureKeys(
diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
index 1dada92..f4454ae 100644
--- a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
+++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
@@ -146,8 +146,8 @@
if (isDumpstateBinderServiceRunningLocked()) {
Slog.w(TAG, "'dumpstate' is already running. Cannot start a new bugreport"
+ " while another one is currently in progress.");
- // TODO(b/111441001): Use a new error code; add this to the documentation of the API.
- reportError(listener, IDumpstateListener.BUGREPORT_ERROR_RUNTIME_ERROR);
+ reportError(listener,
+ IDumpstateListener.BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS);
return;
}
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index d52e237..a4c04b2 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -22,6 +22,10 @@
import android.apex.ApexInfoList;
import android.apex.ApexSessionInfo;
import android.apex.IApexService;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageInfo;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.PackageParserException;
@@ -49,17 +53,29 @@
class ApexManager {
static final String TAG = "ApexManager";
private final IApexService mApexService;
+ private final Context mContext;
private final Object mLock = new Object();
@GuardedBy("mLock")
private Map<String, PackageInfo> mActivePackagesCache;
- ApexManager() {
+ ApexManager(Context context) {
try {
mApexService = IApexService.Stub.asInterface(
ServiceManager.getServiceOrThrow("apexservice"));
} catch (ServiceNotFoundException e) {
throw new IllegalStateException("Required service apexservice not available");
}
+ mContext = context;
+ }
+
+ void systemReady() {
+ mContext.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ onBootCompleted();
+ mContext.unregisterReceiver(this);
+ }
+ }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
}
private void populateActivePackagesCacheIfNeeded() {
@@ -207,6 +223,7 @@
* @return true if APEX packages can be managed on this device, false otherwise.
*/
boolean isApexSupported() {
+ populateActivePackagesCacheIfNeeded();
// There is no system-wide property available to check if APEX are flattened and hence can't
// be updated. In absence of such property, we assume that if we didn't index APEX packages
// since they were flattened, no APEX management should be possible.
@@ -214,6 +231,21 @@
}
/**
+ * Abandons the (only) active session previously submitted.
+ *
+ * @return {@code true} upon success, {@code false} if any remote exception occurs
+ */
+ boolean abortActiveSession() {
+ try {
+ mApexService.abortActiveSession();
+ return true;
+ } catch (RemoteException re) {
+ Slog.e(TAG, "Unable to contact apexservice", re);
+ return false;
+ }
+ }
+
+ /**
* Dumps various state information to the provided {@link PrintWriter} object.
*
* @param pw the {@link PrintWriter} object to send information to.
@@ -255,6 +287,12 @@
ipw.println("State: ACTIVATED");
} else if (si.isActivationFailed) {
ipw.println("State: ACTIVATION FAILED");
+ } else if (si.isSuccess) {
+ ipw.println("State: SUCCESS");
+ } else if (si.isRollbackInProgress) {
+ ipw.println("State: ROLLBACK IN PROGRESS");
+ } else if (si.isRolledBack) {
+ ipw.println("State: ROLLED BACK");
}
ipw.decreaseIndent();
}
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index b52c021..1c9028d 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -333,7 +333,7 @@
PackageDexOptimizer optimizer = new OTADexoptPackageDexOptimizer(
collectingInstaller, mPackageManagerService.mInstallLock, mContext);
- optimizer.performDexOpt(pkg, pkg.usesLibraryInfos,
+ optimizer.performDexOpt(pkg,
null /* ISAs */,
null /* CompilerStats.PackageStats */,
mPackageManagerService.getDexManager().getPackageUseInfoOrDefault(pkg.packageName),
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 5b38208..580137d 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -125,7 +125,7 @@
* <p>Calls to {@link com.android.server.pm.Installer#dexopt} on {@link #mInstaller} are
* synchronized on {@link #mInstallLock}.
*/
- int performDexOpt(PackageParser.Package pkg, List<SharedLibraryInfo> sharedLibraries,
+ int performDexOpt(PackageParser.Package pkg,
String[] instructionSets, CompilerStats.PackageStats packageStats,
PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
if (pkg.applicationInfo.uid == -1) {
@@ -138,7 +138,7 @@
synchronized (mInstallLock) {
final long acquireTime = acquireWakeLockLI(pkg.applicationInfo.uid);
try {
- return performDexOptLI(pkg, sharedLibraries, instructionSets,
+ return performDexOptLI(pkg, instructionSets,
packageStats, packageUseInfo, options);
} finally {
releaseWakeLockLI(acquireTime);
@@ -152,9 +152,9 @@
*/
@GuardedBy("mInstallLock")
private int performDexOptLI(PackageParser.Package pkg,
- List<SharedLibraryInfo> sharedLibraries,
String[] targetInstructionSets, CompilerStats.PackageStats packageStats,
PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
+ final List<SharedLibraryInfo> sharedLibraries = pkg.usesLibraryInfos;
final String[] instructionSets = targetInstructionSets != null ?
targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo);
final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index cced0f4..b72e836 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -28,10 +28,8 @@
import android.app.PackageDeleteObserver;
import android.app.PackageInstallObserver;
import android.app.admin.DevicePolicyManagerInternal;
-import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.IntentSender.SendIntentException;
import android.content.pm.ApplicationInfo;
@@ -141,7 +139,7 @@
private final Callbacks mCallbacks;
- private volatile boolean mBootCompleted = false;
+ private volatile boolean mOkToSendBroadcasts = false;
/**
* File storing persisted {@link #mSessions} metadata.
@@ -206,29 +204,17 @@
mSessionsDir.mkdirs();
mApexManager = am;
- mStagingManager = new StagingManager(pm, this, am);
+
+ mStagingManager = new StagingManager(pm, this, am, context);
}
- private void onBootCompleted() {
- mBootCompleted = true;
- // Tell APEX manager about it as well
- mApexManager.onBootCompleted();
- }
-
- boolean isBootCompleted() {
- return mBootCompleted;
+ boolean okToSendBroadcasts() {
+ return mOkToSendBroadcasts;
}
public void systemReady() {
mAppOps = mContext.getSystemService(AppOpsManager.class);
- mContext.registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- onBootCompleted();
- mContext.unregisterReceiver(this);
- }
- }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
synchronized (mSessions) {
readSessionsLocked();
@@ -271,6 +257,16 @@
for (PackageInstallerSession session : stagedSessionsToRestore) {
mStagingManager.restoreSession(session);
}
+ // Broadcasts are not sent while we restore sessions on boot, since no processes would be
+ // ready to listen to them. From now on, we greedily assume that broadcasts requests are
+ // safe to send out. The worst that can happen is that a broadcast is attempted before
+ // ActivityManagerService completes its own systemReady(), in which case it will be rejected
+ // with an otherwise harmless exception.
+ // A more appropriate way to do this would be to wait until the correct boot phase is
+ // reached, but since we are not a SystemService we can't override onBootPhase.
+ // Waiting on the BOOT_COMPLETED broadcast can take several minutes, so that's not a viable
+ // way either.
+ mOkToSendBroadcasts = true;
}
@GuardedBy("mSessions")
@@ -1189,7 +1185,7 @@
public void onStagedSessionChanged(PackageInstallerSession session) {
writeSessionsAsync();
- if (mBootCompleted) {
+ if (mOkToSendBroadcasts) {
mPm.sendSessionUpdatedBroadcast(session.generateInfo(false),
session.userId);
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 65d8150d..45b3b5b 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -490,9 +490,9 @@
if (info.childSessionIds == null) {
info.childSessionIds = EMPTY_CHILD_SESSION_ARRAY;
}
- info.isSessionApplied = mStagedSessionApplied;
- info.isSessionReady = mStagedSessionReady;
- info.isSessionFailed = mStagedSessionFailed;
+ info.isStagedSessionApplied = mStagedSessionApplied;
+ info.isStagedSessionReady = mStagedSessionReady;
+ info.isStagedSessionFailed = mStagedSessionFailed;
info.setStagedSessionErrorCode(mStagedSessionErrorCode, mStagedSessionErrorMessage);
}
return info;
@@ -1867,6 +1867,15 @@
synchronized (mLock) {
assertCallerIsOwnerOrRootLocked();
+ if (mCommitted && params.isStaged) {
+ synchronized (mLock) {
+ mDestroyed = true;
+ }
+ mStagingManager.abortCommittedSession(this);
+
+ cleanStageDir();
+ }
+
if (mRelinquished) {
Slog.d(TAG, "Ignoring abandon after commit relinquished control");
return;
@@ -1999,7 +2008,7 @@
// Send broadcast to default launcher only if it's a new install
final boolean isNewInstall = extras == null || !extras.getBoolean(Intent.EXTRA_REPLACING);
- if (success && isNewInstall && mPm.mInstallerService.isBootCompleted()) {
+ if (success && isNewInstall && mPm.mInstallerService.okToSendBroadcasts()) {
mPm.sendSessionCommitBroadcast(generateInfo(), userId);
}
@@ -2087,9 +2096,8 @@
}
}
// For staged sessions, we don't delete the directory where the packages have been copied,
- // since these packages are supposed to be read on reboot. StagingManager is in charge of
- // deleting these dirs when the staged session has reached a final state.
- // TODO(b/118865310): Implement packageDir deletion in StagingManager.
+ // since these packages are supposed to be read on reboot.
+ // Those dirs are deleted when the staged session has reached a final state.
if (stageDir != null && !params.isStaged) {
try {
mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e7e3e83..80e794f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3101,7 +3101,7 @@
}
}
- mApexManager = new ApexManager();
+ mApexManager = new ApexManager(context);
mInstallerService = new PackageInstallerService(context, this, mApexManager);
final Pair<ComponentName, String> instantAppResolverComponent =
getInstantAppResolverLPr();
@@ -9413,12 +9413,12 @@
options.getFlags() | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY);
for (PackageParser.Package depPackage : deps) {
// TODO: Analyze and investigate if we (should) profile libraries.
- pdo.performDexOpt(depPackage, null /* sharedLibraries */, instructionSets,
+ pdo.performDexOpt(depPackage, instructionSets,
getOrCreateCompilerPackageStats(depPackage),
mDexManager.getPackageUseInfoOrDefault(depPackage.packageName), libraryOptions);
}
}
- return pdo.performDexOpt(p, p.usesLibraryInfos, instructionSets,
+ return pdo.performDexOpt(p, instructionSets,
getOrCreateCompilerPackageStats(p),
mDexManager.getPackageUseInfoOrDefault(p.packageName), options);
}
@@ -16324,7 +16324,7 @@
REASON_INSTALL,
DexoptOptions.DEXOPT_BOOT_COMPLETE
| DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE);
- mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryInfos,
+ mPackageDexOptimizer.performDexOpt(pkg,
null /* instructionSets */,
getOrCreateCompilerPackageStats(pkg),
mDexManager.getPackageUseInfoOrDefault(packageName),
@@ -20706,6 +20706,7 @@
storage.registerListener(mStorageListener);
mInstallerService.systemReady();
+ mApexManager.systemReady();
mPackageDexOptimizer.systemReady();
getStorageManagerInternal().addExternalStoragePolicy(
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 5216967..114810d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -300,9 +300,9 @@
pw.println("appPackageName = " + session.getAppPackageName()
+ "; sessionId = " + session.getSessionId()
+ "; isStaged = " + session.isStaged()
- + "; isSessionReady = " + session.isSessionReady()
- + "; isSessionApplied = " + session.isSessionApplied()
- + "; isSessionFailed = " + session.isSessionFailed() + ";");
+ + "; isStagedSessionReady = " + session.isStagedSessionReady()
+ + "; isStagedSessionApplied = " + session.isStagedSessionApplied()
+ + "; isStagedSessionFailed = " + session.isStagedSessionFailed() + ";");
}
} catch (RemoteException e) {
pw.println("Failure ["
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index ff6d7a8..9deea9e 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -2173,7 +2173,8 @@
public boolean hasShareTargets(String packageName, String packageToCheck,
@UserIdInt int userId) {
verifyCaller(packageName, userId);
- enforceSystem();
+ enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APP_PREDICTIONS,
+ "hasShareTargets");
synchronized (mLock) {
throwIfUserLockedL(userId);
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index a38c836..fac3839 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -39,6 +39,7 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
+import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Slog;
@@ -68,15 +69,18 @@
private final PackageInstallerService mPi;
private final PackageManagerService mPm;
private final ApexManager mApexManager;
+ private final PowerManager mPowerManager;
private final Handler mBgHandler;
@GuardedBy("mStagedSessions")
private final SparseArray<PackageInstallerSession> mStagedSessions = new SparseArray<>();
- StagingManager(PackageManagerService pm, PackageInstallerService pi, ApexManager am) {
+ StagingManager(PackageManagerService pm, PackageInstallerService pi, ApexManager am,
+ Context context) {
mPm = pm;
mPi = pi;
mApexManager = am;
+ mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mBgHandler = BackgroundThread.getHandler();
}
@@ -257,7 +261,7 @@
+ "activated");
return;
}
- if (apexSessionInfo.isActivationFailed || apexSessionInfo.isUnknown) {
+ if (isApexSessionFailed(apexSessionInfo)) {
session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
"APEX activation failed. Check logcat messages from apexd for "
+ "more information.");
@@ -286,6 +290,20 @@
session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
"Staged installation of APKs failed. Check logcat messages for"
+ "more information.");
+
+ if (!hasApex) {
+ return;
+ }
+
+ if (!mApexManager.abortActiveSession()) {
+ Slog.e(TAG, "Failed to abort APEXd session");
+ } else {
+ Slog.e(TAG,
+ "Successfully aborted apexd session. Rebooting device in order to revert "
+ + "to the previous state of APEXd.");
+ mPowerManager.reboot(null);
+ }
+
return;
}
@@ -445,11 +463,38 @@
void abortSession(@NonNull PackageInstallerSession session) {
synchronized (mStagedSessions) {
- updateStoredSession(session);
mStagedSessions.remove(session.sessionId);
}
}
+ void abortCommittedSession(@NonNull PackageInstallerSession session) {
+ if (session.isStagedSessionApplied()) {
+ Slog.w(TAG, "Cannot abort applied session!");
+ return;
+ }
+ if (isStagedSessionFinalized(session.sessionId)) {
+ Slog.w(TAG, "Cannot abort session because it is not active or APEXD is not reachable");
+ return;
+ }
+
+ mApexManager.abortActiveSession();
+
+ abortSession(session);
+ }
+
+ private boolean isStagedSessionFinalized(int sessionId) {
+ ApexSessionInfo session = mApexManager.getStagedSessionInfo(sessionId);
+
+ /* checking if the session is in a final state, i.e., not active anymore */
+ return session.isUnknown || session.isActivationFailed || session.isSuccess
+ || session.isRolledBack;
+ }
+
+ private static boolean isApexSessionFailed(ApexSessionInfo apexSessionInfo) {
+ return apexSessionInfo.isActivationFailed || apexSessionInfo.isUnknown
+ || apexSessionInfo.isRolledBack;
+ }
+
@GuardedBy("mStagedSessions")
private boolean isMultiPackageSessionComplete(@NonNull PackageInstallerSession session) {
// This method assumes that the argument is either a parent session of a multi-package
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 93a3d0a..447234e 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -850,7 +850,7 @@
}
for (String packageName : packageNames) {
grantPermissionsToSystemPackage(packageName, userId,
- PHONE_PERMISSIONS, LOCATION_PERMISSIONS, SMS_PERMISSIONS);
+ PHONE_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS, SMS_PERMISSIONS);
}
}
diff --git a/services/core/java/com/android/server/policy/DisplayFoldController.java b/services/core/java/com/android/server/policy/DisplayFoldController.java
index cf026e9..0db3d78 100644
--- a/services/core/java/com/android/server/policy/DisplayFoldController.java
+++ b/services/core/java/com/android/server/policy/DisplayFoldController.java
@@ -35,10 +35,12 @@
/**
* Controls the behavior of foldable devices whose screen can literally bend and fold.
+ * TODO(b/126160895): Move DisplayFoldController from PhoneWindowManager to DisplayPolicy.
*/
class DisplayFoldController {
private static final String TAG = "DisplayFoldController";
+
private final WindowManagerInternal mWindowManagerInternal;
private final DisplayManagerInternal mDisplayManagerInternal;
private final int mDisplayId;
@@ -52,6 +54,8 @@
private final DisplayInfo mNonOverrideDisplayInfo = new DisplayInfo();
private final RemoteCallbackList<IDisplayFoldListener> mListeners = new RemoteCallbackList<>();
private Boolean mFolded;
+ private String mFocusedApp;
+ private final DisplayFoldDurationLogger mDurationLogger = new DisplayFoldDurationLogger();
DisplayFoldController(WindowManagerInternal windowManagerInternal,
DisplayManagerInternal displayManagerInternal, int displayId, Rect foldedArea,
@@ -63,6 +67,14 @@
mHandler = handler;
}
+ void finishedGoingToSleep() {
+ mDurationLogger.onFinishedGoingToSleep();
+ }
+
+ void finishedWakingUp() {
+ mDurationLogger.onFinishedWakingUp(mFolded);
+ }
+
void requestDeviceFolded(boolean folded) {
mHandler.post(() -> setDeviceFolded(folded));
}
@@ -97,6 +109,8 @@
mWindowManagerInternal.clearForcedDisplaySize(mDisplayId);
mDisplayManagerInternal.setDisplayOffsets(mDisplayId, 0, 0);
}
+ mDurationLogger.setDeviceFolded(folded);
+ mDurationLogger.logFocusedAppWithFoldState(folded, mFocusedApp);
mFolded = folded;
final int n = mListeners.beginBroadcast();
@@ -167,6 +181,10 @@
return result;
}
+ void onDefaultDisplayFocusChanged(String pkg) {
+ mFocusedApp = pkg;
+ }
+
static DisplayFoldController create(Context context, int displayId) {
final DisplayManagerInternal displayService =
LocalServices.getService(DisplayManagerInternal.class);
diff --git a/services/core/java/com/android/server/policy/DisplayFoldDurationLogger.java b/services/core/java/com/android/server/policy/DisplayFoldDurationLogger.java
new file mode 100644
index 0000000..bdcd2cd
--- /dev/null
+++ b/services/core/java/com/android/server/policy/DisplayFoldDurationLogger.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2019 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.policy;
+
+import android.annotation.IntDef;
+import android.metrics.LogMaker;
+import android.os.SystemClock;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Logger for tracking duration of usage in folded vs unfolded state.
+ */
+class DisplayFoldDurationLogger {
+ static final int SCREEN_STATE_UNKNOWN = -1;
+ static final int SCREEN_STATE_OFF = 0;
+ static final int SCREEN_STATE_ON_UNFOLDED = 1;
+ static final int SCREEN_STATE_ON_FOLDED = 2;
+
+ @IntDef(flag = true, prefix = {"SCREEN_STATE_"}, value = {
+ SCREEN_STATE_UNKNOWN,
+ SCREEN_STATE_OFF,
+ SCREEN_STATE_ON_UNFOLDED,
+ SCREEN_STATE_ON_FOLDED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ScreenState {}
+
+ private @ScreenState int mScreenState = SCREEN_STATE_UNKNOWN;
+ private Long mLastChanged = null;
+
+ private static final int LOG_SUBTYPE_UNFOLDED = 0;
+ private static final int LOG_SUBTYPE_FOLDED = 1;
+ private static final int LOG_SUBTYPE_DURATION_MASK = 0x80000000;
+
+ private final MetricsLogger mLogger = new MetricsLogger();
+
+ void onFinishedWakingUp(Boolean folded) {
+ if (folded == null) {
+ mScreenState = SCREEN_STATE_UNKNOWN;
+ } else if (folded) {
+ mScreenState = SCREEN_STATE_ON_FOLDED;
+ } else {
+ mScreenState = SCREEN_STATE_ON_UNFOLDED;
+ }
+ mLastChanged = SystemClock.uptimeMillis();
+ }
+
+ void onFinishedGoingToSleep() {
+ log();
+ mScreenState = SCREEN_STATE_OFF;
+ mLastChanged = null;
+ }
+
+ void setDeviceFolded(boolean folded) {
+ // This function is called even when the screen is in ADO mode, but we're only
+ // interested in the case that the screen is actually on.
+ if (!isOn()) {
+ return;
+ }
+ log();
+ mScreenState = folded ? SCREEN_STATE_ON_FOLDED : SCREEN_STATE_ON_UNFOLDED;
+ mLastChanged = SystemClock.uptimeMillis();
+ }
+
+ void logFocusedAppWithFoldState(boolean folded, String packageName) {
+ mLogger.write(
+ new LogMaker(MetricsProto.MetricsEvent.ACTION_DISPLAY_FOLD)
+ .setType(MetricsProto.MetricsEvent.TYPE_ACTION)
+ .setSubtype(folded ? LOG_SUBTYPE_FOLDED : LOG_SUBTYPE_UNFOLDED)
+ .setPackageName(packageName));
+ }
+
+ private void log() {
+ if (mLastChanged == null) {
+ return;
+ }
+ int subtype;
+ switch (mScreenState) {
+ case SCREEN_STATE_ON_UNFOLDED:
+ subtype = LOG_SUBTYPE_UNFOLDED | LOG_SUBTYPE_DURATION_MASK;
+ break;
+ case SCREEN_STATE_ON_FOLDED:
+ subtype = LOG_SUBTYPE_FOLDED | LOG_SUBTYPE_DURATION_MASK;
+ break;
+ default:
+ return;
+ }
+ mLogger.write(
+ new LogMaker(MetricsProto.MetricsEvent.ACTION_DISPLAY_FOLD)
+ .setType(MetricsProto.MetricsEvent.TYPE_ACTION)
+ .setSubtype(subtype)
+ .setLatency(SystemClock.uptimeMillis() - mLastChanged));
+ }
+
+ private boolean isOn() {
+ return mScreenState == SCREEN_STATE_ON_UNFOLDED || mScreenState == SCREEN_STATE_ON_FOLDED;
+ }
+}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 0759419..c0e5974 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3241,6 +3241,14 @@
}
@Override
+ public void onDefaultDisplayFocusChangedLw(WindowState newFocus) {
+ if (mDisplayFoldController != null) {
+ mDisplayFoldController.onDefaultDisplayFocusChanged(
+ newFocus != null ? newFocus.getOwningPackage() : null);
+ }
+ }
+
+ @Override
public void registerShortcutKey(long shortcutCode, IShortcutService shortcutService)
throws RemoteException {
synchronized (mLock) {
@@ -4440,6 +4448,9 @@
mKeyguardDelegate.onFinishedGoingToSleep(why,
mCameraGestureTriggeredDuringGoingToSleep);
}
+ if (mDisplayFoldController != null) {
+ mDisplayFoldController.finishedGoingToSleep();
+ }
mCameraGestureTriggeredDuringGoingToSleep = false;
}
@@ -4480,6 +4491,9 @@
if (mKeyguardDelegate != null) {
mKeyguardDelegate.onFinishedWakingUp();
}
+ if (mDisplayFoldController != null) {
+ mDisplayFoldController.finishedWakingUp();
+ }
}
private void wakeUpFromPowerKey(long eventTime) {
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 5cd0014..d58707c 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -1489,6 +1489,11 @@
}
/**
+ * A new window on default display has been focused.
+ */
+ default void onDefaultDisplayFocusChangedLw(WindowState newFocus) {}
+
+ /**
* Updates the flag about whether AOD is showing.
*
* @return whether the value was changed.
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index ae1090c..c408549 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -209,11 +209,8 @@
mKeyguardState.reset();
mHandler.post(() -> {
try {
- // There are no longer any keyguard windows on secondary displays, so pass
- // {@code null}. All that means is that showWhenLocked activities on
- // external displays now get to show.
ActivityTaskManager.getService().setLockScreenShown(true /* keyguardShowing */,
- false /* aodShowing */, null /* secondaryDisplaysShowing */);
+ false /* aodShowing */);
} catch (RemoteException e) {
// Local call.
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 176dbbf..89d24b1 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -36,6 +36,7 @@
import android.database.ContentObserver;
import android.hardware.SensorManager;
import android.hardware.SystemSensorManager;
+import android.hardware.display.AmbientDisplayConfiguration;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
import android.hardware.power.V1_0.PowerHint;
@@ -81,7 +82,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IBatteryStats;
-import com.android.internal.hardware.AmbientDisplayConfiguration;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
import com.android.server.EventLogTags;
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
index e6fa500..af78995 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
@@ -280,7 +280,7 @@
Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD,
mDynamicPowerSavingsDefaultDisableThreshold);
final boolean isStickyAutoDisableEnabled = getGlobalSetting(
- Settings.Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED, 0) != 0;
+ Settings.Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED, 1) != 0;
final int stickyAutoDisableThreshold = getGlobalSetting(
Settings.Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL, 90);
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 11839a5..88a5fb4 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -23,6 +23,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
@@ -40,6 +41,7 @@
import android.os.HandlerThread;
import android.os.ParcelFileDescriptor;
import android.os.Process;
+import android.os.SystemClock;
import android.provider.DeviceConfig;
import android.util.IntArray;
import android.util.Log;
@@ -47,6 +49,7 @@
import android.util.SparseLongArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
import com.android.server.LocalServices;
import com.android.server.pm.Installer;
@@ -121,6 +124,13 @@
private final RollbackPackageHealthObserver mPackageHealthObserver;
private final AppDataRollbackHelper mAppDataRollbackHelper;
+ // This field stores the difference in Millis between the uptime (millis since device
+ // has booted) and current time (device wall clock) - it's used to update rollback data
+ // timestamps when the time is changed, by the user or by change of timezone.
+ // No need for guarding with lock because value is only accessed in handler thread.
+ private long mRelativeBootTime = calculateRelativeBootTime();
+
+
RollbackManagerServiceImpl(Context context) {
mContext = context;
// Note that we're calling onStart here because this object is only constructed on
@@ -217,6 +227,8 @@
}
}
}, enableRollbackFilter, null, getHandler());
+
+ registerTimeChangeReceiver();
}
@Override
@@ -268,6 +280,45 @@
callerPackageName, statusReceiver));
}
+ private void registerTimeChangeReceiver() {
+ final BroadcastReceiver timeChangeIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final long oldRelativeBootTime = mRelativeBootTime;
+ mRelativeBootTime = calculateRelativeBootTime();
+ final long timeDifference = mRelativeBootTime - oldRelativeBootTime;
+
+ synchronized (mLock) {
+ ensureRollbackDataLoadedLocked();
+
+ Iterator<RollbackData> iter = mAvailableRollbacks.iterator();
+ while (iter.hasNext()) {
+ RollbackData data = iter.next();
+
+ data.timestamp = data.timestamp.plusMillis(timeDifference);
+ try {
+ mRollbackStore.saveAvailableRollback(data);
+ } catch (IOException ioe) {
+ // TODO: figure out the right way to deal with this, especially if
+ // it fails for some data and succeeds for others.
+ Log.e(TAG, "Unable to save rollback info for : " + data.rollbackId,
+ ioe);
+ }
+ }
+
+ }
+ }
+ };
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_TIME_CHANGED);
+ mContext.registerReceiver(timeChangeIntentReceiver, filter,
+ null /* broadcastPermission */, getHandler());
+ }
+
+ private static long calculateRelativeBootTime() {
+ return System.currentTimeMillis() - SystemClock.elapsedRealtime();
+ }
+
/**
* Performs the actual work to commit a rollback.
* The work is done on the current thread. This may be a long running
@@ -362,20 +413,24 @@
int sessionId = packageInstaller.createSession(params);
PackageInstaller.Session session = packageInstaller.openSession(sessionId);
- File packageCode = RollbackStore.getPackageCode(data, info.getPackageName());
- if (packageCode == null) {
+ File[] packageCodePaths = RollbackStore.getPackageCodePaths(
+ data, info.getPackageName());
+ if (packageCodePaths == null) {
sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE,
- "Backup copy of package code inaccessible");
+ "Backup copy of package inaccessible");
return;
}
- try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(packageCode,
- ParcelFileDescriptor.MODE_READ_ONLY)) {
- final long token = Binder.clearCallingIdentity();
- try {
- session.write(packageCode.getName(), 0, packageCode.length(), fd);
- } finally {
- Binder.restoreCallingIdentity(token);
+ for (File packageCodePath : packageCodePaths) {
+ try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(packageCodePath,
+ ParcelFileDescriptor.MODE_READ_ONLY)) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ session.write(packageCodePath.getName(), 0, packageCodePath.length(),
+ fd);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
}
parentSession.addChildSessionId(sessionId);
@@ -434,7 +489,10 @@
mAvailableRollbacks = null;
mRecentlyExecutedRollbacks = null;
}
- getHandler().post(() -> ensureRollbackDataLoaded());
+ getHandler().post(() -> {
+ updateRollbackLifetimeDurationInMillis();
+ ensureRollbackDataLoaded();
+ });
}
@Override
@@ -528,7 +586,7 @@
PackageInstaller.SessionInfo session = installer.getSessionInfo(
data.stagedSessionId);
if (session != null) {
- if (session.isSessionApplied()) {
+ if (session.isStagedSessionApplied()) {
synchronized (mLock) {
data.isAvailable = true;
}
@@ -538,7 +596,7 @@
Log.e(TAG, "Unable to save rollback info for : "
+ data.rollbackId, ioe);
}
- } else if (session.isSessionFailed()) {
+ } else if (session.isStagedSessionFailed()) {
// TODO: Do we need to remove this from
// mAvailableRollbacks, or is it okay to leave as
// unavailable until the next reboot when it will go
@@ -950,7 +1008,13 @@
}
try {
- RollbackStore.backupPackageCode(data, packageName, pkgInfo.applicationInfo.sourceDir);
+ ApplicationInfo appInfo = pkgInfo.applicationInfo;
+ RollbackStore.backupPackageCodePath(data, packageName, appInfo.sourceDir);
+ if (!ArrayUtils.isEmpty(appInfo.splitSourceDirs)) {
+ for (String sourceDir : appInfo.splitSourceDirs) {
+ RollbackStore.backupPackageCodePath(data, packageName, sourceDir);
+ }
+ }
} catch (IOException e) {
Log.e(TAG, "Unable to copy package for rollback for " + packageName, e);
return false;
@@ -1195,13 +1259,13 @@
}
if (pi.isStaged()) {
- if (!pi.isSessionFailed()) {
+ if (!pi.isStagedSessionFailed()) {
// TODO: The session really isn't "enabled" at this point, since more work might
// be required post reboot.
// TODO: We need to make this case consistent with the call from onFinished.
// Ideally, we'd call completeEnableRollback excatly once per multi-package session
// with the parentSessionId only.
- completeEnableRollback(pi.sessionId, pi.isSessionReady());
+ completeEnableRollback(pi.sessionId, pi.isStagedSessionReady());
} else {
// TODO: Clean up the saved rollback when the session fails. This may need to be
// unified with the case where things fail post reboot.
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index 3a2b69f..d24f217 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -218,10 +218,10 @@
&& (sessionId != PackageInstaller.SessionInfo.INVALID_ID)) {
PackageInstaller.SessionInfo sessionInfo =
packageInstaller.getSessionInfo(sessionId);
- if (sessionInfo.isSessionReady()) {
+ if (sessionInfo.isStagedSessionReady()) {
mContext.unregisterReceiver(listener);
mContext.getSystemService(PowerManager.class).reboot("Rollback staged install");
- } else if (sessionInfo.isSessionFailed()) {
+ } else if (sessionInfo.isStagedSessionFailed()) {
mContext.unregisterReceiver(listener);
}
}
diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java
index be904ea..bb4e89e 100644
--- a/services/core/java/com/android/server/rollback/RollbackStore.java
+++ b/services/core/java/com/android/server/rollback/RollbackStore.java
@@ -234,9 +234,11 @@
}
/**
- * Creates a backup copy of the apk or apex for a package.
+ * Creates a backup copy of an apk or apex for a package.
+ * For packages containing splits, this method should be called for each
+ * of the package's split apks in addition to the base apk.
*/
- static void backupPackageCode(RollbackData data, String packageName, String codePath)
+ static void backupPackageCodePath(RollbackData data, String packageName, String codePath)
throws IOException {
File sourceFile = new File(codePath);
File targetDir = new File(data.backupDir, packageName);
@@ -248,16 +250,16 @@
}
/**
- * Returns the apk or apex file backed up for the given package.
- * Returns null if none found.
+ * Returns the apk or apex files backed up for the given package.
+ * Includes the base apk and any splits. Returns null if none found.
*/
- static File getPackageCode(RollbackData data, String packageName) {
+ static File[] getPackageCodePaths(RollbackData data, String packageName) {
File targetDir = new File(data.backupDir, packageName);
File[] files = targetDir.listFiles();
- if (files == null || files.length != 1) {
+ if (files == null || files.length == 0) {
return null;
}
- return files[0];
+ return files;
}
/**
diff --git a/services/core/java/com/android/server/timezone/RulesManagerService.java b/services/core/java/com/android/server/timezone/RulesManagerService.java
index 4b413e5..296a652 100644
--- a/services/core/java/com/android/server/timezone/RulesManagerService.java
+++ b/services/core/java/com/android/server/timezone/RulesManagerService.java
@@ -16,14 +16,13 @@
package com.android.server.timezone;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.EventLogTags;
-import com.android.server.SystemService;
-import com.android.timezone.distro.DistroException;
-import com.android.timezone.distro.DistroVersion;
-import com.android.timezone.distro.StagedDistroOperation;
-import com.android.timezone.distro.TimeZoneDistro;
-import com.android.timezone.distro.installer.TimeZoneDistroInstaller;
+import static android.app.timezone.RulesState.DISTRO_STATUS_INSTALLED;
+import static android.app.timezone.RulesState.DISTRO_STATUS_NONE;
+import static android.app.timezone.RulesState.DISTRO_STATUS_UNKNOWN;
+import static android.app.timezone.RulesState.STAGED_OPERATION_INSTALL;
+import static android.app.timezone.RulesState.STAGED_OPERATION_NONE;
+import static android.app.timezone.RulesState.STAGED_OPERATION_UNINSTALL;
+import static android.app.timezone.RulesState.STAGED_OPERATION_UNKNOWN;
import android.app.timezone.Callback;
import android.app.timezone.DistroFormatVersion;
@@ -37,6 +36,21 @@
import android.os.RemoteException;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.EventLogTags;
+import com.android.server.SystemService;
+import com.android.timezone.distro.DistroException;
+import com.android.timezone.distro.DistroVersion;
+import com.android.timezone.distro.StagedDistroOperation;
+import com.android.timezone.distro.TimeZoneDistro;
+import com.android.timezone.distro.installer.TimeZoneDistroInstaller;
+
+import libcore.icu.ICU;
+import libcore.timezone.TimeZoneDataFiles;
+import libcore.timezone.TimeZoneFinder;
+import libcore.timezone.TzDataSetVersion;
+import libcore.timezone.ZoneInfoDB;
+
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
@@ -46,18 +60,6 @@
import java.util.Arrays;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
-import libcore.icu.ICU;
-import libcore.timezone.TzDataSetVersion;
-import libcore.timezone.TimeZoneFinder;
-import libcore.timezone.ZoneInfoDB;
-
-import static android.app.timezone.RulesState.DISTRO_STATUS_INSTALLED;
-import static android.app.timezone.RulesState.DISTRO_STATUS_NONE;
-import static android.app.timezone.RulesState.DISTRO_STATUS_UNKNOWN;
-import static android.app.timezone.RulesState.STAGED_OPERATION_INSTALL;
-import static android.app.timezone.RulesState.STAGED_OPERATION_NONE;
-import static android.app.timezone.RulesState.STAGED_OPERATION_UNINSTALL;
-import static android.app.timezone.RulesState.STAGED_OPERATION_UNKNOWN;
public final class RulesManagerService extends IRulesManager.Stub {
@@ -96,8 +98,6 @@
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
static final String REQUIRED_QUERY_PERMISSION =
android.Manifest.permission.QUERY_TIME_ZONE_RULES;
- private static final File SYSTEM_TZ_DATA_FILE = new File("/system/usr/share/zoneinfo/tzdata");
- private static final File TZ_DATA_DIR = new File("/data/misc/zoneinfo");
private final AtomicBoolean mOperationInProgress = new AtomicBoolean(false);
private final PermissionHelper mPermissionHelper;
@@ -108,12 +108,14 @@
private static RulesManagerService create(Context context) {
RulesManagerServiceHelperImpl helper = new RulesManagerServiceHelperImpl(context);
+ File baseVersionFile = new File(TimeZoneDataFiles.getRuntimeModuleTzVersionFile());
+ File tzDataDir = new File(TimeZoneDataFiles.getDataTimeZoneRootDir());
return new RulesManagerService(
helper /* permissionHelper */,
helper /* executor */,
helper /* intentHelper */,
PackageTracker.create(context),
- new TimeZoneDistroInstaller(TAG, SYSTEM_TZ_DATA_FILE, TZ_DATA_DIR));
+ new TimeZoneDistroInstaller(TAG, baseVersionFile, tzDataDir));
}
// A constructor that can be used by tests to supply mocked / faked dependencies.
@@ -143,11 +145,11 @@
/** Like {@link #getRulesState()} without the permission check. */
private RulesState getRulesStateInternal() {
synchronized(this) {
- String systemRulesVersion;
+ TzDataSetVersion baseVersion;
try {
- systemRulesVersion = mInstaller.getSystemRulesVersion();
+ baseVersion = mInstaller.readBaseVersion();
} catch (IOException e) {
- Slog.w(TAG, "Failed to read system rules", e);
+ Slog.w(TAG, "Failed to read base rules version", e);
return null;
}
@@ -196,7 +198,7 @@
Slog.w(TAG, "Failed to read staged distro.", e);
}
}
- return new RulesState(systemRulesVersion, DISTRO_FORMAT_VERSION_SUPPORTED,
+ return new RulesState(baseVersion.rulesVersion, DISTRO_FORMAT_VERSION_SUPPORTED,
operationInProgress, stagedOperationStatus, stagedDistroRulesVersion,
distroStatus, installedDistroRulesVersion);
}
@@ -454,13 +456,13 @@
pw.println("Operation in progress: " + value);
break;
}
- case 's': {
- // Report system image rules version
+ case 'b': {
+ // Report base rules version
String value = "Unknown";
if (rulesState != null) {
- value = rulesState.getSystemRulesVersion();
+ value = rulesState.getBaseRulesVersion();
}
- pw.println("System rules version: " + value);
+ pw.println("Base rules version: " + value);
break;
}
case 'c': {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 087de69..e976975 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2751,7 +2751,9 @@
final Configuration srcConfig = task.getConfiguration();
overrideConfig.colorMode = srcConfig.colorMode;
overrideConfig.densityDpi = srcConfig.densityDpi;
- overrideConfig.screenLayout = srcConfig.screenLayout;
+ overrideConfig.screenLayout = srcConfig.screenLayout
+ & (Configuration.SCREENLAYOUT_LONG_MASK
+ | Configuration.SCREENLAYOUT_SIZE_MASK);
// The smallest screen width is the short side of screen bounds. Because the bounds
// and density won't be changed, smallestScreenWidthDp is also fixed.
overrideConfig.smallestScreenWidthDp = srcConfig.smallestScreenWidthDp;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index cb4664f..486a4ea 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -3098,8 +3098,7 @@
}
@Override
- public void setLockScreenShown(boolean keyguardShowing, boolean aodShowing,
- int[] secondaryDisplaysShowing) {
+ public void setLockScreenShown(boolean keyguardShowing, boolean aodShowing) {
if (checkCallingPermission(android.Manifest.permission.DEVICE_POWER)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires permission "
@@ -3116,8 +3115,7 @@
mH.sendMessage(msg);
}
try {
- mKeyguardController.setKeyguardShown(keyguardShowing, aodShowing,
- secondaryDisplaysShowing);
+ mKeyguardController.setKeyguardShown(keyguardShowing, aodShowing);
} finally {
Binder.restoreCallingIdentity(ident);
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 91d573d..b028569 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -2787,6 +2787,9 @@
public int focusChangedLw(WindowState lastFocus, WindowState newFocus) {
mFocusedWindow = newFocus;
mLastFocusedWindow = lastFocus;
+ if (mDisplayContent.isDefaultDisplay) {
+ mService.mPolicy.onDefaultDisplayFocusChangedLw(newFocus);
+ }
if ((updateSystemUiVisibilityLw() & SYSTEM_UI_CHANGING_LAYOUT) != 0) {
// If the navigation bar has been hidden or shown, we need to do another
// layout pass to update that window.
@@ -3271,7 +3274,12 @@
}
final WindowState w = mTopFullscreenOpaqueWindowState;
- if (w != mFocusedWindow) {
+ if (w == null || w != mFocusedWindow) {
+ return false;
+ }
+ // If the bounds of activity window is different from its parent, then reject to be seamless
+ // because the window position may change after rotation that will look like a sudden jump.
+ if (w.mAppToken != null && !w.mAppToken.matchParentBounds()) {
return false;
}
@@ -3279,8 +3287,7 @@
// it and is in the fullscreen opaque state. Seamless rotation
// requires freezing various Surface states and won't work well
// with animations, so we disable it in the animation case for now.
- if (w != null && !w.isAnimatingLw()
- && w.getAttrs().rotationAnimation == ROTATION_ANIMATION_SEAMLESS) {
+ if (!w.isAnimatingLw() && w.getAttrs().rotationAnimation == ROTATION_ANIMATION_SEAMLESS) {
return true;
}
return false;
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index b5be2ac5..feb711a 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -49,7 +49,6 @@
import com.android.server.wm.ActivityTaskManagerInternal.SleepToken;
import java.io.PrintWriter;
-import java.util.Arrays;
/**
* Controls Keyguard occluding, dismissing and transitions depending on what kind of activities are
@@ -120,18 +119,15 @@
/**
* Update the Keyguard showing state.
*/
- void setKeyguardShown(boolean keyguardShowing, boolean aodShowing,
- int[] secondaryDisplaysShowing) {
+ void setKeyguardShown(boolean keyguardShowing, boolean aodShowing) {
boolean showingChanged = keyguardShowing != mKeyguardShowing || aodShowing != mAodShowing;
// If keyguard is going away, but SystemUI aborted the transition, need to reset state.
showingChanged |= mKeyguardGoingAway && keyguardShowing;
- if (!showingChanged && Arrays.equals(secondaryDisplaysShowing,
- mSecondaryDisplayIdsShowing)) {
+ if (!showingChanged) {
return;
}
mKeyguardShowing = keyguardShowing;
mAodShowing = aodShowing;
- mSecondaryDisplayIdsShowing = secondaryDisplaysShowing;
mWindowManager.setAodShowing(aodShowing);
if (showingChanged) {
dismissDockedStackIfNeeded();
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index c6c85fd..0480d43 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -1159,7 +1159,8 @@
/**
* @return whether the given active task should be presented to the user through SystemUI.
*/
- private boolean isVisibleRecentTask(TaskRecord task) {
+ @VisibleForTesting
+ boolean isVisibleRecentTask(TaskRecord task) {
if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "isVisibleRecentTask: task=" + task
+ " minVis=" + mMinNumVisibleTasks + " maxVis=" + mMaxNumVisibleTasks
+ " sessionDuration=" + mActiveTasksSessionDurationMs
@@ -1195,6 +1196,17 @@
}
}
+ // Tasks managed by/associated with an ActivityView should be excluded from recents.
+ // singleTaskInstance is set on the VirtualDisplay managed by ActivityView
+ // TODO(b/126185105): Find a different signal to use besides isSingleTaskInstance
+ final ActivityStack stack = task.getStack();
+ if (stack != null) {
+ ActivityDisplay display = stack.getDisplay();
+ if (display != null && display.isSingleTaskInstance()) {
+ return false;
+ }
+ }
+
// If we're in lock task mode, ignore the root task
if (task == mService.getLockTaskController().getRootTask()) {
return false;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index ab1b6ae..706901b 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -4373,7 +4373,11 @@
Slog.d(TAG, "relayoutVisibleWindow: " + this + " mAnimatingExit=true, mRemoveOnExit="
+ mRemoveOnExit + ", mDestroying=" + mDestroying);
- mWinAnimator.cancelExitAnimationForNextAnimationLocked();
+ // Cancel the existing exit animation for the next enter animation.
+ if (isSelfAnimating()) {
+ cancelAnimation();
+ destroySurfaceUnchecked();
+ }
mAnimatingExit = false;
}
if (mDestroying) {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 2e2da6d..969ced4 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -248,14 +248,6 @@
mWallpaperControllerLocked = win.getDisplayContent().mWallpaperController;
}
- void cancelExitAnimationForNextAnimationLocked() {
- if (DEBUG_ANIM) Slog.d(TAG,
- "cancelExitAnimationForNextAnimationLocked: " + mWin);
-
- mWin.cancelAnimation();
- mWin.destroySurfaceUnchecked();
- }
-
void onAnimationFinished() {
// Done animating, clean up.
if (DEBUG_ANIM) Slog.v(
diff --git a/services/core/java/com/android/server/wm/WindowTraceBuffer.java b/services/core/java/com/android/server/wm/WindowTraceBuffer.java
index 2ce6e6c..a4ee907 100644
--- a/services/core/java/com/android/server/wm/WindowTraceBuffer.java
+++ b/services/core/java/com/android/server/wm/WindowTraceBuffer.java
@@ -98,9 +98,8 @@
ProtoOutputStream proto = new ProtoOutputStream();
proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
os.write(proto.getBytes());
- while (!mBuffer.isEmpty()) {
- proto = mBuffer.poll();
- mBufferUsedSize -= proto.getRawSize();
+ for (ProtoOutputStream protoOutputStream : mBuffer) {
+ proto = protoOutputStream;
byte[] protoBytes = proto.getBytes();
os.write(protoBytes);
}
diff --git a/services/core/java/com/android/server/wm/WindowTracing.java b/services/core/java/com/android/server/wm/WindowTracing.java
index 3b17abc..48b9340 100644
--- a/services/core/java/com/android/server/wm/WindowTracing.java
+++ b/services/core/java/com/android/server/wm/WindowTracing.java
@@ -208,6 +208,7 @@
pw.println(" frame: Log trace once per frame");
pw.println(" transaction: Log each transaction");
pw.println(" size: Set the maximum log size (in KB)");
+ pw.println(" status: Print trace status");
pw.println(" level [lvl]: Set the log level between");
pw.println(" lvl may be one of:");
pw.println(" critical: Only visible windows with reduced information");
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index fe94168..03240c0 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -36,13 +36,14 @@
#include "android_runtime/Log.h"
#include <arpa/inet.h>
+#include <cinttypes>
+#include <iomanip>
#include <limits>
#include <linux/in.h>
#include <linux/in6.h>
#include <pthread.h>
#include <string.h>
-#include <cinttypes>
-#include <iomanip>
+#include <utils/SystemClock.h>
static jobject mCallbacksObj = nullptr;
@@ -111,10 +112,9 @@
using android::hardware::hidl_vec;
using android::hardware::hidl_string;
using android::hardware::hidl_death_recipient;
-using android::hardware::gnss::V1_0::GnssConstellationType;
-using android::hardware::gnss::V1_0::GnssLocation;
-using android::hardware::gnss::V1_0::GnssLocationFlags;
+using android::hardware::gnss::V1_0::GnssConstellationType;
+using android::hardware::gnss::V1_0::GnssLocationFlags;
using android::hardware::gnss::V1_0::IAGnssRilCallback;
using android::hardware::gnss::V1_0::IGnssBatching;
using android::hardware::gnss::V1_0::IGnssBatchingCallback;
@@ -128,13 +128,17 @@
using android::hardware::gnss::V1_0::IGnssXtra;
using android::hardware::gnss::V1_0::IGnssXtraCallback;
+using android::hardware::gnss::V2_0::ElapsedRealtimeFlags;
using android::hardware::gnss::V2_0::IGnssCallback;
+
using android::hardware::gnss::measurement_corrections::V1_0::MeasurementCorrections;
using android::hardware::gnss::measurement_corrections::V1_0::SingleSatCorrection;
using android::hardware::gnss::measurement_corrections::V1_0::ReflectingPlane;
using android::hidl::base::V1_0::IBase;
+using GnssLocation_V1_0 = android::hardware::gnss::V1_0::GnssLocation;
+using GnssLocation_V2_0 = android::hardware::gnss::V2_0::GnssLocation;
using IGnss_V1_0 = android::hardware::gnss::V1_0::IGnss;
using IGnss_V1_1 = android::hardware::gnss::V1_1::IGnss;
using IGnss_V2_0 = android::hardware::gnss::V2_0::IGnss;
@@ -204,6 +208,20 @@
namespace android {
+namespace {
+
+// Returns true if location has lat/long information.
+bool hasLatLong(const GnssLocation_V1_0& location) {
+ return (static_cast<uint32_t>(location.gnssLocationFlags) &
+ GnssLocationFlags::HAS_LAT_LONG) != 0;
+}
+
+// Returns true if location has lat/long information.
+bool hasLatLong(const GnssLocation_V2_0& location) {
+ return hasLatLong(location.v1_0);
+}
+
+} // namespace
template<class T>
class JavaMethodHelper {
public:
@@ -216,7 +234,7 @@
T value);
private:
- static const char *const signature_;
+ static const char* const signature_;
};
template<class T>
@@ -234,6 +252,8 @@
public:
JavaObject(JNIEnv* env, const char* class_name);
JavaObject(JNIEnv* env, const char* class_name, const char * sz_arg_1);
+ JavaObject(JNIEnv* env, const char* class_name, jobject object);
+
virtual ~JavaObject();
template<class T>
@@ -260,6 +280,11 @@
object_ = env_->NewObject(clazz_, ctor, env->NewStringUTF(sz_arg_1));
}
+JavaObject::JavaObject(JNIEnv* env, const char* class_name, jobject object)
+ : env_(env), object_(object) {
+ clazz_ = env_->FindClass(class_name);
+}
+
JavaObject::~JavaObject() {
env_->DeleteLocalRef(clazz_);
}
@@ -303,6 +328,8 @@
template<>
const char *const JavaMethodHelper<int64_t>::signature_ = "(J)V";
template<>
+const char *const JavaMethodHelper<uint64_t>::signature_ = "(J)V";
+template<>
const char *const JavaMethodHelper<float>::signature_ = "(F)V";
template<>
const char *const JavaMethodHelper<double>::signature_ = "(D)V";
@@ -416,7 +443,8 @@
return env;
}
-static jobject translateLocation(JNIEnv* env, const GnssLocation& location) {
+static jobject translateGnssLocation(JNIEnv* env,
+ const GnssLocation_V1_0& location) {
JavaObject object(env, "android/location/Location", "gps");
uint16_t flags = static_cast<uint32_t>(location.gnssLocationFlags);
@@ -446,23 +474,33 @@
SET(BearingAccuracyDegrees, location.bearingAccuracyDegrees);
}
SET(Time, location.timestamp);
+ SET(ElapsedRealtimeNanos, android::elapsedRealtimeNano());
return object.get();
}
-static GnssLocation createGnssLocation(
- jint gnssLocationFlags,
- jdouble latitudeDegrees,
- jdouble longitudeDegrees,
- jdouble altitudeMeters,
- jfloat speedMetersPerSec,
- jfloat bearingDegrees,
- jfloat horizontalAccuracyMeters,
- jfloat verticalAccuracyMeters,
- jfloat speedAccuracyMetersPerSecond,
- jfloat bearingAccuracyDegrees,
+static jobject translateGnssLocation(JNIEnv* env,
+ const GnssLocation_V2_0& location) {
+ JavaObject object(env, "android/location/Location",
+ translateGnssLocation(env, location.v1_0));
+
+ const uint16_t flags = static_cast<uint16_t>(location.elapsedRealtime.flags);
+
+ // Overwrite ElapsedRealtimeNanos when available from HAL.
+ if (flags & ElapsedRealtimeFlags::HAS_TIMESTAMP_NS) {
+ SET(ElapsedRealtimeNanos, location.elapsedRealtime.timestampNs);
+ }
+
+ return object.get();
+}
+
+static GnssLocation_V1_0 createGnssLocation_V1_0(
+ jint gnssLocationFlags, jdouble latitudeDegrees, jdouble longitudeDegrees,
+ jdouble altitudeMeters, jfloat speedMetersPerSec, jfloat bearingDegrees,
+ jfloat horizontalAccuracyMeters, jfloat verticalAccuracyMeters,
+ jfloat speedAccuracyMetersPerSecond, jfloat bearingAccuracyDegrees,
jlong timestamp) {
- GnssLocation location;
+ GnssLocation_V1_0 location;
location.gnssLocationFlags = static_cast<uint16_t>(gnssLocationFlags);
location.latitudeDegrees = static_cast<double>(latitudeDegrees);
location.longitudeDegrees = static_cast<double>(longitudeDegrees);
@@ -478,11 +516,30 @@
return location;
}
+static GnssLocation_V2_0 createGnssLocation_V2_0(
+ jint gnssLocationFlags, jdouble latitudeDegrees, jdouble longitudeDegrees,
+ jdouble altitudeMeters, jfloat speedMetersPerSec, jfloat bearingDegrees,
+ jfloat horizontalAccuracyMeters, jfloat verticalAccuracyMeters,
+ jfloat speedAccuracyMetersPerSecond, jfloat bearingAccuracyDegrees,
+ jlong timestamp, jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos) {
+ GnssLocation_V2_0 location;
+ location.v1_0 = createGnssLocation_V1_0(
+ gnssLocationFlags, latitudeDegrees, longitudeDegrees, altitudeMeters,
+ speedMetersPerSec, bearingDegrees, horizontalAccuracyMeters,
+ verticalAccuracyMeters, speedAccuracyMetersPerSecond,
+ bearingAccuracyDegrees, timestamp);
+
+ location.elapsedRealtime.flags = static_cast<uint16_t>(elapsedRealtimeFlags);
+ location.elapsedRealtime.timestampNs = static_cast<uint64_t>(elapsedRealtimeNanos);
+
+ return location;
+}
+
/*
* GnssCallback class implements the callback methods for IGnss interface.
*/
struct GnssCallback : public IGnssCallback {
- Return<void> gnssLocationCb(const GnssLocation& location) override;
+ Return<void> gnssLocationCb(const GnssLocation_V1_0& location) override;
Return<void> gnssStatusCb(const IGnssCallback::GnssStatusValue status) override;
Return<void> gnssSvStatusCb(const IGnssCallback::GnssSvStatus& svStatus) override;
Return<void> gnssNmeaCb(int64_t timestamp, const android::hardware::hidl_string& nmea) override;
@@ -496,7 +553,13 @@
// New in 1.1
Return<void> gnssNameCb(const android::hardware::hidl_string& name) override;
+ // New in 2.0
Return<void> gnssSetCapabilitiesCb_2_0(uint32_t capabilities) override;
+ Return<void> gnssLocationCb_2_0(const GnssLocation_V2_0& location) override;
+
+ // Templated implementation for gnnsLocationCb and gnnsLocationCb_2_0.
+ template <class T>
+ Return<void> gnssLocationCbImpl(const T& location);
// TODO(b/73306084): Reconsider allocation cost vs threadsafety on these statics
static const char* sNmeaString;
@@ -517,22 +580,30 @@
const char* GnssCallback::sNmeaString = nullptr;
size_t GnssCallback::sNmeaStringLength = 0;
-Return<void> GnssCallback::gnssLocationCb(const GnssLocation& location) {
+template<class T>
+Return<void> GnssCallback::gnssLocationCbImpl(const T& location) {
JNIEnv* env = getJniEnv();
- jobject jLocation = translateLocation(env, location);
- bool hasLatLong = (static_cast<uint32_t>(location.gnssLocationFlags) &
- GnssLocationFlags::HAS_LAT_LONG) != 0;
+ jobject jLocation = translateGnssLocation(env, location);
env->CallVoidMethod(mCallbacksObj,
method_reportLocation,
- boolToJbool(hasLatLong),
+ boolToJbool(hasLatLong(location)),
jLocation);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
env->DeleteLocalRef(jLocation);
return Void();
}
+Return<void> GnssCallback::gnssLocationCb(const GnssLocation_V1_0& location) {
+ return gnssLocationCbImpl<GnssLocation_V1_0>(location);
+}
+
+Return<void>
+GnssCallback::gnssLocationCb_2_0(const GnssLocation_V2_0& location) {
+ return gnssLocationCbImpl<GnssLocation_V2_0>(location);
+}
+
Return<void> GnssCallback::gnssStatusCb(const IGnssCallback::GnssStatusValue status) {
JNIEnv* env = getJniEnv();
env->CallVoidMethod(mCallbacksObj, method_reportStatus, status);
@@ -681,12 +752,13 @@
// Methods from ::android::hardware::gps::V1_0::IGnssGeofenceCallback follow.
Return<void> gnssGeofenceTransitionCb(
int32_t geofenceId,
- const GnssLocation& location,
+ const GnssLocation_V1_0& location,
GeofenceTransition transition,
hardware::gnss::V1_0::GnssUtcTime timestamp) override;
- Return<void> gnssGeofenceStatusCb(
+ Return<void>
+ gnssGeofenceStatusCb(
GeofenceAvailability status,
- const GnssLocation& location) override;
+ const GnssLocation_V1_0& location) override;
Return<void> gnssGeofenceAddCb(int32_t geofenceId,
GeofenceStatus status) override;
Return<void> gnssGeofenceRemoveCb(int32_t geofenceId,
@@ -698,13 +770,12 @@
};
Return<void> GnssGeofenceCallback::gnssGeofenceTransitionCb(
- int32_t geofenceId,
- const GnssLocation& location,
+ int32_t geofenceId, const GnssLocation_V1_0& location,
GeofenceTransition transition,
hardware::gnss::V1_0::GnssUtcTime timestamp) {
JNIEnv* env = getJniEnv();
- jobject jLocation = translateLocation(env, location);
+ jobject jLocation = translateGnssLocation(env, location);
env->CallVoidMethod(mCallbacksObj,
method_reportGeofenceTransition,
@@ -718,16 +789,14 @@
return Void();
}
-Return<void> GnssGeofenceCallback::gnssGeofenceStatusCb(
- GeofenceAvailability status,
- const GnssLocation& location) {
+Return<void>
+GnssGeofenceCallback::gnssGeofenceStatusCb(GeofenceAvailability status,
+ const GnssLocation_V1_0& location) {
JNIEnv* env = getJniEnv();
- jobject jLocation = translateLocation(env, location);
+ jobject jLocation = translateGnssLocation(env, location);
- env->CallVoidMethod(mCallbacksObj,
- method_reportGeofenceStatus,
- status,
+ env->CallVoidMethod(mCallbacksObj, method_reportGeofenceStatus, status,
jLocation);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
env->DeleteLocalRef(jLocation);
@@ -1296,18 +1365,18 @@
* Methods from ::android::hardware::gps::V1_0::IGnssBatchingCallback
* follow.
*/
- Return<void> gnssLocationBatchCb(const hidl_vec<GnssLocation> & locations)
- override;
+ Return<void> gnssLocationBatchCb(const hidl_vec<GnssLocation_V1_0>& locations) override;
};
-Return<void> GnssBatchingCallback::gnssLocationBatchCb(const hidl_vec<GnssLocation> & locations) {
+Return<void> GnssBatchingCallback::gnssLocationBatchCb(
+ const hidl_vec<GnssLocation_V1_0>& locations) {
JNIEnv* env = getJniEnv();
jobjectArray jLocations = env->NewObjectArray(locations.size(),
env->FindClass("android/location/Location"), nullptr);
for (uint16_t i = 0; i < locations.size(); ++i) {
- jobject jLocation = translateLocation(env, locations[i]);
+ jobject jLocation = translateGnssLocation(env, locations[i]);
env->SetObjectArrayElement(jLocations, i, jLocation);
env->DeleteLocalRef(jLocation);
}
@@ -1760,7 +1829,7 @@
agnssRilIface->setRefLocation(location);
}
-static void android_location_GnssLocationProvider_agps_set_id(JNIEnv *env, jobject /* obj */,
+static void android_location_GnssLocationProvider_agps_set_id(JNIEnv* env, jobject /* obj */,
jint type, jstring setid_string) {
if (agnssRilIface == nullptr) {
ALOGE("no AGPS RIL interface in agps_set_id");
@@ -1806,9 +1875,34 @@
jfloat verticalAccuracyMeters,
jfloat speedAccuracyMetersPerSecond,
jfloat bearingAccuracyDegrees,
- jlong timestamp) {
+ jlong timestamp,
+ jint elapsedRealtimeFlags,
+ jlong elapsedRealtimeNanos) {
+ if (gnssHal_V2_0 != nullptr) {
+ GnssLocation_V2_0 location = createGnssLocation_V2_0(
+ gnssLocationFlags,
+ latitudeDegrees,
+ longitudeDegrees,
+ altitudeMeters,
+ speedMetersPerSec,
+ bearingDegrees,
+ horizontalAccuracyMeters,
+ verticalAccuracyMeters,
+ speedAccuracyMetersPerSecond,
+ bearingAccuracyDegrees,
+ timestamp,
+ elapsedRealtimeFlags,
+ elapsedRealtimeNanos);
+ auto result = gnssHal_V2_0->injectBestLocation_2_0(location);
+
+ if (!result.isOk() || !result) {
+ ALOGE("%s: Gnss injectBestLocation() failed.", __func__);
+ }
+ return;
+ }
+
if (gnssHal_V1_1 != nullptr) {
- GnssLocation location = createGnssLocation(
+ GnssLocation_V1_0 location = createGnssLocation_V1_0(
gnssLocationFlags,
latitudeDegrees,
longitudeDegrees,
@@ -1821,12 +1915,14 @@
bearingAccuracyDegrees,
timestamp);
auto result = gnssHal_V1_1->injectBestLocation(location);
+
if (!result.isOk() || !result) {
ALOGE("%s: Gnss injectBestLocation() failed.", __func__);
}
- } else {
- ALOGE("%s: injectBestLocation() is called but gnssHal_V1_1 is not available.", __func__);
+ return;
}
+
+ ALOGE("%s: injectBestLocation() is called but gnssHal_V1_1 is not available.", __func__);
}
static void android_location_GnssLocationProvider_inject_location(JNIEnv* /* env */,
@@ -2695,45 +2791,36 @@
{"native_init", "()Z", reinterpret_cast<void *>(android_location_GnssLocationProvider_init)},
{"native_cleanup", "()V", reinterpret_cast<void *>(
android_location_GnssLocationProvider_cleanup)},
- {"native_set_position_mode",
- "(IIIIIZ)Z",
- reinterpret_cast<void*>(android_location_GnssLocationProvider_set_position_mode)},
- {"native_start", "()Z", reinterpret_cast<void*>(android_location_GnssLocationProvider_start)},
- {"native_stop", "()Z", reinterpret_cast<void*>(android_location_GnssLocationProvider_stop)},
- {"native_delete_aiding_data",
- "(I)V",
- reinterpret_cast<void*>(android_location_GnssLocationProvider_delete_aiding_data)},
+ {"native_set_position_mode", "(IIIIIZ)Z", reinterpret_cast<void *>(
+ android_location_GnssLocationProvider_set_position_mode)},
+ {"native_start", "()Z", reinterpret_cast<void *>(
+ android_location_GnssLocationProvider_start)},
+ {"native_stop", "()Z", reinterpret_cast<void *>(
+ android_location_GnssLocationProvider_stop)},
+ {"native_delete_aiding_data", "(I)V", reinterpret_cast<void *>(
+ android_location_GnssLocationProvider_delete_aiding_data)},
{"native_read_nmea", "([BI)I", reinterpret_cast<void *>(
android_location_GnssLocationProvider_read_nmea)},
{"native_inject_time", "(JJI)V", reinterpret_cast<void *>(
android_location_GnssLocationProvider_inject_time)},
- {"native_inject_best_location",
- "(IDDDFFFFFFJ)V",
- reinterpret_cast<void *>(android_location_GnssLocationProvider_inject_best_location)},
- {"native_inject_location",
- "(DDF)V",
- reinterpret_cast<void *>(android_location_GnssLocationProvider_inject_location)},
+ {"native_inject_best_location", "(IDDDFFFFFFJIJ)V", reinterpret_cast<void *>(
+ android_location_GnssLocationProvider_inject_best_location)},
+ {"native_inject_location", "(DDF)V", reinterpret_cast<void *>(
+ android_location_GnssLocationProvider_inject_location)},
{"native_supports_xtra", "()Z", reinterpret_cast<void *>(
android_location_GnssLocationProvider_supports_xtra)},
- {"native_inject_xtra_data",
- "([BI)V",
- reinterpret_cast<void *>(android_location_GnssLocationProvider_inject_xtra_data)},
- {"native_agps_set_id",
- "(ILjava/lang/String;)V",
- reinterpret_cast<void *>(android_location_GnssLocationProvider_agps_set_id)},
- {"native_agps_set_ref_location_cellid",
- "(IIIII)V",
- reinterpret_cast<void *>(
- android_location_GnssLocationProvider_agps_set_reference_location_cellid)},
- {"native_set_agps_server",
- "(ILjava/lang/String;I)V",
- reinterpret_cast<void *>(android_location_GnssLocationProvider_set_agps_server)},
- {"native_send_ni_response",
- "(II)V",
- reinterpret_cast<void *>(android_location_GnssLocationProvider_send_ni_response)},
- {"native_get_internal_state",
- "()Ljava/lang/String;",
- reinterpret_cast<void *>(android_location_GnssLocationProvider_get_internal_state)},
+ {"native_inject_xtra_data", "([BI)V", reinterpret_cast<void *>(
+ android_location_GnssLocationProvider_inject_xtra_data)},
+ {"native_agps_set_id", "(ILjava/lang/String;)V", reinterpret_cast<void *>(
+ android_location_GnssLocationProvider_agps_set_id)},
+ {"native_agps_set_ref_location_cellid", "(IIIII)V", reinterpret_cast<void *>(
+ android_location_GnssLocationProvider_agps_set_reference_location_cellid)},
+ {"native_set_agps_server", "(ILjava/lang/String;I)V", reinterpret_cast<void *>(
+ android_location_GnssLocationProvider_set_agps_server)},
+ {"native_send_ni_response", "(II)V", reinterpret_cast<void *>(
+ android_location_GnssLocationProvider_send_ni_response)},
+ {"native_get_internal_state", "()Ljava/lang/String;", reinterpret_cast<void *>(
+ android_location_GnssLocationProvider_get_internal_state)},
{"native_is_gnss_visibility_control_supported", "()Z", reinterpret_cast<void *>(
android_location_GnssLocationProvider_is_gnss_visibility_control_supported)},
};
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 3070488..f496e81 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -5046,7 +5046,8 @@
if (quality == DevicePolicyManager.PASSWORD_QUALITY_MANAGED) {
quality = PASSWORD_QUALITY_UNSPECIFIED;
}
- final PasswordMetrics metrics = PasswordMetrics.computeForPassword(password);
+ // TODO(b/120484642): remove getBytes() below
+ final PasswordMetrics metrics = PasswordMetrics.computeForPassword(password.getBytes());
final int realQuality = metrics.quality;
if (realQuality < quality
&& quality != DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) {
@@ -5133,16 +5134,22 @@
try {
if (token == null) {
if (!TextUtils.isEmpty(password)) {
- mLockPatternUtils.saveLockPassword(password, null, quality, userHandle);
+ mLockPatternUtils.saveLockPassword(password.getBytes(), null, quality,
+ userHandle);
} else {
mLockPatternUtils.clearLock(null, userHandle);
}
result = true;
} else {
- result = mLockPatternUtils.setLockCredentialWithToken(password,
- TextUtils.isEmpty(password) ? LockPatternUtils.CREDENTIAL_TYPE_NONE
- : LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
- quality, tokenHandle, token, userHandle);
+ if (!TextUtils.isEmpty(password)) {
+ result = mLockPatternUtils.setLockCredentialWithToken(password.getBytes(),
+ LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
+ quality, tokenHandle, token, userHandle);
+ } else {
+ result = mLockPatternUtils.setLockCredentialWithToken(null,
+ LockPatternUtils.CREDENTIAL_TYPE_NONE,
+ quality, tokenHandle, token, userHandle);
+ }
}
boolean requireEntry = (flags & DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY) != 0;
if (requireEntry) {
diff --git a/services/net/java/android/net/NetworkStackClient.java b/services/net/java/android/net/NetworkStackClient.java
index 1eb7b98..830dbbe 100644
--- a/services/net/java/android/net/NetworkStackClient.java
+++ b/services/net/java/android/net/NetworkStackClient.java
@@ -30,6 +30,7 @@
import android.net.dhcp.DhcpServingParamsParcel;
import android.net.dhcp.IDhcpServerCallbacks;
import android.net.ip.IIpClientCallbacks;
+import android.net.util.SharedLog;
import android.os.Binder;
import android.os.IBinder;
import android.os.Process;
@@ -40,6 +41,7 @@
import com.android.internal.annotations.GuardedBy;
+import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
@@ -61,6 +63,9 @@
@GuardedBy("mPendingNetStackRequests")
private INetworkStackConnector mConnector;
+ @GuardedBy("mLog")
+ private final SharedLog mLog = new SharedLog(TAG);
+
private volatile boolean mNetworkStackStartRequested = false;
private interface NetworkStackCallback {
@@ -129,13 +134,14 @@
private class NetworkStackConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
+ log("Network stack service connected");
registerNetworkStackService(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
// TODO: crash/reboot the system ?
- Slog.wtf(TAG, "Lost network stack connector");
+ logWtf("Lost network stack connector", null);
}
};
@@ -144,6 +150,7 @@
ServiceManager.addService(Context.NETWORK_STACK_SERVICE, service, false /* allowIsolated */,
DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL);
+ log("Network stack service registered");
final ArrayList<NetworkStackCallback> requests;
synchronized (mPendingNetStackRequests) {
@@ -166,6 +173,7 @@
* started.
*/
public void start(Context context) {
+ log("Starting network stack");
mNetworkStackStartRequested = true;
// Try to bind in-process if the library is available
IBinder connector = null;
@@ -177,7 +185,7 @@
connector = (IBinder) service.getMethod("makeConnector", Context.class)
.invoke(null, context);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
- Slog.wtf(TAG, "Could not create network stack connector from NetworkStackService");
+ logWtf("Could not create network stack connector from NetworkStackService", e);
// TODO: crash/reboot system here ?
return;
} catch (ClassNotFoundException e) {
@@ -186,26 +194,28 @@
// In-process network stack. Add the service to the service manager here.
if (connector != null) {
+ log("Registering in-process network stack connector");
registerNetworkStackService(connector);
return;
}
// Start the network stack process. The service will be added to the service manager in
// NetworkStackConnection.onServiceConnected().
+ log("Starting network stack process");
final Intent intent = new Intent(INetworkStackConnector.class.getName());
final ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null) {
- Slog.wtf(TAG, "Could not resolve the network stack with " + intent);
+ logWtf("Could not resolve the network stack with " + intent, null);
// TODO: crash/reboot system server ?
return;
}
final PackageManager pm = context.getPackageManager();
int uid = -1;
try {
- uid = pm.getPackageUid(comp.getPackageName(), UserHandle.USER_SYSTEM);
+ uid = pm.getPackageUidAsUser(comp.getPackageName(), UserHandle.USER_SYSTEM);
} catch (PackageManager.NameNotFoundException e) {
- Slog.wtf("Network stack package not found", e);
+ logWtf("Network stack package not found", e);
// Fall through
}
if (uid != Process.NETWORK_STACK_UID) {
@@ -221,10 +231,31 @@
if (!context.bindServiceAsUser(intent, new NetworkStackConnection(),
Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM)) {
- Slog.wtf(TAG,
- "Could not bind to network stack in-process, or in app with " + intent);
+ logWtf("Could not bind to network stack in-process, or in app with " + intent, null);
+ return;
// TODO: crash/reboot system server if no network stack after a timeout ?
}
+
+ log("Network stack service start requested");
+ }
+
+ private void log(@NonNull String message) {
+ synchronized (mLog) {
+ mLog.log(message);
+ }
+ }
+
+ private void logWtf(@NonNull String message, @Nullable Throwable e) {
+ Slog.wtf(TAG, message);
+ synchronized (mLog) {
+ mLog.e(message, e);
+ }
+ }
+
+ private void loge(@NonNull String message, @Nullable Throwable e) {
+ synchronized (mLog) {
+ mLog.e(message, e);
+ }
}
/**
@@ -243,12 +274,12 @@
while ((connector = ServiceManager.getService(Context.NETWORK_STACK_SERVICE)) == null) {
Thread.sleep(20);
if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) {
- Slog.e(TAG, "Timeout waiting for NetworkStack connector");
+ loge("Timeout waiting for NetworkStack connector", null);
return null;
}
}
} catch (InterruptedException e) {
- Slog.e(TAG, "Error waiting for NetworkStack connector", e);
+ loge("Error waiting for NetworkStack connector", e);
return null;
}
@@ -286,4 +317,20 @@
request.onNetworkStackConnected(connector);
}
+
+ /**
+ * Dump NetworkStackClient logs to the specified {@link PrintWriter}.
+ */
+ public void dump(PrintWriter pw) {
+ // dump is thread-safe on SharedLog
+ mLog.dump(null, pw, null);
+
+ final int requestsQueueLength;
+ synchronized (mPendingNetStackRequests) {
+ requestsQueueLength = mPendingNetStackRequests.size();
+ }
+
+ pw.println();
+ pw.println("pendingNetStackRequests length: " + requestsQueueLength);
+ }
}
diff --git a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
index 8e3023b..339607b 100644
--- a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
+++ b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
@@ -275,6 +275,9 @@
public void stop() {
closeSocket();
+ // Wake up mMulticastTransmitter thread to interrupt a potential 1 day sleep before
+ // the thread's termination.
+ maybeNotifyMulticastTransmitter();
mMulticastTransmitter = null;
mUnicastResponder = null;
}
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index ebc816d..782196d 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -23,6 +23,7 @@
"androidx.test.runner",
"mockito-target-extended-minus-junit4",
"platform-test-annotations",
+ "truth-prebuilt",
],
libs: [
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/AppCompactorTest.java b/services/tests/mockingservicestests/src/com/android/server/am/AppCompactorTest.java
new file mode 100644
index 0000000..d32f1f7
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/am/AppCompactorTest.java
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) 2019 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.am;
+
+import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_ACTION_1;
+import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_ACTION_2;
+import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_STATSD_SAMPLE_RATE;
+import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_1;
+import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_2;
+import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_3;
+import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_4;
+import static android.provider.DeviceConfig.ActivityManager.KEY_USE_COMPACTION;
+
+import static com.android.server.am.ActivityManagerService.Injector;
+import static com.android.server.am.AppCompactor.compactActionIntToString;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.provider.DeviceConfig;
+
+import com.android.server.appop.AppOpsService;
+import com.android.server.testables.TestableDeviceConfig;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.io.File;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests for {@link AppCompactor}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:AppCompactorTest
+ */
+@RunWith(MockitoJUnitRunner.class)
+public final class AppCompactorTest {
+
+ @Mock
+ private AppOpsService mAppOpsService;
+ private AppCompactor mCompactorUnderTest;
+ private HandlerThread mHandlerThread;
+ private Handler mHandler;
+ private CountDownLatch mCountDown;
+
+ @Rule
+ public TestableDeviceConfig mDeviceConfig = new TestableDeviceConfig();
+
+ @Before
+ public void setUp() {
+ mHandlerThread = new HandlerThread("");
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
+ ActivityManagerService ams = new ActivityManagerService(new TestInjector());
+ mCompactorUnderTest = new AppCompactor(ams,
+ new AppCompactor.PropertyChangedCallbackForTest() {
+ @Override
+ public void onPropertyChanged() {
+ if (mCountDown != null) {
+ mCountDown.countDown();
+ }
+ }
+ });
+ }
+
+ @After
+ public void tearDown() {
+ mHandlerThread.quit();
+ mCountDown = null;
+ }
+
+ @Test
+ public void init_setsDefaults() {
+ mCompactorUnderTest.init();
+ assertThat(mCompactorUnderTest.useCompaction()).isEqualTo(
+ AppCompactor.DEFAULT_USE_COMPACTION);
+ assertThat(mCompactorUnderTest.mCompactActionSome).isEqualTo(
+ compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_1));
+ assertThat(mCompactorUnderTest.mCompactActionFull).isEqualTo(
+ compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_2));
+ assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo(
+ AppCompactor.DEFAULT_COMPACT_THROTTLE_1);
+ assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo(
+ AppCompactor.DEFAULT_COMPACT_THROTTLE_2);
+ assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo(
+ AppCompactor.DEFAULT_COMPACT_THROTTLE_3);
+ assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo(
+ AppCompactor.DEFAULT_COMPACT_THROTTLE_4);
+ assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo(
+ AppCompactor.DEFAULT_STATSD_SAMPLE_RATE);
+ }
+
+ @Test
+ public void init_withDeviceConfigSetsParameters() {
+ // When the DeviceConfig already has a flag value stored (note this test will need to
+ // change if the default value changes from false).
+ assertThat(AppCompactor.DEFAULT_USE_COMPACTION).isFalse();
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_USE_COMPACTION, "true", false);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_ACTION_1,
+ Integer.toString((AppCompactor.DEFAULT_COMPACT_ACTION_1 + 1 % 4) + 1), false);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_ACTION_2,
+ Integer.toString((AppCompactor.DEFAULT_COMPACT_ACTION_2 + 1 % 4) + 1), false);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_THROTTLE_1,
+ Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1), false);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_THROTTLE_2,
+ Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1), false);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_THROTTLE_3,
+ Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1), false);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_THROTTLE_4,
+ Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1), false);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_STATSD_SAMPLE_RATE,
+ Float.toString(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f), false);
+
+ // Then calling init will read and set that flag.
+ mCompactorUnderTest.init();
+ assertThat(mCompactorUnderTest.useCompaction()).isTrue();
+ assertThat(mCompactorUnderTest.mCompactionThread.isAlive()).isTrue();
+
+ assertThat(mCompactorUnderTest.mCompactActionSome).isEqualTo(
+ compactActionIntToString((AppCompactor.DEFAULT_COMPACT_ACTION_1 + 1 % 4) + 1));
+ assertThat(mCompactorUnderTest.mCompactActionFull).isEqualTo(
+ compactActionIntToString((AppCompactor.DEFAULT_COMPACT_ACTION_2 + 1 % 4) + 1));
+ assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo(
+ AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1);
+ assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo(
+ AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1);
+ assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo(
+ AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1);
+ assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo(
+ AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1);
+ assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo(
+ AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f);
+ }
+
+ @Test
+ public void useCompaction_listensToDeviceConfigChanges() throws InterruptedException {
+ assertThat(mCompactorUnderTest.useCompaction()).isEqualTo(
+ AppCompactor.DEFAULT_USE_COMPACTION);
+ // When we call init and change some the flag value...
+ mCompactorUnderTest.init();
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_USE_COMPACTION, "true", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+ // Then that new flag value is updated in the implementation.
+ assertThat(mCompactorUnderTest.useCompaction()).isTrue();
+ assertThat(mCompactorUnderTest.mCompactionThread.isAlive()).isTrue();
+
+ // And again, setting the flag the other way.
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_USE_COMPACTION, "false", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+ assertThat(mCompactorUnderTest.useCompaction()).isFalse();
+ }
+
+ @Test
+ public void useCompaction_listensToDeviceConfigChangesBadValues() throws InterruptedException {
+ assertThat(mCompactorUnderTest.useCompaction()).isEqualTo(
+ AppCompactor.DEFAULT_USE_COMPACTION);
+ mCompactorUnderTest.init();
+
+ // When we push an invalid flag value...
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_USE_COMPACTION, "foobar", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+ // Then we set the default.
+ assertThat(mCompactorUnderTest.useCompaction()).isEqualTo(
+ AppCompactor.DEFAULT_USE_COMPACTION);
+ }
+
+ @Test
+ public void compactAction_listensToDeviceConfigChanges() throws InterruptedException {
+ mCompactorUnderTest.init();
+
+ // When we override new values for the compaction action with reasonable values...
+
+ // There are four possible values for compactAction[Some|Full].
+ for (int i = 1; i < 5; i++) {
+ mCountDown = new CountDownLatch(2);
+ int expectedSome = (AppCompactor.DEFAULT_COMPACT_ACTION_1 + i) % 4 + 1;
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_ACTION_1, Integer.toString(expectedSome), false);
+ int expectedFull = (AppCompactor.DEFAULT_COMPACT_ACTION_2 + i) % 4 + 1;
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_ACTION_2, Integer.toString(expectedFull), false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+ // Then the updates are reflected in the flags.
+ assertThat(mCompactorUnderTest.mCompactActionSome).isEqualTo(
+ compactActionIntToString(expectedSome));
+ assertThat(mCompactorUnderTest.mCompactActionFull).isEqualTo(
+ compactActionIntToString(expectedFull));
+ }
+ }
+
+ @Test
+ public void compactAction_listensToDeviceConfigChangesBadValues() throws InterruptedException {
+ mCompactorUnderTest.init();
+
+ // When we override new values for the compaction action with bad values ...
+ mCountDown = new CountDownLatch(2);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_ACTION_1, "foo", false);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_ACTION_2, "foo", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+ // Then the default values are reflected in the flag
+ assertThat(mCompactorUnderTest.mCompactActionSome).isEqualTo(
+ compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_1));
+ assertThat(mCompactorUnderTest.mCompactActionFull).isEqualTo(
+ compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_2));
+
+ mCountDown = new CountDownLatch(2);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_ACTION_1, "", false);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_ACTION_2, "", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+ assertThat(mCompactorUnderTest.mCompactActionSome).isEqualTo(
+ compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_1));
+ assertThat(mCompactorUnderTest.mCompactActionFull).isEqualTo(
+ compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_2));
+ }
+
+ @Test
+ public void compactThrottle_listensToDeviceConfigChanges() throws InterruptedException {
+ mCompactorUnderTest.init();
+
+ // When we override new reasonable throttle values after init...
+ mCountDown = new CountDownLatch(4);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_THROTTLE_1,
+ Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1), false);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_THROTTLE_2,
+ Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1), false);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_THROTTLE_3,
+ Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1), false);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_THROTTLE_4,
+ Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1), false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+ // Then those flags values are reflected in the compactor.
+ assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo(
+ AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1);
+ assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo(
+ AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1);
+ assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo(
+ AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1);
+ assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo(
+ AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1);
+ }
+
+ @Test
+ public void compactThrottle_listensToDeviceConfigChangesBadValues()
+ throws InterruptedException {
+ mCompactorUnderTest.init();
+
+ // When one of the throttles is overridden with a bad value...
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_THROTTLE_1, "foo", false);
+ // Then all the throttles have the defaults set.
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+ assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo(
+ AppCompactor.DEFAULT_COMPACT_THROTTLE_1);
+ assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo(
+ AppCompactor.DEFAULT_COMPACT_THROTTLE_2);
+ assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo(
+ AppCompactor.DEFAULT_COMPACT_THROTTLE_3);
+ assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo(
+ AppCompactor.DEFAULT_COMPACT_THROTTLE_4);
+
+ // Repeat for each of the throttle keys.
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_THROTTLE_2, "foo", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+ assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo(
+ AppCompactor.DEFAULT_COMPACT_THROTTLE_1);
+ assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo(
+ AppCompactor.DEFAULT_COMPACT_THROTTLE_2);
+ assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo(
+ AppCompactor.DEFAULT_COMPACT_THROTTLE_3);
+ assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo(
+ AppCompactor.DEFAULT_COMPACT_THROTTLE_4);
+
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_THROTTLE_3, "foo", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+ assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo(
+ AppCompactor.DEFAULT_COMPACT_THROTTLE_1);
+ assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo(
+ AppCompactor.DEFAULT_COMPACT_THROTTLE_2);
+ assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo(
+ AppCompactor.DEFAULT_COMPACT_THROTTLE_3);
+ assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo(
+ AppCompactor.DEFAULT_COMPACT_THROTTLE_4);
+
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_THROTTLE_4, "foo", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+ assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo(
+ AppCompactor.DEFAULT_COMPACT_THROTTLE_1);
+ assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo(
+ AppCompactor.DEFAULT_COMPACT_THROTTLE_2);
+ assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo(
+ AppCompactor.DEFAULT_COMPACT_THROTTLE_3);
+ assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo(
+ AppCompactor.DEFAULT_COMPACT_THROTTLE_4);
+ }
+
+ @Test
+ public void statsdSampleRate_listensToDeviceConfigChanges() throws InterruptedException {
+ mCompactorUnderTest.init();
+
+ // When we override mStatsdSampleRate with a reasonable values ...
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_STATSD_SAMPLE_RATE,
+ Float.toString(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f), false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+ // Then that override is reflected in the compactor.
+ assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo(
+ AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f);
+ }
+
+ @Test
+ public void statsdSanokeRate_listensToDeviceConfigChangesBadValues()
+ throws InterruptedException {
+ mCompactorUnderTest.init();
+
+ // When we override mStatsdSampleRate with a reasonable values ...
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_STATSD_SAMPLE_RATE, "foo", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+ // Then that override is reflected in the compactor.
+ assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo(
+ AppCompactor.DEFAULT_STATSD_SAMPLE_RATE);
+ }
+
+ @Test
+ public void statsdSanokeRate_listensToDeviceConfigChangesOutOfRangeValues()
+ throws InterruptedException {
+ mCompactorUnderTest.init();
+
+ // When we override mStatsdSampleRate with an value outside of [0..1]...
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_STATSD_SAMPLE_RATE,
+ Float.toString(-1.0f), false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+ // Then the values is capped in the range.
+ assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo(0.0f);
+
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_STATSD_SAMPLE_RATE,
+ Float.toString(1.01f), false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+ // Then the values is capped in the range.
+ assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo(1.0f);
+ }
+
+ private class TestInjector extends Injector {
+ @Override
+ public AppOpsService getAppOpsService(File file, Handler handler) {
+ return mAppOpsService;
+ }
+
+ @Override
+ public Handler getUiHandler(ActivityManagerService service) {
+ return mHandler;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java b/services/tests/mockingservicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java
similarity index 60%
rename from services/tests/servicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java
rename to services/tests/mockingservicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java
index 0fd5921..5db8867 100644
--- a/services/tests/servicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 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.
@@ -16,52 +16,98 @@
package com.android.server.am;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+
import android.content.ContentResolver;
+import android.os.SystemProperties;
import android.provider.Settings;
-import android.test.mock.MockContentResolver;
import android.text.TextUtils;
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
-import com.android.internal.util.Preconditions;
-import com.android.internal.util.test.FakeSettingsProvider;
-
+import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
-import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+import org.mockito.stubbing.Answer;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
-import java.util.Map;
/**
- * Tests for {@link SettingsToPropertiesMapper}
- *
- * Build/Install/Run:
- * atest FrameworksServicesTests:SettingsToPropertiesMapperTest
+ * Test SettingsToPropertiesMapper.
*/
-@RunWith(AndroidJUnit4.class)
-@SmallTest
public class SettingsToPropertiesMapperTest {
private static final String NAME_VALID_CHARACTERS_REGEX = "^[\\w\\-@:]*$";
private static final String[] TEST_MAPPING = new String[] {
Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS
};
- private TestMapper mTestMapper;
- private MockContentResolver mMockContentResolver;
+ private MockitoSession mSession;
+
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private ContentResolver mMockContentResolver;
+
+ private SettingsToPropertiesMapper mTestMapper;
+
+ private HashMap<String, String> mSystemSettingsMap;
+ private HashMap<String, String> mGlobalSettingsMap;
@Before
- public void setupForEach() {
- // Use FakeSettingsProvider to not affect global state
- mMockContentResolver = new MockContentResolver(InstrumentationRegistry.getContext());
- mMockContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
- mTestMapper = new TestMapper(mMockContentResolver);
+ public void setUp() throws Exception {
+ mSession =
+ ExtendedMockito.mockitoSession().initMocks(
+ this)
+ .strictness(Strictness.LENIENT)
+ .spyStatic(SystemProperties.class)
+ .spyStatic(Settings.Global.class)
+ .spyStatic(SettingsToPropertiesMapper.class)
+ .startMocking();
+ mSystemSettingsMap = new HashMap<>();
+ mGlobalSettingsMap = new HashMap<>();
+
+ // Mock SystemProperties setter and various getters
+ doAnswer((Answer<Void>) invocationOnMock -> {
+ String key = invocationOnMock.getArgument(0);
+ String value = invocationOnMock.getArgument(1);
+
+ mSystemSettingsMap.put(key, value);
+ return null;
+ }
+ ).when(() -> SystemProperties.set(anyString(), anyString()));
+
+ doAnswer((Answer<String>) invocationOnMock -> {
+ String key = invocationOnMock.getArgument(0);
+
+ String storedValue = mSystemSettingsMap.get(key);
+ return storedValue == null ? "" : storedValue;
+ }
+ ).when(() -> SystemProperties.get(anyString()));
+
+ // Mock Settings.Global methods
+ doAnswer((Answer<String>) invocationOnMock -> {
+ String key = invocationOnMock.getArgument(1);
+
+ return mGlobalSettingsMap.get(key);
+ }
+ ).when(() -> Settings.Global.getString(any(), anyString()));
+
+ mTestMapper = new SettingsToPropertiesMapper(
+ mMockContentResolver, TEST_MAPPING, new String[] {});
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mSession.finishMocking();
}
@Test
@@ -108,30 +154,27 @@
@Test
public void testUpdatePropertiesFromSettings() {
- Settings.Global.putString(mMockContentResolver,
- Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, "testValue");
+ mGlobalSettingsMap.put(Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, "testValue");
String systemPropertyName = "persist.device_config.global_settings."
+ "sqlite_compatibility_wal_flags";
mTestMapper.updatePropertiesFromSettings();
- String propValue = mTestMapper.systemPropertiesGet(systemPropertyName);
+ String propValue = mSystemSettingsMap.get(systemPropertyName);
Assert.assertEquals("testValue", propValue);
- Settings.Global.putString(mMockContentResolver,
- Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, "testValue2");
+ mGlobalSettingsMap.put(Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, "testValue2");
mTestMapper.updatePropertyFromSetting(
Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS,
systemPropertyName);
- propValue = mTestMapper.systemPropertiesGet(systemPropertyName);
+ propValue = mSystemSettingsMap.get(systemPropertyName);
Assert.assertEquals("testValue2", propValue);
- Settings.Global.putString(mMockContentResolver,
- Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, null);
+ mGlobalSettingsMap.put(Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, null);
mTestMapper.updatePropertyFromSetting(
Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS,
systemPropertyName);
- propValue = mTestMapper.systemPropertiesGet(systemPropertyName);
+ propValue = mSystemSettingsMap.get(systemPropertyName);
Assert.assertEquals("", propValue);
}
@@ -163,71 +206,37 @@
public void testUpdatePropertiesFromSettings_PropertyAndSettingNotPresent() {
// Test that empty property will not not be set if setting is not set
mTestMapper.updatePropertiesFromSettings();
- String propValue = mTestMapper.systemPropertiesGet("TestProperty");
+ String propValue = mSystemSettingsMap.get("TestProperty");
Assert.assertNull("Property should not be set if setting is null", propValue);
}
@Test
public void testIsNativeFlagsResetPerformed() {
- mTestMapper.systemPropertiesSet("device_config.reset_performed", "true");
+ mSystemSettingsMap.put("device_config.reset_performed", "true");
Assert.assertTrue(mTestMapper.isNativeFlagsResetPerformed());
- mTestMapper.systemPropertiesSet("device_config.reset_performed", "false");
+ mSystemSettingsMap.put("device_config.reset_performed", "false");
Assert.assertFalse(mTestMapper.isNativeFlagsResetPerformed());
- mTestMapper.systemPropertiesSet("device_config.reset_performed", "");
+ mSystemSettingsMap.put("device_config.reset_performed", "");
Assert.assertFalse(mTestMapper.isNativeFlagsResetPerformed());
}
@Test
public void testGetResetNativeCategories() {
- mTestMapper.systemPropertiesSet("device_config.reset_performed", "");
- Assert.assertEquals(mTestMapper.getResetNativeCategories().length, 0);
-
- mTestMapper.systemPropertiesSet("device_config.reset_performed", "true");
- mTestMapper.setFileContent("");
- Assert.assertEquals(mTestMapper.getResetNativeCategories().length, 0);
-
- mTestMapper.systemPropertiesSet("device_config.reset_performed", "true");
- mTestMapper.setFileContent("persist.device_config.category1.flag;"
+ doReturn("persist.device_config.category1.flag;"
+ "persist.device_config.category2.flag;persist.device_config.category3.flag;"
- + "persist.device_config.category3.flag2");
+ + "persist.device_config.category3.flag2")
+ .when(() -> SettingsToPropertiesMapper.getResetFlagsFileContent());
+
+ mSystemSettingsMap.put("device_config.reset_performed", "");
+ Assert.assertEquals(mTestMapper.getResetNativeCategories().length, 0);
+
+ mSystemSettingsMap.put("device_config.reset_performed", "true");
List<String> categories = Arrays.asList(mTestMapper.getResetNativeCategories());
Assert.assertEquals(3, categories.size());
Assert.assertTrue(categories.contains("category1"));
Assert.assertTrue(categories.contains("category2"));
Assert.assertTrue(categories.contains("category3"));
}
-
- private static class TestMapper extends SettingsToPropertiesMapper {
- private final Map<String, String> mProps = new HashMap<>();
-
- private String mFileContent = "";
-
- TestMapper(ContentResolver contentResolver) {
- super(contentResolver, TEST_MAPPING, new String[] {});
- }
-
- @Override
- protected String systemPropertiesGet(String key) {
- Preconditions.checkNotNull(key);
- return mProps.get(key);
- }
-
- @Override
- protected void systemPropertiesSet(String key, String value) {
- Preconditions.checkNotNull(value);
- Preconditions.checkNotNull(key);
- mProps.put(key, value);
- }
-
- protected void setFileContent(String fileContent) {
- mFileContent = fileContent;
- }
-
- @Override
- protected String getResetFlagsFileContent() {
- return mFileContent;
- }
- }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java
new file mode 100644
index 0000000..b766822
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2019 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.testables;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyString;
+
+import android.provider.DeviceConfig;
+import android.util.Pair;
+
+import com.android.dx.mockito.inline.extended.StaticMockitoSession;
+
+import org.junit.rules.TestRule;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+import org.mockito.quality.Strictness;
+import org.mockito.stubbing.Answer;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
+
+/**
+ * TestableDeviceConfig uses ExtendedMockito to replace the real implementation of DeviceConfig
+ * with essentially a local HashMap in the callers process. This allows for unit testing that do not
+ * modify the real DeviceConfig on the device at all.
+ *
+ * <p>TestableDeviceConfig should be defined as a rule on your test so it can clean up after itself.
+ * Like the following:</p>
+ * <pre class="prettyprint">
+ * @Rule
+ * public final TestableDeviceConfig mTestableDeviceConfig = new TestableDeviceConfig();
+ * </pre>
+ */
+public final class TestableDeviceConfig implements TestRule {
+
+ private StaticMockitoSession mMockitoSession;
+ private Map<DeviceConfig.OnPropertyChangedListener, Pair<String, Executor>>
+ mOnPropertyChangedListenerMap = new HashMap<>();
+ private Map<String, String> mKeyValueMap = new ConcurrentHashMap<>();
+
+ /**
+ * Clears out all local overrides.
+ */
+ public void clearDeviceConfig() {
+ mKeyValueMap.clear();
+ }
+
+ @Override
+ public Statement apply(Statement base, Description description) {
+ mMockitoSession = mockitoSession()
+ .initMocks(this)
+ .strictness(Strictness.LENIENT)
+ .spyStatic(DeviceConfig.class)
+ .startMocking();
+
+ doAnswer((Answer<Void>) invocationOnMock -> {
+ String namespace = invocationOnMock.getArgument(0);
+ Executor executor = invocationOnMock.getArgument(1);
+ DeviceConfig.OnPropertyChangedListener onPropertyChangedListener =
+ invocationOnMock.getArgument(2);
+ mOnPropertyChangedListenerMap.put(
+ onPropertyChangedListener, new Pair<>(namespace, executor));
+ return null;
+ }).when(() -> DeviceConfig.addOnPropertyChangedListener(
+ anyString(), any(Executor.class),
+ any(DeviceConfig.OnPropertyChangedListener.class)));
+
+ doAnswer((Answer<Boolean>) invocationOnMock -> {
+ String namespace = invocationOnMock.getArgument(0);
+ String name = invocationOnMock.getArgument(1);
+ String value = invocationOnMock.getArgument(2);
+ mKeyValueMap.put(getKey(namespace, name), value);
+ for (DeviceConfig.OnPropertyChangedListener listener :
+ mOnPropertyChangedListenerMap.keySet()) {
+ if (namespace.equals(mOnPropertyChangedListenerMap.get(listener).first)) {
+ mOnPropertyChangedListenerMap.get(listener).second.execute(
+ () -> listener.onPropertyChanged(namespace, name, value));
+ }
+ }
+ return true;
+ }
+ ).when(() -> DeviceConfig.setProperty(anyString(), anyString(), anyString(), anyBoolean()));
+
+ doAnswer((Answer<String>) invocationOnMock -> {
+ String namespace = invocationOnMock.getArgument(0);
+ String name = invocationOnMock.getArgument(1);
+ return mKeyValueMap.get(getKey(namespace, name));
+ }).when(() -> DeviceConfig.getProperty(anyString(), anyString()));
+
+
+ return new TestWatcher() {
+ @Override
+ protected void succeeded(Description description) {
+ mMockitoSession.finishMocking();
+ mOnPropertyChangedListenerMap.clear();
+ }
+
+ @Override
+ protected void failed(Throwable e, Description description) {
+ mMockitoSession.finishMocking(e);
+ mOnPropertyChangedListenerMap.clear();
+ }
+ }.apply(base, description);
+ }
+
+ private static String getKey(String namespace, String name) {
+ return namespace + "/" + name;
+ }
+
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java
new file mode 100644
index 0000000..39b5840
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2019 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.testables;
+
+import static android.provider.DeviceConfig.OnPropertyChangedListener;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.ActivityThread;
+import android.platform.test.annotations.Presubmit;
+import android.provider.DeviceConfig;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/** Tests that ensure appropriate settings are backed up. */
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class TestableDeviceConfigTest {
+ private static final String sNamespace = "namespace1";
+ private static final String sKey = "key1";
+ private static final String sValue = "value1";
+ private static final long WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS = 2000; // 2 sec
+
+ @Rule
+ public TestableDeviceConfig mTestableDeviceConfig = new TestableDeviceConfig();
+
+ @Test
+ public void getProperty_empty() {
+ String result = DeviceConfig.getProperty(sNamespace, sKey);
+ assertThat(result).isNull();
+ }
+
+ @Test
+ public void setAndGetProperty_sameNamespace() {
+ DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
+ String result = DeviceConfig.getProperty(sNamespace, sKey);
+ assertThat(result).isEqualTo(sValue);
+ }
+
+ @Test
+ public void setAndGetProperty_differentNamespace() {
+ String newNamespace = "namespace2";
+ DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
+ String result = DeviceConfig.getProperty(newNamespace, sKey);
+ assertThat(result).isNull();
+ }
+
+ @Test
+ public void setAndGetProperty_multipleNamespaces() {
+ String newNamespace = "namespace2";
+ String newValue = "value2";
+ DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
+ DeviceConfig.setProperty(newNamespace, sKey, newValue, false);
+ String result = DeviceConfig.getProperty(sNamespace, sKey);
+ assertThat(result).isEqualTo(sValue);
+ result = DeviceConfig.getProperty(newNamespace, sKey);
+ assertThat(result).isEqualTo(newValue);
+ }
+
+ @Test
+ public void setAndGetProperty_overrideValue() {
+ String newValue = "value2";
+ DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
+ DeviceConfig.setProperty(sNamespace, sKey, newValue, false);
+ String result = DeviceConfig.getProperty(sNamespace, sKey);
+ assertThat(result).isEqualTo(newValue);
+ }
+
+ @Test
+ public void testListener() throws InterruptedException {
+ CountDownLatch countDownLatch = new CountDownLatch(1);
+
+ OnPropertyChangedListener changeListener = (namespace, name, value) -> {
+ assertThat(namespace).isEqualTo(sNamespace);
+ assertThat(name).isEqualTo(sKey);
+ assertThat(value).isEqualTo(sValue);
+ countDownLatch.countDown();
+ };
+ try {
+ DeviceConfig.addOnPropertyChangedListener(sNamespace,
+ ActivityThread.currentApplication().getMainExecutor(), changeListener);
+ DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
+ assertThat(countDownLatch.await(
+ WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue();
+ } finally {
+ DeviceConfig.removeOnPropertyChangedListener(changeListener);
+ }
+ }
+
+}
+
+
diff --git a/services/tests/rescueparty/how_to_run.txt b/services/tests/rescueparty/how_to_run.txt
index 9528d39..a3a26d6 100644
--- a/services/tests/rescueparty/how_to_run.txt
+++ b/services/tests/rescueparty/how_to_run.txt
@@ -1,4 +1,3 @@
-# Per http://go/westworld-local-development#step3-test-atom-and-metric-locally-on-device ,
# In one terminal:
make statsd_testdrive
./out/host/linux-x86/bin/statsd_testdrive 122
diff --git a/services/tests/servicestests/src/com/android/server/am/AppCompactorTest.java b/services/tests/servicestests/src/com/android/server/am/AppCompactorTest.java
deleted file mode 100644
index 63015be..0000000
--- a/services/tests/servicestests/src/com/android/server/am/AppCompactorTest.java
+++ /dev/null
@@ -1,449 +0,0 @@
-/*
- * Copyright (C) 2019 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.am;
-
-import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_ACTION_1;
-import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_ACTION_2;
-import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_STATSD_SAMPLE_RATE;
-import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_1;
-import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_2;
-import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_3;
-import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_4;
-import static android.provider.DeviceConfig.ActivityManager.KEY_USE_COMPACTION;
-
-import static com.android.server.am.ActivityManagerService.Injector;
-import static com.android.server.am.AppCompactor.compactActionIntToString;
-
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
-
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.provider.DeviceConfig;
-import android.support.test.uiautomator.UiDevice;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.server.appop.AppOpsService;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Tests for {@link AppCompactor}.
- *
- * Build/Install/Run:
- * atest FrameworksServicesTests:AppCompactorTest
- */
-@RunWith(AndroidJUnit4.class)
-public final class AppCompactorTest {
-
- private static final String CLEAR_DEVICE_CONFIG_KEY_CMD =
- "device_config delete activity_manager";
-
- @Mock private AppOpsService mAppOpsService;
- private AppCompactor mCompactorUnderTest;
- private HandlerThread mHandlerThread;
- private Handler mHandler;
- private CountDownLatch mCountDown;
-
- private static void clearDeviceConfig() throws IOException {
- UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
- uiDevice.executeShellCommand(
- CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_USE_COMPACTION);
- uiDevice.executeShellCommand(
- CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_ACTION_1);
- uiDevice.executeShellCommand(
- CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_ACTION_2);
- uiDevice.executeShellCommand(
- CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_THROTTLE_1);
- uiDevice.executeShellCommand(
- CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_THROTTLE_2);
- uiDevice.executeShellCommand(
- CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_THROTTLE_3);
- uiDevice.executeShellCommand(
- CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_THROTTLE_4);
- uiDevice.executeShellCommand(
- CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_STATSD_SAMPLE_RATE);
- }
-
- @Before
- public void setUp() throws IOException {
- MockitoAnnotations.initMocks(this);
- clearDeviceConfig();
- mHandlerThread = new HandlerThread("");
- mHandlerThread.start();
- mHandler = new Handler(mHandlerThread.getLooper());
- ActivityManagerService ams = new ActivityManagerService(new TestInjector());
- mCompactorUnderTest = new AppCompactor(ams,
- new AppCompactor.PropertyChangedCallbackForTest() {
- @Override
- public void onPropertyChanged() {
- if (mCountDown != null) {
- mCountDown.countDown();
- }
- }
- });
- }
-
- @After
- public void tearDown() throws IOException {
- mHandlerThread.quit();
- mCountDown = null;
- clearDeviceConfig();
- }
-
- @Test
- public void init_setsDefaults() {
- mCompactorUnderTest.init();
- assertThat(mCompactorUnderTest.useCompaction(),
- is(mCompactorUnderTest.DEFAULT_USE_COMPACTION));
- assertThat(mCompactorUnderTest.mCompactActionSome, is(
- compactActionIntToString(mCompactorUnderTest.DEFAULT_COMPACT_ACTION_1)));
- assertThat(mCompactorUnderTest.mCompactActionFull, is(
- compactActionIntToString(mCompactorUnderTest.DEFAULT_COMPACT_ACTION_2)));
- assertThat(mCompactorUnderTest.mCompactThrottleSomeSome,
- is(AppCompactor.DEFAULT_COMPACT_THROTTLE_1));
- assertThat(mCompactorUnderTest.mCompactThrottleSomeFull,
- is(AppCompactor.DEFAULT_COMPACT_THROTTLE_2));
- assertThat(mCompactorUnderTest.mCompactThrottleFullSome,
- is(AppCompactor.DEFAULT_COMPACT_THROTTLE_3));
- assertThat(mCompactorUnderTest.mCompactThrottleFullFull,
- is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4));
- assertThat(mCompactorUnderTest.mStatsdSampleRate,
- is(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE));
- }
-
- @Test
- public void init_withDeviceConfigSetsParameters() {
- // When the DeviceConfig already has a flag value stored (note this test will need to
- // change if the default value changes from false).
- assertThat(mCompactorUnderTest.DEFAULT_USE_COMPACTION, is(false));
- DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
- KEY_USE_COMPACTION, "true", false);
- DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
- KEY_COMPACT_ACTION_1,
- Integer.toString((AppCompactor.DEFAULT_COMPACT_ACTION_1 + 1 % 4) + 1), false);
- DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
- KEY_COMPACT_ACTION_2,
- Integer.toString((AppCompactor.DEFAULT_COMPACT_ACTION_2 + 1 % 4) + 1), false);
- DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
- KEY_COMPACT_THROTTLE_1,
- Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1), false);
- DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
- KEY_COMPACT_THROTTLE_2,
- Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1), false);
- DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
- KEY_COMPACT_THROTTLE_3,
- Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1), false);
- DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
- KEY_COMPACT_THROTTLE_4,
- Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1), false);
- DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
- KEY_COMPACT_STATSD_SAMPLE_RATE,
- Float.toString(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f), false);
-
- // Then calling init will read and set that flag.
- mCompactorUnderTest.init();
- assertThat(mCompactorUnderTest.useCompaction(), is(true));
- assertThat(mCompactorUnderTest.mCompactionThread.isAlive(), is(true));
-
- assertThat(mCompactorUnderTest.mCompactActionSome,
- is(compactActionIntToString((AppCompactor.DEFAULT_COMPACT_ACTION_1 + 1 % 4) + 1)));
- assertThat(mCompactorUnderTest.mCompactActionFull,
- is(compactActionIntToString((AppCompactor.DEFAULT_COMPACT_ACTION_2 + 1 % 4) + 1)));
- assertThat(mCompactorUnderTest.mCompactThrottleSomeSome,
- is(AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1));
- assertThat(mCompactorUnderTest.mCompactThrottleSomeFull,
- is(AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1));
- assertThat(mCompactorUnderTest.mCompactThrottleFullSome,
- is(AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1));
- assertThat(mCompactorUnderTest.mCompactThrottleFullFull,
- is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1));
- assertThat(mCompactorUnderTest.mStatsdSampleRate,
- is(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f));
- }
-
- @Test
- public void useCompaction_listensToDeviceConfigChanges() throws InterruptedException {
- assertThat(mCompactorUnderTest.useCompaction(),
- is(mCompactorUnderTest.DEFAULT_USE_COMPACTION));
- // When we call init and change some the flag value...
- mCompactorUnderTest.init();
- mCountDown = new CountDownLatch(1);
- DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
- KEY_USE_COMPACTION, "true", false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
-
- // Then that new flag value is updated in the implementation.
- assertThat(mCompactorUnderTest.useCompaction(), is(true));
- assertThat(mCompactorUnderTest.mCompactionThread.isAlive(), is(true));
-
- // And again, setting the flag the other way.
- mCountDown = new CountDownLatch(1);
- DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
- KEY_USE_COMPACTION, "false", false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
- assertThat(mCompactorUnderTest.useCompaction(), is(false));
- }
-
- @Test
- public void useCompaction_listensToDeviceConfigChangesBadValues() throws InterruptedException {
- assertThat(mCompactorUnderTest.useCompaction(),
- is(mCompactorUnderTest.DEFAULT_USE_COMPACTION));
- mCompactorUnderTest.init();
-
- // When we push an invalid flag value...
- mCountDown = new CountDownLatch(1);
- DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
- KEY_USE_COMPACTION, "foobar", false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
-
- // Then we set the default.
- assertThat(mCompactorUnderTest.useCompaction(), is(AppCompactor.DEFAULT_USE_COMPACTION));
- }
-
- @Test
- public void compactAction_listensToDeviceConfigChanges() throws InterruptedException {
- mCompactorUnderTest.init();
-
- // When we override new values for the compaction action with reasonable values...
-
- // There are four possible values for compactAction[Some|Full].
- for (int i = 1; i < 5; i++) {
- mCountDown = new CountDownLatch(2);
- int expectedSome = (mCompactorUnderTest.DEFAULT_COMPACT_ACTION_1 + i) % 4 + 1;
- DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
- KEY_COMPACT_ACTION_1, Integer.toString(expectedSome), false);
- int expectedFull = (mCompactorUnderTest.DEFAULT_COMPACT_ACTION_2 + i) % 4 + 1;
- DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
- KEY_COMPACT_ACTION_2, Integer.toString(expectedFull), false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
-
- // Then the updates are reflected in the flags.
- assertThat(mCompactorUnderTest.mCompactActionSome,
- is(compactActionIntToString(expectedSome)));
- assertThat(mCompactorUnderTest.mCompactActionFull,
- is(compactActionIntToString(expectedFull)));
- }
- }
-
- @Test
- public void compactAction_listensToDeviceConfigChangesBadValues() throws InterruptedException {
- mCompactorUnderTest.init();
-
- // When we override new values for the compaction action with bad values ...
- mCountDown = new CountDownLatch(2);
- DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
- KEY_COMPACT_ACTION_1, "foo", false);
- DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
- KEY_COMPACT_ACTION_2, "foo", false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
-
- // Then the default values are reflected in the flag
- assertThat(mCompactorUnderTest.mCompactActionSome,
- is(compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_1)));
- assertThat(mCompactorUnderTest.mCompactActionFull,
- is(compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_2)));
-
- mCountDown = new CountDownLatch(2);
- DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
- KEY_COMPACT_ACTION_1, "", false);
- DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
- KEY_COMPACT_ACTION_2, "", false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
-
- assertThat(mCompactorUnderTest.mCompactActionSome,
- is(compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_1)));
- assertThat(mCompactorUnderTest.mCompactActionFull,
- is(compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_2)));
- }
-
- @Test
- public void compactThrottle_listensToDeviceConfigChanges() throws InterruptedException {
- mCompactorUnderTest.init();
-
- // When we override new reasonable throttle values after init...
- mCountDown = new CountDownLatch(4);
- DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
- KEY_COMPACT_THROTTLE_1,
- Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1), false);
- DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
- KEY_COMPACT_THROTTLE_2,
- Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1), false);
- DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
- KEY_COMPACT_THROTTLE_3,
- Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1), false);
- DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
- KEY_COMPACT_THROTTLE_4,
- Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1), false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
-
- // Then those flags values are reflected in the compactor.
- assertThat(mCompactorUnderTest.mCompactThrottleSomeSome,
- is(AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1));
- assertThat(mCompactorUnderTest.mCompactThrottleSomeFull,
- is(AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1));
- assertThat(mCompactorUnderTest.mCompactThrottleFullSome,
- is(AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1));
- assertThat(mCompactorUnderTest.mCompactThrottleFullFull,
- is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1));
- }
-
- @Test
- public void compactThrottle_listensToDeviceConfigChangesBadValues()
- throws IOException, InterruptedException {
- mCompactorUnderTest.init();
-
- // When one of the throttles is overridden with a bad value...
- mCountDown = new CountDownLatch(1);
- DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
- KEY_COMPACT_THROTTLE_1, "foo", false);
- // Then all the throttles have the defaults set.
- assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
- assertThat(mCompactorUnderTest.mCompactThrottleSomeSome,
- is(AppCompactor.DEFAULT_COMPACT_THROTTLE_1));
- assertThat(mCompactorUnderTest.mCompactThrottleSomeFull,
- is(AppCompactor.DEFAULT_COMPACT_THROTTLE_2));
- assertThat(mCompactorUnderTest.mCompactThrottleFullSome,
- is(AppCompactor.DEFAULT_COMPACT_THROTTLE_3));
- assertThat(mCompactorUnderTest.mCompactThrottleFullFull,
- is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4));
- clearDeviceConfig();
-
- // Repeat for each of the throttle keys.
- mCountDown = new CountDownLatch(1);
- DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
- KEY_COMPACT_THROTTLE_2, "foo", false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
- assertThat(mCompactorUnderTest.mCompactThrottleSomeSome,
- is(AppCompactor.DEFAULT_COMPACT_THROTTLE_1));
- assertThat(mCompactorUnderTest.mCompactThrottleSomeFull,
- is(AppCompactor.DEFAULT_COMPACT_THROTTLE_2));
- assertThat(mCompactorUnderTest.mCompactThrottleFullSome,
- is(AppCompactor.DEFAULT_COMPACT_THROTTLE_3));
- assertThat(mCompactorUnderTest.mCompactThrottleFullFull,
- is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4));
- clearDeviceConfig();
-
- mCountDown = new CountDownLatch(1);
- DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
- KEY_COMPACT_THROTTLE_3, "foo", false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
- assertThat(mCompactorUnderTest.mCompactThrottleSomeSome,
- is(AppCompactor.DEFAULT_COMPACT_THROTTLE_1));
- assertThat(mCompactorUnderTest.mCompactThrottleSomeFull,
- is(AppCompactor.DEFAULT_COMPACT_THROTTLE_2));
- assertThat(mCompactorUnderTest.mCompactThrottleFullSome,
- is(AppCompactor.DEFAULT_COMPACT_THROTTLE_3));
- assertThat(mCompactorUnderTest.mCompactThrottleFullFull,
- is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4));
- clearDeviceConfig();
-
- mCountDown = new CountDownLatch(1);
- DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
- KEY_COMPACT_THROTTLE_4, "foo", false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
- assertThat(mCompactorUnderTest.mCompactThrottleSomeSome,
- is(AppCompactor.DEFAULT_COMPACT_THROTTLE_1));
- assertThat(mCompactorUnderTest.mCompactThrottleSomeFull,
- is(AppCompactor.DEFAULT_COMPACT_THROTTLE_2));
- assertThat(mCompactorUnderTest.mCompactThrottleFullSome,
- is(AppCompactor.DEFAULT_COMPACT_THROTTLE_3));
- assertThat(mCompactorUnderTest.mCompactThrottleFullFull,
- is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4));
- }
-
- @Test
- public void statsdSampleRate_listensToDeviceConfigChanges() throws InterruptedException {
- mCompactorUnderTest.init();
-
- // When we override mStatsdSampleRate with a reasonable values ...
- mCountDown = new CountDownLatch(1);
- DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
- KEY_COMPACT_STATSD_SAMPLE_RATE,
- Float.toString(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f), false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
-
- // Then that override is reflected in the compactor.
- assertThat(mCompactorUnderTest.mStatsdSampleRate,
- is(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f));
- }
-
- @Test
- public void statsdSanokeRate_listensToDeviceConfigChangesBadValues()
- throws InterruptedException {
- mCompactorUnderTest.init();
-
- // When we override mStatsdSampleRate with a reasonable values ...
- mCountDown = new CountDownLatch(1);
- DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
- KEY_COMPACT_STATSD_SAMPLE_RATE, "foo", false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
-
- // Then that override is reflected in the compactor.
- assertThat(mCompactorUnderTest.mStatsdSampleRate,
- is(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE));
- }
-
- @Test
- public void statsdSanokeRate_listensToDeviceConfigChangesOutOfRangeValues()
- throws InterruptedException {
- mCompactorUnderTest.init();
-
- // When we override mStatsdSampleRate with an value outside of [0..1]...
- mCountDown = new CountDownLatch(1);
- DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
- KEY_COMPACT_STATSD_SAMPLE_RATE,
- Float.toString(-1.0f), false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
-
- // Then the values is capped in the range.
- assertThat(mCompactorUnderTest.mStatsdSampleRate, is(0.0f));
-
- mCountDown = new CountDownLatch(1);
- DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
- KEY_COMPACT_STATSD_SAMPLE_RATE,
- Float.toString(1.01f), false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
-
- // Then the values is capped in the range.
- assertThat(mCompactorUnderTest.mStatsdSampleRate, is(1.0f));
- }
-
- private class TestInjector extends Injector {
- @Override
- public AppOpsService getAppOpsService(File file, Handler handler) {
- return mAppOpsService;
- }
-
- @Override
- public Handler getUiHandler(ActivityManagerService service) {
- return mHandler;
- }
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index de782a5..4293247 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -4167,7 +4167,7 @@
assertTrue(dpm.isResetPasswordTokenActive(admin1));
// test reset password with token
- when(getServices().lockPatternUtils.setLockCredentialWithToken(eq(password),
+ when(getServices().lockPatternUtils.setLockCredentialWithToken(eq(password.getBytes()),
eq(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD),
eq(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC), eq(handle), eq(token),
eq(UserHandle.USER_SYSTEM)))
@@ -5214,7 +5214,7 @@
.thenReturn(DpmMockContext.CALLER_USER_HANDLE);
dpms.mUserPasswordMetrics.put(
DpmMockContext.CALLER_USER_HANDLE,
- PasswordMetrics.computeForPassword("asdf"));
+ PasswordMetrics.computeForPassword("asdf".getBytes()));
assertEquals(PASSWORD_COMPLEXITY_MEDIUM, dpm.getPasswordComplexity());
}
@@ -5231,10 +5231,10 @@
dpms.mUserPasswordMetrics.put(
DpmMockContext.CALLER_USER_HANDLE,
- PasswordMetrics.computeForPassword("asdf"));
+ PasswordMetrics.computeForPassword("asdf".getBytes()));
dpms.mUserPasswordMetrics.put(
parentUser.id,
- PasswordMetrics.computeForPassword("parentUser"));
+ PasswordMetrics.computeForPassword("parentUser".getBytes()));
assertEquals(PASSWORD_COMPLEXITY_HIGH, dpm.getPasswordComplexity());
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/CachedSyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/CachedSyntheticPasswordTests.java
index d2caa0a..94d21dd 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/CachedSyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/CachedSyntheticPasswordTests.java
@@ -18,24 +18,22 @@
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
-import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
import static com.android.server.testutils.TestUtils.assertExpectException;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.when;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.os.RemoteException;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.VerifyCredentialResponse;
-import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationResult;
-
-import java.util.ArrayList;
import org.mockito.ArgumentCaptor;
+import java.util.ArrayList;
+
/**
* Run the synthetic password tests with caching enabled.
*
@@ -56,10 +54,10 @@
}
public void testSyntheticPasswordClearCredentialUntrusted() throws RemoteException {
- final String PASSWORD = "testSyntheticPasswordClearCredential-password";
- final String NEWPASSWORD = "testSyntheticPasswordClearCredential-newpassword";
+ final byte[] password = "testSyntheticPasswordClearCredential-password".getBytes();
+ final byte[] newPassword = "testSyntheticPasswordClearCredential-newpassword".getBytes();
- initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
+ initializeCredentialUnderSP(password, PRIMARY_USER_ID);
long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
// clear password
mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, null,
@@ -67,45 +65,46 @@
assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
// set a new password
- mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
+ mService.setLockCredential(newPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
- assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
- .getResponseCode());
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(newPassword,
+ LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ .getResponseCode());
assertNotEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
}
public void testSyntheticPasswordChangeCredentialUntrusted() throws RemoteException {
- final String PASSWORD = "testSyntheticPasswordClearCredential-password";
- final String NEWPASSWORD = "testSyntheticPasswordClearCredential-newpassword";
+ final byte[] password = "testSyntheticPasswordClearCredential-password".getBytes();
+ final byte[] newPassword = "testSyntheticPasswordClearCredential-newpassword".getBytes();
- initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
+ initializeCredentialUnderSP(password, PRIMARY_USER_ID);
long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
// Untrusted change password
- mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
+ mService.setLockCredential(newPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
assertNotEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
assertNotEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
// Verify the password
- assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
- .getResponseCode());
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(newPassword,
+ LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID).getResponseCode());
}
public void testUntrustedCredentialChangeMaintainsAuthSecret() throws RemoteException {
- final String PASSWORD = "testUntrustedCredentialChangeMaintainsAuthSecret-password";
- final String NEWPASSWORD = "testUntrustedCredentialChangeMaintainsAuthSecret-newpassword";
+ final byte[] password =
+ "testUntrustedCredentialChangeMaintainsAuthSecret-password".getBytes();
+ final byte[] newPassword =
+ "testUntrustedCredentialChangeMaintainsAuthSecret-newpassword".getBytes();
- initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
+ initializeCredentialUnderSP(password, PRIMARY_USER_ID);
// Untrusted change password
- mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
+ mService.setLockCredential(newPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
// Verify the password
- assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
- .getResponseCode());
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(newPassword,
+ LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ .getResponseCode());
// Ensure the same secret was passed each time
ArgumentCaptor<ArrayList<Byte>> secret = ArgumentCaptor.forClass(ArrayList.class);
@@ -114,27 +113,29 @@
}
public void testUntrustedCredentialChangeBlockedIfSpNotCached() throws RemoteException {
- final String PASSWORD = "testUntrustedCredentialChangeBlockedIfSpNotCached-password";
- final String NEWPASSWORD = "testUntrustedCredentialChangeBlockedIfSpNotCached-newpassword";
+ final byte[] password =
+ "testUntrustedCredentialChangeBlockedIfSpNotCached-password".getBytes();
+ final byte[] newPassword =
+ "testUntrustedCredentialChangeBlockedIfSpNotCached-newpassword".getBytes();
// Disable caching for this test
enableSpCaching(false);
- initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
+ initializeCredentialUnderSP(password, PRIMARY_USER_ID);
long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
// Untrusted change password
assertExpectException(IllegalStateException.class, /* messageRegex= */ null,
- () -> mService.setLockCredential(
- NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
- null, PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID));
+ () -> mService.setLockCredential(newPassword,
+ LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
+ PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID));
assertEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
// Verify the new password doesn't work but the old one still does
- assertEquals(VerifyCredentialResponse.RESPONSE_ERROR, mService.verifyCredential(
- NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ assertEquals(VerifyCredentialResponse.RESPONSE_ERROR, mService.verifyCredential(newPassword,
+ LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
.getResponseCode());
- assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(password,
+ LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
.getResponseCode());
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
index bf71318..f4632db 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
@@ -133,12 +133,12 @@
}
@Override
- protected void tieProfileLockToParent(int userId, String password) {
- mStorage.writeChildProfileLock(userId, password.getBytes());
+ protected void tieProfileLockToParent(int userId, byte[] password) {
+ mStorage.writeChildProfileLock(userId, password);
}
@Override
- protected String getDecryptedPasswordForTiedProfile(int userId) throws FileNotFoundException,
+ protected byte[] getDecryptedPasswordForTiedProfile(int userId) throws FileNotFoundException,
KeyPermanentlyInvalidatedException {
byte[] storedData = mStorage.readChildProfileLock(userId);
if (storedData == null) {
@@ -151,7 +151,7 @@
} catch (RemoteException e) {
// shouldn't happen.
}
- return new String(storedData);
+ return storedData;
}
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
index 5124803..6e0ba3c 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
@@ -84,8 +84,8 @@
initializeStorageWithCredential(PRIMARY_USER_ID, "password", CREDENTIAL_TYPE_PASSWORD, sid);
try {
- mService.setLockCredential("newpwd", CREDENTIAL_TYPE_PASSWORD, "badpwd",
- PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
+ mService.setLockCredential("newpwd".getBytes(), CREDENTIAL_TYPE_PASSWORD,
+ "badpwd".getBytes(), PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
fail("Did not fail when enrolling using incorrect credential");
} catch (RemoteException expected) {
assertTrue(expected.getMessage().equals(FAILED_MESSAGE));
@@ -96,7 +96,7 @@
public void testClearPasswordPrimaryUser() throws RemoteException {
final String PASSWORD = "password";
initializeStorageWithCredential(PRIMARY_USER_ID, PASSWORD, CREDENTIAL_TYPE_PASSWORD, 1234);
- mService.setLockCredential(null, CREDENTIAL_TYPE_NONE, PASSWORD,
+ mService.setLockCredential(null, CREDENTIAL_TYPE_NONE, PASSWORD.getBytes(),
PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID);
assertFalse(mService.havePassword(PRIMARY_USER_ID));
assertFalse(mService.havePattern(PRIMARY_USER_ID));
@@ -106,7 +106,8 @@
public void testManagedProfileUnifiedChallenge() throws RemoteException {
final String firstUnifiedPassword = "testManagedProfileUnifiedChallenge-pwd-1";
final String secondUnifiedPassword = "testManagedProfileUnifiedChallenge-pwd-2";
- mService.setLockCredential(firstUnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
+ mService.setLockCredential(firstUnifiedPassword.getBytes(),
+ LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
null, PASSWORD_QUALITY_COMPLEX, PRIMARY_USER_ID);
mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
final long primarySid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
@@ -125,8 +126,8 @@
mGateKeeperService.clearAuthToken(TURNED_OFF_PROFILE_USER_ID);
// verify credential
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- firstUnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
- .getResponseCode());
+ firstUnifiedPassword.getBytes(), LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0,
+ PRIMARY_USER_ID).getResponseCode());
// Verify that we have a new auth token for the profile
assertNotNull(mGateKeeperService.getAuthToken(MANAGED_PROFILE_USER_ID));
@@ -141,15 +142,16 @@
*/
mStorageManager.setIgnoreBadUnlock(true);
// Change primary password and verify that profile SID remains
- mService.setLockCredential(secondUnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
- firstUnifiedPassword, PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
+ mService.setLockCredential(secondUnifiedPassword.getBytes(),
+ LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
+ firstUnifiedPassword.getBytes(), PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
mStorageManager.setIgnoreBadUnlock(false);
assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID));
assertNull(mGateKeeperService.getAuthToken(TURNED_OFF_PROFILE_USER_ID));
// Clear unified challenge
mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE,
- secondUnifiedPassword, PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID);
+ secondUnifiedPassword.getBytes(), PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID);
assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
assertEquals(0, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID));
assertEquals(0, mGateKeeperService.getSecureUserId(TURNED_OFF_PROFILE_USER_ID));
@@ -158,14 +160,16 @@
public void testManagedProfileSeparateChallenge() throws RemoteException {
final String primaryPassword = "testManagedProfileSeparateChallenge-primary";
final String profilePassword = "testManagedProfileSeparateChallenge-profile";
- mService.setLockCredential(primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
+ mService.setLockCredential(primaryPassword.getBytes(),
+ LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
PASSWORD_QUALITY_COMPLEX, PRIMARY_USER_ID);
/* Currently in LockSettingsService.setLockCredential, unlockUser() is called with the new
* credential as part of verifyCredential() before the new credential is committed in
* StorageManager. So we relax the check in our mock StorageManager to allow that.
*/
mStorageManager.setIgnoreBadUnlock(true);
- mService.setLockCredential(profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
+ mService.setLockCredential(profilePassword.getBytes(),
+ LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
PASSWORD_QUALITY_COMPLEX, MANAGED_PROFILE_USER_ID);
mStorageManager.setIgnoreBadUnlock(false);
@@ -179,31 +183,32 @@
mGateKeeperService.clearAuthToken(MANAGED_PROFILE_USER_ID);
// verify primary credential
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
- .getResponseCode());
+ primaryPassword.getBytes(), LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0,
+ PRIMARY_USER_ID).getResponseCode());
assertNull(mGateKeeperService.getAuthToken(MANAGED_PROFILE_USER_ID));
// verify profile credential
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0,
+ profilePassword.getBytes(), LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0,
MANAGED_PROFILE_USER_ID).getResponseCode());
assertNotNull(mGateKeeperService.getAuthToken(MANAGED_PROFILE_USER_ID));
assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID));
// Change primary credential and make sure we don't affect profile
mStorageManager.setIgnoreBadUnlock(true);
- mService.setLockCredential("pwd", LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
- primaryPassword, PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
+ mService.setLockCredential("pwd".getBytes(), LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
+ primaryPassword.getBytes(), PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
mStorageManager.setIgnoreBadUnlock(false);
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0,
+ profilePassword.getBytes(), LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0,
MANAGED_PROFILE_USER_ID).getResponseCode());
assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID));
}
private void testCreateCredential(int userId, String credential, int type, int quality)
throws RemoteException {
- mService.setLockCredential(credential, type, null, quality, userId);
+ mService.setLockCredential(credential.getBytes(), type, null, quality,
+ userId);
assertVerifyCredentials(userId, credential, type, -1);
}
@@ -212,7 +217,8 @@
mHasSecureLockScreen = false;
try {
- mService.setLockCredential(credential, type, null, quality, userId);
+ mService.setLockCredential(credential.getBytes(), type, null, quality,
+ userId);
fail("An exception should have been thrown.");
} catch (UnsupportedOperationException e) {
// Success - the exception was expected.
@@ -226,15 +232,16 @@
String oldCredential, int oldType, int quality) throws RemoteException {
final long sid = 1234;
initializeStorageWithCredential(userId, oldCredential, oldType, sid);
- mService.setLockCredential(newCredential, newType, oldCredential, quality, userId);
+ mService.setLockCredential(newCredential.getBytes(), newType, oldCredential.getBytes(),
+ quality, userId);
assertVerifyCredentials(userId, newCredential, newType, sid);
}
private void assertVerifyCredentials(int userId, String credential, int type, long sid)
throws RemoteException{
final long challenge = 54321;
- VerifyCredentialResponse response = mService.verifyCredential(credential, type, challenge,
- userId);
+ VerifyCredentialResponse response = mService.verifyCredential(credential.getBytes(),
+ type, challenge, userId);
assertEquals(GateKeeperResponse.RESPONSE_OK, response.getResponseCode());
if (sid != -1) assertEquals(sid, mGateKeeperService.getSecureUserId(userId));
@@ -253,18 +260,19 @@
incorrectType = LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
}
// check for bad type
- assertEquals(GateKeeperResponse.RESPONSE_ERROR, mService.verifyCredential(credential,
- incorrectType, challenge, userId).getResponseCode());
+ assertEquals(GateKeeperResponse.RESPONSE_ERROR, mService.verifyCredential(
+ credential.getBytes(), incorrectType, challenge, userId).getResponseCode());
// check for bad credential
- assertEquals(GateKeeperResponse.RESPONSE_ERROR, mService.verifyCredential("0" + credential,
- type, challenge, userId).getResponseCode());
+ assertEquals(GateKeeperResponse.RESPONSE_ERROR, mService.verifyCredential(
+ ("0" + credential).getBytes(), type, challenge, userId).getResponseCode());
}
private void initializeStorageWithCredential(int userId, String credential, int type, long sid)
throws RemoteException {
+ byte[] credentialBytes = credential == null ? null : credential.getBytes();
byte[] oldHash = new VerifyHandle(credential.getBytes(), sid).toBytes();
if (mService.shouldMigrateToSyntheticPasswordLocked(userId)) {
- mService.initializeSyntheticPasswordLocked(oldHash, credential, type,
+ mService.initializeSyntheticPasswordLocked(oldHash, credentialBytes, type,
type == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD ? PASSWORD_QUALITY_ALPHABETIC
: PASSWORD_QUALITY_SOMETHING, userId);
} else {
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
index 929c3b5..fcfc6d2 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
@@ -85,23 +85,24 @@
public void testWrongPassword() throws Exception {
when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false);
when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true);
- when(mLockPatternUtils.checkPassword("1234", mUserId)).thenReturn(false);
+ when(mLockPatternUtils.checkPassword("1234".getBytes(), mUserId)).thenReturn(false);
assertEquals(-1, mCommand.exec(mBinder, in, out, err,
new String[] { "set-pin", "--old", "1234" },
mShellCallback, mResultReceiver));
- verify(mLockPatternUtils, never()).saveLockPassword(any(), any(), anyInt(), anyInt());
+ verify(mLockPatternUtils, never()).saveLockPassword(any(byte[].class), any(byte[].class),
+ anyInt(), anyInt());
}
@Test
public void testChangePin() throws Exception {
when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false);
when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true);
- when(mLockPatternUtils.checkPassword("1234", mUserId)).thenReturn(true);
+ when(mLockPatternUtils.checkPassword("1234".getBytes(), mUserId)).thenReturn(true);
assertEquals(0, mCommand.exec(new Binder(), in, out, err,
new String[] { "set-pin", "--old", "1234", "4321" },
mShellCallback, mResultReceiver));
- verify(mLockPatternUtils).saveLockPassword("4321", "1234", PASSWORD_QUALITY_NUMERIC,
- mUserId);
+ verify(mLockPatternUtils).saveLockPassword("4321".getBytes(), "1234".getBytes(),
+ PASSWORD_QUALITY_NUMERIC, mUserId);
}
@Test
@@ -118,12 +119,12 @@
public void testChangePassword() throws Exception {
when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false);
when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true);
- when(mLockPatternUtils.checkPassword("1234", mUserId)).thenReturn(true);
+ when(mLockPatternUtils.checkPassword("1234".getBytes(), mUserId)).thenReturn(true);
assertEquals(0, mCommand.exec(new Binder(), in, out, err,
new String[] { "set-password", "--old", "1234", "4321" },
mShellCallback, mResultReceiver));
- verify(mLockPatternUtils).saveLockPassword("4321", "1234", PASSWORD_QUALITY_ALPHABETIC,
- mUserId);
+ verify(mLockPatternUtils).saveLockPassword("4321".getBytes(), "1234".getBytes(),
+ PASSWORD_QUALITY_ALPHABETIC, mUserId);
}
@Test
@@ -144,7 +145,8 @@
assertEquals(0, mCommand.exec(new Binder(), in, out, err,
new String[] { "set-pattern", "--old", "1234", "4321" },
mShellCallback, mResultReceiver));
- verify(mLockPatternUtils).saveLockPattern(stringToPattern("4321"), "1234", mUserId);
+ verify(mLockPatternUtils).saveLockPattern(stringToPattern("4321"), "1234".getBytes(),
+ mUserId);
}
@Test
@@ -165,6 +167,6 @@
assertEquals(0, mCommand.exec(new Binder(), in, out, err,
new String[] { "clear", "--old", "1234" },
mShellCallback, mResultReceiver));
- verify(mLockPatternUtils).clearLock("1234", mUserId);
+ verify(mLockPatternUtils).clearLock("1234".getBytes(), mUserId);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java b/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java
index 6f68179..b9cb730 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java
@@ -93,9 +93,13 @@
}
@Override
- protected byte[] scrypt(String password, byte[] salt, int N, int r, int p, int outLen) {
+ protected byte[] scrypt(byte[] password, byte[] salt, int n, int r, int p, int outLen) {
try {
- PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 10, outLen * 8);
+ char[] passwordChars = new char[password.length];
+ for (int i = 0; i < password.length; i++) {
+ passwordChars[i] = (char) password[i];
+ }
+ PBEKeySpec spec = new PBEKeySpec(passwordChars, salt, 10, outLen * 8);
SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
return f.generateSecret(spec).getEncoded();
} catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
index 89e155e..e6e020d 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
@@ -65,22 +65,23 @@
public void testPasswordBasedSyntheticPassword() throws RemoteException {
final int USER_ID = 10;
- final String PASSWORD = "user-password";
- final String BADPASSWORD = "bad-password";
+ final byte[] password = "user-password".getBytes();
+ final byte[] badPassword = "bad-password".getBytes();
MockSyntheticPasswordManager manager = new MockSyntheticPasswordManager(mContext, mStorage,
mGateKeeperService, mUserManager);
AuthenticationToken authToken = manager.newSyntheticPasswordAndSid(mGateKeeperService, null,
null, USER_ID);
- long handle = manager.createPasswordBasedSyntheticPassword(mGateKeeperService, PASSWORD,
- LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, authToken, PASSWORD_QUALITY_ALPHABETIC,
- USER_ID);
+ long handle = manager.createPasswordBasedSyntheticPassword(mGateKeeperService,
+ password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, authToken,
+ PASSWORD_QUALITY_ALPHABETIC, USER_ID);
AuthenticationResult result = manager.unwrapPasswordBasedSyntheticPassword(
- mGateKeeperService, handle, PASSWORD, USER_ID, null);
- assertEquals(result.authToken.deriveKeyStorePassword(), authToken.deriveKeyStorePassword());
+ mGateKeeperService, handle, password, USER_ID, null);
+ assertArrayEquals(result.authToken.deriveKeyStorePassword(),
+ authToken.deriveKeyStorePassword());
result = manager.unwrapPasswordBasedSyntheticPassword(mGateKeeperService, handle,
- BADPASSWORD, USER_ID, null);
+ badPassword, USER_ID, null);
assertNull(result.authToken);
}
@@ -97,30 +98,30 @@
}
public void testPasswordMigration() throws RemoteException {
- final String PASSWORD = "testPasswordMigration-password";
+ final byte[] password = "testPasswordMigration-password".getBytes();
disableSyntheticPassword();
- mService.setLockCredential(PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
+ mService.setLockCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
final byte[] primaryStorageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
enableSyntheticPassword();
// Performs migration
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
.getResponseCode());
assertEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
assertTrue(hasSyntheticPassword(PRIMARY_USER_ID));
// SP-based verification
- assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(password,
+ LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
.getResponseCode());
assertArrayNotEquals(primaryStorageKey,
mStorageManager.getUserUnlockToken(PRIMARY_USER_ID));
}
- protected void initializeCredentialUnderSP(String password, int userId) throws RemoteException {
+ protected void initializeCredentialUnderSP(byte[] password, int userId) throws RemoteException {
enableSyntheticPassword();
int quality = password != null ? PASSWORD_QUALITY_ALPHABETIC
: PASSWORD_QUALITY_UNSPECIFIED;
@@ -130,62 +131,64 @@
}
public void testSyntheticPasswordChangeCredential() throws RemoteException {
- final String PASSWORD = "testSyntheticPasswordChangeCredential-password";
- final String NEWPASSWORD = "testSyntheticPasswordChangeCredential-newpassword";
+ final byte[] password = "testSyntheticPasswordChangeCredential-password".getBytes();
+ final byte[] newPassword = "testSyntheticPasswordChangeCredential-newpassword".getBytes();
- initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
+ initializeCredentialUnderSP(password, PRIMARY_USER_ID);
long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
- mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, PASSWORD,
+ mService.setLockCredential(newPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, password,
PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ newPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
.getResponseCode());
assertEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
}
public void testSyntheticPasswordVerifyCredential() throws RemoteException {
- final String PASSWORD = "testSyntheticPasswordVerifyCredential-password";
- final String BADPASSWORD = "testSyntheticPasswordVerifyCredential-badpassword";
+ final byte[] password = "testSyntheticPasswordVerifyCredential-password".getBytes();
+ final byte[] badPassword = "testSyntheticPasswordVerifyCredential-badpassword".getBytes();
- initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
+ initializeCredentialUnderSP(password, PRIMARY_USER_ID);
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
.getResponseCode());
assertEquals(VerifyCredentialResponse.RESPONSE_ERROR, mService.verifyCredential(
- BADPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
- .getResponseCode());
+ badPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ .getResponseCode());
}
public void testSyntheticPasswordClearCredential() throws RemoteException {
- final String PASSWORD = "testSyntheticPasswordClearCredential-password";
- final String NEWPASSWORD = "testSyntheticPasswordClearCredential-newpassword";
+ final byte[] password = "testSyntheticPasswordClearCredential-password".getBytes();
+ final byte[] badPassword = "testSyntheticPasswordClearCredential-newpassword".getBytes();
- initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
+ initializeCredentialUnderSP(password, PRIMARY_USER_ID);
long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
// clear password
- mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, PASSWORD,
+ mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, password,
PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID);
assertEquals(0 ,mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
// set a new password
- mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
+ mService.setLockCredential(badPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
- .getResponseCode());
+ badPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ .getResponseCode());
assertNotEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
}
public void testSyntheticPasswordChangeCredentialKeepsAuthSecret() throws RemoteException {
- final String PASSWORD = "testSyntheticPasswordChangeCredentialKeepsAuthSecret-password";
- final String NEWPASSWORD = "testSyntheticPasswordChangeCredentialKeepsAuthSecret-new";
+ final byte[] password =
+ "testSyntheticPasswordChangeCredentialKeepsAuthSecret-password".getBytes();
+ final byte[] badPassword =
+ "testSyntheticPasswordChangeCredentialKeepsAuthSecret-new".getBytes();
- initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
- mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, PASSWORD,
+ initializeCredentialUnderSP(password, PRIMARY_USER_ID);
+ mService.setLockCredential(badPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, password,
PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ badPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
.getResponseCode());
// Check the same secret was passed each time
@@ -195,24 +198,25 @@
}
public void testSyntheticPasswordVerifyPassesPrimaryUserAuthSecret() throws RemoteException {
- final String PASSWORD = "testSyntheticPasswordVerifyPassesPrimaryUserAuthSecret-password";
- final String NEWPASSWORD = "testSyntheticPasswordVerifyPassesPrimaryUserAuthSecret-new";
+ final byte[] password =
+ "testSyntheticPasswordVerifyPassesPrimaryUserAuthSecret-password".getBytes();
+ final byte[] newPassword =
+ "testSyntheticPasswordVerifyPassesPrimaryUserAuthSecret-new".getBytes();
- initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
+ initializeCredentialUnderSP(password, PRIMARY_USER_ID);
reset(mAuthSecretService);
- assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(password,
+ LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
.getResponseCode());
verify(mAuthSecretService).primaryUserCredential(any(ArrayList.class));
}
public void testSecondaryUserDoesNotPassAuthSecret() throws RemoteException {
- final String PASSWORD = "testSecondaryUserDoesNotPassAuthSecret-password";
- final String NEWPASSWORD = "testSecondaryUserDoesNotPassAuthSecret-new";
+ final byte[] password = "testSecondaryUserDoesNotPassAuthSecret-password".getBytes();
- initializeCredentialUnderSP(PASSWORD, SECONDARY_USER_ID);
+ initializeCredentialUnderSP(password, SECONDARY_USER_ID);
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, SECONDARY_USER_ID)
+ password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, SECONDARY_USER_ID)
.getResponseCode());
verify(mAuthSecretService, never()).primaryUserCredential(any(ArrayList.class));
}
@@ -228,8 +232,8 @@
}
public void testSyntheticPasswordAndCredentialDoesNotPassAuthSecret() throws RemoteException {
- final String PASSWORD = "passwordForASyntheticPassword";
- initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
+ final byte[] password = "passwordForASyntheticPassword".getBytes();
+ initializeCredentialUnderSP(password, PRIMARY_USER_ID);
reset(mAuthSecretService);
mService.onUnlockUser(PRIMARY_USER_ID);
@@ -238,9 +242,9 @@
}
public void testSyntheticPasswordButNoCredentialPassesAuthSecret() throws RemoteException {
- final String PASSWORD = "getASyntheticPassword";
- initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
- mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, PASSWORD,
+ final byte[] password = "getASyntheticPassword".getBytes();
+ initializeCredentialUnderSP(password, PRIMARY_USER_ID);
+ mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, password,
PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID);
reset(mAuthSecretService);
@@ -250,7 +254,7 @@
}
public void testManagedProfileUnifiedChallengeMigration() throws RemoteException {
- final String UnifiedPassword = "testManagedProfileUnifiedChallengeMigration-pwd";
+ final byte[] UnifiedPassword = "testManagedProfileUnifiedChallengeMigration-pwd".getBytes();
disableSyntheticPassword();
mService.setLockCredential(UnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
@@ -284,8 +288,10 @@
}
public void testManagedProfileSeparateChallengeMigration() throws RemoteException {
- final String primaryPassword = "testManagedProfileSeparateChallengeMigration-primary";
- final String profilePassword = "testManagedProfileSeparateChallengeMigration-profile";
+ final byte[] primaryPassword =
+ "testManagedProfileSeparateChallengeMigration-primary".getBytes();
+ final byte[] profilePassword =
+ "testManagedProfileSeparateChallengeMigration-profile".getBytes();
disableSyntheticPassword();
mService.setLockCredential(primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
@@ -326,92 +332,92 @@
}
public void testTokenBasedResetPassword() throws RemoteException {
- final String PASSWORD = "password";
- final String PATTERN = "123654";
- final String TOKEN = "some-high-entropy-secure-token";
- initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
+ final byte[] password = "password".getBytes();
+ final byte[] pattern = "123654".getBytes();
+ final byte[] token = "some-high-entropy-secure-token".getBytes();
+ initializeCredentialUnderSP(password, PRIMARY_USER_ID);
final byte[] storageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
- long handle = mLocalService.addEscrowToken(TOKEN.getBytes(), PRIMARY_USER_ID);
+ long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID);
assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
- mService.verifyCredential(PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0,
+ mService.verifyCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0,
PRIMARY_USER_ID).getResponseCode();
assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
- mLocalService.setLockCredentialWithToken(PATTERN, LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
- handle, TOKEN.getBytes(), PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID);
+ mLocalService.setLockCredentialWithToken(pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
+ handle, token, PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID);
// Verify DPM gets notified about new device lock
mService.mHandler.runWithScissors(() -> {}, 0 /*now*/); // Flush runnables on handler
- PasswordMetrics metric = PasswordMetrics.computeForPassword(PATTERN);
+ PasswordMetrics metric = PasswordMetrics.computeForPassword(pattern);
metric.quality = PASSWORD_QUALITY_SOMETHING;
verify(mDevicePolicyManager).setActivePasswordState(metric, PRIMARY_USER_ID);
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- PATTERN, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, 0, PRIMARY_USER_ID)
+ pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, 0, PRIMARY_USER_ID)
.getResponseCode());
assertArrayEquals(storageKey, mStorageManager.getUserUnlockToken(PRIMARY_USER_ID));
}
public void testTokenBasedClearPassword() throws RemoteException {
- final String PASSWORD = "password";
- final String PATTERN = "123654";
- final String TOKEN = "some-high-entropy-secure-token";
- initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
+ final byte[] password = "password".getBytes();
+ final byte[] pattern = "123654".getBytes();
+ final byte[] token = "some-high-entropy-secure-token".getBytes();
+ initializeCredentialUnderSP(password, PRIMARY_USER_ID);
final byte[] storageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
- long handle = mLocalService.addEscrowToken(TOKEN.getBytes(), PRIMARY_USER_ID);
+ long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID);
assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
- mService.verifyCredential(PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
+ mService.verifyCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
0, PRIMARY_USER_ID).getResponseCode();
assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
mLocalService.setLockCredentialWithToken(null, LockPatternUtils.CREDENTIAL_TYPE_NONE,
- handle, TOKEN.getBytes(), PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID);
- mLocalService.setLockCredentialWithToken(PATTERN, LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
- handle, TOKEN.getBytes(), PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID);
+ handle, token, PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID);
+ mLocalService.setLockCredentialWithToken(pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
+ handle, token, PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID);
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- PATTERN, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, 0, PRIMARY_USER_ID)
+ pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, 0, PRIMARY_USER_ID)
.getResponseCode());
assertArrayEquals(storageKey, mStorageManager.getUserUnlockToken(PRIMARY_USER_ID));
}
public void testTokenBasedResetPasswordAfterCredentialChanges() throws RemoteException {
- final String PASSWORD = "password";
- final String PATTERN = "123654";
- final String NEWPASSWORD = "password";
- final String TOKEN = "some-high-entropy-secure-token";
- initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
+ final byte[] password = "password".getBytes();
+ final byte[] pattern = "123654".getBytes();
+ final byte[] newPassword = "password".getBytes();
+ final byte[] token = "some-high-entropy-secure-token".getBytes();
+ initializeCredentialUnderSP(password, PRIMARY_USER_ID);
final byte[] storageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
- long handle = mLocalService.addEscrowToken(TOKEN.getBytes(), PRIMARY_USER_ID);
+ long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID);
assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
- mService.verifyCredential(PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
+ mService.verifyCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
0, PRIMARY_USER_ID).getResponseCode();
assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
- mService.setLockCredential(PATTERN, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, PASSWORD,
+ mService.setLockCredential(pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, password,
PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID);
- mLocalService.setLockCredentialWithToken(NEWPASSWORD,
- LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, handle, TOKEN.getBytes(),
+ mLocalService.setLockCredentialWithToken(newPassword,
+ LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, handle, token,
PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ newPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
.getResponseCode());
assertArrayEquals(storageKey, mStorageManager.getUserUnlockToken(PRIMARY_USER_ID));
}
public void testEscrowTokenActivatedImmediatelyIfNoUserPasswordNeedsMigration()
throws RemoteException {
- final String TOKEN = "some-high-entropy-secure-token";
+ final String token = "some-high-entropy-secure-token";
enableSyntheticPassword();
- long handle = mLocalService.addEscrowToken(TOKEN.getBytes(), PRIMARY_USER_ID);
+ long handle = mLocalService.addEscrowToken(token.getBytes(), PRIMARY_USER_ID);
assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
assertTrue(hasSyntheticPassword(PRIMARY_USER_ID));
@@ -419,9 +425,9 @@
public void testEscrowTokenActivatedImmediatelyIfNoUserPasswordNoMigration()
throws RemoteException {
- final String TOKEN = "some-high-entropy-secure-token";
+ final String token = "some-high-entropy-secure-token";
initializeCredentialUnderSP(null, PRIMARY_USER_ID);
- long handle = mLocalService.addEscrowToken(TOKEN.getBytes(), PRIMARY_USER_ID);
+ long handle = mLocalService.addEscrowToken(token.getBytes(), PRIMARY_USER_ID);
assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
assertTrue(hasSyntheticPassword(PRIMARY_USER_ID));
@@ -429,38 +435,38 @@
public void testEscrowTokenActivatedLaterWithUserPasswordNeedsMigration()
throws RemoteException {
- final String TOKEN = "some-high-entropy-secure-token";
- final String PASSWORD = "password";
+ final byte[] token = "some-high-entropy-secure-token".getBytes();
+ final byte[] password = "password".getBytes();
// Set up pre-SP user password
disableSyntheticPassword();
- mService.setLockCredential(PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
+ mService.setLockCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
enableSyntheticPassword();
- long handle = mLocalService.addEscrowToken(TOKEN.getBytes(), PRIMARY_USER_ID);
+ long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID);
// Token not activated immediately since user password exists
assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
// Activate token (password gets migrated to SP at the same time)
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
.getResponseCode());
// Verify token is activated
assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
}
public void testSetLockCredentialWithTokenFailsWithoutLockScreen() throws Exception {
- final String password = "password";
- final String pattern = "123654";
- final String token = "some-high-entropy-secure-token";
+ final byte[] password = "password".getBytes();
+ final byte[] pattern = "123654".getBytes();
+ final byte[] token = "some-high-entropy-secure-token".getBytes();
mHasSecureLockScreen = false;
enableSyntheticPassword();
- long handle = mLocalService.addEscrowToken(token.getBytes(), PRIMARY_USER_ID);
+ long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID);
assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
try {
mLocalService.setLockCredentialWithToken(password,
- LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, handle, token.getBytes(),
+ LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, handle, token,
PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
fail("An exception should have been thrown.");
} catch (UnsupportedOperationException e) {
@@ -470,7 +476,7 @@
try {
mLocalService.setLockCredentialWithToken(pattern,
- LockPatternUtils.CREDENTIAL_TYPE_PATTERN, handle, token.getBytes(),
+ LockPatternUtils.CREDENTIAL_TYPE_PATTERN, handle, token,
PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
fail("An exception should have been thrown.");
} catch (UnsupportedOperationException e) {
@@ -480,14 +486,14 @@
}
public void testgetHashFactorPrimaryUser() throws RemoteException {
- final String password = "password";
+ final byte[] password = "password".getBytes();
mService.setLockCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
final byte[] hashFactor = mService.getHashFactor(password, PRIMARY_USER_ID);
assertNotNull(hashFactor);
- mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, password,
- PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID);
+ mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE,
+ password, PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID);
final byte[] newHashFactor = mService.getHashFactor(null, PRIMARY_USER_ID);
assertNotNull(newHashFactor);
// Hash factor should never change after password change/removal
@@ -495,16 +501,16 @@
}
public void testgetHashFactorManagedProfileUnifiedChallenge() throws RemoteException {
- final String pattern = "1236";
- mService.setLockCredential(pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, null,
- PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID);
+ final byte[] pattern = "1236".getBytes();
+ mService.setLockCredential(pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
+ null, PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID);
mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
assertNotNull(mService.getHashFactor(null, MANAGED_PROFILE_USER_ID));
}
public void testgetHashFactorManagedProfileSeparateChallenge() throws RemoteException {
- final String primaryPassword = "primary";
- final String profilePassword = "profile";
+ final byte[] primaryPassword = "primary".getBytes();
+ final byte[] profilePassword = "profile".getBytes();
mService.setLockCredential(primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
mService.setLockCredential(profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
@@ -557,7 +563,7 @@
public void testGsiDisablesAuthSecret() throws RemoteException {
mGsiService.setIsGsiRunning(true);
- final String password = "testGsiDisablesAuthSecret-password";
+ final byte[] password = "testGsiDisablesAuthSecret-password".getBytes();
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
index c2d4846..a992dd1 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
@@ -139,7 +139,7 @@
mSnapshotListenersStorage,
TEST_USER_ID,
TEST_CREDENTIAL_TYPE,
- TEST_CREDENTIAL,
+ TEST_CREDENTIAL.getBytes(),
/*credentialUpdated=*/ false,
mPlatformKeyManager,
mTestOnlyInsecureCertificateHelper,
@@ -163,17 +163,17 @@
@Test
public void isPin_isTrueForNumericString() {
- assertTrue(KeySyncTask.isPin("3298432574398654376547"));
+ assertTrue(KeySyncTask.isPin("3298432574398654376547".getBytes()));
}
@Test
public void isPin_isFalseForStringContainingLetters() {
- assertFalse(KeySyncTask.isPin("398i54369548654"));
+ assertFalse(KeySyncTask.isPin("398i54369548654".getBytes()));
}
@Test
public void isPin_isFalseForStringContainingSymbols() {
- assertFalse(KeySyncTask.isPin("-3987543643"));
+ assertFalse(KeySyncTask.isPin("-3987543643".getBytes()));
}
@Test
@@ -182,8 +182,8 @@
byte[] salt = randomBytes(16);
assertArrayEquals(
- KeySyncTask.hashCredentialsBySaltedSha256(salt, credentials),
- KeySyncTask.hashCredentialsBySaltedSha256(salt, credentials));
+ KeySyncTask.hashCredentialsBySaltedSha256(salt, credentials.getBytes()),
+ KeySyncTask.hashCredentialsBySaltedSha256(salt, credentials.getBytes()));
}
@Test
@@ -192,8 +192,8 @@
assertFalse(
Arrays.equals(
- KeySyncTask.hashCredentialsBySaltedSha256(salt, "password1234"),
- KeySyncTask.hashCredentialsBySaltedSha256(salt, "password12345")));
+ KeySyncTask.hashCredentialsBySaltedSha256(salt, "password1234".getBytes()),
+ KeySyncTask.hashCredentialsBySaltedSha256(salt, "password12345".getBytes())));
}
@Test
@@ -202,34 +202,38 @@
assertFalse(
Arrays.equals(
- KeySyncTask.hashCredentialsBySaltedSha256(randomBytes(64), credentials),
- KeySyncTask.hashCredentialsBySaltedSha256(randomBytes(64), credentials)));
+ KeySyncTask.hashCredentialsBySaltedSha256(randomBytes(64),
+ credentials.getBytes()),
+ KeySyncTask.hashCredentialsBySaltedSha256(randomBytes(64),
+ credentials.getBytes())));
}
@Test
public void hashCredentialsBySaltedSha256_returnsDifferentHashEvenIfConcatIsSame() {
assertFalse(
Arrays.equals(
- KeySyncTask.hashCredentialsBySaltedSha256(utf8Bytes("123"), "4567"),
- KeySyncTask.hashCredentialsBySaltedSha256(utf8Bytes("1234"), "567")));
+ KeySyncTask.hashCredentialsBySaltedSha256(utf8Bytes("123"),
+ "4567".getBytes()),
+ KeySyncTask.hashCredentialsBySaltedSha256(utf8Bytes("1234"),
+ "567".getBytes())));
}
@Test
public void getUiFormat_returnsPinIfPin() {
assertEquals(UI_FORMAT_PIN,
- KeySyncTask.getUiFormat(CREDENTIAL_TYPE_PASSWORD, "1234"));
+ KeySyncTask.getUiFormat(CREDENTIAL_TYPE_PASSWORD, "1234".getBytes()));
}
@Test
public void getUiFormat_returnsPasswordIfPassword() {
assertEquals(UI_FORMAT_PASSWORD,
- KeySyncTask.getUiFormat(CREDENTIAL_TYPE_PASSWORD, "1234a"));
+ KeySyncTask.getUiFormat(CREDENTIAL_TYPE_PASSWORD, "1234a".getBytes()));
}
@Test
public void getUiFormat_returnsPatternIfPattern() {
assertEquals(UI_FORMAT_PATTERN,
- KeySyncTask.getUiFormat(CREDENTIAL_TYPE_PATTERN, "1234"));
+ KeySyncTask.getUiFormat(CREDENTIAL_TYPE_PATTERN, "1234".getBytes()));
}
@@ -291,7 +295,7 @@
mSnapshotListenersStorage,
TEST_USER_ID,
CREDENTIAL_TYPE_PASSWORD,
- /*credential=*/ password,
+ /*credential=*/ password.getBytes(),
/*credentialUpdated=*/ false,
mPlatformKeyManager,
mTestOnlyInsecureCertificateHelper,
@@ -332,7 +336,7 @@
mSnapshotListenersStorage,
TEST_USER_ID,
CREDENTIAL_TYPE_PATTERN,
- /*credential=*/ pattern,
+ /*credential=*/ pattern.getBytes(),
/*credentialUpdated=*/ false,
mPlatformKeyManager,
mTestOnlyInsecureCertificateHelper,
@@ -366,7 +370,7 @@
mSnapshotListenersStorage,
TEST_USER_ID,
CREDENTIAL_TYPE_PASSWORD,
- /*credential=*/ shortPassword,
+ /*credential=*/ shortPassword.getBytes(),
/*credentialUpdated=*/ false,
mPlatformKeyManager,
mTestOnlyInsecureCertificateHelper,
@@ -526,7 +530,7 @@
verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID);
byte[] lockScreenHash = KeySyncTask.hashCredentialsBySaltedSha256(
keyDerivationParams.getSalt(),
- TEST_CREDENTIAL);
+ TEST_CREDENTIAL.getBytes());
Long counterId = mRecoverableKeyStoreDb.getCounterId(TEST_USER_ID, TEST_RECOVERY_AGENT_UID);
assertThat(counterId).isNotNull();
byte[] recoveryKey = decryptThmEncryptedKey(
@@ -649,7 +653,7 @@
mSnapshotListenersStorage,
TEST_USER_ID,
CREDENTIAL_TYPE_PASSWORD,
- password,
+ password.getBytes(),
/*credentialUpdated=*/ false,
mPlatformKeyManager,
mTestOnlyInsecureCertificateHelper,
@@ -680,7 +684,7 @@
mSnapshotListenersStorage,
TEST_USER_ID,
CREDENTIAL_TYPE_PASSWORD,
- /*credential=*/ pin,
+ /*credential=*/ pin.getBytes(),
/*credentialUpdated=*/ false,
mPlatformKeyManager,
mTestOnlyInsecureCertificateHelper,
@@ -712,7 +716,7 @@
mSnapshotListenersStorage,
TEST_USER_ID,
CREDENTIAL_TYPE_PATTERN,
- "12345",
+ "12345".getBytes(),
/*credentialUpdated=*/ false,
mPlatformKeyManager,
mTestOnlyInsecureCertificateHelper,
@@ -796,7 +800,7 @@
mSnapshotListenersStorage,
TEST_USER_ID,
/*credentialType=*/ 3,
- "12345",
+ "12345".getBytes(),
/*credentialUpdated=*/ false,
mPlatformKeyManager,
mTestOnlyInsecureCertificateHelper,
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelperTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelperTest.java
index 9b4c3be..6921bb2 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelperTest.java
@@ -27,30 +27,30 @@
@Test
public void testDoesCredentailSupportInsecureMode_forNonWhitelistedPassword() throws Exception {
assertThat(mHelper.doesCredentialSupportInsecureMode(
- LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, "secret12345")).isFalse();
+ LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, "secret12345".getBytes())).isFalse();
assertThat(mHelper.doesCredentialSupportInsecureMode(
- LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, "1234")).isFalse();
+ LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, "1234".getBytes())).isFalse();
}
@Test
public void testDoesCredentailSupportInsecureMode_forWhitelistedPassword() throws Exception {
assertThat(mHelper.doesCredentialSupportInsecureMode(
LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
- TrustedRootCertificates.INSECURE_PASSWORD_PREFIX)).isTrue();
+ TrustedRootCertificates.INSECURE_PASSWORD_PREFIX.getBytes())).isTrue();
assertThat(mHelper.doesCredentialSupportInsecureMode(
LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
- TrustedRootCertificates.INSECURE_PASSWORD_PREFIX + "12")).isTrue();
+ (TrustedRootCertificates.INSECURE_PASSWORD_PREFIX + "12").getBytes())).isTrue();
}
@Test
public void testDoesCredentailSupportInsecureMode_Pattern() throws Exception {
assertThat(mHelper.doesCredentialSupportInsecureMode(
LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
- TrustedRootCertificates.INSECURE_PASSWORD_PREFIX)).isFalse();
+ TrustedRootCertificates.INSECURE_PASSWORD_PREFIX.getBytes())).isFalse();
assertThat(mHelper.doesCredentialSupportInsecureMode(
LockPatternUtils.CREDENTIAL_TYPE_NONE,
- TrustedRootCertificates.INSECURE_PASSWORD_PREFIX)).isFalse();
+ TrustedRootCertificates.INSECURE_PASSWORD_PREFIX.getBytes())).isFalse();
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index 76beb8f..7e6b7da 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -6286,6 +6286,15 @@
mManager.getShareTargets(filter);
}
+ public void testHasShareTargets_permission() {
+ assertExpectException(SecurityException.class, "Missing permission", () ->
+ mManager.hasShareTargets(CALLING_PACKAGE_1));
+
+ // Has permission, now it should pass.
+ mCallerPermissions.add(permission.MANAGE_APP_PREDICTIONS);
+ mManager.hasShareTargets(CALLING_PACKAGE_1);
+ }
+
public void testDumpsys_crossProfile() {
prepareCrossProfileDataSet();
dumpsysOnLogcat("test1", /* force= */ true);
diff --git a/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java
index 1b106dd..5c6fe0f 100644
--- a/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java
@@ -16,34 +16,9 @@
package com.android.server.timezone;
-import com.android.timezone.distro.DistroVersion;
-import com.android.timezone.distro.StagedDistroOperation;
-import com.android.timezone.distro.TimeZoneDistro;
-import com.android.timezone.distro.installer.TimeZoneDistroInstaller;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import android.app.timezone.Callback;
-import android.app.timezone.DistroRulesVersion;
-import android.app.timezone.ICallback;
-import android.app.timezone.RulesManager;
-import android.app.timezone.RulesState;
-import android.os.ParcelFileDescriptor;
-
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.concurrent.Executor;
-import javax.annotation.Nullable;
-
-import libcore.io.IoUtils;
-import libcore.timezone.TzDataSetVersion;
-
import static com.android.server.timezone.RulesManagerService.REQUIRED_QUERY_PERMISSION;
import static com.android.server.timezone.RulesManagerService.REQUIRED_UPDATER_PERMISSION;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -61,11 +36,43 @@
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
+import android.app.timezone.Callback;
+import android.app.timezone.DistroRulesVersion;
+import android.app.timezone.ICallback;
+import android.app.timezone.RulesManager;
+import android.app.timezone.RulesState;
+import android.os.ParcelFileDescriptor;
+
+import com.android.timezone.distro.DistroVersion;
+import com.android.timezone.distro.StagedDistroOperation;
+import com.android.timezone.distro.TimeZoneDistro;
+import com.android.timezone.distro.installer.TimeZoneDistroInstaller;
+
+import libcore.io.IoUtils;
+import libcore.timezone.TzDataSetVersion;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.concurrent.Executor;
+
+import javax.annotation.Nullable;
+
/**
* White box interaction / unit testing of the {@link RulesManagerService}.
*/
public class RulesManagerServiceTest {
+ private static final int CURRENT_FORMAT_MAJOR_VERSION =
+ TzDataSetVersion.currentFormatMajorVersion();
+ private static final int CURRENT_FORMAT_MINOR_VERSION =
+ TzDataSetVersion.currentFormatMinorVersion();
+
private RulesManagerService mRulesManagerService;
private FakeExecutor mFakeExecutor;
@@ -116,8 +123,8 @@
}
@Test
- public void getRulesState_systemRulesError() throws Exception {
- configureDeviceCannotReadSystemRulesVersion();
+ public void getRulesState_baseVersionError() throws Exception {
+ configureDeviceCannotReadBaseVersion();
assertNull(mRulesManagerService.getRulesState());
}
@@ -126,18 +133,18 @@
public void getRulesState_stagedInstall() throws Exception {
configureCallerHasPermission();
- configureDeviceSystemRulesVersion("2016a");
+ configureDeviceBaseVersion("2016a");
DistroVersion stagedDistroVersion = new DistroVersion(
- TzDataSetVersion.currentFormatMajorVersion(),
- TzDataSetVersion.currentFormatMinorVersion() - 1,
+ CURRENT_FORMAT_MAJOR_VERSION,
+ CURRENT_FORMAT_MINOR_VERSION - 1,
"2016c",
- 3);
+ 3 /* revision */);
configureStagedInstall(stagedDistroVersion);
DistroVersion installedDistroVersion = new DistroVersion(
- TzDataSetVersion.currentFormatMajorVersion(),
- TzDataSetVersion.currentFormatMinorVersion() - 1,
+ CURRENT_FORMAT_MAJOR_VERSION,
+ CURRENT_FORMAT_MINOR_VERSION - 1,
"2016b",
4);
configureInstalledDistroVersion(installedDistroVersion);
@@ -158,13 +165,13 @@
public void getRulesState_nothingStaged() throws Exception {
configureCallerHasPermission();
- configureDeviceSystemRulesVersion("2016a");
+ configureDeviceBaseVersion("2016a");
configureNoStagedOperation();
DistroVersion installedDistroVersion = new DistroVersion(
- TzDataSetVersion.currentFormatMajorVersion(),
- TzDataSetVersion.currentFormatMinorVersion() - 1,
+ CURRENT_FORMAT_MAJOR_VERSION,
+ CURRENT_FORMAT_MINOR_VERSION - 1,
"2016b",
4);
configureInstalledDistroVersion(installedDistroVersion);
@@ -183,13 +190,13 @@
public void getRulesState_uninstallStaged() throws Exception {
configureCallerHasPermission();
- configureDeviceSystemRulesVersion("2016a");
+ configureDeviceBaseVersion("2016a");
configureStagedUninstall();
DistroVersion installedDistroVersion = new DistroVersion(
- TzDataSetVersion.currentFormatMajorVersion(),
- TzDataSetVersion.currentFormatMinorVersion() - 1,
+ CURRENT_FORMAT_MAJOR_VERSION,
+ CURRENT_FORMAT_MINOR_VERSION - 1,
"2016b",
4);
configureInstalledDistroVersion(installedDistroVersion);
@@ -208,8 +215,8 @@
public void getRulesState_installedRulesError() throws Exception {
configureCallerHasPermission();
- String systemRulesVersion = "2016a";
- configureDeviceSystemRulesVersion(systemRulesVersion);
+ String baseRulesVersion = "2016a";
+ configureDeviceBaseVersion(baseRulesVersion);
configureStagedUninstall();
configureDeviceCannotReadInstalledDistroVersion();
@@ -226,14 +233,14 @@
public void getRulesState_stagedRulesError() throws Exception {
configureCallerHasPermission();
- String systemRulesVersion = "2016a";
- configureDeviceSystemRulesVersion(systemRulesVersion);
+ String baseRulesVersion = "2016a";
+ configureDeviceBaseVersion(baseRulesVersion);
configureDeviceCannotReadStagedDistroOperation();
DistroVersion installedDistroVersion = new DistroVersion(
- TzDataSetVersion.currentFormatMajorVersion(),
- TzDataSetVersion.currentFormatMinorVersion() - 1,
+ CURRENT_FORMAT_MAJOR_VERSION,
+ CURRENT_FORMAT_MINOR_VERSION - 1,
"2016b",
4);
configureInstalledDistroVersion(installedDistroVersion);
@@ -252,13 +259,13 @@
public void getRulesState_noInstalledRules() throws Exception {
configureCallerHasPermission();
- String systemRulesVersion = "2016a";
- configureDeviceSystemRulesVersion(systemRulesVersion);
+ String baseRulesVersion = "2016a";
+ configureDeviceBaseVersion(baseRulesVersion);
configureNoStagedOperation();
configureInstalledDistroVersion(null);
RulesState expectedRuleState = new RulesState(
- systemRulesVersion, RulesManagerService.DISTRO_FORMAT_VERSION_SUPPORTED,
+ baseRulesVersion, RulesManagerService.DISTRO_FORMAT_VERSION_SUPPORTED,
false /* operationInProgress */,
RulesState.STAGED_OPERATION_NONE, null /* stagedDistroRulesVersion */,
RulesState.DISTRO_STATUS_NONE, null /* installedDistroRulesVersion */);
@@ -269,15 +276,15 @@
public void getRulesState_operationInProgress() throws Exception {
configureCallerHasPermission();
- String systemRulesVersion = "2016a";
+ String baseRulesVersion = "2016a";
String installedRulesVersion = "2016b";
int revision = 3;
- configureDeviceSystemRulesVersion(systemRulesVersion);
+ configureDeviceBaseVersion(baseRulesVersion);
DistroVersion installedDistroVersion = new DistroVersion(
- TzDataSetVersion.currentFormatMajorVersion(),
- TzDataSetVersion.currentFormatMinorVersion() - 1,
+ CURRENT_FORMAT_MAJOR_VERSION,
+ CURRENT_FORMAT_MINOR_VERSION - 1,
installedRulesVersion,
revision);
configureInstalledDistroVersion(installedDistroVersion);
@@ -297,7 +304,7 @@
DistroRulesVersion expectedInstalledDistroRulesVersion =
new DistroRulesVersion(installedRulesVersion, revision);
RulesState expectedRuleState = new RulesState(
- systemRulesVersion, RulesManagerService.DISTRO_FORMAT_VERSION_SUPPORTED,
+ baseRulesVersion, RulesManagerService.DISTRO_FORMAT_VERSION_SUPPORTED,
true /* operationInProgress */,
RulesState.STAGED_OPERATION_UNKNOWN, null /* stagedDistroRulesVersion */,
RulesState.DISTRO_STATUS_INSTALLED, expectedInstalledDistroRulesVersion);
@@ -858,11 +865,20 @@
.thenReturn(true);
// Set up the mocks to return (arbitrary) information about the current device state.
- when(mMockTimeZoneDistroInstaller.getSystemRulesVersion()).thenReturn("2017a");
- when(mMockTimeZoneDistroInstaller.getInstalledDistroVersion()).thenReturn(
- new DistroVersion(2, 3, "2017b", 4));
+ TzDataSetVersion baseVersion = new TzDataSetVersion(
+ CURRENT_FORMAT_MAJOR_VERSION, CURRENT_FORMAT_MINOR_VERSION, "2017a",
+ 1 /* revision */);
+ when(mMockTimeZoneDistroInstaller.readBaseVersion()).thenReturn(baseVersion);
+ DistroVersion installedDistroVersion = new DistroVersion(
+ CURRENT_FORMAT_MAJOR_VERSION, CURRENT_FORMAT_MINOR_VERSION, "2017b",
+ 4 /* revision */);
+ when(mMockTimeZoneDistroInstaller.getInstalledDistroVersion())
+ .thenReturn(installedDistroVersion);
+ DistroVersion stagedDistroVersion = new DistroVersion(
+ CURRENT_FORMAT_MAJOR_VERSION, CURRENT_FORMAT_MINOR_VERSION, "2017c",
+ 7 /* revision */);
when(mMockTimeZoneDistroInstaller.getStagedDistroOperation()).thenReturn(
- StagedDistroOperation.install(new DistroVersion(5, 6, "2017c", 7)));
+ StagedDistroOperation.install(stagedDistroVersion));
// Do the dump call.
String dumpedOutput = doDumpCallAndCapture(rulesManagerService, args);
@@ -973,8 +989,11 @@
return new CheckToken(1, new PackageVersions(1, 1));
}
- private void configureDeviceSystemRulesVersion(String systemRulesVersion) throws Exception {
- when(mMockTimeZoneDistroInstaller.getSystemRulesVersion()).thenReturn(systemRulesVersion);
+ private void configureDeviceBaseVersion(String baseRulesVersion) throws Exception {
+ TzDataSetVersion tzDataSetVersion = new TzDataSetVersion(
+ CURRENT_FORMAT_MAJOR_VERSION, CURRENT_FORMAT_MINOR_VERSION, baseRulesVersion,
+ 1 /* revision */);
+ when(mMockTimeZoneDistroInstaller.readBaseVersion()).thenReturn(tzDataSetVersion);
}
private void configureInstalledDistroVersion(@Nullable DistroVersion installedDistroVersion)
@@ -1002,8 +1021,8 @@
.thenThrow(new IOException("Simulated failure"));
}
- private void configureDeviceCannotReadSystemRulesVersion() throws Exception {
- when(mMockTimeZoneDistroInstaller.getSystemRulesVersion())
+ private void configureDeviceCannotReadBaseVersion() throws Exception {
+ when(mMockTimeZoneDistroInstaller.readBaseVersion())
.thenThrow(new IOException("Simulated failure"));
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index a1db3e8..1319bad 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -406,7 +406,7 @@
}
@Test
- public void testFixedScreenConfigurationWhenMovingToDisplay() {
+ public void testSizeCompatMode_FixedScreenConfigurationWhenMovingToDisplay() {
// Initialize different bounds on a new display.
final ActivityDisplay newDisplay = addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP);
newDisplay.setBounds(0, 0, 1000, 2000);
@@ -431,7 +431,7 @@
}
@Test
- public void testFixedScreenBoundsWhenDisplaySizeChanged() {
+ public void testSizeCompatMode_FixedScreenBoundsWhenDisplaySizeChanged() {
when(mActivity.mAppWindowToken.getOrientationIgnoreVisibility()).thenReturn(
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
mTask.getWindowConfiguration().setAppBounds(mStack.getDisplay().getBounds());
@@ -446,4 +446,28 @@
assertEquals(originalBounds, mActivity.getBounds());
}
+
+ @Test
+ public void testSizeCompatMode_FixedScreenLayoutSizeBits() {
+ final int fixedScreenLayout = Configuration.SCREENLAYOUT_LONG_NO
+ | Configuration.SCREENLAYOUT_SIZE_NORMAL;
+ mTask.getConfiguration().screenLayout = fixedScreenLayout
+ | Configuration.SCREENLAYOUT_LAYOUTDIR_LTR;
+ mTask.getWindowConfiguration().setAppBounds(mStack.getDisplay().getBounds());
+ mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+ mActivity.info.maxAspectRatio = 1.5f;
+ ensureActivityConfiguration();
+
+ // The initial configuration should inherit from parent.
+ assertEquals(mTask.getConfiguration().screenLayout,
+ mActivity.getConfiguration().screenLayout);
+
+ mTask.getConfiguration().screenLayout = Configuration.SCREENLAYOUT_LAYOUTDIR_RTL
+ | Configuration.SCREENLAYOUT_LONG_YES | Configuration.SCREENLAYOUT_SIZE_LARGE;
+ mActivity.onConfigurationChanged(mTask.getConfiguration());
+
+ // The size and aspect ratio bits don't change, but the layout direction should be updated.
+ assertEquals(fixedScreenLayout | Configuration.SCREENLAYOUT_LAYOUTDIR_RTL,
+ mActivity.getConfiguration().screenLayout);
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
index e007c86..a98a604 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
@@ -29,7 +29,6 @@
import android.platform.test.annotations.Presubmit;
import android.view.SurfaceControl;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import com.android.server.wm.WindowTestUtils.TestAppWindowToken;
@@ -49,7 +48,6 @@
*/
@SmallTest
@Presubmit
-@FlakyTest(bugId = 124357362)
public class AppWindowTokenAnimationTests extends WindowTestsBase {
private TestAppWindowToken mToken;
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 69f7ced..b26aa05 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -68,7 +68,6 @@
import android.view.ViewRootImpl;
import android.view.test.InsetsModeSession;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
@@ -97,7 +96,6 @@
public class DisplayContentTests extends WindowTestsBase {
@Test
- @FlakyTest(detail = "Promote to presubmit when shown to be stable.")
public void testForAllWindows() {
final WindowState exitingAppWindow = createWindow(null, TYPE_BASE_APPLICATION,
mDisplayContent, "exiting app");
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index 8349ac7f..07dd93c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -25,20 +25,27 @@
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM;
import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.graphics.PixelFormat;
import android.platform.test.annotations.Presubmit;
+import android.view.Surface;
import android.view.WindowManager;
import androidx.test.filters.SmallTest;
@@ -196,4 +203,33 @@
DisplayPolicy.updateLightNavigationBarLw(0, opaqueDarkNavBar,
opaqueDarkNavBar, imeDrawLightNavBar, imeDrawLightNavBar));
}
+
+ @Test
+ public void testShouldRotateSeamlessly() {
+ final DisplayPolicy policy = mDisplayContent.getDisplayPolicy();
+ final WindowManager.LayoutParams attrs = mAppWindow.mAttrs;
+ attrs.x = attrs.y = 0;
+ attrs.height = attrs.width = WindowManager.LayoutParams.MATCH_PARENT;
+ attrs.rotationAnimation = ROTATION_ANIMATION_SEAMLESS;
+ final DisplayRotation displayRotation = mock(DisplayRotation.class);
+ doReturn(Surface.ROTATION_180).when(displayRotation).getUpsideDownRotation();
+
+ synchronized (mWm.mGlobalLock) {
+ policy.focusChangedLw(null /* lastFocus */, mAppWindow);
+ policy.applyPostLayoutPolicyLw(
+ mAppWindow, attrs, null /* attached */, null /* imeTarget */);
+ spyOn(policy);
+ doReturn(true).when(policy).navigationBarCanMove();
+ // The focused fullscreen opaque window without override bounds should be able to be
+ // rotated seamlessly.
+ assertTrue(policy.shouldRotateSeamlessly(
+ displayRotation, Surface.ROTATION_0, Surface.ROTATION_90));
+
+ spyOn(mAppWindow.mAppToken);
+ doReturn(false).when(mAppWindow.mAppToken).matchParentBounds();
+ // No seamless rotation if the window may be positioned with offset after rotation.
+ assertFalse(policy.shouldRotateSeamlessly(
+ displayRotation, Surface.ROTATION_0, Surface.ROTATION_90));
+ }
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 3e025f6..fc1eb1c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -30,7 +30,6 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -96,6 +95,7 @@
private TestActivityTaskManagerService mTestService;
private ActivityDisplay mDisplay;
private ActivityDisplay mOtherDisplay;
+ private ActivityDisplay mSingleTaskDisplay;
private ActivityStack mStack;
private ActivityStack mHomeStack;
private TestTaskPersister mTaskPersister;
@@ -547,6 +547,41 @@
assertTrimmed(mTasks.get(0), mTasks.get(1));
}
+ /**
+ * Tests that tasks on singleTaskDisplay are not visible and not trimmed/removed.
+ */
+ @Test
+ public void testVisibleTasks_singleTaskDisplay() {
+ mRecentTasks.setOnlyTestVisibleRange();
+ mRecentTasks.setParameters(-1 /* min */, 3 /* max */, -1 /* ms */);
+
+ ActivityStack singleTaskStack = mSingleTaskDisplay.createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+ TaskRecord excludedTask1 = createTaskBuilder(".ExcludedTask1")
+ .setStack(singleTaskStack)
+ .build();
+
+ assertFalse("Tasks on singleTaskDisplay should not be visible recents",
+ mRecentTasks.isVisibleRecentTask(excludedTask1));
+
+ mRecentTasks.add(excludedTask1);
+
+ // Add N+1 visible tasks.
+ mRecentTasks.add(mTasks.get(0));
+ mRecentTasks.add(mTasks.get(1));
+ mRecentTasks.add(mTasks.get(2));
+ mRecentTasks.add(mTasks.get(3));
+
+ // excludedTask is not trimmed.
+ assertTrimmed(mTasks.get(0));
+
+ mRecentTasks.removeAllVisibleTasks();
+
+ // Only visible tasks removed.
+ assertTrimmed(mTasks.get(0), mTasks.get(1), mTasks.get(2), mTasks.get(3));
+ }
+
@Test
public void testBackStackTasks_expectNoTrim() {
mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */);
@@ -879,8 +914,12 @@
super.createDefaultDisplay();
mDisplay = mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY);
mOtherDisplay = TestActivityDisplay.create(mTestStackSupervisor, DEFAULT_DISPLAY + 1);
+ mSingleTaskDisplay = TestActivityDisplay.create(mTestStackSupervisor,
+ DEFAULT_DISPLAY + 2);
+ mSingleTaskDisplay.setDisplayToSingleTaskInstance();
mRootActivityContainer.addChild(mOtherDisplay, ActivityDisplay.POSITION_TOP);
mRootActivityContainer.addChild(mDisplay, ActivityDisplay.POSITION_TOP);
+ mRootActivityContainer.addChild(mSingleTaskDisplay, ActivityDisplay.POSITION_TOP);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
index b1f942e..70ed62a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
@@ -175,10 +175,14 @@
@Test
public void testStackOutset() {
final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
- spyOn(stack);
-
final int stackOutset = 10;
- doReturn(stackOutset).when(stack).getStackOutset();
+ // Clear the handler and hold the lock for mock, to prevent multi-thread issue.
+ waitUntilHandlersIdle();
+ synchronized (mWm.mGlobalLock) {
+ spyOn(stack);
+
+ doReturn(stackOutset).when(stack).getStackOutset();
+ }
final Rect stackBounds = new Rect(200, 200, 800, 1000);
// Update surface position and size by the given bounds.
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index b0e20b8..b03f63b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -110,8 +110,11 @@
// TODO: Let the insets source with new mode keep the visibility control, and remove this
// setup code. Now mTopFullscreenOpaqueWindowState will take back the control of insets
// visibility.
- spyOn(mDisplayContent);
- doNothing().when(mDisplayContent).layoutAndAssignWindowLayersIfNeeded();
+ // Hold the lock to protect the mock from accesssing by other threads.
+ synchronized (mWm.mGlobalLock) {
+ spyOn(mDisplayContent);
+ doNothing().when(mDisplayContent).layoutAndAssignWindowLayersIfNeeded();
+ }
}
@Test
diff --git a/telephony/java/android/telephony/CellSignalStrength.java b/telephony/java/android/telephony/CellSignalStrength.java
index e6182ed..740b970 100644
--- a/telephony/java/android/telephony/CellSignalStrength.java
+++ b/telephony/java/android/telephony/CellSignalStrength.java
@@ -119,7 +119,7 @@
/** @hide */
protected static final int getAsuFromRssiDbm(int dbm) {
if (dbm == CellInfo.UNAVAILABLE) return 99;
- return (dbm / 2) + 113;
+ return (dbm + 113) / 2;
}
// Range for RSCP in ASU (0-96, 255) as defined in TS 27.007 8.69
diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java
index f53cb82..6e839ab 100644
--- a/telephony/java/android/telephony/DisconnectCause.java
+++ b/telephony/java/android/telephony/DisconnectCause.java
@@ -26,7 +26,7 @@
* @hide
*/
@SystemApi
-public class DisconnectCause {
+public final class DisconnectCause {
/** The disconnect cause is not valid (Not received a disconnect cause) */
public static final int NOT_VALID = -1;
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index ffebc04..bb0673f 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -176,26 +176,21 @@
/**
* Listen for {@link PreciseCallState.State} of ringing, background and foreground calls.
- * {@more}
- * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
- * READ_PRECISE_PHONE_STATE}
*
* @hide
*/
+ @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
@SystemApi
public static final int LISTEN_PRECISE_CALL_STATE = 0x00000800;
/**
* Listen for {@link PreciseDataConnectionState} on the data connection (cellular).
*
- * {@more}
- * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
- * READ_PRECISE_PHONE_STATE}
- *
* @see #onPreciseDataConnectionStateChanged
*
* @hide
*/
+ @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
@SystemApi
public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 0x00001000;
@@ -331,12 +326,10 @@
/**
* Listen for call disconnect causes which contains {@link DisconnectCause} and
* {@link PreciseDisconnectCause}.
- * {@more}
- * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
- * READ_PRECISE_PHONE_STATE}
*
* @hide
*/
+ @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
@SystemApi
public static final int LISTEN_CALL_DISCONNECT_CAUSES = 0x02000000;
@@ -356,13 +349,10 @@
* Listen for IMS call disconnect causes which contains
* {@link android.telephony.ims.ImsReasonInfo}
*
- * {@more}
- * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
- * READ_PRECISE_PHONE_STATE}
- *
* @see #onImsCallDisconnectCauseChanged(ImsReasonInfo)
* @hide
*/
+ @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
@SystemApi
public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 0x08000000;
@@ -579,8 +569,9 @@
* @param callState {@link PreciseCallState}
* @hide
*/
+ @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
@SystemApi
- public void onPreciseCallStateChanged(PreciseCallState callState) {
+ public void onPreciseCallStateChanged(@NonNull PreciseCallState callState) {
// default implementation empty
}
@@ -591,6 +582,7 @@
*
* @hide
*/
+ @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
@SystemApi
public void onCallDisconnectCauseChanged(int disconnectCause, int preciseDisconnectCause) {
// default implementation empty
@@ -602,6 +594,7 @@
*
* @hide
*/
+ @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
@SystemApi
public void onImsCallDisconnectCauseChanged(@NonNull ImsReasonInfo imsReasonInfo) {
// default implementation empty
@@ -613,6 +606,7 @@
*
* @hide
*/
+ @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
@SystemApi
public void onPreciseDataConnectionStateChanged(
PreciseDataConnectionState dataConnectionState) {
diff --git a/telephony/java/android/telephony/PreciseDisconnectCause.java b/telephony/java/android/telephony/PreciseDisconnectCause.java
index af88748..54980a2 100644
--- a/telephony/java/android/telephony/PreciseDisconnectCause.java
+++ b/telephony/java/android/telephony/PreciseDisconnectCause.java
@@ -23,7 +23,7 @@
* @hide
*/
@SystemApi
-public class PreciseDisconnectCause {
+public final class PreciseDisconnectCause {
/** The disconnect cause is not valid (Not received a disconnect cause).*/
public static final int NOT_VALID = -1;
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index d2ae106..d461bd0 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -215,13 +215,52 @@
* @see android.telephony#CellSignalStrengthGsm
*/
public @NonNull List<CellSignalStrength> getCellSignalStrengths() {
- List<CellSignalStrength> cssList = new ArrayList<>(2); // Usually have 2 or fewer elems
- if (mLte.isValid()) cssList.add(mLte);
- if (mCdma.isValid()) cssList.add(mCdma);
- if (mTdscdma.isValid()) cssList.add(mTdscdma);
- if (mWcdma.isValid()) cssList.add(mWcdma);
- if (mGsm.isValid()) cssList.add(mGsm);
- if (mNr.isValid()) cssList.add(mNr);
+ return getCellSignalStrengths(CellSignalStrength.class);
+ }
+
+ /**
+ * Returns a List of CellSignalStrength Components of this SignalStrength Report.
+ *
+ * Use this API to access underlying
+ * {@link android.telephony#CellSignalStrength CellSignalStrength} objects that provide more
+ * granular information about the SignalStrength report. Only valid (non-empty)
+ * CellSignalStrengths will be returned. The order of any returned elements is not guaranteed,
+ * and the list may contain more than one instance of a CellSignalStrength type.
+ *
+ * @param clazz a class type that extends
+ * {@link android.telephony.CellSignalStrength CellSignalStrength} to filter possible
+ * return values.
+ * @return a List of CellSignalStrength or an empty List if there are no valid measurements.
+ *
+ * @see android.telephony#CellSignalStrength
+ * @see android.telephony#CellSignalStrengthNr
+ * @see android.telephony#CellSignalStrengthLte
+ * @see android.telephony#CellSignalStrengthTdscdma
+ * @see android.telephony#CellSignalStrengthWcdma
+ * @see android.telephony#CellSignalStrengthCdma
+ * @see android.telephony#CellSignalStrengthGsm
+ */
+ public <T extends CellSignalStrength> @NonNull List<T> getCellSignalStrengths(
+ @NonNull Class<T> clazz) {
+ List<T> cssList = new ArrayList<>(2); // Usually have 2 or fewer elems
+ if (mLte.isValid() && clazz.isAssignableFrom(CellSignalStrengthLte.class)) {
+ cssList.add((T) mLte);
+ }
+ if (mCdma.isValid() && clazz.isAssignableFrom(CellSignalStrengthCdma.class)) {
+ cssList.add((T) mCdma);
+ }
+ if (mTdscdma.isValid() && clazz.isAssignableFrom(CellSignalStrengthTdscdma.class)) {
+ cssList.add((T) mTdscdma);
+ }
+ if (mWcdma.isValid() && clazz.isAssignableFrom(CellSignalStrengthWcdma.class)) {
+ cssList.add((T) mWcdma);
+ }
+ if (mGsm.isValid() && clazz.isAssignableFrom(CellSignalStrengthGsm.class)) {
+ cssList.add((T) mGsm);
+ }
+ if (mNr.isValid() && clazz.isAssignableFrom(CellSignalStrengthNr.class)) {
+ cssList.add((T) mNr);
+ }
return cssList;
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f1869b0..c1d1440 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -8665,24 +8665,26 @@
/**
- * Returns a well-formed IETF BCP 47 language tag representing the locale from the SIM, e.g,
- * en-US. Returns {@code null} if no locale could be derived from subscriptions.
+ * Returns a locale based on the country and language from the SIM. Returns {@code null} if
+ * no locale could be derived from subscriptions.
*
* <p>Requires Permission:
* {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
*
* @see Locale#toLanguageTag()
- * @see Locale#forLanguageTag(String)
*
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- @Nullable public String getSimLocale() {
+ @Nullable public Locale getSimLocale() {
try {
final ITelephony telephony = getITelephony();
if (telephony != null) {
- return telephony.getSimLocaleForSubscriber(getSubId());
+ String languageTag = telephony.getSimLocaleForSubscriber(getSubId());
+ if (!TextUtils.isEmpty(languageTag)) {
+ return Locale.forLanguageTag(languageTag);
+ }
}
} catch (RemoteException ex) {
}
diff --git a/telephony/java/android/telephony/UiccCardInfo.java b/telephony/java/android/telephony/UiccCardInfo.java
index 19f357a..0192ffb 100644
--- a/telephony/java/android/telephony/UiccCardInfo.java
+++ b/telephony/java/android/telephony/UiccCardInfo.java
@@ -30,6 +30,7 @@
private final String mEid;
private final String mIccId;
private final int mSlotIndex;
+ private final boolean mIsRemovable;
public static final Creator<UiccCardInfo> CREATOR = new Creator<UiccCardInfo>() {
@Override
@@ -49,6 +50,7 @@
mEid = in.readString();
mIccId = in.readString();
mSlotIndex = in.readInt();
+ mIsRemovable = in.readByte() != 0;
}
@Override
@@ -58,6 +60,7 @@
dest.writeString(mEid);
dest.writeString(mIccId);
dest.writeInt(mSlotIndex);
+ dest.writeByte((byte) (mIsRemovable ? 1 : 0));
}
@Override
@@ -65,16 +68,21 @@
return 0;
}
- public UiccCardInfo(boolean isEuicc, int cardId, String eid, String iccId, int slotIndex) {
+ /**
+ * @hide
+ */
+ public UiccCardInfo(boolean isEuicc, int cardId, String eid, String iccId, int slotIndex,
+ boolean isRemovable) {
this.mIsEuicc = isEuicc;
this.mCardId = cardId;
this.mEid = eid;
this.mIccId = iccId;
this.mSlotIndex = slotIndex;
+ this.mIsRemovable = isRemovable;
}
/**
- * Return whether the UiccCardInfo is an eUICC.
+ * Return whether the UICC is an eUICC.
* @return true if the UICC is an eUICC.
*/
public boolean isEuicc() {
@@ -127,7 +135,17 @@
* @hide
*/
public UiccCardInfo getUnprivileged() {
- return new UiccCardInfo(mIsEuicc, mCardId, null, null, mSlotIndex);
+ return new UiccCardInfo(mIsEuicc, mCardId, null, null, mSlotIndex, mIsRemovable);
+ }
+
+ /**
+ * Return whether the UICC or eUICC is removable.
+ * <p>
+ * UICCs are generally removable, but eUICCs may be removable or built in to the device.
+ * @return true if the UICC or eUICC is removable
+ */
+ public boolean isRemovable() {
+ return mIsRemovable;
}
@Override
@@ -144,12 +162,13 @@
&& (mCardId == that.mCardId)
&& (Objects.equals(mEid, that.mEid))
&& (Objects.equals(mIccId, that.mIccId))
- && (mSlotIndex == that.mSlotIndex));
+ && (mSlotIndex == that.mSlotIndex)
+ && (mIsRemovable == that.mIsRemovable));
}
@Override
public int hashCode() {
- return Objects.hash(mIsEuicc, mCardId, mEid, mIccId, mSlotIndex);
+ return Objects.hash(mIsEuicc, mCardId, mEid, mIccId, mSlotIndex, mIsRemovable);
}
@Override
@@ -164,6 +183,8 @@
+ mIccId
+ ", mSlotIndex="
+ mSlotIndex
+ + ", mIsRemovable="
+ + mIsRemovable
+ ")";
}
}
diff --git a/telephony/java/android/telephony/UiccSlotInfo.java b/telephony/java/android/telephony/UiccSlotInfo.java
index a39992b..93a7da0 100644
--- a/telephony/java/android/telephony/UiccSlotInfo.java
+++ b/telephony/java/android/telephony/UiccSlotInfo.java
@@ -15,15 +15,15 @@
*/
package android.telephony;
+import android.annotation.IntDef;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
-import android.annotation.IntDef;
-
/**
* Class for the information of a UICC slot.
* @hide
@@ -61,6 +61,7 @@
private final @CardStateInfo int mCardStateInfo;
private final int mLogicalSlotIdx;
private final boolean mIsExtendedApduSupported;
+ private final boolean mIsRemovable;
public static final Creator<UiccSlotInfo> CREATOR = new Creator<UiccSlotInfo>() {
@Override
@@ -81,6 +82,7 @@
mCardStateInfo = in.readInt();
mLogicalSlotIdx = in.readInt();
mIsExtendedApduSupported = in.readByte() != 0;
+ mIsRemovable = in.readByte() != 0;
}
@Override
@@ -91,6 +93,7 @@
dest.writeInt(mCardStateInfo);
dest.writeInt(mLogicalSlotIdx);
dest.writeByte((byte) (mIsExtendedApduSupported ? 1 : 0));
+ dest.writeByte((byte) (mIsRemovable ? 1 : 0));
}
@Override
@@ -98,6 +101,11 @@
return 0;
}
+ /**
+ * Construct a UiccSlotInfo.
+ * @deprecated apps should not be constructing UiccSlotInfo objects
+ */
+ @Deprecated
public UiccSlotInfo(boolean isActive, boolean isEuicc, String cardId,
@CardStateInfo int cardStateInfo, int logicalSlotIdx, boolean isExtendedApduSupported) {
this.mIsActive = isActive;
@@ -106,6 +114,22 @@
this.mCardStateInfo = cardStateInfo;
this.mLogicalSlotIdx = logicalSlotIdx;
this.mIsExtendedApduSupported = isExtendedApduSupported;
+ this.mIsRemovable = false;
+ }
+
+ /**
+ * @hide
+ */
+ public UiccSlotInfo(boolean isActive, boolean isEuicc, String cardId,
+ @CardStateInfo int cardStateInfo, int logicalSlotIdx, boolean isExtendedApduSupported,
+ boolean isRemovable) {
+ this.mIsActive = isActive;
+ this.mIsEuicc = isEuicc;
+ this.mCardId = cardId;
+ this.mCardStateInfo = cardStateInfo;
+ this.mLogicalSlotIdx = logicalSlotIdx;
+ this.mIsExtendedApduSupported = isExtendedApduSupported;
+ this.mIsRemovable = isRemovable;
}
public boolean getIsActive() {
@@ -136,6 +160,16 @@
return mIsExtendedApduSupported;
}
+ /**
+ * Return whether the UICC slot is for a removable UICC.
+ * <p>
+ * UICCs are generally removable, but eUICCs may be removable or built in to the device.
+ * @return true if the slot is for removable UICCs
+ */
+ public boolean isRemovable() {
+ return mIsRemovable;
+ }
+
@Override
public boolean equals(Object obj) {
if (this == obj) {
@@ -151,7 +185,8 @@
&& (Objects.equals(mCardId, that.mCardId))
&& (mCardStateInfo == that.mCardStateInfo)
&& (mLogicalSlotIdx == that.mLogicalSlotIdx)
- && (mIsExtendedApduSupported == that.mIsExtendedApduSupported);
+ && (mIsExtendedApduSupported == that.mIsExtendedApduSupported)
+ && (mIsRemovable == that.mIsRemovable);
}
@Override
@@ -163,6 +198,7 @@
result = 31 * result + mCardStateInfo;
result = 31 * result + mLogicalSlotIdx;
result = 31 * result + (mIsExtendedApduSupported ? 1 : 0);
+ result = 31 * result + (mIsRemovable ? 1 : 0);
return result;
}
@@ -180,6 +216,8 @@
+ mLogicalSlotIdx
+ ", mIsExtendedApduSupported="
+ mIsExtendedApduSupported
+ + ", mIsRemovable="
+ + mIsRemovable
+ ")";
}
}
diff --git a/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java b/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java
index 837ef54..d11a0de 100644
--- a/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java
+++ b/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java
@@ -99,7 +99,7 @@
public int mRttMode;
// RTT Audio Speech Indicator
/** @hide */
- public boolean mHasRttAudioSpeech = false;
+ public boolean mIsReceivingRttAudio = false;
/** @hide */
public ImsStreamMediaProfile(Parcel in) {
@@ -201,7 +201,7 @@
", videoQuality=" + mVideoQuality +
", videoDirection=" + mVideoDirection +
", rttMode=" + mRttMode +
- ", hasRttAudioSpeech=" + mHasRttAudioSpeech + " }";
+ ", hasRttAudioSpeech=" + mIsReceivingRttAudio + " }";
}
@Override
@@ -216,7 +216,7 @@
out.writeInt(mVideoQuality);
out.writeInt(mVideoDirection);
out.writeInt(mRttMode);
- out.writeBoolean(mHasRttAudioSpeech);
+ out.writeBoolean(mIsReceivingRttAudio);
}
private void readFromParcel(Parcel in) {
@@ -225,7 +225,7 @@
mVideoQuality = in.readInt();
mVideoDirection = in.readInt();
mRttMode = in.readInt();
- mHasRttAudioSpeech = in.readBoolean();
+ mIsReceivingRttAudio = in.readBoolean();
}
public static final Creator<ImsStreamMediaProfile> CREATOR =
@@ -256,8 +256,12 @@
mRttMode = rttMode;
}
- public void setRttAudioSpeech(boolean audioOn) {
- mHasRttAudioSpeech = audioOn;
+ /**
+ * Sets whether the remote party is transmitting audio over the RTT call.
+ * @param audioOn true if audio is being received, false otherwise.
+ */
+ public void setReceivingRttAudio(boolean audioOn) {
+ mIsReceivingRttAudio = audioOn;
}
public int getAudioQuality() {
@@ -280,7 +284,10 @@
return mRttMode;
}
- public boolean getRttAudioSpeech() {
- return mHasRttAudioSpeech;
+ /**
+ * @return true if remote party is transmitting audio, false otherwise.
+ */
+ public boolean isReceivingRttAudio() {
+ return mIsReceivingRttAudio;
}
}
diff --git a/telephony/java/android/telephony/ims/Rcs1To1Thread.java b/telephony/java/android/telephony/ims/Rcs1To1Thread.java
index d4a78ff..0bb1b43 100644
--- a/telephony/java/android/telephony/ims/Rcs1To1Thread.java
+++ b/telephony/java/android/telephony/ims/Rcs1To1Thread.java
@@ -22,6 +22,8 @@
* Rcs1To1Thread represents a single RCS conversation thread with a total of two
* {@link RcsParticipant}s. Please see Section 5 (1-to-1 Messaging) - GSMA RCC.71 (RCS Universal
* Profile Service Definition Document)
+ *
+ * @hide
*/
public class Rcs1To1Thread extends RcsThread {
private int mThreadId;
diff --git a/telephony/java/android/telephony/ims/RcsEvent.java b/telephony/java/android/telephony/ims/RcsEvent.java
index a547c5c0..994b27a 100644
--- a/telephony/java/android/telephony/ims/RcsEvent.java
+++ b/telephony/java/android/telephony/ims/RcsEvent.java
@@ -17,6 +17,8 @@
/**
* The base class for events that can happen on {@link RcsParticipant}s and {@link RcsThread}s.
+ *
+ * @hide
*/
public abstract class RcsEvent {
private final long mTimestamp;
diff --git a/telephony/java/android/telephony/ims/RcsEventQueryParams.java b/telephony/java/android/telephony/ims/RcsEventQueryParams.java
index 9dbfe43..5f8fa80 100644
--- a/telephony/java/android/telephony/ims/RcsEventQueryParams.java
+++ b/telephony/java/android/telephony/ims/RcsEventQueryParams.java
@@ -37,6 +37,8 @@
* The parameters to pass into
* {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} in order to select a
* subset of {@link RcsEvent}s present in the message store.
+ *
+ * @hide
*/
public final class RcsEventQueryParams implements Parcelable {
/**
diff --git a/telephony/java/android/telephony/ims/RcsEventQueryResult.java b/telephony/java/android/telephony/ims/RcsEventQueryResult.java
index 92bda81..d6347e3 100644
--- a/telephony/java/android/telephony/ims/RcsEventQueryResult.java
+++ b/telephony/java/android/telephony/ims/RcsEventQueryResult.java
@@ -22,6 +22,8 @@
* The result of a {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)}
* call. This class allows getting the token for querying the next batch of events in order to
* prevent handling large amounts of data at once.
+ *
+ * @hide
*/
public class RcsEventQueryResult {
private RcsQueryContinuationToken mContinuationToken;
diff --git a/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.java b/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.java
index 14af8ea..4742ba2 100644
--- a/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.java
+++ b/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.java
@@ -24,6 +24,8 @@
* Pass an instance of this class to
* {@link RcsMessage#insertFileTransfer(RcsFileTransferCreationParams)} create an
* {@link RcsFileTransferPart} and save it into storage.
+ *
+ * @hide
*/
public final class RcsFileTransferCreationParams implements Parcelable {
private String mRcsFileTransferSessionId;
diff --git a/telephony/java/android/telephony/ims/RcsFileTransferPart.java b/telephony/java/android/telephony/ims/RcsFileTransferPart.java
index 9531c2e..3816cd4 100644
--- a/telephony/java/android/telephony/ims/RcsFileTransferPart.java
+++ b/telephony/java/android/telephony/ims/RcsFileTransferPart.java
@@ -26,6 +26,8 @@
/**
* A part of a composite {@link RcsMessage} that holds a file transfer. Please see Section 7
* (File Transfer) - GSMA RCC.71 (RCS Universal Profile Service Definition Document)
+ *
+ * @hide
*/
public class RcsFileTransferPart {
/**
diff --git a/telephony/java/android/telephony/ims/RcsGroupThread.java b/telephony/java/android/telephony/ims/RcsGroupThread.java
index 6e17bc2..8cd633b 100644
--- a/telephony/java/android/telephony/ims/RcsGroupThread.java
+++ b/telephony/java/android/telephony/ims/RcsGroupThread.java
@@ -29,6 +29,8 @@
* RcsGroupThread represents a single RCS conversation thread where {@link RcsParticipant}s can join
* or leave. Please see Section 6 (Group Chat) - GSMA RCC.71 (RCS Universal Profile Service
* Definition Document)
+ *
+ * @hide
*/
public class RcsGroupThread extends RcsThread {
/**
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadEvent.java
index 99086aa..4a6b963 100644
--- a/telephony/java/android/telephony/ims/RcsGroupThreadEvent.java
+++ b/telephony/java/android/telephony/ims/RcsGroupThreadEvent.java
@@ -19,6 +19,8 @@
/**
* An event that happened on an {@link RcsGroupThread}.
+ *
+ * @hide
*/
public abstract class RcsGroupThreadEvent extends RcsEvent {
private final int mRcsGroupThreadId;
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.java
index cbd762d..3c6c74f 100644
--- a/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.java
+++ b/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.java
@@ -22,6 +22,8 @@
/**
* An event that indicates an {@link RcsGroupThread}'s icon was changed. Please see R6-2-5 - GSMA
* RCC.71 (RCS Universal Profile Service Definition Document)
+ *
+ * @hide
*/
public final class RcsGroupThreadIconChangedEvent extends RcsGroupThreadEvent {
private final Uri mNewIcon;
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.java
index a2a4fab..5403253 100644
--- a/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.java
+++ b/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.java
@@ -21,6 +21,8 @@
/**
* An event that indicates an {@link RcsGroupThread}'s name was changed. Please see R6-2-5 - GSMA
* RCC.71 (RCS Universal Profile Service Definition Document)
+ *
+ * @hide
*/
public final class RcsGroupThreadNameChangedEvent extends RcsGroupThreadEvent {
private final String mNewName;
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.java
index 183cd9a..48be479 100644
--- a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.java
+++ b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.java
@@ -20,6 +20,8 @@
/**
* An event that indicates an RCS participant has joined an {@link RcsThread}. Please see US6-3 -
* GSMA RCC.71 (RCS Universal Profile Service Definition Document)
+ *
+ * @hide
*/
public final class RcsGroupThreadParticipantJoinedEvent extends RcsGroupThreadEvent {
private final RcsParticipant mJoinedParticipantId;
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.java
index c12549b..b724a3f 100644
--- a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.java
+++ b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.java
@@ -20,6 +20,8 @@
/**
* An event that indicates an RCS participant has left an {@link RcsThread}. Please see US6-23 -
* GSMA RCC.71 (RCS Universal Profile Service Definition Document)
+ *
+ * @hide
*/
public final class RcsGroupThreadParticipantLeftEvent extends RcsGroupThreadEvent {
private RcsParticipant mLeavingParticipant;
diff --git a/telephony/java/android/telephony/ims/RcsIncomingMessage.java b/telephony/java/android/telephony/ims/RcsIncomingMessage.java
index 61911ab..06e2a41 100644
--- a/telephony/java/android/telephony/ims/RcsIncomingMessage.java
+++ b/telephony/java/android/telephony/ims/RcsIncomingMessage.java
@@ -19,6 +19,8 @@
/**
* This is a single instance of a message received over RCS.
+ *
+ * @hide
*/
public class RcsIncomingMessage extends RcsMessage {
/**
diff --git a/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.java b/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.java
index 61dedbc..58dc1bc 100644
--- a/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.java
+++ b/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.java
@@ -24,6 +24,8 @@
* {@link RcsIncomingMessageCreationParams} is a collection of parameters that should be passed
* into {@link RcsThread#addIncomingMessage(RcsIncomingMessageCreationParams)} to generate an
* {@link RcsIncomingMessage} on that {@link RcsThread}
+ *
+ * @hide
*/
public final class RcsIncomingMessageCreationParams extends RcsMessageCreationParams implements
Parcelable {
diff --git a/telephony/java/android/telephony/ims/RcsManager.java b/telephony/java/android/telephony/ims/RcsManager.java
index 22e4b22..63dc1ac 100644
--- a/telephony/java/android/telephony/ims/RcsManager.java
+++ b/telephony/java/android/telephony/ims/RcsManager.java
@@ -20,6 +20,8 @@
/**
* The manager class for RCS related utilities.
+ *
+ * @hide
*/
@SystemService(Context.TELEPHONY_RCS_SERVICE)
public class RcsManager {
diff --git a/telephony/java/android/telephony/ims/RcsMessage.java b/telephony/java/android/telephony/ims/RcsMessage.java
index 3227413..b0d0d5a 100644
--- a/telephony/java/android/telephony/ims/RcsMessage.java
+++ b/telephony/java/android/telephony/ims/RcsMessage.java
@@ -27,6 +27,8 @@
/**
* This is a single instance of a message sent or received over RCS.
+ *
+ * @hide
*/
public abstract class RcsMessage {
/**
diff --git a/telephony/java/android/telephony/ims/RcsMessageCreationParams.java b/telephony/java/android/telephony/ims/RcsMessageCreationParams.java
index c46c605..f0eea88 100644
--- a/telephony/java/android/telephony/ims/RcsMessageCreationParams.java
+++ b/telephony/java/android/telephony/ims/RcsMessageCreationParams.java
@@ -27,6 +27,8 @@
* {@link RcsThread#addIncomingMessage(RcsIncomingMessageCreationParams)} and
* {@link RcsThread#addOutgoingMessage(RcsOutgoingMessageCreationParams)} to create and persist
* {@link RcsMessage}s on an {@link RcsThread}
+ *
+ * @hide
*/
public class RcsMessageCreationParams {
// The globally unique id of the RcsMessage to be created.
diff --git a/telephony/java/android/telephony/ims/RcsMessageQueryParams.java b/telephony/java/android/telephony/ims/RcsMessageQueryParams.java
index 535a597..6491ac9 100644
--- a/telephony/java/android/telephony/ims/RcsMessageQueryParams.java
+++ b/telephony/java/android/telephony/ims/RcsMessageQueryParams.java
@@ -31,6 +31,8 @@
* The parameters to pass into
* {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} in order to select a
* subset of {@link RcsMessage}s present in the message store.
+ *
+ * @hide
*/
public final class RcsMessageQueryParams implements Parcelable {
/**
diff --git a/telephony/java/android/telephony/ims/RcsMessageQueryResult.java b/telephony/java/android/telephony/ims/RcsMessageQueryResult.java
index 3514b48..e4020c1 100644
--- a/telephony/java/android/telephony/ims/RcsMessageQueryResult.java
+++ b/telephony/java/android/telephony/ims/RcsMessageQueryResult.java
@@ -32,6 +32,8 @@
* The result of a {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)}
* call. This class allows getting the token for querying the next batch of messages in order to
* prevent handling large amounts of data at once.
+ *
+ * @hide
*/
public final class RcsMessageQueryResult implements Parcelable {
// The token to continue the query to get the next batch of results
diff --git a/telephony/java/android/telephony/ims/RcsMessageSnippet.java b/telephony/java/android/telephony/ims/RcsMessageSnippet.java
index b0b930c..9064251 100644
--- a/telephony/java/android/telephony/ims/RcsMessageSnippet.java
+++ b/telephony/java/android/telephony/ims/RcsMessageSnippet.java
@@ -23,6 +23,8 @@
/**
* An immutable summary of the latest {@link RcsMessage} on an {@link RcsThread}
+ *
+ * @hide
*/
public final class RcsMessageSnippet implements Parcelable {
private final String mText;
diff --git a/telephony/java/android/telephony/ims/RcsMessageStore.java b/telephony/java/android/telephony/ims/RcsMessageStore.java
index 31f2983..3111652 100644
--- a/telephony/java/android/telephony/ims/RcsMessageStore.java
+++ b/telephony/java/android/telephony/ims/RcsMessageStore.java
@@ -26,6 +26,8 @@
/**
* RcsMessageStore is the application interface to RcsProvider and provides access methods to
* RCS related database tables.
+ *
+ * @hide
*/
public class RcsMessageStore {
/**
diff --git a/telephony/java/android/telephony/ims/RcsMessageStoreException.java b/telephony/java/android/telephony/ims/RcsMessageStoreException.java
index f25bb17..3b3fcf2 100644
--- a/telephony/java/android/telephony/ims/RcsMessageStoreException.java
+++ b/telephony/java/android/telephony/ims/RcsMessageStoreException.java
@@ -19,6 +19,8 @@
/**
* An exception that happened on {@link RcsMessageStore} or one of the derived storage classes in
* {@link android.telephony.ims}
+ *
+ * @hide
*/
public class RcsMessageStoreException extends Exception {
diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessage.java b/telephony/java/android/telephony/ims/RcsOutgoingMessage.java
index 06fb832..1b4bfe5 100644
--- a/telephony/java/android/telephony/ims/RcsOutgoingMessage.java
+++ b/telephony/java/android/telephony/ims/RcsOutgoingMessage.java
@@ -23,6 +23,8 @@
/**
* This is a single instance of a message sent over RCS.
+ *
+ * @hide
*/
public class RcsOutgoingMessage extends RcsMessage {
RcsOutgoingMessage(int id) {
diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.java b/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.java
index 979634a..81e3244 100644
--- a/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.java
+++ b/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.java
@@ -23,6 +23,8 @@
* {@link RcsOutgoingMessageCreationParams} is a collection of parameters that should be passed
* into {@link RcsThread#addOutgoingMessage(RcsOutgoingMessageCreationParams)} to generate an
* {@link RcsOutgoingMessage} on that {@link RcsThread}
+ *
+ * @hide
*/
public final class RcsOutgoingMessageCreationParams extends RcsMessageCreationParams
implements Parcelable {
diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessageDelivery.java b/telephony/java/android/telephony/ims/RcsOutgoingMessageDelivery.java
index 1c87b13..2db49c6 100644
--- a/telephony/java/android/telephony/ims/RcsOutgoingMessageDelivery.java
+++ b/telephony/java/android/telephony/ims/RcsOutgoingMessageDelivery.java
@@ -21,6 +21,8 @@
/**
* This class holds the delivery information of an {@link RcsOutgoingMessage} for each
* {@link RcsParticipant} that the message was intended for.
+ *
+ * @hide
*/
public class RcsOutgoingMessageDelivery {
// The participant that this delivery is intended for
diff --git a/telephony/java/android/telephony/ims/RcsParticipant.java b/telephony/java/android/telephony/ims/RcsParticipant.java
index 7ba5d8e..bcf134a 100644
--- a/telephony/java/android/telephony/ims/RcsParticipant.java
+++ b/telephony/java/android/telephony/ims/RcsParticipant.java
@@ -20,6 +20,8 @@
/**
* RcsParticipant is an RCS capable contact that can participate in {@link RcsThread}s.
+ *
+ * @hide
*/
public class RcsParticipant {
// The row ID of this participant in the database
diff --git a/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java b/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java
index cc2613f..61801f3 100644
--- a/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java
+++ b/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java
@@ -21,6 +21,8 @@
/**
* An event that indicates an {@link RcsParticipant}'s alias was changed. Please see US18-2 - GSMA
* RCC.71 (RCS Universal Profile Service Definition Document)
+ *
+ * @hide
*/
public final class RcsParticipantAliasChangedEvent extends RcsEvent {
// The participant that changed their alias
diff --git a/telephony/java/android/telephony/ims/RcsParticipantQueryParams.java b/telephony/java/android/telephony/ims/RcsParticipantQueryParams.java
index d24d079..ada9b8a 100644
--- a/telephony/java/android/telephony/ims/RcsParticipantQueryParams.java
+++ b/telephony/java/android/telephony/ims/RcsParticipantQueryParams.java
@@ -30,6 +30,8 @@
* The parameters to pass into
* {@link RcsMessageStore#getRcsParticipants(RcsParticipantQueryParams)} in order to select a
* subset of {@link RcsThread}s present in the message store.
+ *
+ * @hide
*/
public final class RcsParticipantQueryParams implements Parcelable {
/**
diff --git a/telephony/java/android/telephony/ims/RcsParticipantQueryResult.java b/telephony/java/android/telephony/ims/RcsParticipantQueryResult.java
index 505f1a5..92e2fa78 100644
--- a/telephony/java/android/telephony/ims/RcsParticipantQueryResult.java
+++ b/telephony/java/android/telephony/ims/RcsParticipantQueryResult.java
@@ -28,6 +28,8 @@
* The result of a {@link RcsMessageStore#getRcsParticipants(RcsParticipantQueryParams)}
* call. This class allows getting the token for querying the next batch of participants in order to
* prevent handling large amounts of data at once.
+ *
+ * @hide
*/
public final class RcsParticipantQueryResult implements Parcelable {
// A token for the caller to continue their query for the next batch of results
diff --git a/telephony/java/android/telephony/ims/RcsQueryContinuationToken.java b/telephony/java/android/telephony/ims/RcsQueryContinuationToken.java
index 08643de..970c110 100644
--- a/telephony/java/android/telephony/ims/RcsQueryContinuationToken.java
+++ b/telephony/java/android/telephony/ims/RcsQueryContinuationToken.java
@@ -31,6 +31,8 @@
* @see RcsMessageQueryResult#getContinuationToken()
* @see RcsParticipantQueryResult#getContinuationToken()
* @see RcsThreadQueryResult#getContinuationToken()
+ *
+ * @hide
*/
public final class RcsQueryContinuationToken implements Parcelable {
/**
diff --git a/telephony/java/android/telephony/ims/RcsThread.java b/telephony/java/android/telephony/ims/RcsThread.java
index e015dd3..cf1dc76 100644
--- a/telephony/java/android/telephony/ims/RcsThread.java
+++ b/telephony/java/android/telephony/ims/RcsThread.java
@@ -27,6 +27,8 @@
/**
* RcsThread represents a single RCS conversation thread. It holds messages that were sent and
* received and events that occurred on that thread.
+ *
+ * @hide
*/
public abstract class RcsThread {
/**
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryParams.java b/telephony/java/android/telephony/ims/RcsThreadQueryParams.java
index 05a5a39..81eee40 100644
--- a/telephony/java/android/telephony/ims/RcsThreadQueryParams.java
+++ b/telephony/java/android/telephony/ims/RcsThreadQueryParams.java
@@ -35,6 +35,8 @@
/**
* The parameters to pass into {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParams)} in
* order to select a subset of {@link RcsThread}s present in the message store.
+ *
+ * @hide
*/
public final class RcsThreadQueryParams implements Parcelable {
/**
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryResult.java b/telephony/java/android/telephony/ims/RcsThreadQueryResult.java
index 1cac61d..9f2fba5 100644
--- a/telephony/java/android/telephony/ims/RcsThreadQueryResult.java
+++ b/telephony/java/android/telephony/ims/RcsThreadQueryResult.java
@@ -32,6 +32,8 @@
* The result of a {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParams)}
* call. This class allows getting the token for querying the next batch of threads in order to
* prevent handling large amounts of data at once.
+ *
+ * @hide
*/
public final class RcsThreadQueryResult implements Parcelable {
// A token for the caller to continue their query for the next batch of results
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index a922ab6..6de608e 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -84,6 +84,6 @@
void notifyActiveDataSubIdChanged(int activeDataSubId);
void notifyRadioPowerStateChanged(in int state);
void notifyEmergencyNumberList();
- void notifyCallQualityChanged(in CallQuality callQuality, int phoneId);
+ void notifyCallQualityChanged(in CallQuality callQuality, int phoneId, int callNetworkType);
void notifyImsDisconnectCause(int subId, in ImsReasonInfo imsReasonInfo);
}
diff --git a/test-base/Android.bp b/test-base/Android.bp
index 157609c..8aa0aaf 100644
--- a/test-base/Android.bp
+++ b/test-base/Android.bp
@@ -44,7 +44,7 @@
// ==========================================
// This is only intended for inclusion in the android.test.runner-minus-junit,
// robolectric_android-all-stub and repackaged.android.test.* libraries.
-// Must not be used elewhere.
+// Must not be used elsewhere.
java_library_static {
name: "android.test.base_static",
installable: false,
@@ -61,19 +61,6 @@
sdk_version: "current",
}
-// Build the legacy-test library
-// =============================
-// This contains the junit.framework and android.test classes that were in
-// Android API level 25 excluding those from android.test.runner.
-// Also contains the com.android.internal.util.Predicate[s] classes.
-java_library {
- name: "legacy-test",
- installable: true,
-
- sdk_version: "current",
- static_libs: ["android.test.base_static"],
-}
-
// Build the repackaged.android.test.base library
// ==============================================
// This contains repackaged versions of the classes from
@@ -93,8 +80,8 @@
// ===============================================
// This contains the android.test classes from android.test.base plus
// the com.android.internal.util.Predicate[s] classes. This is only
-// intended for inclusion in the android.test.legacy and
-// legacy-android-test static libraries and must not be used elsewhere.
+// intended for inclusion in android.test.legacy and must not be used
+// elsewhere.
java_library_static {
name: "android.test.base-minus-junit",
diff --git a/test-legacy/Android.bp b/test-legacy/Android.bp
deleted file mode 100644
index a69f422..0000000
--- a/test-legacy/Android.bp
+++ /dev/null
@@ -1,36 +0,0 @@
-//
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-// Build the legacy-android-test library
-// =====================================
-// This contains the android.test classes that were in Android API level 25,
-// including those from android.test.runner.
-// Also contains the com.android.internal.util.Predicate[s] classes.
-java_library_static {
- name: "legacy-android-test",
-
- static_libs: [
- "android.test.base-minus-junit",
- "android.test.runner-minus-junit",
- "android.test.mock_static",
- ],
-
- no_framework_libs: true,
- libs: [
- "framework",
- "junit",
- ],
-}
diff --git a/test-legacy/Android.mk b/test-legacy/Android.mk
index da47de0..af26c5b 100644
--- a/test-legacy/Android.mk
+++ b/test-legacy/Android.mk
@@ -24,35 +24,16 @@
# Built against the SDK so that it can be statically included in APKs
# without breaking link type checks.
#
-# This builds directly from the source rather than simply statically
-# including the android.test.base-minus-junit and
-# android.test.runner-minus-junit libraries because the latter library
-# cannot itself be built against the SDK. That is because it uses on
-# an internal method (setTestContext) on the AndroidTestCase class.
-# That class is provided by both the android.test.base-minus-junit and
-# the current SDK and as the latter is first on the classpath its
-# version is used. Unfortunately, it does not provide the internal
-# method and so compilation fails.
-#
-# Building from source avoids that because the compiler will use the
-# source version of AndroidTestCase instead of the one from the current
-# SDK.
-#
-# The use of the internal method does not prevent this from being
-# statically included because the class that provides the method is
-# also included in this library.
include $(CLEAR_VARS)
LOCAL_MODULE := android.test.legacy
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, ../test-base/src/android) \
- $(call all-java-files-under, ../test-base/src/com) \
- $(call all-java-files-under, ../test-runner/src/android) \
-
LOCAL_SDK_VERSION := current
LOCAL_JAVA_LIBRARIES := junit android.test.mock.stubs
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android.test.base-minus-junit \
+ android.test.runner-minus-junit \
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index 43b765d..e1d6e01 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -30,19 +30,3 @@
srcs_lib_whitelist_pkgs: ["android"],
compile_dex: true,
}
-
-// Build the android.test.mock_static library
-// ==========================================
-// This is only intended for inclusion in the legacy-android-test.
-// Must not be used elewhere.
-java_library_static {
- name: "android.test.mock_static",
-
- java_version: "1.8",
- srcs: ["src/**/*.java"],
-
- no_framework_libs: true,
- libs: [
- "framework",
- ],
-}
diff --git a/test-runner/Android.bp b/test-runner/Android.bp
index db5053e..3521202 100644
--- a/test-runner/Android.bp
+++ b/test-runner/Android.bp
@@ -45,7 +45,7 @@
// Build the android.test.runner-minus-junit library
// =================================================
-// This is only intended for inclusion in the legacy-android-test static
+// This is only intended for inclusion in the android.test.legacy static
// library and must not be used elsewhere.
java_library {
name: "android.test.runner-minus-junit",
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/CustomRenderer.java b/tests/HwAccelerationTest/src/com/android/test/hwui/CustomRenderer.java
index 60bd60f..fece8ba 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/CustomRenderer.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/CustomRenderer.java
@@ -49,7 +49,7 @@
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
mContent.setLeftTopRightBottom(0, 0, width, height);
- RecordingCanvas canvas = mContent.startRecording();
+ RecordingCanvas canvas = mContent.beginRecording();
canvas.drawColor(Color.WHITE);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.BLACK);
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MyLittleTextureView.java b/tests/HwAccelerationTest/src/com/android/test/hwui/MyLittleTextureView.java
index 8bd7d79..08d5d4f 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/MyLittleTextureView.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/MyLittleTextureView.java
@@ -60,14 +60,14 @@
outline.setAlpha(1f);
childNode.setOutline(outline);
{
- Canvas canvas = childNode.startRecording();
+ Canvas canvas = childNode.beginRecording();
canvas.drawColor(Color.BLUE);
}
childNode.endRecording();
childNode.setElevation(20f);
{
- Canvas canvas = mContent.startRecording();
+ Canvas canvas = mContent.beginRecording();
canvas.drawColor(Color.WHITE);
canvas.enableZ();
canvas.drawRenderNode(childNode);
diff --git a/tests/RollbackTest/Android.mk b/tests/RollbackTest/Android.mk
index 9e5d8ce..db9376b 100644
--- a/tests/RollbackTest/Android.mk
+++ b/tests/RollbackTest/Android.mk
@@ -21,6 +21,7 @@
LOCAL_SDK_VERSION := current
LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src)
LOCAL_MANIFEST_FILE := TestApp/Av1.xml
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/TestApp/res_v1
LOCAL_PACKAGE_NAME := RollbackTestAppAv1
include $(BUILD_PACKAGE)
ROLLBACK_TEST_APP_AV1 := $(LOCAL_INSTALLED_MODULE)
@@ -32,6 +33,7 @@
LOCAL_SDK_VERSION := current
LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src)
LOCAL_MANIFEST_FILE := TestApp/Av2.xml
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/TestApp/res_v2
LOCAL_PACKAGE_NAME := RollbackTestAppAv2
include $(BUILD_PACKAGE)
ROLLBACK_TEST_APP_AV2 := $(LOCAL_INSTALLED_MODULE)
@@ -43,6 +45,7 @@
LOCAL_SDK_VERSION := current
LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src)
LOCAL_MANIFEST_FILE := TestApp/ACrashingV2.xml
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/TestApp/res_v2
LOCAL_PACKAGE_NAME := RollbackTestAppACrashingV2
include $(BUILD_PACKAGE)
ROLLBACK_TEST_APP_A_CRASHING_V2 := $(LOCAL_INSTALLED_MODULE)
@@ -54,6 +57,7 @@
LOCAL_SDK_VERSION := current
LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src)
LOCAL_MANIFEST_FILE := TestApp/Bv1.xml
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/TestApp/res_v1
LOCAL_PACKAGE_NAME := RollbackTestAppBv1
include $(BUILD_PACKAGE)
ROLLBACK_TEST_APP_BV1 := $(LOCAL_INSTALLED_MODULE)
@@ -65,10 +69,39 @@
LOCAL_SDK_VERSION := current
LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src)
LOCAL_MANIFEST_FILE := TestApp/Bv2.xml
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/TestApp/res_v2
LOCAL_PACKAGE_NAME := RollbackTestAppBv2
include $(BUILD_PACKAGE)
ROLLBACK_TEST_APP_BV2 := $(LOCAL_INSTALLED_MODULE)
+# RollbackTestAppASplitV1.apk
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src)
+LOCAL_MANIFEST_FILE := TestApp/Av1.xml
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/TestApp/res_v1
+LOCAL_PACKAGE_NAME := RollbackTestAppASplitV1
+LOCAL_PACKAGE_SPLITS := anydpi
+include $(BUILD_PACKAGE)
+ROLLBACK_TEST_APP_A_SPLIT_V1 := $(LOCAL_INSTALLED_MODULE)
+ROLLBACK_TEST_APP_A_SPLIT_V1_SPLIT := $(installed_apk_splits)
+
+# RollbackTestAppASplitV2.apk
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src)
+LOCAL_MANIFEST_FILE := TestApp/Av2.xml
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/TestApp/res_v2
+LOCAL_PACKAGE_NAME := RollbackTestAppASplitV2
+LOCAL_PACKAGE_SPLITS := anydpi
+include $(BUILD_PACKAGE)
+ROLLBACK_TEST_APP_A_SPLIT_V2 := $(LOCAL_INSTALLED_MODULE)
+ROLLBACK_TEST_APP_A_SPLIT_V2_SPLIT := $(installed_apk_splits)
+
# RollbackTest
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-java-files-under, RollbackTest/src)
@@ -82,6 +115,10 @@
$(ROLLBACK_TEST_APP_A_CRASHING_V2) \
$(ROLLBACK_TEST_APP_BV1) \
$(ROLLBACK_TEST_APP_BV2) \
+ $(ROLLBACK_TEST_APP_A_SPLIT_V1) \
+ $(ROLLBACK_TEST_APP_A_SPLIT_V1_SPLIT) \
+ $(ROLLBACK_TEST_APP_A_SPLIT_V2) \
+ $(ROLLBACK_TEST_APP_A_SPLIT_V2_SPLIT) \
$(ROLLBACK_TEST_APEX_V1) \
$(ROLLBACK_TEST_APEX_V2)
LOCAL_MANIFEST_FILE := RollbackTest/AndroidManifest.xml
@@ -103,5 +140,9 @@
ROLLBACK_TEST_APP_AV1 :=
ROLLBACK_TEST_APP_AV2 :=
ROLLBACK_TEST_APP_A_CRASHING_V2 :=
+ROLLBACK_TEST_APP_A_SPLIT_V1 :=
+ROLLBACK_TEST_APP_A_SPLIT_V1_SPLIT :=
+ROLLBACK_TEST_APP_A_SPLIT_V2 :=
+ROLLBACK_TEST_APP_A_SPLIT_V2_SPLIT :=
ROLLBACK_TEST_APP_BV1 :=
ROLLBACK_TEST_APP_BV2 :=
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index bd0881f..7be83ed 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -32,6 +32,7 @@
import android.content.rollback.RollbackManager;
import android.os.Handler;
import android.os.HandlerThread;
+import android.provider.DeviceConfig;
import android.support.test.InstrumentationRegistry;
import android.util.Log;
@@ -331,6 +332,64 @@
}
/**
+ * Test the scheduling aspect of rollback expiration.
+ */
+ @Test
+ public void testRollbackExpiresAfterLifetime() throws Exception {
+ long expirationTime = TimeUnit.SECONDS.toMillis(30);
+ long defaultExpirationTime = TimeUnit.HOURS.toMillis(48);
+ RollbackManager rm = RollbackTestUtils.getRollbackManager();
+
+ try {
+ RollbackTestUtils.adoptShellPermissionIdentity(
+ Manifest.permission.INSTALL_PACKAGES,
+ Manifest.permission.DELETE_PACKAGES,
+ Manifest.permission.MANAGE_ROLLBACKS,
+ Manifest.permission.WRITE_DEVICE_CONFIG);
+
+ DeviceConfig.setProperty(DeviceConfig.Rollback.BOOT_NAMESPACE,
+ DeviceConfig.Rollback.ROLLBACK_LIFETIME_IN_MILLIS,
+ Long.toString(expirationTime), false /* makeDefault*/);
+
+ // Pull the new expiration time from DeviceConfig
+ rm.reloadPersistedData();
+
+ // Uninstall TEST_APP_A
+ RollbackTestUtils.uninstall(TEST_APP_A);
+ assertEquals(-1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+ // Install v1 of the app (without rollbacks enabled).
+ RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
+ assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+ // Upgrade from v1 to v2, with rollbacks enabled.
+ RollbackTestUtils.install("RollbackTestAppAv2.apk", true);
+ assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+ // Check that the rollback data has not expired
+ Thread.sleep(1000);
+ RollbackInfo rollback = getUniqueRollbackInfoForPackage(
+ rm.getAvailableRollbacks(), TEST_APP_A);
+ assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback);
+
+ // Give it a little more time, but still not the long enough to expire
+ Thread.sleep(expirationTime / 2);
+ rollback = getUniqueRollbackInfoForPackage(
+ rm.getAvailableRollbacks(), TEST_APP_A);
+ assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback);
+
+ // Check that the data has expired after the expiration time (with a buffer of 1 second)
+ Thread.sleep(expirationTime / 2);
+ assertNull(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), TEST_APP_A));
+ } finally {
+ DeviceConfig.setProperty(DeviceConfig.Rollback.BOOT_NAMESPACE,
+ DeviceConfig.Rollback.ROLLBACK_LIFETIME_IN_MILLIS,
+ Long.toString(defaultExpirationTime), false /* makeDefault*/);
+ RollbackTestUtils.dropShellPermissionIdentity();
+ }
+ }
+
+ /**
* Test explicit expiration of rollbacks.
* Does not test the scheduling aspects of rollback expiration.
*/
@@ -442,6 +501,39 @@
}
/**
+ * Test rollback of apks involving splits.
+ */
+ @Test
+ public void testRollbackWithSplits() throws Exception {
+ try {
+ RollbackTestUtils.adoptShellPermissionIdentity(
+ Manifest.permission.INSTALL_PACKAGES,
+ Manifest.permission.DELETE_PACKAGES,
+ Manifest.permission.MANAGE_ROLLBACKS);
+
+ RollbackTestUtils.uninstall(TEST_APP_A);
+ RollbackTestUtils.installSplit(false,
+ "RollbackTestAppASplitV1.apk",
+ "RollbackTestAppASplitV1_anydpi.apk");
+ processUserData(TEST_APP_A);
+
+ RollbackTestUtils.installSplit(true,
+ "RollbackTestAppASplitV2.apk",
+ "RollbackTestAppASplitV2_anydpi.apk");
+ processUserData(TEST_APP_A);
+
+ RollbackManager rm = RollbackTestUtils.getRollbackManager();
+ RollbackInfo rollback = getUniqueRollbackInfoForPackage(
+ rm.getAvailableRollbacks(), TEST_APP_A);
+ assertNotNull(rollback);
+ RollbackTestUtils.rollback(rollback.getRollbackId());
+ processUserData(TEST_APP_A);
+ } finally {
+ RollbackTestUtils.dropShellPermissionIdentity();
+ }
+ }
+
+ /**
* Test restrictions on rollback broadcast sender.
* A random app should not be able to send a ROLLBACK_COMMITTED broadcast.
*/
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
index 097d33d..f28714c 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
@@ -130,6 +130,19 @@
*/
static void install(String resourceName, boolean enableRollback)
throws InterruptedException, IOException {
+ installSplit(enableRollback, resourceName);
+ }
+
+ /**
+ * Installs the apk with the given name and its splits.
+ *
+ * @param enableRollback if rollback should be enabled.
+ * @param resourceNames names of class loader resources for the apk and
+ * its splits to install.
+ * @throws AssertionError if the installation fails.
+ */
+ static void installSplit(boolean enableRollback, String... resourceNames)
+ throws InterruptedException, IOException {
Context context = InstrumentationRegistry.getContext();
PackageInstaller.Session session = null;
PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
@@ -142,12 +155,14 @@
session = packageInstaller.openSession(sessionId);
ClassLoader loader = RollbackTest.class.getClassLoader();
- try (OutputStream packageInSession = session.openWrite(resourceName, 0, -1);
- InputStream is = loader.getResourceAsStream(resourceName);) {
- byte[] buffer = new byte[4096];
- int n;
- while ((n = is.read(buffer)) >= 0) {
- packageInSession.write(buffer, 0, n);
+ for (String resourceName : resourceNames) {
+ try (OutputStream packageInSession = session.openWrite(resourceName, 0, -1);
+ InputStream is = loader.getResourceAsStream(resourceName);) {
+ byte[] buffer = new byte[4096];
+ int n;
+ while ((n = is.read(buffer)) >= 0) {
+ packageInSession.write(buffer, 0, n);
+ }
}
}
@@ -345,7 +360,7 @@
PackageInstaller.SessionInfo info =
intent.getParcelableExtra(PackageInstaller.EXTRA_SESSION);
if (info != null && info.getSessionId() == sessionId) {
- if (info.isSessionReady() || info.isSessionFailed()) {
+ if (info.isStagedSessionReady() || info.isStagedSessionFailed()) {
try {
sessionStatus.put(info);
} catch (InterruptedException e) {
@@ -365,13 +380,13 @@
PackageInstaller.SessionInfo info = installer.getSessionInfo(sessionId);
try {
- if (info.isSessionReady() || info.isSessionFailed()) {
+ if (info.isStagedSessionReady() || info.isStagedSessionFailed()) {
sessionStatus.put(info);
}
info = sessionStatus.take();
context.unregisterReceiver(sessionUpdatedReceiver);
- if (info.isSessionFailed()) {
+ if (info.isStagedSessionFailed()) {
throw new AssertionError(info.getStagedSessionErrorMessage());
}
} catch (InterruptedException e) {
diff --git a/tests/RollbackTest/TestApp/ACrashingV2.xml b/tests/RollbackTest/TestApp/ACrashingV2.xml
index 5708d23..77bfd4e 100644
--- a/tests/RollbackTest/TestApp/ACrashingV2.xml
+++ b/tests/RollbackTest/TestApp/ACrashingV2.xml
@@ -23,7 +23,6 @@
<uses-sdk android:minSdkVersion="19" />
<application android:label="Rollback Test App A v2">
- <meta-data android:name="version" android:value="2" />
<receiver android:name="com.android.tests.rollback.testapp.ProcessUserData"
android:exported="true" />
<activity android:name="com.android.tests.rollback.testapp.CrashingMainActivity">
diff --git a/tests/RollbackTest/TestApp/Av1.xml b/tests/RollbackTest/TestApp/Av1.xml
index 996d831..63729fb 100644
--- a/tests/RollbackTest/TestApp/Av1.xml
+++ b/tests/RollbackTest/TestApp/Av1.xml
@@ -23,7 +23,6 @@
<uses-sdk android:minSdkVersion="19" />
<application android:label="Rollback Test App A v1">
- <meta-data android:name="version" android:value="1" />
<receiver android:name="com.android.tests.rollback.testapp.ProcessUserData"
android:exported="true" />
<activity android:name="com.android.tests.rollback.testapp.MainActivity">
diff --git a/tests/RollbackTest/TestApp/Av2.xml b/tests/RollbackTest/TestApp/Av2.xml
index 21c7260..f0e909f 100644
--- a/tests/RollbackTest/TestApp/Av2.xml
+++ b/tests/RollbackTest/TestApp/Av2.xml
@@ -23,7 +23,6 @@
<uses-sdk android:minSdkVersion="19" />
<application android:label="Rollback Test App A v2">
- <meta-data android:name="version" android:value="2" />
<receiver android:name="com.android.tests.rollback.testapp.ProcessUserData"
android:exported="true" />
<activity android:name="com.android.tests.rollback.testapp.MainActivity">
diff --git a/tests/RollbackTest/TestApp/Bv1.xml b/tests/RollbackTest/TestApp/Bv1.xml
index de0fd0d..ca9c2ec 100644
--- a/tests/RollbackTest/TestApp/Bv1.xml
+++ b/tests/RollbackTest/TestApp/Bv1.xml
@@ -23,7 +23,6 @@
<uses-sdk android:minSdkVersion="19" />
<application android:label="Rollback Test App B v1">
- <meta-data android:name="version" android:value="1" />
<receiver android:name="com.android.tests.rollback.testapp.ProcessUserData"
android:exported="true" />
<activity android:name="com.android.tests.rollback.testapp.MainActivity">
diff --git a/tests/RollbackTest/TestApp/Bv2.xml b/tests/RollbackTest/TestApp/Bv2.xml
index 6c2e66a..bd3e613 100644
--- a/tests/RollbackTest/TestApp/Bv2.xml
+++ b/tests/RollbackTest/TestApp/Bv2.xml
@@ -23,7 +23,6 @@
<uses-sdk android:minSdkVersion="19" />
<application android:label="Rollback Test App B v2">
- <meta-data android:name="version" android:value="2" />
<receiver android:name="com.android.tests.rollback.testapp.ProcessUserData"
android:exported="true" />
<activity android:name="com.android.tests.rollback.testapp.MainActivity">
diff --git a/tests/RollbackTest/TestApp/res_v1/values-anydpi/values.xml b/tests/RollbackTest/TestApp/res_v1/values-anydpi/values.xml
new file mode 100644
index 0000000..90d3da2
--- /dev/null
+++ b/tests/RollbackTest/TestApp/res_v1/values-anydpi/values.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <integer name="split_version">1</integer>
+</resources>
diff --git a/tests/RollbackTest/TestApp/res_v1/values/values.xml b/tests/RollbackTest/TestApp/res_v1/values/values.xml
new file mode 100644
index 0000000..0447c74
--- /dev/null
+++ b/tests/RollbackTest/TestApp/res_v1/values/values.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <integer name="app_version">1</integer>
+ <integer name="split_version">0</integer>
+</resources>
diff --git a/tests/RollbackTest/TestApp/res_v2/values-anydpi/values.xml b/tests/RollbackTest/TestApp/res_v2/values-anydpi/values.xml
new file mode 100644
index 0000000..9a1aa7f
--- /dev/null
+++ b/tests/RollbackTest/TestApp/res_v2/values-anydpi/values.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <integer name="split_version">2</integer>
+</resources>
diff --git a/tests/RollbackTest/TestApp/res_v2/values/values.xml b/tests/RollbackTest/TestApp/res_v2/values/values.xml
new file mode 100644
index 0000000..fd988f5
--- /dev/null
+++ b/tests/RollbackTest/TestApp/res_v2/values/values.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <integer name="app_version">2</integer>
+ <integer name="split_version">0</integer>
+</resources>
diff --git a/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/ProcessUserData.java b/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/ProcessUserData.java
index fde6a83..38c658e 100644
--- a/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/ProcessUserData.java
+++ b/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/ProcessUserData.java
@@ -19,9 +19,7 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.os.Bundle;
+import android.content.res.Resources;
import java.io.File;
import java.io.FileNotFoundException;
@@ -35,6 +33,8 @@
*/
public class ProcessUserData extends BroadcastReceiver {
+ private static final String TAG = "RollbackTestApp";
+
/**
* Exception thrown in case of issue with user data.
*/
@@ -66,14 +66,19 @@
* @throws UserDataException in case of problems with app user data.
*/
public void processUserData(Context context) throws UserDataException {
- int appVersion = 0;
- try {
- ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(
- context.getPackageName(), PackageManager.GET_META_DATA);
- Bundle bundle = appInfo.metaData;
- appVersion = bundle.getInt("version");
- } catch (PackageManager.NameNotFoundException e) {
- throw new UserDataException("Unable to get app version info", e);
+ Resources res = context.getResources();
+ String packageName = context.getPackageName();
+
+ int appVersionId = res.getIdentifier("app_version", "integer", packageName);
+ int appVersion = res.getInteger(appVersionId);
+
+ int splitVersionId = res.getIdentifier("split_version", "integer", packageName);
+ int splitVersion = res.getInteger(splitVersionId);
+
+ // Make sure the app version and split versions are compatible.
+ if (appVersion != splitVersion) {
+ throw new UserDataException("Split version " + splitVersion
+ + " does not match app version " + appVersion);
}
// Read the version of the app's user data and ensure it is compatible
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index 0032960..ffc1a92 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -44,6 +44,7 @@
Maybe<ResourceName> ToResourceName(
const android::ResTable::resource_name& name_in) {
+ // TODO: Remove this when ResTable and AssetManager(1) are removed from AAPT2
ResourceName name_out;
if (!name_in.package) {
return {};
@@ -79,6 +80,41 @@
return name_out;
}
+Maybe<ResourceName> ToResourceName(const android::AssetManager2::ResourceName& name_in) {
+ ResourceName name_out;
+ if (!name_in.package) {
+ return {};
+ }
+
+ name_out.package = std::string(name_in.package, name_in.package_len);
+
+ const ResourceType* type;
+ if (name_in.type16) {
+ type = ParseResourceType(
+ util::Utf16ToUtf8(StringPiece16(name_in.type16, name_in.type_len)));
+ } else if (name_in.type) {
+ type = ParseResourceType(StringPiece(name_in.type, name_in.type_len));
+ } else {
+ return {};
+ }
+
+ if (!type) {
+ return {};
+ }
+
+ name_out.type = *type;
+
+ if (name_in.entry16) {
+ name_out.entry =
+ util::Utf16ToUtf8(StringPiece16(name_in.entry16, name_in.entry_len));
+ } else if (name_in.entry) {
+ name_out.entry = std::string(name_in.entry, name_in.entry_len);
+ } else {
+ return {};
+ }
+ return name_out;
+}
+
bool ParseResourceName(const StringPiece& str, ResourceNameRef* out_ref,
bool* out_private) {
if (str.empty()) {
diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h
index e282fd58..a8a3120 100644
--- a/tools/aapt2/ResourceUtils.h
+++ b/tools/aapt2/ResourceUtils.h
@@ -20,6 +20,7 @@
#include <functional>
#include <memory>
+#include "androidfw/AssetManager2.h"
#include "androidfw/ConfigDescription.h"
#include "androidfw/ResourceTypes.h"
#include "androidfw/StringPiece.h"
@@ -78,6 +79,12 @@
const android::ResTable::resource_name& name);
/**
+ * Convert an android::AssetManager2::ResourceName to an aapt::ResourceName struct.
+ */
+Maybe<ResourceName> ToResourceName(
+ const android::AssetManager2::ResourceName& name_in);
+
+/**
* Returns a boolean value if the string is equal to TRUE, true, True, FALSE,
* false, or False.
*/
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index 0512bdc..bec6c69 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -769,7 +769,10 @@
auto collection = util::make_unique<io::FileCollection>();
// Collect data from the path for each input file.
- for (const std::string& arg : args) {
+ std::vector<std::string> sorted_args = args;
+ std::sort(sorted_args.begin(), sorted_args.end());
+
+ for (const std::string& arg : sorted_args) {
collection->InsertFile(arg);
}
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 22edd2f..a7b8d25 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -717,28 +717,20 @@
return true;
}
-static int32_t FindFrameworkAssetManagerCookie(const android::AssetManager& assets) {
+static android::ApkAssetsCookie FindFrameworkAssetManagerCookie(
+ const android::AssetManager2& assets) {
using namespace android;
// Find the system package (0x01). AAPT always generates attributes with the type 0x01, so
// we're looking for the first attribute resource in the system package.
- const ResTable& table = assets.getResources(true);
- Res_value val;
- ssize_t idx = table.getResource(0x01010000, &val, true);
- if (idx != NO_ERROR) {
- // Try as a bag.
- const ResTable::bag_entry* entry;
- ssize_t cnt = table.lockBag(0x01010000, &entry);
- if (cnt >= 0) {
- idx = entry->stringBlock;
- }
- table.unlockBag(entry);
- }
+ Res_value val{};
+ ResTable_config config{};
+ uint32_t type_spec_flags;
+ ApkAssetsCookie idx = assets.GetResource(0x01010000, true /** may_be_bag */,
+ 0 /** density_override */, &val, &config,
+ &type_spec_flags);
- if (idx < 0) {
- return 0;
- }
- return table.getTableCookie(idx);
+ return idx;
}
class Linker {
@@ -750,17 +742,17 @@
file_collection_(util::make_unique<io::FileCollection>()) {
}
- void ExtractCompileSdkVersions(android::AssetManager* assets) {
+ void ExtractCompileSdkVersions(android::AssetManager2* assets) {
using namespace android;
- int32_t cookie = FindFrameworkAssetManagerCookie(*assets);
- if (cookie == 0) {
+ android::ApkAssetsCookie cookie = FindFrameworkAssetManagerCookie(*assets);
+ if (cookie == android::kInvalidCookie) {
// No Framework assets loaded. Not a failure.
return;
}
std::unique_ptr<Asset> manifest(
- assets->openNonAsset(cookie, kAndroidManifestPath, Asset::AccessMode::ACCESS_BUFFER));
+ assets->OpenNonAsset(kAndroidManifestPath, cookie, Asset::AccessMode::ACCESS_BUFFER));
if (manifest == nullptr) {
// No errors.
return;
diff --git a/tools/aapt2/io/FileSystem.cpp b/tools/aapt2/io/FileSystem.cpp
index 51cc903..e15f935 100644
--- a/tools/aapt2/io/FileSystem.cpp
+++ b/tools/aapt2/io/FileSystem.cpp
@@ -79,6 +79,7 @@
return nullptr;
}
+ std::vector<std::string> sorted_files;
while (struct dirent *entry = readdir(d.get())) {
std::string prefix_path = root.to_string();
file::AppendPath(&prefix_path, entry->d_name);
@@ -105,10 +106,15 @@
continue;
}
- collection->InsertFile(full_path);
+ sorted_files.push_back(full_path);
}
}
+ std::sort(sorted_files.begin(), sorted_files.end());
+ for (const std::string& full_path : sorted_files) {
+ collection->InsertFile(full_path);
+ }
+
return collection;
}
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index a844a43..78e0074 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -20,9 +20,11 @@
#include "android-base/logging.h"
#include "android-base/stringprintf.h"
-#include "androidfw/AssetManager.h"
+#include "androidfw/Asset.h"
+#include "androidfw/AssetManager2.h"
#include "androidfw/ConfigDescription.h"
#include "androidfw/ResourceTypes.h"
+#include "androidfw/ResourceUtils.h"
#include "NameMangler.h"
#include "Resource.h"
@@ -30,6 +32,7 @@
#include "ValueVisitor.h"
#include "util/Util.h"
+using ::android::ApkAssets;
using ::android::ConfigDescription;
using ::android::StringPiece;
using ::android::StringPiece16;
@@ -214,51 +217,75 @@
}
bool AssetManagerSymbolSource::AddAssetPath(const StringPiece& path) {
- int32_t cookie = 0;
- return assets_.addAssetPath(android::String8(path.data(), path.size()), &cookie);
+ if (std::unique_ptr<const ApkAssets> apk = ApkAssets::Load(path.data())) {
+ apk_assets_.push_back(std::move(apk));
+
+ std::vector<const ApkAssets*> apk_assets;
+ for (const std::unique_ptr<const ApkAssets>& apk_asset : apk_assets_) {
+ apk_assets.push_back(apk_asset.get());
+ }
+
+ asset_manager_.SetApkAssets(apk_assets, true /* invalidate_caches */,
+ false /* filter_incompatible_configs */);
+ return true;
+ }
+ return false;
}
std::map<size_t, std::string> AssetManagerSymbolSource::GetAssignedPackageIds() const {
std::map<size_t, std::string> package_map;
- const android::ResTable& table = assets_.getResources(false);
- const size_t package_count = table.getBasePackageCount();
- for (size_t i = 0; i < package_count; i++) {
- package_map[table.getBasePackageId(i)] =
- util::Utf16ToUtf8(android::StringPiece16(table.getBasePackageName(i).string()));
- }
+ asset_manager_.ForEachPackage([&package_map](const std::string& name, uint8_t id) -> bool {
+ package_map.insert(std::make_pair(id, name));
+ return true;
+ });
+
return package_map;
}
bool AssetManagerSymbolSource::IsPackageDynamic(uint32_t packageId) const {
- return assets_.getResources(false).isPackageDynamic(packageId);
+ if (packageId == 0) {
+ return true;
+ }
+
+ for (const std::unique_ptr<const ApkAssets>& assets : apk_assets_) {
+ for (const std::unique_ptr<const android::LoadedPackage>& loaded_package
+ : assets->GetLoadedArsc()->GetPackages()) {
+ if (packageId == loaded_package->GetPackageId() && loaded_package->IsDynamic()) {
+ return true;
+ }
+ }
+ }
+
+ return false;
}
static std::unique_ptr<SymbolTable::Symbol> LookupAttributeInTable(
- const android::ResTable& table, ResourceId id) {
- // Try as a bag.
- const android::ResTable::bag_entry* entry;
- ssize_t count = table.lockBag(id.id, &entry);
- if (count < 0) {
- table.unlockBag(entry);
+ android::AssetManager2& am, ResourceId id) {
+ if (am.GetApkAssets().empty()) {
+ return {};
+ }
+
+ const android::ResolvedBag* bag = am.GetBag(id.id);
+ if (bag == nullptr) {
return nullptr;
}
// We found a resource.
std::unique_ptr<SymbolTable::Symbol> s = util::make_unique<SymbolTable::Symbol>(id);
- // Check to see if it is an attribute.
- for (size_t i = 0; i < (size_t)count; i++) {
- if (entry[i].map.name.ident == android::ResTable_map::ATTR_TYPE) {
- s->attribute = std::make_shared<Attribute>(entry[i].map.value.data);
+ const size_t count = bag->entry_count;
+ for (uint32_t i = 0; i < count; i++) {
+ if (bag->entries[i].key == android::ResTable_map::ATTR_TYPE) {
+ s->attribute = std::make_shared<Attribute>(bag->entries[i].value.data);
break;
}
}
if (s->attribute) {
- for (size_t i = 0; i < (size_t)count; i++) {
- const android::ResTable_map& map_entry = entry[i].map;
- if (Res_INTERNALID(map_entry.name.ident)) {
- switch (map_entry.name.ident) {
+ for (size_t i = 0; i < count; i++) {
+ const android::ResolvedBag::Entry& map_entry = bag->entries[i];
+ if (Res_INTERNALID(map_entry.key)) {
+ switch (map_entry.key) {
case android::ResTable_map::ATTR_MIN:
s->attribute->min_int = static_cast<int32_t>(map_entry.value.data);
break;
@@ -269,74 +296,65 @@
continue;
}
- android::ResTable::resource_name entry_name;
- if (!table.getResourceName(map_entry.name.ident, false, &entry_name)) {
- table.unlockBag(entry);
+ android::AssetManager2::ResourceName name;
+ if (!am.GetResourceName(map_entry.key, &name)) {
return nullptr;
}
- Maybe<ResourceName> parsed_name = ResourceUtils::ToResourceName(entry_name);
+ Maybe<ResourceName> parsed_name = ResourceUtils::ToResourceName(name);
if (!parsed_name) {
return nullptr;
}
Attribute::Symbol symbol;
symbol.symbol.name = parsed_name.value();
- symbol.symbol.id = ResourceId(map_entry.name.ident);
+ symbol.symbol.id = ResourceId(map_entry.key);
symbol.value = map_entry.value.data;
s->attribute->symbols.push_back(std::move(symbol));
}
}
- table.unlockBag(entry);
+
return s;
}
std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindByName(
const ResourceName& name) {
- const android::ResTable& table = assets_.getResources(false);
+ const std::string mangled_entry = NameMangler::MangleEntry(name.package, name.entry);
- const std::u16string package16 = util::Utf8ToUtf16(name.package);
- const std::u16string type16 = util::Utf8ToUtf16(to_string(name.type));
- const std::u16string entry16 = util::Utf8ToUtf16(name.entry);
- const std::u16string mangled_entry16 =
- util::Utf8ToUtf16(NameMangler::MangleEntry(name.package, name.entry));
-
+ bool found = false;
+ ResourceId res_id = 0;
uint32_t type_spec_flags;
- ResourceId res_id;
// There can be mangled resources embedded within other packages. Here we will
// look into each package and look-up the mangled name until we find the resource.
- const size_t count = table.getBasePackageCount();
- for (size_t i = 0; i < count; i++) {
- const android::String16 package_name = table.getBasePackageName(i);
- StringPiece16 real_package16 = package16;
- StringPiece16 real_entry16 = entry16;
- std::u16string scratch_entry16;
- if (StringPiece16(package_name) != package16) {
- real_entry16 = mangled_entry16;
- real_package16 = package_name.string();
+ asset_manager_.ForEachPackage([&](const std::string& package_name, uint8_t id) -> bool {
+ ResourceName real_name(name.package, name.type, name.entry);
+
+ if (package_name != name.package) {
+ real_name.entry = mangled_entry;
+ real_name.package = package_name;
}
- type_spec_flags = 0;
- res_id = table.identifierForName(real_entry16.data(), real_entry16.size(), type16.data(),
- type16.size(), real_package16.data(), real_package16.size(),
- &type_spec_flags);
- if (res_id.is_valid()) {
- break;
+ res_id = asset_manager_.GetResourceId(real_name.to_string());
+ if (res_id.is_valid() && asset_manager_.GetResourceFlags(res_id.id, &type_spec_flags)) {
+ found = true;
+ return false;
}
- }
- if (!res_id.is_valid()) {
+ return true;
+ });
+
+ if (!found) {
return {};
}
std::unique_ptr<SymbolTable::Symbol> s;
if (name.type == ResourceType::kAttr) {
- s = LookupAttributeInTable(table, res_id);
+ s = LookupAttributeInTable(asset_manager_, res_id);
} else {
s = util::make_unique<SymbolTable::Symbol>();
s->id = res_id;
- s->is_dynamic = table.isResourceDynamic(res_id.id);
+ s->is_dynamic = IsPackageDynamic(ResourceId(res_id).package_id());
}
if (s) {
@@ -346,13 +364,13 @@
return {};
}
-static Maybe<ResourceName> GetResourceName(const android::ResTable& table,
+static Maybe<ResourceName> GetResourceName(android::AssetManager2& am,
ResourceId id) {
- android::ResTable::resource_name res_name = {};
- if (!table.getResourceName(id.id, true, &res_name)) {
+ android::AssetManager2::ResourceName name;
+ if (!am.GetResourceName(id.id, &name)) {
return {};
}
- return ResourceUtils::ToResourceName(res_name);
+ return ResourceUtils::ToResourceName(name);
}
std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindById(
@@ -361,22 +379,30 @@
// Exit early and avoid the error logs from AssetManager.
return {};
}
- const android::ResTable& table = assets_.getResources(false);
- Maybe<ResourceName> maybe_name = GetResourceName(table, id);
+
+ if (apk_assets_.empty()) {
+ return {};
+ }
+
+ Maybe<ResourceName> maybe_name = GetResourceName(asset_manager_, id);
if (!maybe_name) {
return {};
}
- uint32_t type_spec_flags = 0;
- table.getResourceFlags(id.id, &type_spec_flags);
+ uint32_t type_spec_flags = 0;
+ if (!asset_manager_.GetResourceFlags(id.id, &type_spec_flags)) {
+ return {};
+ }
+
+ ResourceName& name = maybe_name.value();
std::unique_ptr<SymbolTable::Symbol> s;
- if (maybe_name.value().type == ResourceType::kAttr) {
- s = LookupAttributeInTable(table, id);
+ if (name.type == ResourceType::kAttr) {
+ s = LookupAttributeInTable(asset_manager_, id);
} else {
s = util::make_unique<SymbolTable::Symbol>();
s->id = id;
- s->is_dynamic = table.isResourceDynamic(id.id);
+ s->is_dynamic = IsPackageDynamic(ResourceId(id).package_id());
}
if (s) {
diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h
index 2d8bd02..6997cd6 100644
--- a/tools/aapt2/process/SymbolTable.h
+++ b/tools/aapt2/process/SymbolTable.h
@@ -22,7 +22,8 @@
#include <vector>
#include "android-base/macros.h"
-#include "androidfw/AssetManager.h"
+#include "androidfw/Asset.h"
+#include "androidfw/AssetManager2.h"
#include "utils/JenkinsHash.h"
#include "utils/LruCache.h"
@@ -201,12 +202,13 @@
std::unique_ptr<SymbolTable::Symbol> FindByReference(
const Reference& ref) override;
- android::AssetManager* GetAssetManager() {
- return &assets_;
+ android::AssetManager2* GetAssetManager() {
+ return &asset_manager_;
}
private:
- android::AssetManager assets_;
+ android::AssetManager2 asset_manager_;
+ std::vector<std::unique_ptr<const android::ApkAssets>> apk_assets_;
DISALLOW_COPY_AND_ASSIGN(AssetManagerSymbolSource);
};
diff --git a/tools/aapt2/process/SymbolTable_test.cpp b/tools/aapt2/process/SymbolTable_test.cpp
index 1f59d70..ddc2101 100644
--- a/tools/aapt2/process/SymbolTable_test.cpp
+++ b/tools/aapt2/process/SymbolTable_test.cpp
@@ -76,40 +76,54 @@
EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("com.android.lib:id/foo")), NotNull());
}
-TEST(SymbolTableTest, FindByNameWhenSymbolIsMangledInResTable) {
+using SymbolTableTestFixture = CommandTestFixture;
+TEST_F(SymbolTableTestFixture, FindByNameWhenSymbolIsMangledInResTable) {
using namespace android;
+ StdErrDiagnostics diag;
- std::unique_ptr<IAaptContext> context =
- test::ContextBuilder()
- .SetCompilationPackage("com.android.app")
- .SetPackageId(0x7f)
- .SetPackageType(PackageType::kApp)
- .SetMinSdkVersion(SDK_LOLLIPOP_MR1)
- .SetNameManglerPolicy(NameManglerPolicy{"com.android.app"})
- .Build();
+ // Create a static library.
+ const std::string static_lib_compiled_files_dir = GetTestPath("static-lib-compiled");
+ ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
+ R"(<?xml version="1.0" encoding="utf-8"?>
+ <resources>
+ <item type="id" name="foo"/>
+ </resources>)",
+ static_lib_compiled_files_dir, &diag));
- // Create a ResourceTable with a mangled resource, simulating a static library being merged into
- // the main application package.
- std::unique_ptr<ResourceTable> table =
- test::ResourceTableBuilder()
- .AddSimple("com.android.app:id/" + NameMangler::MangleEntry("com.android.lib", "foo"),
- ResourceId(0x7f020000))
- .AddSimple("com.android.app:id/bar", ResourceId(0x7f020001))
- .Build();
+ const std::string static_lib_apk = GetTestPath("static_lib.apk");
+ std::vector<std::string> link_args = {
+ "--manifest", GetDefaultManifest("com.android.lib"),
+ "--min-sdk-version", "22",
+ "--static-lib",
+ "-o", static_lib_apk,
+ };
+ ASSERT_TRUE(Link(link_args, static_lib_compiled_files_dir, &diag));
- BigBuffer buffer(1024u);
- TableFlattener flattener({}, &buffer);
- ASSERT_TRUE(flattener.Consume(context.get(), table.get()));
+ // Merge the static library into the main application package. The static library resources will
+ // be mangled with the library package name.
+ const std::string app_compiled_files_dir = GetTestPath("app-compiled");
+ ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
+ R"(<?xml version="1.0" encoding="utf-8"?>
+ <resources>
+ <item type="id" name="bar"/>
+ </resources>)",
+ app_compiled_files_dir, &diag));
- std::unique_ptr<uint8_t[]> data = util::Copy(buffer);
+ const std::string out_apk = GetTestPath("out.apk");
+ link_args = {
+ "--manifest", GetDefaultManifest("com.android.app"),
+ "--min-sdk-version", "22",
+ "-o", out_apk,
+ static_lib_apk
+ };
+ ASSERT_TRUE(Link(link_args, app_compiled_files_dir, &diag));
// Construct the test AssetManager.
auto asset_manager_source = util::make_unique<AssetManagerSymbolSource>();
- ResTable& res_table = const_cast<ResTable&>(
- asset_manager_source->GetAssetManager()->getResources(false /*required*/));
- ASSERT_THAT(res_table.add(data.get(), buffer.size()), Eq(NO_ERROR));
+ asset_manager_source->AddAssetPath(out_apk);
- SymbolTable symbol_table(context->GetNameMangler());
+ NameMangler name_mangler(NameManglerPolicy{"com.android.app"});
+ SymbolTable symbol_table(&name_mangler);
symbol_table.AppendSource(std::move(asset_manager_source));
EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("com.android.lib:id/foo")), NotNull());
diff --git a/tools/aapt2/test/Fixture.cpp b/tools/aapt2/test/Fixture.cpp
index 3fcdfb7..a51b4a4 100644
--- a/tools/aapt2/test/Fixture.cpp
+++ b/tools/aapt2/test/Fixture.cpp
@@ -37,6 +37,8 @@
namespace aapt {
+const char* CommandTestFixture::kDefaultPackageName = "com.aapt.command.test";
+
void ClearDirectory(const android::StringPiece& path) {
const std::string root_dir = path.to_string();
std::unique_ptr<DIR, decltype(closedir)*> dir(opendir(root_dir.data()), closedir);
@@ -124,12 +126,12 @@
return LinkCommand(diag).Execute(link_args, &std::cerr) == 0;
}
-std::string CommandTestFixture::GetDefaultManifest() {
+std::string CommandTestFixture::GetDefaultManifest(const char* package_name) {
const std::string manifest_file = GetTestPath("AndroidManifest.xml");
- CHECK(WriteFile(manifest_file, R"(
+ CHECK(WriteFile(manifest_file, android::base::StringPrintf(R"(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.aapt.command.test">
- </manifest>)"));
+ package="%s">
+ </manifest>)", package_name)));
return manifest_file;
}
diff --git a/tools/aapt2/test/Fixture.h b/tools/aapt2/test/Fixture.h
index 3079c75..fce2aeb 100644
--- a/tools/aapt2/test/Fixture.h
+++ b/tools/aapt2/test/Fixture.h
@@ -81,7 +81,7 @@
IDiagnostics* diag);
// Creates a minimal android manifest within the test directory and returns the file path.
- std::string GetDefaultManifest();
+ std::string GetDefaultManifest(const char* package_name = kDefaultPackageName);
// Returns pointer to data inside APK files
std::unique_ptr<io::IData> OpenFileAsData(LoadedApk* apk,
@@ -91,6 +91,7 @@
void AssertLoadXml(LoadedApk* apk, const io::IData* data,
android::ResXMLTree* out_tree);
+ static const char* kDefaultPackageName;
private:
DISALLOW_COPY_AND_ASSIGN(CommandTestFixture);
};
diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp
index 604b257..5d57de6 100644
--- a/tools/aapt2/util/Files.cpp
+++ b/tools/aapt2/util/Files.cpp
@@ -102,12 +102,21 @@
#endif
bool mkdirs(const std::string& path) {
- #ifdef _WIN32
- // Start after the drive path if present. Calling mkdir with only the drive will cause an error.
- size_t current_pos = 1u;
- if (path.size() >= 3 && path[1] == ':' &&
- (path[2] == '\\' || path[2] == '/')) {
- current_pos = 3u;
+ #ifdef _WIN32
+ // Start after the long path prefix if present.
+ bool require_drive = false;
+ size_t current_pos = 0u;
+ if (util::StartsWith(path, R"(\\?\)")) {
+ require_drive = true;
+ current_pos = 4u;
+ }
+
+ // Start after the drive path if present.
+ if (path.size() >= 3 && path[current_pos + 1] == ':' &&
+ (path[current_pos + 2] == '\\' || path[current_pos + 2] == '/')) {
+ current_pos += 3u;
+ } else if (require_drive) {
+ return false;
}
#else
// Start after the first character so that we don't consume the root '/'.
diff --git a/tools/aapt2/util/Files_test.cpp b/tools/aapt2/util/Files_test.cpp
index 202cc26..6c38080 100644
--- a/tools/aapt2/util/Files_test.cpp
+++ b/tools/aapt2/util/Files_test.cpp
@@ -19,6 +19,7 @@
#include <sstream>
#include "android-base/stringprintf.h"
+#include "android-base/utf8.h"
#include "test/Test.h"
@@ -65,5 +66,40 @@
EXPECT_EQ(expected_path_, base);
}
+#ifdef _WIN32
+TEST_F(FilesTest, WindowsMkdirsLongPath) {
+ // Creating directory paths longer than the Windows maximum path length (260 charatcers) should
+ // succeed.
+ const std::string kDirName = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
+ const size_t kRecursiveDepth = 10u;
+
+ // Recursively create the test file path and clean up the created directories after the files have
+ // been created.
+ std::function<void(std::string, size_t)> CreateResursiveDirs =
+ [&kDirName, &CreateResursiveDirs](std::string current_path, const size_t n) -> void {
+ AppendPath(¤t_path, kDirName);
+
+ if (n == 0) {
+ ASSERT_TRUE(file::mkdirs(current_path)) << "Failed to create path " << current_path;
+ } else {
+ CreateResursiveDirs(current_path, n - 1);
+ }
+
+ // Clean up the created directories.
+ _rmdir(current_path.data());
+ };
+
+ CreateResursiveDirs(
+ android::base::StringPrintf(R"(\\?\%s)", android::base::GetExecutableDirectory().data()),
+ kRecursiveDepth);
+}
+
+TEST_F(FilesTest, WindowsMkdirsLongPathMissingDrive) {
+ ASSERT_FALSE(file::mkdirs(R"(\\?\local\path\to\file)"));
+ ASSERT_FALSE(file::mkdirs(R"(\\?\:local\path\to\file)"));
+ ASSERT_FALSE(file::mkdirs(R"(\\?\\local\path\to\file)"));
+}
+#endif
+
} // namespace files
} // namespace aapt
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 3881e9e..4d6ff48 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -16,6 +16,7 @@
package android.net.wifi;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
@@ -41,6 +42,8 @@
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.BitSet;
import java.util.HashMap;
@@ -339,6 +342,90 @@
public static final String[] strings = { "current", "disabled", "enabled" };
}
+ /**
+ * Security types we support.
+ */
+ /** @hide */
+ public static final int SECURITY_TYPE_OPEN = 0;
+ /** @hide */
+ public static final int SECURITY_TYPE_WEP = 1;
+ /** @hide */
+ public static final int SECURITY_TYPE_PSK = 2;
+ /** @hide */
+ public static final int SECURITY_TYPE_EAP = 3;
+ /** @hide */
+ public static final int SECURITY_TYPE_SAE = 4;
+ /** @hide */
+ public static final int SECURITY_TYPE_EAP_SUITE_B = 5;
+ /** @hide */
+ public static final int SECURITY_TYPE_OWE = 6;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "SECURITY_TYPE_" }, value = {
+ SECURITY_TYPE_OPEN,
+ SECURITY_TYPE_WEP,
+ SECURITY_TYPE_PSK,
+ SECURITY_TYPE_EAP,
+ SECURITY_TYPE_SAE,
+ SECURITY_TYPE_EAP_SUITE_B,
+ SECURITY_TYPE_OWE
+ })
+ public @interface SecurityType {}
+
+ /**
+ * @hide
+ * Set security params (sets the various bitsets exposed in WifiConfiguration).
+ *
+ * @param securityType One of the security types from {@link SecurityType}.
+ */
+ public void setSecurityParams(@SecurityType int securityType) {
+ // Clear all the bitsets.
+ allowedKeyManagement.clear();
+ allowedProtocols.clear();
+ allowedAuthAlgorithms.clear();
+ allowedPairwiseCiphers.clear();
+ allowedGroupCiphers.clear();
+ allowedGroupManagementCiphers.clear();
+ allowedSuiteBCiphers.clear();
+
+ switch (securityType) {
+ case SECURITY_TYPE_OPEN:
+ allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+ break;
+ case SECURITY_TYPE_WEP:
+ allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+ allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
+ allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
+ break;
+ case SECURITY_TYPE_PSK:
+ allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+ break;
+ case SECURITY_TYPE_EAP:
+ allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
+ allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X);
+ break;
+ case SECURITY_TYPE_SAE:
+ allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SAE);
+ requirePMF = true;
+ break;
+ case SECURITY_TYPE_EAP_SUITE_B:
+ allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SUITE_B_192);
+ allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256);
+ allowedGroupManagementCiphers.set(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256);
+ allowedSuiteBCiphers.set(WifiConfiguration.SuiteBCipher.ECDHE_ECDSA);
+ allowedSuiteBCiphers.set(WifiConfiguration.SuiteBCipher.ECDHE_RSA);
+ requirePMF = true;
+ break;
+ case SECURITY_TYPE_OWE:
+ allowedKeyManagement.set(WifiConfiguration.KeyMgmt.OWE);
+ requirePMF = true;
+ break;
+ default:
+ throw new IllegalArgumentException("unknown security type " + securityType);
+ }
+ }
+
/** @hide */
public static final int UNKNOWN_UID = -1;
diff --git a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
index 333b82c..c99bd2e 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
@@ -269,58 +269,26 @@
}
- /**
- * Set defaults for the various low level credential type fields in the newly created
- * WifiConfiguration object.
- *
- * See {@link com.android.server.wifi.WifiConfigManager#setDefaultsInWifiConfiguration(
- * WifiConfiguration)}.
- *
- * @param configuration provided WifiConfiguration object.
- */
- private static void setDefaultsInWifiConfiguration(
- @NonNull WifiConfiguration configuration) {
- configuration.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
- configuration.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
- configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
- configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
- configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
- }
-
private void setSecurityParamsInWifiConfiguration(
@NonNull WifiConfiguration configuration) {
if (!TextUtils.isEmpty(mWpa2PskPassphrase)) { // WPA-PSK network.
- configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+ configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK);
// WifiConfiguration.preSharedKey needs quotes around ASCII password.
configuration.preSharedKey = "\"" + mWpa2PskPassphrase + "\"";
} else if (!TextUtils.isEmpty(mWpa3SaePassphrase)) { // WPA3-SAE network.
- configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SAE);
- // PMF mandatory for SAE.
- configuration.requirePMF = true;
+ configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_SAE);
// WifiConfiguration.preSharedKey needs quotes around ASCII password.
configuration.preSharedKey = "\"" + mWpa3SaePassphrase + "\"";
} else if (mWpa2EnterpriseConfig != null) { // WPA-EAP network
- configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
- configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X);
+ configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP);
configuration.enterpriseConfig = mWpa2EnterpriseConfig;
} else if (mWpa3EnterpriseConfig != null) { // WPA3-SuiteB network
- configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SUITE_B_192);
- configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256);
- // TODO (b/113878056): Verify these params once we verify SuiteB configuration.
- configuration.allowedGroupManagementCiphers.set(
- WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256);
- configuration.allowedSuiteBCiphers.set(
- WifiConfiguration.SuiteBCipher.ECDHE_ECDSA);
- configuration.allowedSuiteBCiphers.set(
- WifiConfiguration.SuiteBCipher.ECDHE_RSA);
- configuration.requirePMF = true;
+ configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B);
configuration.enterpriseConfig = mWpa3EnterpriseConfig;
} else if (mIsEnhancedOpen) { // OWE network
- configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.OWE);
- // PMF mandatory.
- configuration.requirePMF = true;
+ configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OWE);
} else { // Open network
- configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+ configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OPEN);
}
}
@@ -330,7 +298,6 @@
*/
private WifiConfiguration buildWifiConfiguration() {
final WifiConfiguration wifiConfiguration = new WifiConfiguration();
- setDefaultsInWifiConfiguration(wifiConfiguration);
// WifiConfiguration.SSID needs quotes around unicode SSID.
if (mSsidPatternMatcher.getType() == PatternMatcher.PATTERN_LITERAL) {
wifiConfiguration.SSID = "\"" + mSsidPatternMatcher.getPath() + "\"";
diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
index 233fa2c..f02404f 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
@@ -303,58 +303,26 @@
return this;
}
- /**
- * Set defaults for the various low level credential type fields in the newly created
- * WifiConfiguration object.
- *
- * See {@link com.android.server.wifi.WifiConfigManager#setDefaultsInWifiConfiguration(
- * WifiConfiguration)}.
- *
- * @param configuration provided WifiConfiguration object.
- */
- private static void setDefaultsInWifiConfiguration(
- @NonNull WifiConfiguration configuration) {
- configuration.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
- configuration.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
- configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
- configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
- configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
- }
-
private void setSecurityParamsInWifiConfiguration(
@NonNull WifiConfiguration configuration) {
if (!TextUtils.isEmpty(mWpa2PskPassphrase)) { // WPA-PSK network.
- configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+ configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK);
// WifiConfiguration.preSharedKey needs quotes around ASCII password.
configuration.preSharedKey = "\"" + mWpa2PskPassphrase + "\"";
} else if (!TextUtils.isEmpty(mWpa3SaePassphrase)) { // WPA3-SAE network.
- configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SAE);
- // PMF mandatory for SAE.
- configuration.requirePMF = true;
+ configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_SAE);
// WifiConfiguration.preSharedKey needs quotes around ASCII password.
configuration.preSharedKey = "\"" + mWpa3SaePassphrase + "\"";
} else if (mWpa2EnterpriseConfig != null) { // WPA-EAP network
- configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
- configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X);
+ configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP);
configuration.enterpriseConfig = mWpa2EnterpriseConfig;
} else if (mWpa3EnterpriseConfig != null) { // WPA3-SuiteB network
- configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SUITE_B_192);
- configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256);
- // TODO (b/113878056): Verify these params once we verify SuiteB configuration.
- configuration.allowedGroupManagementCiphers.set(
- WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256);
- configuration.allowedSuiteBCiphers.set(
- WifiConfiguration.SuiteBCipher.ECDHE_ECDSA);
- configuration.allowedSuiteBCiphers.set(
- WifiConfiguration.SuiteBCipher.ECDHE_RSA);
- configuration.requirePMF = true;
+ configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B);
configuration.enterpriseConfig = mWpa3EnterpriseConfig;
} else if (mIsEnhancedOpen) { // OWE network
- configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.OWE);
- // PMF mandatory.
- configuration.requirePMF = true;
+ configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OWE);
} else { // Open network
- configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+ configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OPEN);
}
}
@@ -364,7 +332,6 @@
*/
private WifiConfiguration buildWifiConfiguration() {
final WifiConfiguration wifiConfiguration = new WifiConfiguration();
- setDefaultsInWifiConfiguration(wifiConfiguration);
// WifiConfiguration.SSID needs quotes around unicode SSID.
wifiConfiguration.SSID = "\"" + mSsid + "\"";
if (mBssid != null) {
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index acc0518..b73551f 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -259,8 +259,17 @@
* {@hide}
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public boolean ignoreLocationSettings;
+ /**
+ * This scan request will be hidden from app-ops noting for location information. This
+ * should only be used by FLP/NLP module on the device which is using the scan results to
+ * compute results for behalf on their clients. FLP/NLP module using this flag should ensure
+ * that they note in app-ops the eventual delivery of location information computed using
+ * these results to their client .
+ * {@hide}
+ */
+ @SystemApi
+ public boolean hideFromAppOps;
/** Implement the Parcelable interface {@hide} */
public int describeContents() {
@@ -279,6 +288,7 @@
dest.writeInt(isPnoScan ? 1 : 0);
dest.writeInt(type);
dest.writeInt(ignoreLocationSettings ? 1 : 0);
+ dest.writeInt(hideFromAppOps ? 1 : 0);
if (channels != null) {
dest.writeInt(channels.length);
for (int i = 0; i < channels.length; i++) {
@@ -314,6 +324,7 @@
settings.isPnoScan = in.readInt() == 1;
settings.type = in.readInt();
settings.ignoreLocationSettings = in.readInt() == 1;
+ settings.hideFromAppOps = in.readInt() == 1;
int num_channels = in.readInt();
settings.channels = new ChannelSpec[num_channels];
for (int i = 0; i < num_channels; i++) {
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
index f931ad2..479adbc 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
@@ -26,6 +26,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.regex.PatternSyntaxException;
/**
* A class representing a Wi-Fi P2p configuration for setting up a connection
@@ -252,7 +253,12 @@
* Specify the network name, a.k.a. group name,
* for creating or joining a group.
* <p>
- * Must be called - an empty network name is not valid.
+ * A network name shall begin with "DIRECT-xy". x and y are selected
+ * from the following character set: upper case letters, lower case
+ * letters and numbers.
+ * <p>
+ * Must be called - an empty network name or an network name
+ * not conforming to the P2P Group ID naming rule is not valid.
*
* @param networkName network name of a group.
* @return The builder to facilitate chaining
@@ -263,6 +269,14 @@
throw new IllegalArgumentException(
"network name must be non-empty.");
}
+ try {
+ if (!networkName.matches("^DIRECT-[a-zA-Z0-9]{2}.*")) {
+ throw new IllegalArgumentException(
+ "network name must starts with the prefix DIRECT-xy.");
+ }
+ } catch (PatternSyntaxException e) {
+ // can never happen (fixed pattern)
+ }
mNetworkName = networkName;
return this;
}
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
index bef33b7..feac0e5 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
@@ -69,16 +69,6 @@
assertEquals(MacAddress.ALL_ZEROS_ADDRESS, wifiNetworkSpecifier.bssidPatternMatcher.second);
assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedKeyManagement
.get(WifiConfiguration.KeyMgmt.NONE));
- assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedProtocols
- .get(WifiConfiguration.Protocol.RSN));
- assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedAuthAlgorithms
- .get(WifiConfiguration.AuthAlgorithm.OPEN));
- assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedPairwiseCiphers
- .get(WifiConfiguration.PairwiseCipher.CCMP));
- assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupCiphers
- .get(WifiConfiguration.GroupCipher.CCMP));
- assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupCiphers
- .get(WifiConfiguration.GroupCipher.TKIP));
}
/**
@@ -105,16 +95,6 @@
wifiNetworkSpecifier.bssidPatternMatcher.second);
assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedKeyManagement
.get(WifiConfiguration.KeyMgmt.WPA_PSK));
- assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedProtocols
- .get(WifiConfiguration.Protocol.RSN));
- assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedAuthAlgorithms
- .get(WifiConfiguration.AuthAlgorithm.OPEN));
- assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedPairwiseCiphers
- .get(WifiConfiguration.PairwiseCipher.CCMP));
- assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupCiphers
- .get(WifiConfiguration.GroupCipher.CCMP));
- assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupCiphers
- .get(WifiConfiguration.GroupCipher.TKIP));
assertEquals("\"" + TEST_PRESHARED_KEY + "\"",
wifiNetworkSpecifier.wifiConfiguration.preSharedKey);
}
@@ -150,16 +130,6 @@
.get(WifiConfiguration.KeyMgmt.WPA_EAP));
assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedKeyManagement
.get(WifiConfiguration.KeyMgmt.IEEE8021X));
- assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedProtocols
- .get(WifiConfiguration.Protocol.RSN));
- assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedAuthAlgorithms
- .get(WifiConfiguration.AuthAlgorithm.OPEN));
- assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedPairwiseCiphers
- .get(WifiConfiguration.PairwiseCipher.CCMP));
- assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupCiphers
- .get(WifiConfiguration.GroupCipher.CCMP));
- assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupCiphers
- .get(WifiConfiguration.GroupCipher.TKIP));
assertTrue(wifiNetworkSpecifier.wifiConfiguration.hiddenSSID);
assertEquals(enterpriseConfig.getEapMethod(),
wifiNetworkSpecifier.wifiConfiguration.enterpriseConfig.getEapMethod());
diff --git a/wifi/tests/src/android/net/wifi/p2p/WifiP2pConfigTest.java b/wifi/tests/src/android/net/wifi/p2p/WifiP2pConfigTest.java
new file mode 100644
index 0000000..560c88e
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/p2p/WifiP2pConfigTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2019 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.p2p;
+
+import static org.junit.Assert.fail;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Unit test harness for {@link android.net.wifi.p2p.WifiP2pConfig}
+ */
+@SmallTest
+public class WifiP2pConfigTest {
+ /**
+ * Check network name setter
+ */
+ @Test
+ public void testBuilderInvalidNetworkName() throws Exception {
+ WifiP2pConfig.Builder b = new WifiP2pConfig.Builder();
+
+ // sunny case
+ try {
+ b.setNetworkName("DIRECT-ab-Hello");
+ } catch (IllegalArgumentException e) {
+ fail("Unexpected IllegalArgumentException");
+ }
+
+ // sunny case, no trailing string
+ try {
+ b.setNetworkName("DIRECT-WR");
+ } catch (IllegalArgumentException e) {
+ fail("Unexpected IllegalArgumentException");
+ }
+
+ // less than 9 characters.
+ try {
+ b.setNetworkName("DIRECT-z");
+ fail("expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) { }
+
+ // not starts with DIRECT-xy.
+ try {
+ b.setNetworkName("ABCDEFGHIJK");
+ fail("expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) { }
+
+ // not starts with uppercase DIRECT-xy
+ try {
+ b.setNetworkName("direct-ab");
+ fail("expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) { }
+
+ // x and y are not selected from upper case letters, lower case letters or
+ // numbers.
+ try {
+ b.setNetworkName("direct-a?");
+ fail("expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) { }
+ }
+}