Merge "HwBlob: s/malloc/calloc/" into qt-dev
diff --git a/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java b/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java
index c149195..bd3b673 100644
--- a/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java
+++ b/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java
@@ -52,7 +52,7 @@
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
textClassificationManager.getTextClassifier();
- textClassificationManager.invalidate();
+ textClassificationManager.invalidateForTesting();
}
}
@@ -68,7 +68,7 @@
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
textClassificationManager.getTextClassifier();
- textClassificationManager.invalidate();
+ textClassificationManager.invalidateForTesting();
}
}
}
diff --git a/cmds/am/proto/instrumentation_data.proto b/cmds/am/proto/instrumentation_data.proto
index 8e29f96..23e9519 100644
--- a/cmds/am/proto/instrumentation_data.proto
+++ b/cmds/am/proto/instrumentation_data.proto
@@ -38,6 +38,7 @@
message TestStatus {
optional sint32 result_code = 3;
optional ResultsBundle results = 4;
+ optional string logcat = 5;
}
enum SessionStatusCode {
diff --git a/cmds/am/src/com/android/commands/am/Instrument.java b/cmds/am/src/com/android/commands/am/Instrument.java
index 70baa87..4d7b5a7 100644
--- a/cmds/am/src/com/android/commands/am/Instrument.java
+++ b/cmds/am/src/com/android/commands/am/Instrument.java
@@ -38,6 +38,7 @@
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStreamReader;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@@ -62,8 +63,15 @@
* other: Failure
*/
public class Instrument {
+ private static final String TAG = "am";
+
public static final String DEFAULT_LOG_DIR = "instrument-logs";
+ private static final int STATUS_TEST_PASSED = 0;
+ private static final int STATUS_TEST_STARTED = 1;
+ private static final int STATUS_TEST_FAILED_ASSERTION = -1;
+ private static final int STATUS_TEST_FAILED_OTHER = -2;
+
private final IActivityManager mAm;
private final IPackageManager mPm;
private final IWindowManager mWm;
@@ -207,6 +215,8 @@
private File mLog;
+ private long mTestStartMs;
+
ProtoStatusReporter() {
if (protoFile) {
if (logPath == null) {
@@ -241,10 +251,22 @@
Bundle results) {
final ProtoOutputStream proto = new ProtoOutputStream();
- final long token = proto.start(InstrumentationData.Session.TEST_STATUS);
+ final long testStatusToken = proto.start(InstrumentationData.Session.TEST_STATUS);
+
proto.write(InstrumentationData.TestStatus.RESULT_CODE, resultCode);
writeBundle(proto, InstrumentationData.TestStatus.RESULTS, results);
- proto.end(token);
+
+ if (resultCode == STATUS_TEST_STARTED) {
+ // Logcat -T takes wall clock time (!?)
+ mTestStartMs = System.currentTimeMillis();
+ } else {
+ if (mTestStartMs > 0) {
+ proto.write(InstrumentationData.TestStatus.LOGCAT, readLogcat(mTestStartMs));
+ }
+ mTestStartMs = 0;
+ }
+
+ proto.end(testStatusToken);
outputProto(proto);
}
@@ -254,12 +276,12 @@
Bundle results) {
final ProtoOutputStream proto = new ProtoOutputStream();
- final long token = proto.start(InstrumentationData.Session.SESSION_STATUS);
+ final long sessionStatusToken = proto.start(InstrumentationData.Session.SESSION_STATUS);
proto.write(InstrumentationData.SessionStatus.STATUS_CODE,
InstrumentationData.SESSION_FINISHED);
proto.write(InstrumentationData.SessionStatus.RESULT_CODE, resultCode);
writeBundle(proto, InstrumentationData.SessionStatus.RESULTS, results);
- proto.end(token);
+ proto.end(sessionStatusToken);
outputProto(proto);
}
@@ -268,11 +290,11 @@
public void onError(String errorText, boolean commandError) {
final ProtoOutputStream proto = new ProtoOutputStream();
- final long token = proto.start(InstrumentationData.Session.SESSION_STATUS);
+ final long sessionStatusToken = proto.start(InstrumentationData.Session.SESSION_STATUS);
proto.write(InstrumentationData.SessionStatus.STATUS_CODE,
InstrumentationData.SESSION_ABORTED);
proto.write(InstrumentationData.SessionStatus.ERROR_TEXT, errorText);
- proto.end(token);
+ proto.end(sessionStatusToken);
outputProto(proto);
}
@@ -514,5 +536,43 @@
}
}
}
+
+ private static String readLogcat(long startTimeMs) {
+ try {
+ // Figure out the timestamp arg for logcat.
+ final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+ final String timestamp = format.format(new Date(startTimeMs));
+
+ // Start the process
+ final Process process = new ProcessBuilder()
+ .command("logcat", "-d", "-v threadtime,uid", "-T", timestamp)
+ .start();
+
+ // Nothing to write. Don't let the command accidentally block.
+ process.getOutputStream().close();
+
+ // Read the output
+ final StringBuilder str = new StringBuilder();
+ final InputStreamReader reader = new InputStreamReader(process.getInputStream());
+ char[] buffer = new char[4096];
+ int amt;
+ while ((amt = reader.read(buffer, 0, buffer.length)) >= 0) {
+ if (amt > 0) {
+ str.append(buffer, 0, amt);
+ }
+ }
+
+ try {
+ process.waitFor();
+ } catch (InterruptedException ex) {
+ // We already have the text, drop the exception.
+ }
+
+ return str.toString();
+
+ } catch (IOException ex) {
+ return "Error reading logcat command:\n" + ex.toString();
+ }
+ }
}
diff --git a/cmds/incidentd/OWNERS b/cmds/incidentd/OWNERS
index cede4ea..bcdcfc3 100644
--- a/cmds/incidentd/OWNERS
+++ b/cmds/incidentd/OWNERS
@@ -1,3 +1,4 @@
joeo@google.com
-kwekua@google.com
+yaochen@google.com
yanmin@google.com
+zhouwenjie@google.com
diff --git a/cmds/statsd/OWNERS b/cmds/statsd/OWNERS
index 1315750..380e499 100644
--- a/cmds/statsd/OWNERS
+++ b/cmds/statsd/OWNERS
@@ -1,11 +1,7 @@
-bookatz@google.com
-cjyu@google.com
-dwchen@google.com
-jinyithu@google.com
+jianjin@google.com
joeo@google.com
-kwekua@google.com
+jtnguyen@google.com
+muhammadq@google.com
singhtejinder@google.com
-stlafon@google.com
yaochen@google.com
-yanglu@google.com
yro@google.com
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 318d90c..3a84b79 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -48,6 +48,7 @@
import "frameworks/base/core/proto/android/stats/enums.proto";
import "frameworks/base/core/proto/android/stats/intelligence/enums.proto";
import "frameworks/base/core/proto/android/stats/launcher/launcher.proto";
+import "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.proto";
import "frameworks/base/core/proto/android/stats/storage/storage_enums.proto";
import "frameworks/base/core/proto/android/stats/style/style_enums.proto";
import "frameworks/base/core/proto/android/telecomm/enums.proto";
@@ -283,6 +284,16 @@
ThermalThrottlingSeverityStateChanged thermal_throttling_severity_state_changed = 189;
RoleRequestResultReported role_request_result_reported =
190 [(log_from_module) = "permissioncontroller"];
+ MediametricsAudiopolicyReported mediametrics_audiopolicy_reported = 191;
+ MediametricsAudiorecordReported mediametrics_audiorecord_reported = 192;
+ MediametricsAudiothreadReported mediametrics_audiothread_reported = 193;
+ MediametricsAudiotrackReported mediametrics_audiotrack_reported = 194;
+ MediametricsCodecReported mediametrics_codec_reported = 195;
+ MediametricsDrmWidevineReported mediametrics_drm_widevine_reported = 196;
+ MediametricsExtractorReported mediametrics_extractor_reported = 197;
+ MediametricsMediadrmReported mediametrics_mediadrm_reported = 198;
+ MediametricsNuPlayerReported mediametrics_nuplayer_reported = 199;
+ MediametricsRecorderReported mediametrics_recorder_reported = 200;
}
// Pulled events will start at field 10000.
@@ -5815,6 +5826,160 @@
}
/**
+ * Track Media Codec usage
+ * Logged from:
+ * frameworks/av/media/libstagefright/MediaCodec.cpp
+ * frameworks/av/services/mediaanalytics/statsd_codec.cpp
+ */
+message MediametricsCodecReported {
+ optional int64 timestamp_nanos = 1;
+ optional string package_name = 2;
+ optional int64 package_version_code = 3;
+ optional int64 media_apex_version = 4;
+
+ optional android.stats.mediametrics.CodecData codec_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES];
+}
+
+/**
+ * Track Media Extractor (pulling video/audio streams out of containers) usage
+ * Logged from:
+ * frameworks/av/media/libstagefright/RemoteMediaExtractor.cpp
+ * frameworks/av/services/mediaanalytics/statsd_extractor.cpp
+ */
+message MediametricsExtractorReported {
+ optional int64 timestamp_nanos = 1;
+ optional string package_name = 2;
+ optional int64 package_version_code = 3;
+ optional int64 media_apex_version = 4;
+
+ optional android.stats.mediametrics.ExtractorData extractor_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES];
+}
+
+/**
+ * Track how we arbitrate between microphone/input requests.
+ * Logged from
+ * frameworks/av/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+ * frameworks/av/services/mediaanalytics/statsd_audiopolicy.cpp
+ */
+message MediametricsAudiopolicyReported {
+ optional int64 timestamp_nanos = 1;
+ optional string package_name = 2;
+ optional int64 package_version_code = 3;
+ optional int64 media_apex_version = 4;
+
+ optional android.stats.mediametrics.AudioPolicyData audiopolicy_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES];
+}
+
+/**
+ * Track how we arbitrate between microphone requests.
+ * Logged from
+ * frameworks/av/media/libaudioclient/AudioRecord.cpp
+ * frameworks/av/services/mediaanalytics/statsd_audiorecord.cpp
+ */
+message MediametricsAudiorecordReported {
+ optional int64 timestamp_nanos = 1;
+ optional string package_name = 2;
+ optional int64 package_version_code = 3;
+ optional int64 media_apex_version = 4;
+
+ optional android.stats.mediametrics.AudioRecordData audiorecord_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES];
+}
+
+/**
+ * Track how we arbitrate between microphone/input requests.
+ * Logged from
+ * frameworks/av/media/libnblog/ReportPerformance.cpp
+ * frameworks/av/services/mediaanalytics/statsd_audiothread.cpp
+ */
+message MediametricsAudiothreadReported {
+ optional int64 timestamp_nanos = 1;
+ optional string package_name = 2;
+ optional int64 package_version_code = 3;
+ optional int64 media_apex_version = 4;
+
+ optional android.stats.mediametrics.AudioThreadData audiothread_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES];
+}
+
+/**
+ * Track how we arbitrate between microphone/input requests.
+ * Logged from
+ * frameworks/av/media/libaudioclient/AudioTrack.cpp
+ * frameworks/av/services/mediaanalytics/statsd_audiotrack.cpp
+ */
+message MediametricsAudiotrackReported {
+ optional int64 timestamp_nanos = 1;
+ optional string package_name = 2;
+ optional int64 package_version_code = 3;
+ optional int64 media_apex_version = 4;
+
+ optional android.stats.mediametrics.AudioTrackData audiotrack_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES];
+}
+
+/**
+ * Track information about DRM framework performance
+ * Logged from
+ * frameworks/av/drm/libmediadrm/DrmHal.cpp
+ * frameworks/av/services/mediaanalytics/statsd_drm.cpp
+ */
+message MediametricsMediadrmReported {
+ optional int64 timestamp_nanos = 1;
+ optional string package_name = 2;
+ optional int64 package_version_code = 3;
+ optional int64 media_apex_version = 4;
+
+ // vendor+description tell about which DRM plugin is in use on this device
+ optional string vendor = 5;
+ optional string description = 6;
+ // from frameworks/av/drm/libmediadrm/protos/metrics.proto
+ optional bytes framework_stats = 7 [(android.os.statsd.log_mode) = MODE_BYTES];
+}
+
+/**
+ * Track information about the widevine DRM plugin performance
+ * Logged from
+ * vendor/widevine/libwvdrmengine/cdm/metrics
+ * frameworks/av/services/mediaanalytics/statsd_drm.cpp
+ */
+message MediametricsDrmWidevineReported {
+ optional int64 timestamp_nanos = 1;
+ optional string package_name = 2;
+ optional int64 package_version_code = 3;
+ optional int64 media_apex_version = 4;
+
+ optional bytes vendor_specific_stats = 5 [(android.os.statsd.log_mode) = MODE_BYTES];
+}
+
+/**
+ * Track information about recordings (e.g. camcorder)
+ * Logged from
+ * frameworks/av/media/libmediaplayerservice/StagefrightRecorder.cpp
+ * frameworks/av/services/mediaanalytics/statsd_recorder.cpp
+ */
+message MediametricsRecorderReported {
+ optional int64 timestamp_nanos = 1;
+ optional string package_name = 2;
+ optional int64 package_version_code = 3;
+ optional int64 media_apex_version = 4;
+
+ optional android.stats.mediametrics.RecorderData recorder_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES];
+}
+
+/**
+ * Track Media Player usage
+ * Logged from:
+ * frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+ * frameworks/av/services/mediaanalytics/statsd_nuplayer.cpp
+ */
+message MediametricsNuPlayerReported {
+ optional int64 timestamp_nanos = 1;
+ optional string package_name = 2;
+ optional int64 package_version_code = 3;
+ optional int64 media_apex_version = 4;
+
+ optional android.stats.mediametrics.NuPlayerData nuplayer_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES];
+}
+
+/**
* State of a dangerous permission requested by a package
*/
message DangerousPermissionState {
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
index fc7b778..3e705fd 100644
--- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
@@ -57,6 +57,7 @@
"AID_BLUETOOTH",
"AID_LMKD",
"com.android.managedprovisioning",
+ "AID_MEDIA",
"AID_NETWORK_STACK"
};
private static final Logger LOGGER = Logger.getLogger(TestDrive.class.getName());
diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index 1b7fbfe..0524450 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -95,7 +95,8 @@
+ "telecom set-phone-account-disabled: Disables the given phone account, if it \n"
+ " has already been registered with telecom.\n"
+ "\n"
- + "telecom set-default-dialer: Sets the default dialer to the given component. \n"
+ + "telecom set-default-dialer: Sets the override default dialer to the given "
+ + "component; this will override whatever the dialer role is set to. \n"
+ "\n"
+ "telecom get-default-dialer: Displays the current default dialer. \n"
+ "\n"
@@ -254,13 +255,8 @@
private void runSetDefaultDialer() throws RemoteException {
final String packageName = nextArgRequired();
- final boolean success = mTelecomService.setDefaultDialer(packageName);
- if (success) {
- System.out.println("Success - " + packageName + " set as default dialer.");
- } else {
- System.out.println("Error - " + packageName + " is not an installed Dialer app, \n"
- + " or is already the default dialer.");
- }
+ mTelecomService.setTestDefaultDialer(packageName);
+ System.out.println("Success - " + packageName + " set as override default dialer.");
}
private void runGetDefaultDialer() throws RemoteException {
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index fc6fffa..b64b2dc 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -317,7 +317,7 @@
* regions and avoid focus switches by touches on this view.
*/
public void onLocationChanged() {
- updateTapExcludeRegion();
+ updateLocationAndTapExcludeRegion();
}
@Override
@@ -329,37 +329,50 @@
public boolean gatherTransparentRegion(Region region) {
// The tap exclude region may be affected by any view on top of it, so we detect the
// possible change by monitoring this function.
- updateTapExcludeRegion();
+ updateLocationAndTapExcludeRegion();
return super.gatherTransparentRegion(region);
}
- /** Compute and send current tap exclude region to WM for this view. */
- private void updateTapExcludeRegion() {
- if (!isAttachedToWindow()) {
+ /**
+ * Sends current location in window and tap exclude region to WM for this view.
+ */
+ private void updateLocationAndTapExcludeRegion() {
+ if (mVirtualDisplay == null || !isAttachedToWindow()) {
return;
}
+ try {
+ int x = mLocationInWindow[0];
+ int y = mLocationInWindow[1];
+ getLocationInWindow(mLocationInWindow);
+ if (x != mLocationInWindow[0] || y != mLocationInWindow[1]) {
+ x = mLocationInWindow[0];
+ y = mLocationInWindow[1];
+ WindowManagerGlobal.getWindowSession().updateDisplayContentLocation(
+ getWindow(), x, y, mVirtualDisplay.getDisplay().getDisplayId());
+ }
+ updateTapExcludeRegion(x, y);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ /** Computes and sends current tap exclude region to WM for this view. */
+ private void updateTapExcludeRegion(int x, int y) throws RemoteException {
if (!canReceivePointerEvents()) {
cleanTapExcludeRegion();
return;
}
- try {
- getLocationInWindow(mLocationInWindow);
- final int x = mLocationInWindow[0];
- final int y = mLocationInWindow[1];
- mTapExcludeRegion.set(x, y, x + getWidth(), y + getHeight());
+ mTapExcludeRegion.set(x, y, x + getWidth(), y + getHeight());
- // There might be views on top of us. We need to subtract those areas from the tap
- // exclude region.
- final ViewParent parent = getParent();
- if (parent instanceof ViewGroup) {
- ((ViewGroup) parent).subtractObscuredTouchableRegion(mTapExcludeRegion, this);
- }
-
- WindowManagerGlobal.getWindowSession().updateTapExcludeRegion(getWindow(), hashCode(),
- mTapExcludeRegion);
- } catch (RemoteException e) {
- e.rethrowAsRuntimeException();
+ // There might be views on top of us. We need to subtract those areas from the tap
+ // exclude region.
+ final ViewParent parent = getParent();
+ if (parent != null) {
+ parent.subtractObscuredTouchableRegion(mTapExcludeRegion, this);
}
+
+ WindowManagerGlobal.getWindowSession().updateTapExcludeRegion(getWindow(), hashCode(),
+ mTapExcludeRegion);
}
private class SurfaceCallback implements SurfaceHolder.Callback {
@@ -379,7 +392,7 @@
mVirtualDisplay.setDisplayState(true);
}
- updateTapExcludeRegion();
+ updateLocationAndTapExcludeRegion();
}
@Override
@@ -387,7 +400,7 @@
if (mVirtualDisplay != null) {
mVirtualDisplay.resize(width, height, getBaseDisplayDensity());
}
- updateTapExcludeRegion();
+ updateLocationAndTapExcludeRegion();
}
@Override
@@ -471,7 +484,8 @@
try {
// TODO: Find a way to consolidate these calls to the server.
- wm.reparentDisplayContent(displayId, mRootSurfaceControl);
+ WindowManagerGlobal.getWindowSession().reparentDisplayContent(
+ getWindow(), mRootSurfaceControl, displayId);
wm.dontOverrideDisplayInfo(displayId);
if (mSingleTaskInstance) {
mActivityTaskManager.setDisplayToSingleTaskInstance(displayId);
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 82a34ce..1ad0cfd 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2764,6 +2764,9 @@
if (itemInfo.packageName != null) {
dr = getDrawable(itemInfo.packageName, itemInfo.icon, appInfo);
}
+ if (dr == null && itemInfo != appInfo) {
+ dr = loadUnbadgedItemIcon(appInfo, appInfo);
+ }
if (dr == null) {
dr = itemInfo.loadDefaultIcon(this);
}
diff --git a/core/java/android/app/SharedElementCallback.java b/core/java/android/app/SharedElementCallback.java
index 9fabfde..0287564 100644
--- a/core/java/android/app/SharedElementCallback.java
+++ b/core/java/android/app/SharedElementCallback.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
+import android.graphics.ColorSpace;
import android.graphics.GraphicBuffer;
import android.graphics.Matrix;
import android.graphics.RectF;
@@ -49,6 +50,7 @@
private static final String BUNDLE_SNAPSHOT_BITMAP = "sharedElement:snapshot:bitmap";
private static final String BUNDLE_SNAPSHOT_GRAPHIC_BUFFER =
"sharedElement:snapshot:graphicBuffer";
+ private static final String BUNDLE_SNAPSHOT_COLOR_SPACE = "sharedElement:snapshot:colorSpace";
private static final String BUNDLE_SNAPSHOT_IMAGE_SCALETYPE = "sharedElement:snapshot:imageScaleType";
private static final String BUNDLE_SNAPSHOT_IMAGE_MATRIX = "sharedElement:snapshot:imageMatrix";
@@ -186,6 +188,10 @@
} else {
GraphicBuffer graphicBuffer = bitmap.createGraphicBufferHandle();
bundle.putParcelable(BUNDLE_SNAPSHOT_GRAPHIC_BUFFER, graphicBuffer);
+ ColorSpace cs = bitmap.getColorSpace();
+ if (cs != null) {
+ bundle.putInt(BUNDLE_SNAPSHOT_COLOR_SPACE, cs.getId());
+ }
}
bundle.putString(BUNDLE_SNAPSHOT_IMAGE_SCALETYPE,
imageView.getScaleType().toString());
@@ -235,8 +241,13 @@
return null;
}
if (bitmap == null) {
+ ColorSpace colorSpace = null;
+ int colorSpaceId = bundle.getInt(BUNDLE_SNAPSHOT_COLOR_SPACE, 0);
+ if (colorSpaceId >= 0 && colorSpaceId < ColorSpace.Named.values().length) {
+ colorSpace = ColorSpace.get(ColorSpace.Named.values()[colorSpaceId]);
+ }
bitmap = Bitmap.wrapHardwareBuffer(HardwareBuffer.createFromGraphicBuffer(buffer),
- null);
+ colorSpace);
}
ImageView imageView = new ImageView(context);
view = imageView;
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index f8dc20e..7fa4360 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -198,6 +198,8 @@
/** @hide */
public static final int REASON_SUB_USAGE_UNEXEMPTED_SYNC_SCHEDULED = 0x000E;
/** @hide */
+ public static final int REASON_SUB_USAGE_FOREGROUND_SERVICE_START = 0x000F;
+ /** @hide */
public static final int REASON_SUB_PREDICTED_RESTORED = 0x0001;
@@ -997,6 +999,9 @@
case REASON_SUB_USAGE_UNEXEMPTED_SYNC_SCHEDULED:
sb.append("-uss");
break;
+ case REASON_SUB_USAGE_FOREGROUND_SERVICE_START:
+ sb.append("-fss");
+ break;
}
break;
}
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 5f34f1b..8046776 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -16,6 +16,8 @@
package android.content;
+import static android.provider.DocumentsContract.EXTRA_ORIENTATION;
+
import android.accounts.Account;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -40,6 +42,7 @@
import android.graphics.ImageDecoder;
import android.graphics.ImageDecoder.ImageInfo;
import android.graphics.ImageDecoder.Source;
+import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
@@ -56,6 +59,7 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.storage.StorageManager;
+import android.system.Int32Ref;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
@@ -3563,9 +3567,14 @@
// Convert to Point, since that's what the API is defined as
final Bundle opts = new Bundle();
opts.putParcelable(EXTRA_SIZE, Point.convert(size));
+ final Int32Ref orientation = new Int32Ref(0);
- return ImageDecoder.decodeBitmap(ImageDecoder.createSource(() -> {
- return content.openTypedAssetFile(uri, "image/*", opts, signal);
+ Bitmap bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(() -> {
+ final AssetFileDescriptor afd = content.openTypedAssetFile(uri, "image/*", opts,
+ signal);
+ final Bundle extras = afd.getExtras();
+ orientation.value = (extras != null) ? extras.getInt(EXTRA_ORIENTATION, 0) : 0;
+ return afd;
}), (ImageDecoder decoder, ImageInfo info, Source source) -> {
decoder.setAllocator(allocator);
@@ -3581,6 +3590,20 @@
decoder.setTargetSampleSize(sample);
}
});
+
+ // Transform the bitmap if requested. We use a side-channel to
+ // communicate the orientation, since EXIF thumbnails don't contain
+ // the rotation flags of the original image.
+ if (orientation.value != 0) {
+ final int width = bitmap.getWidth();
+ final int height = bitmap.getHeight();
+
+ final Matrix m = new Matrix();
+ m.setRotate(orientation.value, width / 2, height / 2);
+ bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, m, false);
+ }
+
+ return bitmap;
}
/** {@hide} */
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 3dd510c..0112396 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3009,7 +3009,14 @@
/**
* Variation of {@link #bindService} that, in the specific case of isolated
* services, allows the caller to generate multiple instances of a service
- * from a single component declaration.
+ * from a single component declaration. In other words, you can use this to bind
+ * to a service that has specified {@link android.R.attr#isolatedProcess} and, in
+ * addition to the existing behavior of running in an isolated process, you can
+ * also through the arguments here have the system bring up multiple concurrent
+ * processes hosting their own instances of that service. The <var>instanceName</var>
+ * you provide here identifies the different instances, and you can use
+ * {@link #updateServiceGroup(ServiceConnection, int, int)} to tell the system how it
+ * should manage each of these instances.
*
* @param service Identifies the service to connect to. The Intent must
* specify an explicit component name.
@@ -3027,6 +3034,8 @@
* @throws IllegalArgumentException If the instanceName is invalid.
*
* @see #bindService
+ * @see #updateServiceGroup
+ * @see android.R.attr#isolatedProcess
*/
public boolean bindIsolatedService(@RequiresPermission @NonNull Intent service,
@BindServiceFlags int flags, @NonNull String instanceName,
@@ -3082,10 +3091,16 @@
* are considered to be related. Supplying 0 reverts to the default behavior
* of not grouping.
* @param importance Additional importance of the processes within a group. Upon calling
- * here, this will override any previous group that was set for that
- * process. This fine-tunes process killing of all processes within
- * a related groups -- higher importance values will be killed before
- * lower ones.
+ * here, this will override any previous importance that was set for that
+ * process. The most important process is 0, and higher values are
+ * successively less important. You can view this as describing how
+ * to order the processes in an array, with the processes at the end of
+ * the array being the least important. This value has no meaning besides
+ * indicating how processes should be ordered in that array one after the
+ * other. This provides a way to fine-tune the system's process killing,
+ * guiding it to kill processes at the end of the array first.
+ *
+ * @see #bindIsolatedService
*/
public void updateServiceGroup(@NonNull ServiceConnection conn, int group,
int importance) {
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index d2f0fb3..338eb2d 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1509,11 +1509,6 @@
* state of the permission can be determined only at install time and cannot be
* changed on updated or at a later point via the package manager APIs.
*
- * <p>The whitelisted non-immutably restricted permissions would be added to
- * the {@link PackageManager#FLAG_PERMISSION_WHITELIST_INSTALLER installer whitelist}
- * while the immutably restricted permissions would be added to the {@link
- * PackageManager#FLAG_PERMISSION_WHITELIST_SYSTEM system whitelist}
- *
* @see PackageManager#addWhitelistedRestrictedPermission(String, String, int)
* @see PackageManager#removeWhitelistedRestrictedPermission(String, String, int)
*/
diff --git a/core/java/android/os/IncidentManager.java b/core/java/android/os/IncidentManager.java
index 08afe31..1f61a3c 100644
--- a/core/java/android/os/IncidentManager.java
+++ b/core/java/android/os/IncidentManager.java
@@ -519,6 +519,13 @@
android.Manifest.permission.PACKAGE_USAGE_STATS
})
public @Nullable IncidentReport getIncidentReport(Uri uri) {
+ final String id = uri.getQueryParameter(URI_PARAM_REPORT_ID);
+ if (id == null) {
+ // If there's no report id, it's a bug report, so we can't return the incident
+ // report.
+ return null;
+ }
+
final String pkg = uri.getQueryParameter(URI_PARAM_CALLING_PACKAGE);
if (pkg == null) {
throw new RuntimeException("Invalid URI: No "
@@ -531,13 +538,6 @@
+ URI_PARAM_RECEIVER_CLASS + " parameter. " + uri);
}
- final String id = uri.getQueryParameter(URI_PARAM_REPORT_ID);
- if (cls == null) {
- // If there's no report id, it's a bug report, so we can't return the incident
- // report.
- return null;
- }
-
try {
return getCompanionServiceLocked().getIncidentReport(pkg, cls, id);
} catch (RemoteException ex) {
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 7cc7ccd..1b41694 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -866,8 +866,8 @@
if (storageManager.needsCheckpoint()) {
Log.i(TAG, "Rescue Party requested wipe. Aborting update instead.");
storageManager.abortChanges("rescueparty", false);
+ return;
}
- return;
} catch (RemoteException e) {
Log.i(TAG, "Failed to handle with checkpointing. Continuing with wipe.");
}
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 6478de2..8536158 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -881,6 +881,7 @@
maybeSetApiBlacklistExemptions(primaryZygoteState, false);
maybeSetHiddenApiAccessLogSampleRate(primaryZygoteState);
+ maybeSetHiddenApiAccessStatslogSampleRate(primaryZygoteState);
}
}
@@ -896,6 +897,7 @@
maybeSetApiBlacklistExemptions(secondaryZygoteState, false);
maybeSetHiddenApiAccessLogSampleRate(secondaryZygoteState);
+ maybeSetHiddenApiAccessStatslogSampleRate(secondaryZygoteState);
}
}
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 774d4ae..9a11104 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -263,6 +263,7 @@
* Namespace for TextClassifier related features.
*
* @hide
+ * @see android.provider.Settings.Global.TEXT_CLASSIFIER_CONSTANTS
*/
@SystemApi
public static final String NAMESPACE_TEXTCLASSIFIER = "textclassifier";
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 4632b75..4ac4850 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -36,6 +36,7 @@
import android.graphics.ImageDecoder;
import android.graphics.Point;
import android.media.ExifInterface;
+import android.media.MediaFile;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@@ -1698,6 +1699,18 @@
} catch (IOException e) {
}
+ // Use ImageDecoder to do full image decode of heif format file
+ // will have right orientation. So, we don't need to add orientation
+ // information into extras.
+ final String mimeType = MediaFile.getMimeTypeForFile(file.getName());
+ if (mimeType.equals("image/heif")
+ || mimeType.equals("image/heif-sequence")
+ || mimeType.equals("image/heic")
+ || mimeType.equals("image/heic-sequence")) {
+ return new AssetFileDescriptor(pfd, 0 /* startOffset */,
+ AssetFileDescriptor.UNKNOWN_LENGTH, null /* extras */);
+ }
+
return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH, extras);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b0e980e..75b40fd 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -11866,6 +11866,7 @@
* sync_adapter_duration (long)
* exempted_sync_duration (long)
* system_interaction_duration (long)
+ * initial_foreground_service_start_duration (long)
* stable_charging_threshold (long)
*
* idle_duration (long) // This is deprecated and used to circumvent b/26355386.
@@ -12038,26 +12039,27 @@
* entity_list_default use ":" as delimiter for values. Ex:
*
* <pre>
- * smart_linkify_enabled (boolean)
- * system_textclassifier_enabled (boolean)
+ * classify_text_max_range_length (int)
+ * detect_language_from_text_enabled (boolean)
+ * entity_list_default (String[])
+ * entity_list_editable (String[])
+ * entity_list_not_editable (String[])
+ * generate_links_log_sample_rate (int)
+ * generate_links_max_text_length (int)
+ * in_app_conversation_action_types_default (String[])
+ * lang_id_context_settings (float[])
+ * lang_id_threshold_override (float)
+ * local_textclassifier_enabled (boolean)
* model_dark_launch_enabled (boolean)
- * smart_selection_enabled (boolean)
- * smart_text_share_enabled (boolean)
+ * notification_conversation_action_types_default (String[])
* smart_linkify_enabled (boolean)
* smart_select_animation_enabled (boolean)
+ * smart_selection_enabled (boolean)
+ * smart_text_share_enabled (boolean)
* suggest_selection_max_range_length (int)
- * classify_text_max_range_length (int)
- * generate_links_max_text_length (int)
- * generate_links_log_sample_rate (int)
- * entity_list_default (String[])
- * entity_list_not_editable (String[])
- * entity_list_editable (String[])
- * in_app_conversation_action_types_default (String[])
- * notification_conversation_action_types_default (String[])
- * lang_id_threshold_override (float)
+ * system_textclassifier_enabled (boolean)
* template_intent_factory_enabled (boolean)
* translate_in_classification_enabled (boolean)
- * detect_language_from_text_enabled (boolean)
* </pre>
*
* <p>
diff --git a/core/java/android/service/contentsuggestions/ContentSuggestionsService.java b/core/java/android/service/contentsuggestions/ContentSuggestionsService.java
index 55e6141..516d336 100644
--- a/core/java/android/service/contentsuggestions/ContentSuggestionsService.java
+++ b/core/java/android/service/contentsuggestions/ContentSuggestionsService.java
@@ -30,6 +30,7 @@
import android.app.contentsuggestions.SelectionsRequest;
import android.content.Intent;
import android.graphics.Bitmap;
+import android.graphics.ColorSpace;
import android.graphics.GraphicBuffer;
import android.os.Bundle;
import android.os.Handler;
@@ -62,11 +63,15 @@
private final IContentSuggestionsService mInterface = new IContentSuggestionsService.Stub() {
@Override
public void provideContextImage(int taskId, GraphicBuffer contextImage,
- Bundle imageContextRequestExtras) {
+ int colorSpaceId, Bundle imageContextRequestExtras) {
Bitmap wrappedBuffer = null;
if (contextImage != null) {
- wrappedBuffer = Bitmap.wrapHardwareBuffer(contextImage, null);
+ ColorSpace colorSpace = null;
+ if (colorSpaceId >= 0 && colorSpaceId < ColorSpace.Named.values().length) {
+ colorSpace = ColorSpace.get(ColorSpace.Named.values()[colorSpaceId]);
+ }
+ wrappedBuffer = Bitmap.wrapHardwareBuffer(contextImage, colorSpace);
}
mHandler.sendMessage(
diff --git a/core/java/android/service/contentsuggestions/IContentSuggestionsService.aidl b/core/java/android/service/contentsuggestions/IContentSuggestionsService.aidl
index 1926478..6240e00 100644
--- a/core/java/android/service/contentsuggestions/IContentSuggestionsService.aidl
+++ b/core/java/android/service/contentsuggestions/IContentSuggestionsService.aidl
@@ -32,6 +32,7 @@
void provideContextImage(
int taskId,
in GraphicBuffer contextImage,
+ int colorSpaceId,
in Bundle imageContextRequestExtras);
void suggestContentSelections(
in SelectionsRequest request,
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index e8b0d92..ae36e4e 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -36,7 +36,6 @@
public static final String PERSIST_PREFIX = "persist." + FFLAG_OVERRIDE_PREFIX;
public static final String SEAMLESS_TRANSFER = "settings_seamless_transfer";
public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid";
- public static final String SAFETY_HUB = "settings_safety_hub";
public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press";
public static final String FORCE_GLOBAL_ACTIONS_GRID_ENABLED =
"settings_global_actions_force_grid_enabled";
@@ -57,7 +56,6 @@
DEFAULT_FLAGS.put(DYNAMIC_SYSTEM, "false");
DEFAULT_FLAGS.put(SEAMLESS_TRANSFER, "false");
DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false");
- DEFAULT_FLAGS.put(SAFETY_HUB, "true");
DEFAULT_FLAGS.put(SCREENRECORD_LONG_PRESS, "false");
DEFAULT_FLAGS.put(FORCE_GLOBAL_ACTIONS_GRID_ENABLED, "false");
DEFAULT_FLAGS.put(GLOBAL_ACTIONS_PANEL_ENABLED, "true");
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index c730fe2..b347a78 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -621,18 +621,6 @@
*/
void setShouldShowIme(int displayId, boolean shouldShow);
- /**
- * Reparent the top layers for a display to the requested surfaceControl. The display that
- * is going to be re-parented (the displayId passed in) needs to have been created by the same
- * process that is requesting the re-parent. This is to ensure clients can't just re-parent
- * display content info to any SurfaceControl, as this would be a security issue.
- *
- * @param displayId The id of the display.
- * @param surfaceControlHandle The SurfaceControl that the top level layers for the
- * display should be re-parented to.
- */
- void reparentDisplayContent(int displayId, in SurfaceControl sc);
-
/**
* Waits for transactions to get applied before injecting input.
* This includes waiting for the input windows to get sent to InputManager.
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index b52fdb8..d269323 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -257,6 +257,31 @@
void updatePointerIcon(IWindow window);
/**
+ * Reparent the top layers for a display to the requested SurfaceControl. The display that is
+ * going to be re-parented (the displayId passed in) needs to have been created by the same
+ * process that is requesting the re-parent. This is to ensure clients can't just re-parent
+ * display content info to any SurfaceControl, as this would be a security issue.
+ *
+ * @param window The window which owns the SurfaceControl. This indicates the z-order of the
+ * windows of this display against the windows on the parent display.
+ * @param sc The SurfaceControl that the top level layers for the display should be re-parented
+ * to.
+ * @param displayId The id of the display to be re-parented.
+ */
+ void reparentDisplayContent(IWindow window, in SurfaceControl sc, int displayId);
+
+ /**
+ * Update the location of a child display in its parent window. This enables windows in the
+ * child display to compute the global transformation matrix.
+ *
+ * @param window The parent window of the display.
+ * @param x The x coordinate in the parent window.
+ * @param y The y coordinate in the parent window.
+ * @param displayId The id of the display to be notified.
+ */
+ void updateDisplayContentLocation(IWindow window, int x, int y, int displayId);
+
+ /**
* Update a tap exclude region identified by provided id in the window. Touches on this region
* will neither be dispatched to this window nor change the focus to this window. Passing an
* invalid region will remove the area from the exclude region of this window.
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 5929c1b..921294a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -7054,10 +7054,12 @@
private FrameMetricsObserver findFrameMetricsObserver(
Window.OnFrameMetricsAvailableListener listener) {
- for (int i = 0; i < mFrameMetricsObservers.size(); i++) {
- FrameMetricsObserver observer = mFrameMetricsObservers.get(i);
- if (observer.mListener == listener) {
- return observer;
+ if (mFrameMetricsObservers != null) {
+ for (int i = 0; i < mFrameMetricsObservers.size(); i++) {
+ FrameMetricsObserver observer = mFrameMetricsObservers.get(i);
+ if (observer.mListener == listener) {
+ return observer;
+ }
}
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index c2ad82f..8460840 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -484,8 +484,17 @@
Log.d(TAG, "setContentCaptureEnabled(): setting to " + enabled + " for " + mContext);
}
+ MainContentCaptureSession mainSession;
synchronized (mLock) {
- mFlags |= enabled ? 0 : ContentCaptureContext.FLAG_DISABLED_BY_APP;
+ if (enabled) {
+ mFlags &= ~ContentCaptureContext.FLAG_DISABLED_BY_APP;
+ } else {
+ mFlags |= ContentCaptureContext.FLAG_DISABLED_BY_APP;
+ }
+ mainSession = mMainSession;
+ }
+ if (mainSession != null) {
+ mainSession.setDisabled(!enabled);
}
}
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 784cf9c..8673fbe 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -28,6 +28,7 @@
import static android.view.contentcapture.ContentCaptureHelper.getSanitizedString;
import static android.view.contentcapture.ContentCaptureHelper.sDebug;
import static android.view.contentcapture.ContentCaptureHelper.sVerbose;
+import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_FALSE;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -80,6 +81,12 @@
*/
public static final String EXTRA_BINDER = "binder";
+ /**
+ * Name of the {@link IResultReceiver} extra used to pass the content capture enabled state.
+ * @hide
+ */
+ public static final String EXTRA_ENABLED_STATE = "enabled";
+
@NonNull
private final AtomicBoolean mDisabled = new AtomicBoolean(false);
@@ -155,6 +162,13 @@
public void send(int resultCode, Bundle resultData) {
final IBinder binder;
if (resultData != null) {
+ // Change in content capture enabled.
+ final boolean hasEnabled = resultData.getBoolean(EXTRA_ENABLED_STATE);
+ if (hasEnabled) {
+ final boolean disabled = (resultCode == RESULT_CODE_FALSE);
+ mDisabled.set(disabled);
+ return;
+ }
binder = resultData.getBinder(EXTRA_BINDER);
if (binder == null) {
Log.wtf(TAG, "No " + EXTRA_BINDER + " extra result");
@@ -578,6 +592,15 @@
return mDisabled.get();
}
+ /**
+ * Called by ContentCaptureManager.setContentCaptureEnabled
+ *
+ * @return whether disabled state was changed.
+ */
+ boolean setDisabled(boolean disabled) {
+ return mDisabled.compareAndSet(!disabled, disabled);
+ }
+
// TODO(b/122454205): refactor "notifyXXXX" methods below to a common "Buffer" object that is
// shared between ActivityContentCaptureSession and ChildContentCaptureSession objects. Such
// change should also get get rid of the "internalNotifyXXXX" methods above
diff --git a/core/java/android/view/textclassifier/ConfigParser.java b/core/java/android/view/textclassifier/ConfigParser.java
index b475412..63de059 100644
--- a/core/java/android/view/textclassifier/ConfigParser.java
+++ b/core/java/android/view/textclassifier/ConfigParser.java
@@ -17,9 +17,19 @@
import android.annotation.Nullable;
import android.provider.DeviceConfig;
+import android.util.ArrayMap;
import android.util.KeyValueListParser;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.internal.util.Preconditions;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Supplier;
/**
* Retrieves settings from {@link DeviceConfig} and {@link android.provider.Settings}.
@@ -27,80 +37,228 @@
*
* @hide
*/
-@VisibleForTesting
+@VisibleForTesting(visibility = Visibility.PACKAGE)
public final class ConfigParser {
private static final String TAG = "ConfigParser";
- private final KeyValueListParser mParser;
+ static final boolean ENABLE_DEVICE_CONFIG = true;
- // TODO: Re-enable DeviceConfig when it has reasonable performance or just delete the
- // option of using DeviceConfig entirely.
- static final boolean ENABLE_DEVICE_CONFIG = false;
+ private static final String STRING_LIST_DELIMITER = ":";
- public ConfigParser(@Nullable String textClassifierConstants) {
- final KeyValueListParser parser = new KeyValueListParser(',');
- try {
- parser.setString(textClassifierConstants);
- } catch (IllegalArgumentException e) {
- // Failed to parse the settings string, log this and move on with defaults.
- Log.w(TAG, "Bad text_classifier_constants: " + textClassifierConstants);
+ private final Supplier<String> mLegacySettingsSupplier;
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
+ private final Map<String, Object> mCache = new ArrayMap<>();
+ @GuardedBy("mLock")
+ private @Nullable KeyValueListParser mSettingsParser; // Call getLegacySettings() instead.
+
+ public ConfigParser(Supplier<String> legacySettingsSupplier) {
+ mLegacySettingsSupplier = Preconditions.checkNotNull(legacySettingsSupplier);
+ }
+
+ private KeyValueListParser getLegacySettings() {
+ synchronized (mLock) {
+ if (mSettingsParser == null) {
+ final String legacySettings = mLegacySettingsSupplier.get();
+ try {
+ mSettingsParser = new KeyValueListParser(',');
+ mSettingsParser.setString(legacySettings);
+ } catch (IllegalArgumentException e) {
+ // Failed to parse the settings string, log this and move on with defaults.
+ Log.w(TAG, "Bad text_classifier_constants: " + legacySettings);
+ }
+ }
+ return mSettingsParser;
}
- mParser = parser;
}
/**
- * Reads a boolean flag.
+ * Reads a boolean setting through the cache.
*/
public boolean getBoolean(String key, boolean defaultValue) {
- if (ENABLE_DEVICE_CONFIG) {
- return DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- key,
- mParser.getBoolean(key, defaultValue));
- } else {
- return mParser.getBoolean(key, defaultValue);
+ synchronized (mLock) {
+ final Object cached = mCache.get(key);
+ if (cached instanceof Boolean) {
+ return (boolean) cached;
+ }
+ final boolean value;
+ if (ENABLE_DEVICE_CONFIG) {
+ value = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+ key,
+ getLegacySettings().getBoolean(key, defaultValue));
+ } else {
+ value = getLegacySettings().getBoolean(key, defaultValue);
+ }
+ mCache.put(key, value);
+ return value;
}
}
/**
- * Reads an integer flag.
+ * Reads an integer setting through the cache.
*/
public int getInt(String key, int defaultValue) {
- if (ENABLE_DEVICE_CONFIG) {
- return DeviceConfig.getInt(
- DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- key,
- mParser.getInt(key, defaultValue));
- } else {
- return mParser.getInt(key, defaultValue);
+ synchronized (mLock) {
+ final Object cached = mCache.get(key);
+ if (cached instanceof Integer) {
+ return (int) cached;
+ }
+ final int value;
+ if (ENABLE_DEVICE_CONFIG) {
+ value = DeviceConfig.getInt(
+ DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+ key,
+ getLegacySettings().getInt(key, defaultValue));
+ } else {
+ value = getLegacySettings().getInt(key, defaultValue);
+ }
+ mCache.put(key, value);
+ return value;
}
}
/**
- * Reads a float flag.
+ * Reads a float setting through the cache.
*/
public float getFloat(String key, float defaultValue) {
- if (ENABLE_DEVICE_CONFIG) {
- return DeviceConfig.getFloat(
- DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- key,
- mParser.getFloat(key, defaultValue));
- } else {
- return mParser.getFloat(key, defaultValue);
+ synchronized (mLock) {
+ final Object cached = mCache.get(key);
+ if (cached instanceof Float) {
+ return (float) cached;
+ }
+ final float value;
+ if (ENABLE_DEVICE_CONFIG) {
+ value = DeviceConfig.getFloat(
+ DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+ key,
+ getLegacySettings().getFloat(key, defaultValue));
+ } else {
+ value = getLegacySettings().getFloat(key, defaultValue);
+ }
+ mCache.put(key, value);
+ return value;
}
}
/**
- * Reads a string flag.
+ * Reads a string setting through the cache.
*/
public String getString(String key, String defaultValue) {
- if (ENABLE_DEVICE_CONFIG) {
- return DeviceConfig.getString(
- DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- key,
- mParser.getString(key, defaultValue));
+ synchronized (mLock) {
+ final Object cached = mCache.get(key);
+ if (cached instanceof String) {
+ return (String) cached;
+ }
+ final String value;
+ if (ENABLE_DEVICE_CONFIG) {
+ value = DeviceConfig.getString(
+ DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+ key,
+ getLegacySettings().getString(key, defaultValue));
+ } else {
+ value = getLegacySettings().getString(key, defaultValue);
+ }
+ mCache.put(key, value);
+ return value;
+ }
+ }
+
+ /**
+ * Reads a string list setting through the cache.
+ */
+ public List<String> getStringList(String key, List<String> defaultValue) {
+ synchronized (mLock) {
+ final Object cached = mCache.get(key);
+ if (cached instanceof List) {
+ final List asList = (List) cached;
+ if (asList.isEmpty()) {
+ return Collections.emptyList();
+ } else if (asList.get(0) instanceof String) {
+ return (List<String>) cached;
+ }
+ }
+ final List<String> value;
+ if (ENABLE_DEVICE_CONFIG) {
+ value = getDeviceConfigStringList(
+ key,
+ getSettingsStringList(key, defaultValue));
+ } else {
+ value = getSettingsStringList(key, defaultValue);
+ }
+ mCache.put(key, value);
+ return value;
+ }
+ }
+
+ /**
+ * Reads a float array through the cache. The returned array should be expected to be of the
+ * same length as that of the defaultValue.
+ */
+ public float[] getFloatArray(String key, float[] defaultValue) {
+ synchronized (mLock) {
+ final Object cached = mCache.get(key);
+ if (cached instanceof float[]) {
+ return (float[]) cached;
+ }
+ final float[] value;
+ if (ENABLE_DEVICE_CONFIG) {
+ value = getDeviceConfigFloatArray(
+ key,
+ getSettingsFloatArray(key, defaultValue));
+ } else {
+ value = getSettingsFloatArray(key, defaultValue);
+ }
+ mCache.put(key, value);
+ return value;
+ }
+ }
+
+ private List<String> getSettingsStringList(String key, List<String> defaultValue) {
+ return parse(mSettingsParser.getString(key, null), defaultValue);
+ }
+
+ private static List<String> getDeviceConfigStringList(String key, List<String> defaultValue) {
+ return parse(
+ DeviceConfig.getString(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key, null),
+ defaultValue);
+ }
+
+ private static float[] getDeviceConfigFloatArray(String key, float[] defaultValue) {
+ return parse(
+ DeviceConfig.getString(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key, null),
+ defaultValue);
+ }
+
+ private float[] getSettingsFloatArray(String key, float[] defaultValue) {
+ return parse(mSettingsParser.getString(key, null), defaultValue);
+ }
+
+ private static List<String> parse(@Nullable String listStr, List<String> defaultValue) {
+ if (listStr != null) {
+ return Collections.unmodifiableList(
+ Arrays.asList(listStr.split(STRING_LIST_DELIMITER)));
+ }
+ return defaultValue;
+ }
+
+ private static float[] parse(@Nullable String arrayStr, float[] defaultValue) {
+ if (arrayStr != null) {
+ final String[] split = arrayStr.split(STRING_LIST_DELIMITER);
+ if (split.length != defaultValue.length) {
+ return defaultValue;
+ }
+ final float[] result = new float[split.length];
+ for (int i = 0; i < split.length; i++) {
+ try {
+ result[i] = Float.parseFloat(split[i]);
+ } catch (NumberFormatException e) {
+ return defaultValue;
+ }
+ }
+ return result;
} else {
- return mParser.getString(key, defaultValue);
+ return defaultValue;
}
}
}
diff --git a/core/java/android/view/textclassifier/TextClassificationConstants.java b/core/java/android/view/textclassifier/TextClassificationConstants.java
index 876e5cc..2964f51 100644
--- a/core/java/android/view/textclassifier/TextClassificationConstants.java
+++ b/core/java/android/view/textclassifier/TextClassificationConstants.java
@@ -16,58 +16,39 @@
package android.view.textclassifier;
-import android.annotation.Nullable;
-
import com.android.internal.util.IndentingPrintWriter;
import java.util.Arrays;
-import java.util.Collections;
import java.util.List;
-import java.util.StringJoiner;
+import java.util.function.Supplier;
/**
* TextClassifier specific settings.
- * This is encoded as a key=value list, separated by commas. Ex:
- *
- * <pre>
- * smart_linkify_enabled (boolean)
- * system_textclassifier_enabled (boolean)
- * model_dark_launch_enabled (boolean)
- * smart_selection_enabled (boolean)
- * smart_text_share_enabled (boolean)
- * smart_linkify_enabled (boolean)
- * smart_select_animation_enabled (boolean)
- * suggest_selection_max_range_length (int)
- * classify_text_max_range_length (int)
- * generate_links_max_text_length (int)
- * generate_links_log_sample_rate (int)
- * entity_list_default (String[])
- * entity_list_not_editable (String[])
- * entity_list_editable (String[])
- * in_app_conversation_action_types_default (String[])
- * notification_conversation_action_types_default (String[])
- * lang_id_threshold_override (float)
- * template_intent_factory_enabled (boolean)
- * translate_in_classification_enabled (boolean)
- * detect_languages_from_text_enabled (boolean)
- * lang_id_context_settings (float[])
- * </pre>
- *
+ * This is encoded as a key=value list, separated by commas.
* <p>
- * Type: string
- * see also android.provider.Settings.Global.TEXT_CLASSIFIER_CONSTANTS
- *
* Example of setting the values for testing.
+ * <p>
+ * <pre>
* adb shell settings put global text_classifier_constants \
* model_dark_launch_enabled=true,smart_selection_enabled=true, \
* entity_list_default=phone:address, \
* lang_id_context_settings=20:1.0:0.4
+ * </pre>
+ * <p>
+ * Settings are also available in device config. These take precedence over those in settings
+ * global.
+ * <p>
+ * <pre>
+ * adb shell cmd device_config put textclassifier system_textclassifier_enabled true
+ * </pre>
+ *
+ * @see android.provider.Settings.Global.TEXT_CLASSIFIER_CONSTANTS
+ * @see android.provider.DeviceConfig.NAMESPACE_TEXTCLASSIFIER
* @hide
*/
+// TODO: Rename to TextClassifierSettings.
public final class TextClassificationConstants {
- private static final String LOG_TAG = TextClassifier.DEFAULT_LOG_TAG;
-
/**
* Whether the smart linkify feature is enabled.
*/
@@ -188,29 +169,26 @@
private static final int CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT = 10 * 1000;
private static final int GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT = 100 * 1000;
private static final int GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT = 100;
- private static final String STRING_LIST_DELIMITER = ":";
- private static final String ENTITY_LIST_DEFAULT_VALUE = new StringJoiner(STRING_LIST_DELIMITER)
- .add(TextClassifier.TYPE_ADDRESS)
- .add(TextClassifier.TYPE_EMAIL)
- .add(TextClassifier.TYPE_PHONE)
- .add(TextClassifier.TYPE_URL)
- .add(TextClassifier.TYPE_DATE)
- .add(TextClassifier.TYPE_DATE_TIME)
- .add(TextClassifier.TYPE_FLIGHT_NUMBER).toString();
- private static final String CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES =
- new StringJoiner(STRING_LIST_DELIMITER)
- .add(ConversationAction.TYPE_TEXT_REPLY)
- .add(ConversationAction.TYPE_CREATE_REMINDER)
- .add(ConversationAction.TYPE_CALL_PHONE)
- .add(ConversationAction.TYPE_OPEN_URL)
- .add(ConversationAction.TYPE_SEND_EMAIL)
- .add(ConversationAction.TYPE_SEND_SMS)
- .add(ConversationAction.TYPE_TRACK_FLIGHT)
- .add(ConversationAction.TYPE_VIEW_CALENDAR)
- .add(ConversationAction.TYPE_VIEW_MAP)
- .add(ConversationAction.TYPE_ADD_CONTACT)
- .add(ConversationAction.TYPE_COPY)
- .toString();
+ private static final List<String> ENTITY_LIST_DEFAULT_VALUE = Arrays.asList(
+ TextClassifier.TYPE_ADDRESS,
+ TextClassifier.TYPE_EMAIL,
+ TextClassifier.TYPE_PHONE,
+ TextClassifier.TYPE_URL,
+ TextClassifier.TYPE_DATE,
+ TextClassifier.TYPE_DATE_TIME,
+ TextClassifier.TYPE_FLIGHT_NUMBER);
+ private static final List<String> CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES = Arrays.asList(
+ ConversationAction.TYPE_TEXT_REPLY,
+ ConversationAction.TYPE_CREATE_REMINDER,
+ ConversationAction.TYPE_CALL_PHONE,
+ ConversationAction.TYPE_OPEN_URL,
+ ConversationAction.TYPE_SEND_EMAIL,
+ ConversationAction.TYPE_SEND_SMS,
+ ConversationAction.TYPE_TRACK_FLIGHT,
+ ConversationAction.TYPE_VIEW_CALENDAR,
+ ConversationAction.TYPE_VIEW_MAP,
+ ConversationAction.TYPE_ADD_CONTACT,
+ ConversationAction.TYPE_COPY);
/**
* < 0 : Not set. Use value from LangId model.
* 0 - 1: Override value in LangId model.
@@ -221,259 +199,185 @@
private static final boolean TEMPLATE_INTENT_FACTORY_ENABLED_DEFAULT = true;
private static final boolean TRANSLATE_IN_CLASSIFICATION_ENABLED_DEFAULT = true;
private static final boolean DETECT_LANGUAGES_FROM_TEXT_ENABLED_DEFAULT = true;
- private static final String LANG_ID_CONTEXT_SETTINGS_DEFAULT =
- new StringJoiner(STRING_LIST_DELIMITER).add("20").add("1.0").add("0.4").toString();
+ private static final float[] LANG_ID_CONTEXT_SETTINGS_DEFAULT = new float[] {20f, 1.0f, 0.4f};
- private final boolean mSystemTextClassifierEnabled;
- private final boolean mLocalTextClassifierEnabled;
- private final boolean mModelDarkLaunchEnabled;
- private final boolean mSmartSelectionEnabled;
- private final boolean mSmartTextShareEnabled;
- private final boolean mSmartLinkifyEnabled;
- private final boolean mSmartSelectionAnimationEnabled;
- private final int mSuggestSelectionMaxRangeLength;
- private final int mClassifyTextMaxRangeLength;
- private final int mGenerateLinksMaxTextLength;
- private final int mGenerateLinksLogSampleRate;
- private final List<String> mEntityListDefault;
- private final List<String> mEntityListNotEditable;
- private final List<String> mEntityListEditable;
- private final List<String> mInAppConversationActionTypesDefault;
- private final List<String> mNotificationConversationActionTypesDefault;
- private final float mLangIdThresholdOverride;
- private final boolean mTemplateIntentFactoryEnabled;
- private final boolean mTranslateInClassificationEnabled;
- private final boolean mDetectLanguagesFromTextEnabled;
- private final float[] mLangIdContextSettings;
+ private final ConfigParser mConfigParser;
- private TextClassificationConstants(@Nullable String settings) {
- ConfigParser configParser = new ConfigParser(settings);
- mSystemTextClassifierEnabled =
- configParser.getBoolean(
- SYSTEM_TEXT_CLASSIFIER_ENABLED,
- SYSTEM_TEXT_CLASSIFIER_ENABLED_DEFAULT);
- mLocalTextClassifierEnabled =
- configParser.getBoolean(
- LOCAL_TEXT_CLASSIFIER_ENABLED,
- LOCAL_TEXT_CLASSIFIER_ENABLED_DEFAULT);
- mModelDarkLaunchEnabled =
- configParser.getBoolean(
- MODEL_DARK_LAUNCH_ENABLED,
- MODEL_DARK_LAUNCH_ENABLED_DEFAULT);
- mSmartSelectionEnabled =
- configParser.getBoolean(
- SMART_SELECTION_ENABLED,
- SMART_SELECTION_ENABLED_DEFAULT);
- mSmartTextShareEnabled =
- configParser.getBoolean(
- SMART_TEXT_SHARE_ENABLED,
- SMART_TEXT_SHARE_ENABLED_DEFAULT);
- mSmartLinkifyEnabled =
- configParser.getBoolean(
- SMART_LINKIFY_ENABLED,
- SMART_LINKIFY_ENABLED_DEFAULT);
- mSmartSelectionAnimationEnabled =
- configParser.getBoolean(
- SMART_SELECT_ANIMATION_ENABLED,
- SMART_SELECT_ANIMATION_ENABLED_DEFAULT);
- mSuggestSelectionMaxRangeLength =
- configParser.getInt(
- SUGGEST_SELECTION_MAX_RANGE_LENGTH,
- SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT);
- mClassifyTextMaxRangeLength =
- configParser.getInt(
- CLASSIFY_TEXT_MAX_RANGE_LENGTH,
- CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT);
- mGenerateLinksMaxTextLength =
- configParser.getInt(
- GENERATE_LINKS_MAX_TEXT_LENGTH,
- GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT);
- mGenerateLinksLogSampleRate =
- configParser.getInt(
- GENERATE_LINKS_LOG_SAMPLE_RATE,
- GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT);
- mEntityListDefault = parseStringList(
- configParser.getString(
- ENTITY_LIST_DEFAULT,
- ENTITY_LIST_DEFAULT_VALUE));
- mEntityListNotEditable = parseStringList(
- configParser.getString(
- ENTITY_LIST_NOT_EDITABLE,
- ENTITY_LIST_DEFAULT_VALUE));
- mEntityListEditable = parseStringList(
- configParser.getString(
- ENTITY_LIST_EDITABLE,
- ENTITY_LIST_DEFAULT_VALUE));
- mInAppConversationActionTypesDefault = parseStringList(
- configParser.getString(
- IN_APP_CONVERSATION_ACTION_TYPES_DEFAULT,
- CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES));
- mNotificationConversationActionTypesDefault = parseStringList(
- configParser.getString(
- NOTIFICATION_CONVERSATION_ACTION_TYPES_DEFAULT,
- CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES));
- mLangIdThresholdOverride =
- configParser.getFloat(
- LANG_ID_THRESHOLD_OVERRIDE,
- LANG_ID_THRESHOLD_OVERRIDE_DEFAULT);
- mTemplateIntentFactoryEnabled =
- configParser.getBoolean(
- TEMPLATE_INTENT_FACTORY_ENABLED,
- TEMPLATE_INTENT_FACTORY_ENABLED_DEFAULT);
- mTranslateInClassificationEnabled =
- configParser.getBoolean(
- TRANSLATE_IN_CLASSIFICATION_ENABLED,
- TRANSLATE_IN_CLASSIFICATION_ENABLED_DEFAULT);
- mDetectLanguagesFromTextEnabled =
- configParser.getBoolean(
- DETECT_LANGUAGES_FROM_TEXT_ENABLED,
- DETECT_LANGUAGES_FROM_TEXT_ENABLED_DEFAULT);
- mLangIdContextSettings = parseFloatArray(
- configParser,
- LANG_ID_CONTEXT_SETTINGS,
- LANG_ID_CONTEXT_SETTINGS_DEFAULT);
- }
-
- /** Load from a settings string. */
- public static TextClassificationConstants loadFromString(String settings) {
- return new TextClassificationConstants(settings);
+ public TextClassificationConstants(Supplier<String> legacySettingsSupplier) {
+ mConfigParser = new ConfigParser(legacySettingsSupplier);
}
public boolean isLocalTextClassifierEnabled() {
- return mLocalTextClassifierEnabled;
+ return mConfigParser.getBoolean(
+ LOCAL_TEXT_CLASSIFIER_ENABLED,
+ LOCAL_TEXT_CLASSIFIER_ENABLED_DEFAULT);
}
public boolean isSystemTextClassifierEnabled() {
- return mSystemTextClassifierEnabled;
+ return mConfigParser.getBoolean(
+ SYSTEM_TEXT_CLASSIFIER_ENABLED,
+ SYSTEM_TEXT_CLASSIFIER_ENABLED_DEFAULT);
}
public boolean isModelDarkLaunchEnabled() {
- return mModelDarkLaunchEnabled;
+ return mConfigParser.getBoolean(
+ MODEL_DARK_LAUNCH_ENABLED,
+ MODEL_DARK_LAUNCH_ENABLED_DEFAULT);
}
public boolean isSmartSelectionEnabled() {
- return mSmartSelectionEnabled;
+ return mConfigParser.getBoolean(
+ SMART_SELECTION_ENABLED,
+ SMART_SELECTION_ENABLED_DEFAULT);
}
public boolean isSmartTextShareEnabled() {
- return mSmartTextShareEnabled;
+ return mConfigParser.getBoolean(
+ SMART_TEXT_SHARE_ENABLED,
+ SMART_TEXT_SHARE_ENABLED_DEFAULT);
}
public boolean isSmartLinkifyEnabled() {
- return mSmartLinkifyEnabled;
+ return mConfigParser.getBoolean(
+ SMART_LINKIFY_ENABLED,
+ SMART_LINKIFY_ENABLED_DEFAULT);
}
public boolean isSmartSelectionAnimationEnabled() {
- return mSmartSelectionAnimationEnabled;
+ return mConfigParser.getBoolean(
+ SMART_SELECT_ANIMATION_ENABLED,
+ SMART_SELECT_ANIMATION_ENABLED_DEFAULT);
}
public int getSuggestSelectionMaxRangeLength() {
- return mSuggestSelectionMaxRangeLength;
+ return mConfigParser.getInt(
+ SUGGEST_SELECTION_MAX_RANGE_LENGTH,
+ SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT);
}
public int getClassifyTextMaxRangeLength() {
- return mClassifyTextMaxRangeLength;
+ return mConfigParser.getInt(
+ CLASSIFY_TEXT_MAX_RANGE_LENGTH,
+ CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT);
}
public int getGenerateLinksMaxTextLength() {
- return mGenerateLinksMaxTextLength;
+ return mConfigParser.getInt(
+ GENERATE_LINKS_MAX_TEXT_LENGTH,
+ GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT);
}
public int getGenerateLinksLogSampleRate() {
- return mGenerateLinksLogSampleRate;
+ return mConfigParser.getInt(
+ GENERATE_LINKS_LOG_SAMPLE_RATE,
+ GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT);
}
public List<String> getEntityListDefault() {
- return mEntityListDefault;
+ return mConfigParser.getStringList(
+ ENTITY_LIST_DEFAULT,
+ ENTITY_LIST_DEFAULT_VALUE);
}
public List<String> getEntityListNotEditable() {
- return mEntityListNotEditable;
+ return mConfigParser.getStringList(
+ ENTITY_LIST_NOT_EDITABLE,
+ ENTITY_LIST_DEFAULT_VALUE);
}
public List<String> getEntityListEditable() {
- return mEntityListEditable;
+ return mConfigParser.getStringList(
+ ENTITY_LIST_EDITABLE,
+ ENTITY_LIST_DEFAULT_VALUE);
}
public List<String> getInAppConversationActionTypes() {
- return mInAppConversationActionTypesDefault;
+ return mConfigParser.getStringList(
+ IN_APP_CONVERSATION_ACTION_TYPES_DEFAULT,
+ CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES);
}
public List<String> getNotificationConversationActionTypes() {
- return mNotificationConversationActionTypesDefault;
+ return mConfigParser.getStringList(
+ NOTIFICATION_CONVERSATION_ACTION_TYPES_DEFAULT,
+ CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES);
}
public float getLangIdThresholdOverride() {
- return mLangIdThresholdOverride;
+ return mConfigParser.getFloat(
+ LANG_ID_THRESHOLD_OVERRIDE,
+ LANG_ID_THRESHOLD_OVERRIDE_DEFAULT);
}
public boolean isTemplateIntentFactoryEnabled() {
- return mTemplateIntentFactoryEnabled;
+ return mConfigParser.getBoolean(
+ TEMPLATE_INTENT_FACTORY_ENABLED,
+ TEMPLATE_INTENT_FACTORY_ENABLED_DEFAULT);
}
public boolean isTranslateInClassificationEnabled() {
- return mTranslateInClassificationEnabled;
+ return mConfigParser.getBoolean(
+ TRANSLATE_IN_CLASSIFICATION_ENABLED,
+ TRANSLATE_IN_CLASSIFICATION_ENABLED_DEFAULT);
}
public boolean isDetectLanguagesFromTextEnabled() {
- return mDetectLanguagesFromTextEnabled;
+ return mConfigParser.getBoolean(
+ DETECT_LANGUAGES_FROM_TEXT_ENABLED,
+ DETECT_LANGUAGES_FROM_TEXT_ENABLED_DEFAULT);
}
public float[] getLangIdContextSettings() {
- return mLangIdContextSettings;
- }
-
- private static List<String> parseStringList(String listStr) {
- return Collections.unmodifiableList(Arrays.asList(listStr.split(STRING_LIST_DELIMITER)));
- }
-
- private static float[] parseFloatArray(
- ConfigParser configParser, String key, String defaultStr) {
- final String str = configParser.getString(key, defaultStr);
- final String[] defaultSplit = defaultStr.split(STRING_LIST_DELIMITER);
- String[] split = str.split(STRING_LIST_DELIMITER);
- if (split.length != defaultSplit.length) {
- Log.v(LOG_TAG, "Error parsing " + key + " flag. Using defaults.");
- split = defaultSplit;
- }
- final float[] result = new float[split.length];
- for (int i = 0; i < split.length; i++) {
- try {
- result[i] = Float.parseFloat(split[i]);
- } catch (NumberFormatException e) {
- Log.v(LOG_TAG, "Error parsing part of " + key + " flag. Using defaults.");
- result[i] = Float.parseFloat(defaultSplit[i]);
- }
- }
- return result;
+ return mConfigParser.getFloatArray(
+ LANG_ID_CONTEXT_SETTINGS,
+ LANG_ID_CONTEXT_SETTINGS_DEFAULT);
}
void dump(IndentingPrintWriter pw) {
pw.println("TextClassificationConstants:");
pw.increaseIndent();
- pw.printPair("isLocalTextClassifierEnabled", mLocalTextClassifierEnabled);
- pw.printPair("isSystemTextClassifierEnabled", mSystemTextClassifierEnabled);
- pw.printPair("isModelDarkLaunchEnabled", mModelDarkLaunchEnabled);
- pw.printPair("isSmartSelectionEnabled", mSmartSelectionEnabled);
- pw.printPair("isSmartTextShareEnabled", mSmartTextShareEnabled);
- pw.printPair("isSmartLinkifyEnabled", mSmartLinkifyEnabled);
- pw.printPair("isSmartSelectionAnimationEnabled", mSmartSelectionAnimationEnabled);
- pw.printPair("getSuggestSelectionMaxRangeLength", mSuggestSelectionMaxRangeLength);
- pw.printPair("getClassifyTextMaxRangeLength", mClassifyTextMaxRangeLength);
- pw.printPair("getGenerateLinksMaxTextLength", mGenerateLinksMaxTextLength);
- pw.printPair("getGenerateLinksLogSampleRate", mGenerateLinksLogSampleRate);
- pw.printPair("getEntityListDefault", mEntityListDefault);
- pw.printPair("getEntityListNotEditable", mEntityListNotEditable);
- pw.printPair("getEntityListEditable", mEntityListEditable);
- pw.printPair("getInAppConversationActionTypes", mInAppConversationActionTypesDefault);
- pw.printPair("getNotificationConversationActionTypes",
- mNotificationConversationActionTypesDefault);
- pw.printPair("getLangIdThresholdOverride", mLangIdThresholdOverride);
- pw.printPair("isTemplateIntentFactoryEnabled", mTemplateIntentFactoryEnabled);
- pw.printPair("isTranslateInClassificationEnabled", mTranslateInClassificationEnabled);
- pw.printPair("isDetectLanguageFromTextEnabled", mDetectLanguagesFromTextEnabled);
- pw.printPair("getLangIdContextSettings", Arrays.toString(mLangIdContextSettings));
+ pw.printPair("classify_text_max_range_length", getClassifyTextMaxRangeLength())
+ .println();
+ pw.printPair("detect_language_from_text_enabled", isDetectLanguagesFromTextEnabled())
+ .println();
+ pw.printPair("entity_list_default", getEntityListDefault())
+ .println();
+ pw.printPair("entity_list_editable", getEntityListEditable())
+ .println();
+ pw.printPair("entity_list_not_editable", getEntityListNotEditable())
+ .println();
+ pw.printPair("generate_links_log_sample_rate", getGenerateLinksLogSampleRate())
+ .println();
+ pw.printPair("generate_links_max_text_length", getGenerateLinksMaxTextLength())
+ .println();
+ pw.printPair("in_app_conversation_action_types_default", getInAppConversationActionTypes())
+ .println();
+ pw.printPair("lang_id_context_settings", Arrays.toString(getLangIdContextSettings()))
+ .println();
+ pw.printPair("lang_id_threshold_override", getLangIdThresholdOverride())
+ .println();
+ pw.printPair("local_textclassifier_enabled", isLocalTextClassifierEnabled())
+ .println();
+ pw.printPair("model_dark_launch_enabled", isModelDarkLaunchEnabled())
+ .println();
+ pw.printPair("notification_conversation_action_types_default",
+ getNotificationConversationActionTypes()).println();
+ pw.printPair("smart_linkify_enabled", isSmartLinkifyEnabled())
+ .println();
+ pw.printPair("smart_select_animation_enabled", isSmartSelectionAnimationEnabled())
+ .println();
+ pw.printPair("smart_selection_enabled", isSmartSelectionEnabled())
+ .println();
+ pw.printPair("smart_text_share_enabled", isSmartTextShareEnabled())
+ .println();
+ pw.printPair("suggest_selection_max_range_length", getSuggestSelectionMaxRangeLength())
+ .println();
+ pw.printPair("system_textclassifier_enabled", isSystemTextClassifierEnabled())
+ .println();
+ pw.printPair("template_intent_factory_enabled", isTemplateIntentFactoryEnabled())
+ .println();
+ pw.printPair("translate_in_classification_enabled", isTranslateInClassificationEnabled())
+ .println();
pw.decreaseIndent();
- pw.println();
}
-}
+}
\ No newline at end of file
diff --git a/core/java/android/view/textclassifier/TextClassificationManager.java b/core/java/android/view/textclassifier/TextClassificationManager.java
index 042b943..95ca9de 100644
--- a/core/java/android/view/textclassifier/TextClassificationManager.java
+++ b/core/java/android/view/textclassifier/TextClassificationManager.java
@@ -45,6 +45,9 @@
private static final String LOG_TAG = "TextClassificationManager";
+ private static final TextClassificationConstants sDefaultSettings =
+ new TextClassificationConstants(() -> null);
+
private final Object mLock = new Object();
private final TextClassificationSessionFactory mDefaultSessionFactory =
classificationContext -> new TextClassificationSession(
@@ -129,9 +132,10 @@
private TextClassificationConstants getSettings() {
synchronized (mLock) {
if (mSettings == null) {
- mSettings = TextClassificationConstants.loadFromString(Settings.Global.getString(
- getApplicationContext().getContentResolver(),
- Settings.Global.TEXT_CLASSIFIER_CONSTANTS));
+ mSettings = new TextClassificationConstants(
+ () -> Settings.Global.getString(
+ getApplicationContext().getContentResolver(),
+ Settings.Global.TEXT_CLASSIFIER_CONSTANTS));
}
return mSettings;
}
@@ -251,7 +255,11 @@
/** @hide */
@VisibleForTesting
- public void invalidate() {
+ public void invalidateForTesting() {
+ invalidate();
+ }
+
+ private void invalidate() {
synchronized (mLock) {
mSettings = null;
mLocalTextClassifier = null;
@@ -280,9 +288,8 @@
if (tcm != null) {
return tcm.getSettings();
} else {
- return TextClassificationConstants.loadFromString(Settings.Global.getString(
- context.getApplicationContext().getContentResolver(),
- Settings.Global.TEXT_CLASSIFIER_CONSTANTS));
+ // Use default settings if there is no tcm.
+ return sDefaultSettings;
}
}
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 3297523..3e95f1b 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -301,7 +301,7 @@
final ZonedDateTime refTime = ZonedDateTime.now();
final Collection<String> entitiesToIdentify = request.getEntityConfig() != null
? request.getEntityConfig().resolveEntityListModifications(
- getEntitiesForHints(request.getEntityConfig().getHints()))
+ getEntitiesForHints(request.getEntityConfig().getHints()))
: mSettings.getEntityListDefault();
final String localesString = concatenateLocales(request.getDefaultLocales());
final String detectLanguageTags = detectLanguageTagsFromText(request.getText());
@@ -779,8 +779,8 @@
final float moreTextScoreRatio = 1f - subjectTextScoreRatio;
Log.v(LOG_TAG,
String.format(Locale.US, "LangIdContextSettings: "
- + "minimumTextSize=%d, penalizeRatio=%.2f, "
- + "subjectTextScoreRatio=%.2f, moreTextScoreRatio=%.2f",
+ + "minimumTextSize=%d, penalizeRatio=%.2f, "
+ + "subjectTextScoreRatio=%.2f, moreTextScoreRatio=%.2f",
minimumTextSize, penalizeRatio, subjectTextScoreRatio, moreTextScoreRatio));
if (end - start < minimumTextSize && penalizeRatio <= 0) {
@@ -903,4 +903,3 @@
}
}
}
-
diff --git a/core/java/com/android/internal/app/AbstractResolverComparator.java b/core/java/com/android/internal/app/AbstractResolverComparator.java
index 87d80d4..9ac979b 100644
--- a/core/java/com/android/internal/app/AbstractResolverComparator.java
+++ b/core/java/com/android/internal/app/AbstractResolverComparator.java
@@ -43,7 +43,7 @@
private static final boolean DEBUG = false;
private static final String TAG = "AbstractResolverComp";
- private AfterCompute mAfterCompute;
+ protected AfterCompute mAfterCompute;
protected final PackageManager mPm;
protected final UsageStatsManager mUsm;
protected String[] mAnnotations;
@@ -70,11 +70,7 @@
Log.d(TAG, "RANKER_SERVICE_RESULT");
}
if (mHandler.hasMessages(RANKER_RESULT_TIMEOUT)) {
- if (msg.obj != null) {
- handleResultMessage(msg);
- } else {
- Log.e(TAG, "Receiving null prediction results.");
- }
+ handleResultMessage(msg);
mHandler.removeMessages(RANKER_RESULT_TIMEOUT);
afterCompute();
}
@@ -99,7 +95,6 @@
mHttp = "http".equals(scheme) || "https".equals(scheme);
mContentType = intent.getType();
getContentAnnotations(intent);
-
mPm = context.getPackageManager();
mUsm = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
mDefaultBrowserPackageName = mHttp
diff --git a/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java b/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java
index 3b4e1a0..fb27a2f 100644
--- a/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java
+++ b/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java
@@ -26,9 +26,10 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.ResolveInfo;
+import android.os.Handler;
import android.os.Message;
import android.os.UserHandle;
-import android.view.textclassifier.Log;
+import android.util.Log;
import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
@@ -38,27 +39,44 @@
import java.util.Map;
/**
- * Uses an {@link AppPredictor} to sort Resolver targets.
+ * Uses an {@link AppPredictor} to sort Resolver targets. If the AppPredictionService appears to be
+ * disabled by returning an empty sorted target list, {@link AppPredictionServiceResolverComparator}
+ * will fallback to using a {@link ResolverRankerServiceResolverComparator}.
*/
class AppPredictionServiceResolverComparator extends AbstractResolverComparator {
private static final String TAG = "APSResolverComparator";
+ private static final long DELAY_COMPUTE_WHEN_DEFAULTING_TO_RESOLVER_MILLIS = 200;
private final AppPredictor mAppPredictor;
private final Context mContext;
private final Map<ComponentName, Integer> mTargetRanks = new HashMap<>();
private final UserHandle mUser;
+ private final Intent mIntent;
+ private final String mReferrerPackage;
+ // If this is non-null (and this is not destroyed), it means APS is disabled and we should fall
+ // back to using the ResolverRankerService.
+ private ResolverRankerServiceResolverComparator mResolverRankerService;
AppPredictionServiceResolverComparator(
- Context context, Intent intent, AppPredictor appPredictor, UserHandle user) {
+ Context context,
+ Intent intent,
+ String referrerPackage,
+ AppPredictor appPredictor,
+ UserHandle user) {
super(context, intent);
mContext = context;
+ mIntent = intent;
mAppPredictor = appPredictor;
mUser = user;
+ mReferrerPackage = referrerPackage;
}
@Override
int compare(ResolveInfo lhs, ResolveInfo rhs) {
+ if (mResolverRankerService != null) {
+ return mResolverRankerService.compare(lhs, rhs);
+ }
Integer lhsRank = mTargetRanks.get(new ComponentName(lhs.activityInfo.packageName,
lhs.activityInfo.name));
Integer rhsRank = mTargetRanks.get(new ComponentName(rhs.activityInfo.packageName,
@@ -75,6 +93,10 @@
@Override
void doCompute(List<ResolvedComponentInfo> targets) {
+ if (targets.isEmpty()) {
+ mHandler.sendEmptyMessage(RANKER_SERVICE_RESULT);
+ return;
+ }
List<AppTarget> appTargets = new ArrayList<>();
for (ResolvedComponentInfo target : targets) {
appTargets.add(new AppTarget.Builder(new AppTargetId(target.name.flattenToString()))
@@ -83,25 +105,48 @@
}
mAppPredictor.sortTargets(appTargets, mContext.getMainExecutor(),
sortedAppTargets -> {
- Message msg =
+ if (sortedAppTargets.isEmpty()) {
+ // APS for chooser is disabled. Fallback to resolver.
+ mResolverRankerService =
+ new ResolverRankerServiceResolverComparator(
+ mContext, mIntent, mReferrerPackage,
+ () -> mHandler.sendEmptyMessage(RANKER_SERVICE_RESULT));
+ mResolverRankerService.initRanker(mContext);
+ Handler computeHandler =
+ new Handler(msg -> {
+ mResolverRankerService.compute(targets);
+ return true;
+ });
+ computeHandler.sendEmptyMessageDelayed(
+ 0, DELAY_COMPUTE_WHEN_DEFAULTING_TO_RESOLVER_MILLIS);
+ } else {
+ Message msg =
Message.obtain(mHandler, RANKER_SERVICE_RESULT, sortedAppTargets);
- msg.sendToTarget();
- });
+ msg.sendToTarget();
+ }
+ }
+ );
}
@Override
void handleResultMessage(Message msg) {
- if (msg.what == RANKER_SERVICE_RESULT) {
+ // Null value is okay if we have defaulted to the ResolverRankerService.
+ if (msg.what == RANKER_SERVICE_RESULT && msg.obj != null) {
final List<AppTarget> sortedAppTargets = (List<AppTarget>) msg.obj;
for (int i = 0; i < sortedAppTargets.size(); i++) {
mTargetRanks.put(new ComponentName(sortedAppTargets.get(i).getPackageName(),
sortedAppTargets.get(i).getClassName()), i);
}
+ } else if (msg.obj == null && mResolverRankerService == null) {
+ Log.e(TAG, "Unexpected null result");
}
}
@Override
float getScore(ComponentName name) {
+ if (mResolverRankerService != null) {
+ return mResolverRankerService.getScore(name);
+ }
Integer rank = mTargetRanks.get(name);
if (rank == null) {
Log.w(TAG, "Score requested for unknown component.");
@@ -113,6 +158,10 @@
@Override
void updateModel(ComponentName componentName) {
+ if (mResolverRankerService != null) {
+ mResolverRankerService.updateModel(componentName);
+ return;
+ }
mAppPredictor.notifyAppTargetEvent(
new AppTargetEvent.Builder(
new AppTarget.Builder(
@@ -121,4 +170,12 @@
.setClassName(componentName.getClassName()).build(),
ACTION_LAUNCH).build());
}
+
+ @Override
+ void destroy() {
+ if (mResolverRankerService != null) {
+ mResolverRankerService.destroy();
+ mResolverRankerService = null;
+ }
+ }
}
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 1eabbd8..659272c 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -453,6 +453,11 @@
if (mChooserListAdapter == null) {
return;
}
+ if (resultList.isEmpty()) {
+ // APS may be disabled, so try querying targets ourselves.
+ queryDirectShareTargets(mChooserListAdapter, true);
+ return;
+ }
final List<DisplayResolveInfo> driList =
getDisplayResolveInfos(mChooserListAdapter);
final List<ShortcutManager.ShareShortcutInfo> shareShortcutInfos =
@@ -919,6 +924,8 @@
mChooserHandler.removeMessages(LIST_VIEW_UPDATE_MESSAGE);
mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT);
mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_RESULT);
+ mChooserHandler.removeMessages(SHORTCUT_MANAGER_SHARE_TARGET_RESULT);
+ mChooserHandler.removeMessages(SHORTCUT_MANAGER_SHARE_TARGET_RESULT_COMPLETED);
if (mAppPredictor != null) {
mAppPredictor.unregisterPredictionUpdates(mAppPredictorCallback);
mAppPredictor.destroy();
@@ -1272,11 +1279,14 @@
return driList;
}
- private void queryDirectShareTargets(ChooserListAdapter adapter) {
- AppPredictor appPredictor = getAppPredictorForDirectShareIfEnabled();
- if (appPredictor != null) {
- appPredictor.requestPredictionUpdate();
- return;
+ private void queryDirectShareTargets(
+ ChooserListAdapter adapter, boolean skipAppPredictionService) {
+ if (!skipAppPredictionService) {
+ AppPredictor appPredictor = getAppPredictorForDirectShareIfEnabled();
+ if (appPredictor != null) {
+ appPredictor.requestPredictionUpdate();
+ return;
+ }
}
// Default to just querying ShortcutManager if AppPredictor not present.
final IntentFilter filter = getTargetIntentFilter();
@@ -1609,7 +1619,7 @@
AbstractResolverComparator resolverComparator;
if (appPredictor != null) {
resolverComparator = new AppPredictionServiceResolverComparator(this, getTargetIntent(),
- appPredictor, getUser());
+ getReferrerPackageName(), appPredictor, getUser());
} else {
resolverComparator =
new ResolverRankerServiceResolverComparator(this, getTargetIntent(),
@@ -2205,7 +2215,7 @@
Log.d(TAG, "querying direct share targets from ShortcutManager");
}
- queryDirectShareTargets(this);
+ queryDirectShareTargets(this, false);
}
if (USE_CHOOSER_TARGET_SERVICE_FOR_DIRECT_TARGETS) {
if (DEBUG) {
@@ -2871,10 +2881,10 @@
if (startType != lastStartType
|| rowPosition == getContentPreviewRowCount() + getProfileRowCount()) {
- row.setBackground(
+ row.setForeground(
getResources().getDrawable(R.drawable.chooser_row_layer_list, null));
} else {
- row.setBackground(null);
+ row.setForeground(null);
}
int columnCount = holder.getColumnCount();
diff --git a/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java b/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java
index a781907..d633467 100644
--- a/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java
+++ b/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java
@@ -66,9 +66,6 @@
// timeout for establishing connections with a ResolverRankerService.
private static final int CONNECTION_COST_TIMEOUT_MILLIS = 200;
- // timeout for establishing connections with a ResolverRankerService, collecting features and
- // predicting ranking scores.
- private static final int WATCHDOG_TIMEOUT_MILLIS = 500;
private final Collator mCollator;
private final Map<String, UsageStats> mStats;
@@ -106,6 +103,10 @@
if (msg.what != RANKER_SERVICE_RESULT) {
return;
}
+ if (msg.obj == null) {
+ Log.e(TAG, "Receiving null prediction results.");
+ return;
+ }
final List<ResolverTarget> receivedTargets = (List<ResolverTarget>) msg.obj;
if (receivedTargets != null && mTargets != null
&& receivedTargets.size() == mTargets.size()) {
@@ -314,7 +315,7 @@
}
// connect to a ranking service.
- private void initRanker(Context context) {
+ void initRanker(Context context) {
synchronized (mLock) {
if (mConnection != null && mRanker != null) {
if (DEBUG) {
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index dd6fb72..2987b4e 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -406,8 +406,13 @@
private int mHiddenApiAccessStatslogSampleRate = 0;
public static void setHiddenApiAccessLogSampleRates(int sampleRate, int newSampleRate) {
- sInstance.mHiddenApiAccessLogSampleRate = sampleRate;
- sInstance.mHiddenApiAccessStatslogSampleRate = newSampleRate;
+ if (sampleRate != -1) {
+ sInstance.mHiddenApiAccessLogSampleRate = sampleRate;
+ }
+
+ if (newSampleRate != -1) {
+ sInstance.mHiddenApiAccessStatslogSampleRate = newSampleRate;
+ }
}
public static HiddenApiUsageLogger getInstance() {
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index c4c16ee..43c0bbe 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -546,7 +546,7 @@
if (CC_LIKELY(transform.isPureTranslate())) {
// snap/round the computed bounds, so they match the rounding behavior
// of the clear done in SurfaceView#draw().
- bounds.snapToPixelBoundaries();
+ bounds.snapGeometryToPixelBoundaries(false);
} else {
// Conservatively round out so the punched hole (in the ZOrderOnTop = true case)
// doesn't extend beyond the other window
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index ecc9d64..986771d 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -136,8 +136,8 @@
// ColorSpace.Named.SRGB.ordinal() = 0;
static constexpr jint SRGB = 0;
- // ColorSpace.Named.DISPLAY_P3.ordinal() = 6;
- static constexpr jint DISPLAY_P3 = 6;
+ // ColorSpace.Named.DISPLAY_P3.ordinal() = 7;
+ static constexpr jint DISPLAY_P3 = 7;
};
constexpr jint fromDataspaceToNamedColorSpaceValue(const ui::Dataspace dataspace) {
diff --git a/core/proto/OWNERS b/core/proto/OWNERS
index 480b1ea..a3d4798 100644
--- a/core/proto/OWNERS
+++ b/core/proto/OWNERS
@@ -1,13 +1,8 @@
# Be sure you are familiar with proto when you modify this directory.
# Metrics
-bookatz@google.com
-cjyu@google.com
-jinyithu@google.com
joeo@google.com
-kwekua@google.com
singhtejinder@google.com
-yanglu@google.com
yaochen@google.com
yro@google.com
diff --git a/core/proto/android/stats/mediametrics/mediametrics.proto b/core/proto/android/stats/mediametrics/mediametrics.proto
new file mode 100644
index 0000000..34ed90a
--- /dev/null
+++ b/core/proto/android/stats/mediametrics/mediametrics.proto
@@ -0,0 +1,234 @@
+/*
+ * 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.mediametrics;
+
+/**
+ * Track how we arbitrate between microphone/input requests.
+ * Logged from
+ * frameworks/av/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+ * frameworks/av/services/mediaanalytics/statsd_audiopolicy.cpp
+ * Next Tag: 10
+ */
+message AudioPolicyData {
+ optional int32 status = 1;
+ optional string request_source = 2;
+ optional string request_package = 3;
+ optional int32 request_session = 4;
+ optional string request_device = 5;
+ optional string active_source = 6;
+ optional string active_package = 7;
+ optional int32 active_session = 8;
+ optional string active_device = 9;
+}
+
+/**
+ * Track properties of audio recording
+ * Logged from
+ * frameworks/av/media/libaudioclient/AudioRecord.cpp
+ * frameworks/av/services/mediaanalytics/statsd_audiorecord.cpp
+ * Next Tag: 16
+ */
+message AudioRecordData {
+ optional string encoding = 1;
+ optional string source = 2;
+ optional int32 latency = 3;
+ optional int32 samplerate = 4;
+ optional int32 channels = 5;
+ optional int64 created_millis = 6;
+ optional int64 duration_millis = 7;
+ optional int32 count = 8;
+ optional int32 error_code = 9;
+ optional string error_function = 10;
+ optional int32 port_id = 11;
+ optional int32 frame_count = 12;
+ optional string attributes = 13;
+ optional int64 channel_mask = 14;
+ optional int64 start_count = 15;
+
+}
+
+/**
+ * Track audio thread performance data
+ * Logged from
+ * frameworks/av/media/libnblog/ReportPerformance.cpp
+ * frameworks/av/services/mediaanalytics/statsd_audiothread.cpp
+ * Next Tag: 28
+ */
+message AudioThreadData {
+ optional string type = 1;
+ optional int32 framecount = 2;
+ optional int32 samplerate = 3;
+ optional string work_millis_hist = 4;
+ optional string latency_millis_hist = 5;
+ optional string warmup_millis_hist = 6;
+ optional int64 underruns = 7;
+ optional int64 overruns = 8;
+ optional int64 active_millis = 9;
+ optional int64 duration_millis = 10;
+
+ optional int32 id = 11;
+ optional int32 port_id = 12;
+ optional int32 sample_rate = 13;
+ optional int64 channel_mask = 14;
+ optional string encoding = 15;
+ optional int32 frame_count = 16;
+ optional string output_device = 17;
+ optional string input_device = 18;
+ optional double io_jitter_mean_millis = 19;
+ optional double io_jitter_stddev_millis = 20;
+ optional double process_time_mean_millis = 21;
+ optional double process_time_stddev_millis = 22;
+ optional double timestamp_jitter_mean_millis = 23;
+ optional double timestamp_jitter_stddev_millis = 24;
+ optional double latency_mean_millis = 25;
+ optional double latency_stddev_millis = 26;
+
+}
+
+/**
+ * Track audio track playback data
+ * Logged from
+ * frameworks/av/media/libaudioclient/AudioTrack.cpp
+ * frameworks/av/services/mediaanalytics/statsd_audiotrack.cpp
+ * Next Tag: 12
+ */
+message AudioTrackData {
+ optional string stream_type = 1;
+ optional string content_type = 2;
+ optional string track_usage = 3;
+ optional int32 sample_rate = 4;
+ optional int64 channel_mask = 5;
+
+ optional int32 underrun_frames = 6;
+ optional int32 startup_glitch = 7;
+
+ optional int32 port_id = 8;
+ optional string encoding = 9;
+ optional int32 frame_count = 10;
+ optional string attributes = 11;
+
+
+}
+
+/**
+ * Track Media Codec usage
+ * Logged from:
+ * frameworks/av/media/libstagefright/MediaCodec.cpp
+ * frameworks/av/services/mediaanalytics/statsd_codec.cpp
+ * Next Tag: 21
+ */
+message CodecData {
+ optional string codec = 1;
+ optional string mime = 2;
+ optional string mode = 3;
+ optional int32 encoder = 4;
+ optional int32 secure = 5;
+ optional int32 width = 6;
+ optional int32 height = 7;
+ optional int32 rotation = 8;
+ optional int32 crypto = 9;
+ optional int32 profile = 10;
+ optional int32 level = 11;
+ optional int32 max_width = 12;
+ optional int32 max_height = 13;
+ optional int32 error_code = 14;
+ optional string error_state = 15;
+ optional int64 latency_max = 16;
+ optional int64 latency_min = 17;
+ optional int64 latency_avg = 18;
+ optional int64 latency_count = 19;
+ optional int64 latency_unknown = 20;
+}
+
+/**
+ * Track Media Extractor (pulling video/audio streams out of containers) usage
+ * Logged from:
+ * frameworks/av/media/libstagefright/RemoteMediaExtractor.cpp
+ * frameworks/av/services/mediaanalytics/statsd_extractor.cpp
+ * Next Tag: 4
+ */
+message ExtractorData {
+ optional string format = 1;
+ optional string mime = 2;
+ optional int32 tracks = 3;
+}
+
+/**
+ * Track Media Player usage
+ * this handles both nuplayer and nuplayer2
+ * Logged from:
+ * frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+ * frameworks/av/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp
+ * frameworks/av/services/mediaanalytics/statsd_nuplayer.cpp
+ * Next Tag: 21
+ */
+message NuPlayerData {
+ optional string whichPlayer = 1;
+
+ optional string video_mime = 2;
+ optional string video_codec = 3;
+ optional int32 width = 4;
+ optional int32 height = 5;
+ optional int64 frames = 6;
+ optional int64 frames_dropped = 7;
+ optional double framerate = 8;
+ optional string audio_mime = 9;
+ optional string audio_codec = 10;
+ optional int64 duration_millis = 11;
+ optional int64 playing_millis = 12;
+ optional int32 error = 13;
+ optional int32 error_code = 14;
+ optional string error_state = 15;
+ optional string data_source_type = 16;
+ optional int64 rebuffering_millis = 17;
+ optional int32 rebuffers = 18;
+ optional int32 rebuffer_at_exit = 19;
+ optional int64 frames_dropped_startup = 20;
+}
+
+/**
+ * Track information about recordings (e.g. camcorder)
+ * Logged from
+ * frameworks/av/media/libmediaplayerservice/StagefrightRecorder.cpp
+ * frameworks/av/services/mediaanalytics/if_statsd.cpp
+ * Next Tag: 22
+ */
+message RecorderData {
+ optional string audio_mime = 1;
+ optional string video_mime = 2;
+ optional int32 video_profile = 3;
+ optional int32 video_level = 4;
+ optional int32 width = 5;
+ optional int32 height = 6;
+ optional int32 rotation = 7;
+ optional int32 framerate = 8;
+ optional int32 capture_fps = 9;
+ optional double capture_fps_enable = 10;
+ optional int64 duration_millis = 11;
+ optional int64 paused_millis = 12;
+ optional int32 paused_count = 13;
+ optional int32 audio_bitrate = 14;
+ optional int32 audio_channels = 15;
+ optional int32 audio_samplerate = 16;
+ optional int32 movie_timescale = 17;
+ optional int32 audio_timescale = 18;
+ optional int32 video_timescale = 19;
+ optional int32 video_bitrate = 20;
+ optional int32 iframe_interval = 21;
+}
diff --git a/core/res/res/layout/chooser_grid_preview_text.xml b/core/res/res/layout/chooser_grid_preview_text.xml
index e889e85..96a642c 100644
--- a/core/res/res/layout/chooser_grid_preview_text.xml
+++ b/core/res/res/layout/chooser_grid_preview_text.xml
@@ -60,7 +60,7 @@
android:minWidth="48dp"
android:minHeight="48dp"
android:clickable="true"
- android:background="?android:attr/selectableItemBackgroundBorderless">
+ style="?attr/borderlessButtonStyle">
<ImageView
android:layout_width="24dp"
diff --git a/core/res/res/layout/resolve_grid_item.xml b/core/res/res/layout/resolve_grid_item.xml
index 256d94e..7098c95 100644
--- a/core/res/res/layout/resolve_grid_item.xml
+++ b/core/res/res/layout/resolve_grid_item.xml
@@ -23,7 +23,7 @@
android:minHeight="100dp"
android:gravity="center"
android:paddingTop="24dp"
- android:paddingBottom="8dp"
+ android:paddingBottom="12dp"
android:paddingLeft="12dp"
android:paddingRight="12dp"
android:focusable="true"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 04ccb74..bca0e93 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4006,7 +4006,7 @@
<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_batterymeterErrorPerimeterPath" translatable="false">@string/config_batterymeterPerimeterPath</string>
<string name="config_batterymeterFillMask" translatable="false">
M2,18 v-14 h8 v14 z
</string>
@@ -4057,4 +4057,38 @@
<!-- Which binder services to include in incident reports containing restricted images. -->
<string-array name="config_restrictedImagesServices" translatable="false"/>
+
+ <!-- Messages that should not be shown to the user during face auth enrollment. This should be
+ used to hide messages that may be too chatty or messages that the user can't do much about.
+ Entries are defined in android.hardware.biometrics.face@1.0 types.hal -->
+ <integer-array name="config_face_acquire_enroll_ignorelist" translatable="false" >
+ </integer-array>
+ <!-- Same as the above, but are defined by vendorCodes -->
+ <integer-array name="config_face_acquire_vendor_enroll_ignorelist" translatable="false" >
+ </integer-array>
+
+ <!-- Messages that should not be shown to the user during face authentication, on keyguard.
+ This includes both lockscreen and bouncer. This should be used to hide messages that may be
+ too chatty or messages that the user can't do much about. Entries are defined in
+ android.hardware.biometrics.face@1.0 types.hal -->
+ <integer-array name="config_face_acquire_keyguard_ignorelist" translatable="false" >
+ </integer-array>
+ <!-- Same as the above, but are defined by vendorCodes -->
+ <integer-array name="config_face_acquire_vendor_keyguard_ignorelist" translatable="false" >
+ </integer-array>
+
+ <!-- Messages that should not be shown to the user during face authentication, on
+ BiometricPrompt. This should be used to hide messages that may be too chatty or messages that
+ the user can't do much about. Entries are defined in
+ android.hardware.biometrics.face@1.0 types.hal -->
+ <integer-array name="config_face_acquire_biometricprompt_ignorelist" translatable="false" >
+ </integer-array>
+ <!-- Same as the above, but are defined by vendorCodes -->
+ <integer-array name="config_face_acquire_vendor_biometricprompt_ignorelist" translatable="false" >
+ </integer-array>
+
+ <!-- The component name for the default profile supervisor, which can be set as a profile owner
+ even after user setup is complete. The defined component should be used for supervision purposes
+ only. The component must be part of a system app. -->
+ <string name="config_defaultSupervisionProfileOwnerComponent" translatable="false"></string>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index fbe340e..32bd58e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2570,6 +2570,13 @@
<java-symbol type="string" name="face_authenticated_no_confirmation_required" />
<java-symbol type="string" name="face_authenticated_confirmation_required" />
+ <java-symbol type="array" name="config_face_acquire_enroll_ignorelist" />
+ <java-symbol type="array" name="config_face_acquire_vendor_enroll_ignorelist" />
+ <java-symbol type="array" name="config_face_acquire_keyguard_ignorelist" />
+ <java-symbol type="array" name="config_face_acquire_vendor_keyguard_ignorelist" />
+ <java-symbol type="array" name="config_face_acquire_biometricprompt_ignorelist" />
+ <java-symbol type="array" name="config_face_acquire_vendor_biometricprompt_ignorelist" />
+
<!-- Face config -->
<java-symbol type="integer" name="config_faceMaxTemplatesPerUser" />
@@ -3240,6 +3247,7 @@
<java-symbol type="string" name="config_icon_mask" />
<java-symbol type="string" name="config_batterymeterPerimeterPath" />
+ <java-symbol type="string" name="config_batterymeterErrorPerimeterPath" />
<java-symbol type="string" name="config_batterymeterFillMask" />
<java-symbol type="string" name="config_batterymeterBoltPath" />
<java-symbol type="string" name="config_batterymeterPowersavePath" />
@@ -3773,4 +3781,6 @@
<java-symbol type="dimen" name="chooser_direct_share_label_placeholder_max_width" />
<java-symbol type="layout" name="chooser_az_label_row" />
<java-symbol type="string" name="chooser_all_apps_button_label" />
+
+ <java-symbol type="string" name="config_defaultSupervisionProfileOwnerComponent" />
</resources>
diff --git a/core/tests/coretests/src/android/view/textclassifier/ConfigParserTest.java b/core/tests/coretests/src/android/view/textclassifier/ConfigParserTest.java
index f1cfe24..d54ce51 100644
--- a/core/tests/coretests/src/android/view/textclassifier/ConfigParserTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/ConfigParserTest.java
@@ -26,16 +26,17 @@
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.IOException;
+import java.util.function.Supplier;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class ConfigParserTest {
- private static final String SETTINGS = "int=42,float=12.3,boolean=true,string=abc";
+ private static final Supplier<String> SETTINGS =
+ () -> "int=42,float=12.3,boolean=true,string=abc";
private static final String CLEAR_DEVICE_CONFIG_KEY_CMD =
"device_config delete " + DeviceConfig.NAMESPACE_TEXTCLASSIFIER;
private static final String[] DEVICE_CONFIG_KEYS = new String[]{
@@ -59,7 +60,6 @@
}
@Test
- @Ignore // TODO: Re-enable once ConfigParser#ENABLE_DEVICE_CONFIG is finalized
public void getBoolean_deviceConfig() {
DeviceConfig.setProperty(
DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
@@ -79,7 +79,6 @@
}
@Test
- @Ignore // TODO: Re-enable once ConfigParser#ENABLE_DEVICE_CONFIG is finalized
public void getInt_deviceConfig() {
DeviceConfig.setProperty(
DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
@@ -97,7 +96,6 @@
}
@Test
- @Ignore // TODO: Re-enable once ConfigParser#ENABLE_DEVICE_CONFIG is finalized
public void getFloat_deviceConfig() {
DeviceConfig.setProperty(
DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
@@ -115,7 +113,6 @@
}
@Test
- @Ignore // TODO: Re-enable once ConfigParser#ENABLE_DEVICE_CONFIG is finalized
public void getString_deviceConfig() {
DeviceConfig.setProperty(
DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
index f6bb1bf..789b829 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
@@ -50,9 +50,11 @@
+ "in_app_conversation_action_types_default=text_reply,"
+ "notification_conversation_action_types_default=send_email:call_phone,"
+ "lang_id_threshold_override=0.3,"
- + "lang_id_context_settings=10:1:0.5";
- final TextClassificationConstants constants =
- TextClassificationConstants.loadFromString(s);
+ + "lang_id_context_settings=10:1:0.5,"
+ + "detect_language_from_text_enabled=true,"
+ + "template_intent_factory_enabled=true,"
+ + "translate_in_classification_enabled=true";
+ final TextClassificationConstants constants = new TextClassificationConstants(() -> s);
assertWithMessage("local_textclassifier_enabled")
.that(constants.isLocalTextClassifierEnabled()).isTrue();
@@ -95,6 +97,12 @@
.that(constants.getLangIdThresholdOverride()).isWithin(EPSILON).of(0.3f);
Assert.assertArrayEquals("lang_id_context_settings",
constants.getLangIdContextSettings(), new float[]{10, 1, 0.5f}, EPSILON);
+ assertWithMessage("detect_language_from_text_enabled")
+ .that(constants.isLocalTextClassifierEnabled()).isTrue();
+ assertWithMessage("template_intent_factory_enabled")
+ .that(constants.isLocalTextClassifierEnabled()).isTrue();
+ assertWithMessage("translate_in_classification_enabled")
+ .that(constants.isLocalTextClassifierEnabled()).isTrue();
}
@Test
@@ -116,9 +124,11 @@
+ "in_app_conversation_action_types_default=view_map:track_flight,"
+ "notification_conversation_action_types_default=share_location,"
+ "lang_id_threshold_override=2,"
- + "lang_id_context_settings=30:0.5:0.3";
- final TextClassificationConstants constants =
- TextClassificationConstants.loadFromString(s);
+ + "lang_id_context_settings=30:0.5:0.3,"
+ + "detect_language_from_text_enabled=false,"
+ + "template_intent_factory_enabled=false,"
+ + "translate_in_classification_enabled=false";
+ final TextClassificationConstants constants = new TextClassificationConstants(() -> s);
assertWithMessage("local_textclassifier_enabled")
.that(constants.isLocalTextClassifierEnabled()).isFalse();
@@ -161,12 +171,17 @@
.that(constants.getLangIdThresholdOverride()).isWithin(EPSILON).of(2f);
Assert.assertArrayEquals("lang_id_context_settings",
constants.getLangIdContextSettings(), new float[]{30, 0.5f, 0.3f}, EPSILON);
+ assertWithMessage("detect_language_from_text_enabled")
+ .that(constants.isLocalTextClassifierEnabled()).isFalse();
+ assertWithMessage("template_intent_factory_enabled")
+ .that(constants.isLocalTextClassifierEnabled()).isFalse();
+ assertWithMessage("translate_in_classification_enabled")
+ .that(constants.isLocalTextClassifierEnabled()).isFalse();
}
@Test
public void testLoadFromString_defaultValues() {
- final TextClassificationConstants constants =
- TextClassificationConstants.loadFromString("");
+ final TextClassificationConstants constants = new TextClassificationConstants(() -> "");
assertWithMessage("local_textclassifier_enabled")
.that(constants.isLocalTextClassifierEnabled()).isTrue();
@@ -213,5 +228,11 @@
.that(constants.getLangIdThresholdOverride()).isWithin(EPSILON).of(-1f);
Assert.assertArrayEquals("lang_id_context_settings",
constants.getLangIdContextSettings(), new float[]{20, 1, 0.4f}, EPSILON);
+ assertWithMessage("detect_language_from_text_enabled")
+ .that(constants.isLocalTextClassifierEnabled()).isTrue();
+ assertWithMessage("template_intent_factory_enabled")
+ .that(constants.isLocalTextClassifierEnabled()).isTrue();
+ assertWithMessage("translate_in_classification_enabled")
+ .that(constants.isLocalTextClassifierEnabled()).isTrue();
}
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
index 4fcd51c..9148185 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
@@ -77,7 +77,7 @@
TextClassifier fallback = TextClassifier.NO_OP;
TextClassifier classifier = new TextClassifierImpl(
- fakeContext, TextClassificationConstants.loadFromString(null), fallback);
+ fakeContext, new TextClassificationConstants(() -> null), fallback);
String text = "Contact me at +12122537077";
String classifiedText = "+12122537077";
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
index aeb8949..e3eb2a3 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
@@ -59,7 +59,7 @@
// TODO: Implement TextClassifierService testing.
private static final TextClassificationConstants TC_CONSTANTS =
- TextClassificationConstants.loadFromString("");
+ new TextClassificationConstants(() -> "");
private static final LocaleList LOCALES = LocaleList.forLanguageTags("en-US");
private static final String NO_TYPE = null;
diff --git a/data/etc/car/Android.bp b/data/etc/car/Android.bp
new file mode 100644
index 0000000..37020fc
--- /dev/null
+++ b/data/etc/car/Android.bp
@@ -0,0 +1,123 @@
+// 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.
+
+
+
+// Privapp permission whitelist files
+
+prebuilt_etc {
+ name: "privapp_whitelist_android.car.cluster.loggingrenderer",
+ sub_dir: "permissions",
+ src: "android.car.cluster.loggingrenderer.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
+ name: "privapp_whitelist_android.car.cluster.sample",
+ sub_dir: "permissions",
+ src: "android.car.cluster.sample.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
+ name: "privapp_whitelist_android.car.usb.handler",
+ sub_dir: "permissions",
+ src: "android.car.usb.handler.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
+ name: "privapp_whitelist_com.android.car.carlauncher",
+ sub_dir: "permissions",
+ src: "com.android.car.carlauncher.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
+ name: "privapp_whitelist_com.android.car.dialer",
+ sub_dir: "permissions",
+ src: "com.android.car.dialer.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
+ name: "privapp_whitelist_com.android.car.hvac",
+ sub_dir: "permissions",
+ src: "com.android.car.hvac.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
+ name: "privapp_whitelist_com.android.car.media",
+ sub_dir: "permissions",
+ src: "com.android.car.media.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
+ name: "privapp_whitelist_com.android.car.notification",
+ sub_dir: "permissions",
+ src: "com.android.car.notification.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
+ name: "privapp_whitelist_com.android.car.radio",
+ sub_dir: "permissions",
+ src: "com.android.car.radio.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
+ name: "privapp_whitelist_com.android.car.settings",
+ sub_dir: "permissions",
+ src: "com.android.car.settings.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
+ name: "privapp_whitelist_com.android.car.themeplayground",
+ sub_dir: "permissions",
+ src: "com.android.car.themeplayground.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
+ name: "privapp_whitelist_com.android.car.trust",
+ sub_dir: "permissions",
+ src: "com.android.car.trust.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
+ name: "privapp_whitelist_com.android.car",
+ sub_dir: "permissions",
+ src: "com.android.car.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
+ name: "privapp_whitelist_com.google.android.car.kitchensink",
+ sub_dir: "permissions",
+ src: "com.google.android.car.kitchensink.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
+ name: "privapp_whitelist_com.android.car.developeroptions",
+ sub_dir: "permissions",
+ src: "com.android.car.developeroptions.xml",
+ filename_from_src: true,
+ product_specific: true,
+}
diff --git a/data/etc/car/android.car.cluster.loggingrenderer.xml b/data/etc/car/android.car.cluster.loggingrenderer.xml
new file mode 100644
index 0000000..784e0e7
--- /dev/null
+++ b/data/etc/car/android.car.cluster.loggingrenderer.xml
@@ -0,0 +1,21 @@
+<?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
+ -->
+<permissions>
+ <privapp-permissions package="android.car.cluster.loggingrenderer">
+ <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+ </privapp-permissions>
+</permissions>
diff --git a/data/etc/car/android.car.cluster.sample.xml b/data/etc/car/android.car.cluster.sample.xml
new file mode 100644
index 0000000..75c57b8
--- /dev/null
+++ b/data/etc/car/android.car.cluster.sample.xml
@@ -0,0 +1,24 @@
+<?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
+ -->
+<permissions>
+ <privapp-permissions package="android.car.cluster.sample">
+ <permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/>
+ <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+ <permission name="android.permission.MANAGE_USERS"/>
+ <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+ </privapp-permissions>
+</permissions>
diff --git a/data/etc/car/android.car.usb.handler.xml b/data/etc/car/android.car.usb.handler.xml
new file mode 100644
index 0000000..c67847c
--- /dev/null
+++ b/data/etc/car/android.car.usb.handler.xml
@@ -0,0 +1,22 @@
+<?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
+ -->
+<permissions>
+ <privapp-permissions package="android.car.usb.handler">
+ <permission name="android.permission.MANAGE_USB"/>
+ <permission name="android.permission.MANAGE_USERS"/>
+ </privapp-permissions>
+</permissions>
diff --git a/data/etc/car/com.android.car.carlauncher.xml b/data/etc/car/com.android.car.carlauncher.xml
new file mode 100644
index 0000000..8ec1cd4
--- /dev/null
+++ b/data/etc/car/com.android.car.carlauncher.xml
@@ -0,0 +1,24 @@
+<?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
+ -->
+<permissions>
+ <privapp-permissions package="com.android.car.carlauncher">
+ <permission name="android.permission.ACTIVITY_EMBEDDING"/>
+ <permission name="android.permission.MANAGE_USERS"/>
+ <permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
+ <permission name="android.permission.PACKAGE_USAGE_STATS"/>
+ </privapp-permissions>
+</permissions>
diff --git a/data/etc/car/com.android.car.developeroptions.xml b/data/etc/car/com.android.car.developeroptions.xml
new file mode 100644
index 0000000..76c8c62
--- /dev/null
+++ b/data/etc/car/com.android.car.developeroptions.xml
@@ -0,0 +1,54 @@
+<?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
+ -->
+<permissions>
+ <privapp-permissions package="com.android.car.developeroptions">
+ <permission name="android.permission.ACCESS_CHECKIN_PROPERTIES"/>
+ <permission name="android.permission.ACCESS_NOTIFICATIONS"/>
+ <permission name="android.permission.BACKUP"/>
+ <permission name="android.permission.BATTERY_STATS"/>
+ <permission name="android.permission.BLUETOOTH_PRIVILEGED"/>
+ <permission name="android.permission.CHANGE_APP_IDLE_STATE"/>
+ <permission name="android.permission.CHANGE_CONFIGURATION"/>
+ <permission name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS"/>
+ <permission name="android.permission.DELETE_PACKAGES"/>
+ <permission name="android.permission.FORCE_STOP_PACKAGES"/>
+ <permission name="android.permission.LOCAL_MAC_ADDRESS"/>
+ <permission name="android.permission.MANAGE_DEBUGGING"/>
+ <permission name="android.permission.MANAGE_FINGERPRINT"/>
+ <permission name="android.permission.MANAGE_USB"/>
+ <permission name="android.permission.MANAGE_USER_OEM_UNLOCK_STATE"/>
+ <permission name="android.permission.MANAGE_USERS"/>
+ <permission name="android.permission.MASTER_CLEAR"/>
+ <permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
+ <permission name="android.permission.MODIFY_PHONE_STATE"/>
+ <permission name="android.permission.MODIFY_PHONE_STATE"/>
+ <permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
+ <permission name="android.permission.MOVE_PACKAGE"/>
+ <permission name="android.permission.OVERRIDE_WIFI_CONFIG"/>
+ <permission name="android.permission.PACKAGE_USAGE_STATS"/>
+ <permission name="android.permission.READ_SEARCH_INDEXABLES"/>
+ <permission name="android.permission.REBOOT"/>
+ <permission name="android.permission.SET_TIME"/>
+ <permission name="android.permission.STATUS_BAR"/>
+ <permission name="android.permission.TETHER_PRIVILEGED"/>
+ <permission name="android.permission.USE_RESERVED_DISK"/>
+ <permission name="android.permission.USER_ACTIVITY"/>
+ <permission name="android.permission.WRITE_APN_SETTINGS"/>
+ <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
+ <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+ </privapp-permissions>
+</permissions>
diff --git a/data/etc/car/com.android.car.dialer.xml b/data/etc/car/com.android.car.dialer.xml
new file mode 100644
index 0000000..d44f5a1
--- /dev/null
+++ b/data/etc/car/com.android.car.dialer.xml
@@ -0,0 +1,22 @@
+<?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
+ -->
+<permissions>
+ <privapp-permissions package="com.android.car.dialer">
+ <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+ <permission name="android.permission.MODIFY_PHONE_STATE"/>
+ </privapp-permissions>
+</permissions>
diff --git a/data/etc/car/com.android.car.hvac.xml b/data/etc/car/com.android.car.hvac.xml
new file mode 100644
index 0000000..d3631e0
--- /dev/null
+++ b/data/etc/car/com.android.car.hvac.xml
@@ -0,0 +1,21 @@
+<?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
+ -->
+<permissions>
+ <privapp-permissions package="com.android.car.hvac">
+ <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+ </privapp-permissions>
+</permissions>
diff --git a/data/etc/car/com.android.car.media.xml b/data/etc/car/com.android.car.media.xml
new file mode 100644
index 0000000..d17453d
--- /dev/null
+++ b/data/etc/car/com.android.car.media.xml
@@ -0,0 +1,21 @@
+<?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
+ -->
+<permissions>
+ <privapp-permissions package="com.android.car.media">
+ <permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
+ </privapp-permissions>
+</permissions>
diff --git a/data/etc/car/com.android.car.notification.xml b/data/etc/car/com.android.car.notification.xml
new file mode 100644
index 0000000..8479512
--- /dev/null
+++ b/data/etc/car/com.android.car.notification.xml
@@ -0,0 +1,23 @@
+<?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
+ -->
+<permissions>
+ <privapp-permissions package="com.android.car.notification">
+ <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+ <permission name="android.permission.MANAGE_USERS"/>
+ <permission name="android.permission.MODIFY_PHONE_STATE"/>
+ </privapp-permissions>
+</permissions>
diff --git a/data/etc/car/com.android.car.radio.xml b/data/etc/car/com.android.car.radio.xml
new file mode 100644
index 0000000..d7853ab
--- /dev/null
+++ b/data/etc/car/com.android.car.radio.xml
@@ -0,0 +1,22 @@
+<?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
+ -->
+<permissions>
+ <privapp-permissions package="com.android.car.radio">
+ <permission name="android.permission.ACCESS_BROADCAST_RADIO"/>
+ <permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
+ </privapp-permissions>
+</permissions>
diff --git a/data/etc/car/com.android.car.settings.xml b/data/etc/car/com.android.car.settings.xml
new file mode 100644
index 0000000..5f7e1c1
--- /dev/null
+++ b/data/etc/car/com.android.car.settings.xml
@@ -0,0 +1,57 @@
+<?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
+ -->
+<permissions>
+ <privapp-permissions package="com.android.car.settings">
+ <permission name="android.permission.ACCESS_CHECKIN_PROPERTIES"/>
+ <permission name="android.permission.ACCESS_NOTIFICATIONS"/>
+ <permission name="android.permission.BACKUP"/>
+ <permission name="android.permission.BATTERY_STATS"/>
+ <permission name="android.permission.BLUETOOTH_PRIVILEGED"/>
+ <permission name="android.permission.CHANGE_APP_IDLE_STATE"/>
+ <permission name="android.permission.CHANGE_CONFIGURATION"/>
+ <permission name="android.permission.DELETE_PACKAGES"/>
+ <permission name="android.permission.DELETE_CACHE_FILES"/>
+ <permission name="android.permission.DUMP"/>
+ <permission name="android.permission.FORCE_STOP_PACKAGES"/>
+ <permission name="android.permission.LOCAL_MAC_ADDRESS"/>
+ <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
+ <permission name="android.permission.MANAGE_DEBUGGING"/>
+ <permission name="android.permission.MANAGE_DEVICE_ADMINS"/>
+ <permission name="android.permission.MANAGE_FINGERPRINT"/>
+ <permission name="android.permission.MANAGE_USB"/>
+ <permission name="android.permission.MANAGE_USERS"/>
+ <permission name="android.permission.MANAGE_USER_OEM_UNLOCK_STATE" />
+ <permission name="android.permission.MASTER_CLEAR"/>
+ <permission name="android.permission.MODIFY_PHONE_STATE"/>
+ <permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
+ <permission name="android.permission.MOVE_PACKAGE"/>
+ <permission name="android.permission.OVERRIDE_WIFI_CONFIG"/>
+ <permission name="android.permission.PACKAGE_USAGE_STATS"/>
+ <permission name="android.permission.READ_SEARCH_INDEXABLES"/>
+ <permission name="android.permission.REBOOT"/>
+ <permission name="android.permission.SET_TIME"/>
+ <permission name="android.permission.SET_TIME_ZONE"/>
+ <permission name="android.permission.STATUS_BAR"/>
+ <permission name="android.permission.TETHER_PRIVILEGED"/>
+ <permission name="android.permission.USE_RESERVED_DISK"/>
+ <permission name="android.permission.USER_ACTIVITY"/>
+ <permission name="android.permission.WRITE_APN_SETTINGS"/>
+ <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
+ <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+ <permission name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
+ </privapp-permissions>
+</permissions>
diff --git a/data/etc/car/com.android.car.themeplayground.xml b/data/etc/car/com.android.car.themeplayground.xml
new file mode 100644
index 0000000..cab4718
--- /dev/null
+++ b/data/etc/car/com.android.car.themeplayground.xml
@@ -0,0 +1,21 @@
+<?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
+ -->
+<permissions>
+ <privapp-permissions package="com.android.car.themeplayground">
+ <permission name="android.permission.MODIFY_DAY_NIGHT_MODE"/>
+ </privapp-permissions>
+</permissions>
diff --git a/data/etc/car/com.android.car.trust.xml b/data/etc/car/com.android.car.trust.xml
new file mode 100644
index 0000000..dc87af2
--- /dev/null
+++ b/data/etc/car/com.android.car.trust.xml
@@ -0,0 +1,30 @@
+<?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
+ -->
+<permissions>
+ <privapp-permissions package="com.android.car.trust">
+ <permission name="android.permission.BLUETOOTH" />
+ <permission name="android.permission.BLUETOOTH_ADMIN" />
+ <permission name="android.permission.ACCESS_COARSE_LOCATION"/>
+ <permission name="android.permission.ACCESS_FINE_LOCATION"/>
+ <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+ <permission name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+ <permission name="android.permission.MANAGE_USERS" />
+ <permission name="android.permission.CONTROL_KEYGUARD" />
+ <permission name="android.permission.PROVIDE_TRUST_AGENT" />
+ <permission name="android.permission.RECEIVE_BOOT_COMPLETED" />
+ </privapp-permissions>
+</permissions>
diff --git a/data/etc/car/com.android.car.xml b/data/etc/car/com.android.car.xml
new file mode 100644
index 0000000..f1797de
--- /dev/null
+++ b/data/etc/car/com.android.car.xml
@@ -0,0 +1,29 @@
+<?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
+ -->
+<permissions>
+ <privapp-permissions package="com.android.car">
+ <permission name="android.permission.LOCATION_HARDWARE"/>
+ <permission name="android.permission.MANAGE_USERS"/>
+ <permission name="android.permission.MODIFY_AUDIO_ROUTING"/>
+ <permission name="android.permission.MODIFY_DAY_NIGHT_MODE"/>
+ <permission name="android.permission.MODIFY_PHONE_STATE"/>
+ <permission name="android.permission.PROVIDE_TRUST_AGENT"/>
+ <permission name="android.permission.REAL_GET_TASKS"/>
+ <permission name="android.permission.REBOOT"/>
+ <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+ </privapp-permissions>
+</permissions>
diff --git a/data/etc/car/com.google.android.car.kitchensink.xml b/data/etc/car/com.google.android.car.kitchensink.xml
new file mode 100644
index 0000000..6b26e8f
--- /dev/null
+++ b/data/etc/car/com.google.android.car.kitchensink.xml
@@ -0,0 +1,31 @@
+<?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
+ -->
+<permissions>
+ <privapp-permissions package="com.google.android.car.kitchensink">
+ <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/>
+ <permission name="android.permission.LOCATION_HARDWARE"/>
+ <permission name="android.permission.MANAGE_USB"/>
+ <permission name="android.permission.MANAGE_USERS"/>
+ <permission name="android.permission.MODIFY_AUDIO_ROUTING"/>
+ <permission name="android.permission.MODIFY_DAY_NIGHT_MODE"/>
+ <permission name="android.permission.MODIFY_PHONE_STATE"/>
+ <permission name="android.permission.PROVIDE_TRUST_AGENT"/>
+ <permission name="android.permission.REAL_GET_TASKS"/>
+ <permission name="android.permission.REBOOT"/>
+ <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+ </privapp-permissions>
+</permissions>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 485add9..27e859c 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -83,6 +83,7 @@
<permission name="android.permission.SET_TIME_ZONE"/>
<permission name="android.permission.SHUTDOWN"/>
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+ <permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/>
</privapp-permissions>
<privapp-permissions package="com.android.mms.service">
@@ -235,6 +236,7 @@
<permission name="android.permission.CALL_PRIVILEGED"/>
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
<permission name="android.permission.MANAGE_USERS"/>
+ <permission name="android.permission.MANAGE_ROLE_HOLDERS"/>
<permission name="android.permission.MODIFY_AUDIO_ROUTING" />
<permission name="android.permission.MODIFY_PHONE_STATE"/>
<permission name="android.permission.STOP_APP_SWITCHES"/>
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 07f81c1..4471017 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -2197,8 +2197,12 @@
}
/**
- *
* @return {@link GraphicBuffer} which is internally used by hardware bitmap
+ *
+ * Note: the GraphicBuffer does *not* have an associated {@link ColorSpace}.
+ * To render this object the same as its rendered with this Bitmap, you
+ * should also call {@link getColorSpace}.
+ *
* @hide
*/
@UnsupportedAppUsage
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 816d1fd..11d635e 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -637,6 +637,7 @@
* @see #setOrientation(Orientation)
*/
public Orientation getOrientation() {
+ updateGradientStateOrientation();
return mGradientState.mOrientation;
}
@@ -653,6 +654,9 @@
* @see #getOrientation()
*/
public void setOrientation(Orientation orientation) {
+ // Update the angle here so that subsequent attempts to obtain the orientation
+ // from the angle overwrite previously configured values during inflation
+ mGradientState.mAngle = getAngleFromOrientation(orientation);
mGradientState.mOrientation = orientation;
mGradientIsDirty = true;
invalidateSelf();
@@ -1242,6 +1246,76 @@
}
/**
+ * Update the orientation of the gradient based on the given angle only if the type is
+ * {@link #LINEAR_GRADIENT}
+ */
+ private void updateGradientStateOrientation() {
+ if (mGradientState.mGradient == LINEAR_GRADIENT) {
+ int angle = mGradientState.mAngle;
+ if (angle % 45 != 0) {
+ throw new IllegalArgumentException("Linear gradient requires 'angle' attribute to "
+ + "be a multiple of 45");
+ }
+
+ Orientation orientation;
+ switch (angle) {
+ case 0:
+ orientation = Orientation.LEFT_RIGHT;
+ break;
+ case 45:
+ orientation = Orientation.BL_TR;
+ break;
+ case 90:
+ orientation = Orientation.BOTTOM_TOP;
+ break;
+ case 135:
+ orientation = Orientation.BR_TL;
+ break;
+ case 180:
+ orientation = Orientation.RIGHT_LEFT;
+ break;
+ case 225:
+ orientation = Orientation.TR_BL;
+ break;
+ case 270:
+ orientation = Orientation.TOP_BOTTOM;
+ break;
+ case 315:
+ orientation = Orientation.TL_BR;
+ break;
+ default:
+ // Should not get here as exception is thrown above if angle is not multiple
+ // of 45 degrees
+ orientation = Orientation.LEFT_RIGHT;
+ break;
+ }
+ mGradientState.mOrientation = orientation;
+ }
+ }
+
+ private int getAngleFromOrientation(Orientation orientation) {
+ switch (orientation) {
+ default:
+ case LEFT_RIGHT:
+ return 0;
+ case BL_TR:
+ return 45;
+ case BOTTOM_TOP:
+ return 90;
+ case BR_TL:
+ return 135;
+ case RIGHT_LEFT:
+ return 180;
+ case TR_BL:
+ return 225;
+ case TOP_BOTTOM:
+ return 270;
+ case TL_BR:
+ return 315;
+ }
+ }
+
+ /**
* This checks mGradientIsDirty, and if it is true, recomputes both our drawing
* rectangle (mRect) and the gradient itself, since it depends on our
* rectangle too.
@@ -1270,6 +1344,7 @@
if (st.mGradient == LINEAR_GRADIENT) {
final float level = st.mUseLevel ? getLevel() / 10000.0f : 1.0f;
+ updateGradientStateOrientation();
switch (st.mOrientation) {
case TOP_BOTTOM:
x0 = r.left; y0 = r.top;
@@ -1312,10 +1387,6 @@
y0 = r.top + (r.bottom - r.top) * st.mCenterY;
float radius = st.mGradientRadius;
- if (Float.compare(radius, 0.0f) == -1 || Float.isNaN(radius)) {
- throw new IllegalArgumentException("Gradient radius must be a valid "
- + "number greater than or equal to 0");
- }
if (st.mGradientRadiusType == RADIUS_TYPE_FRACTION) {
// Fall back to parent width or height if intrinsic
// size is not specified.
@@ -1511,8 +1582,6 @@
st.mAttrGradient, R.styleable.GradientDrawableGradient);
try {
updateGradientDrawableGradient(t.getResources(), a);
- } catch (XmlPullParserException e) {
- rethrowAsRuntimeException(e);
} finally {
a.recycle();
}
@@ -1700,8 +1769,7 @@
}
}
- private void updateGradientDrawableGradient(Resources r, TypedArray a)
- throws XmlPullParserException {
+ private void updateGradientDrawableGradient(Resources r, TypedArray a) {
final GradientState st = mGradientState;
// Account for any configuration changes.
@@ -1764,42 +1832,7 @@
}
int angle = (int) a.getFloat(R.styleable.GradientDrawableGradient_angle, st.mAngle);
- angle %= 360;
-
- if (angle % 45 != 0) {
- throw new XmlPullParserException(a.getPositionDescription()
- + "<gradient> tag requires 'angle' attribute to "
- + "be a multiple of 45");
- }
-
- st.mAngle = angle;
-
- switch (angle) {
- case 0:
- st.mOrientation = Orientation.LEFT_RIGHT;
- break;
- case 45:
- st.mOrientation = Orientation.BL_TR;
- break;
- case 90:
- st.mOrientation = Orientation.BOTTOM_TOP;
- break;
- case 135:
- st.mOrientation = Orientation.BR_TL;
- break;
- case 180:
- st.mOrientation = Orientation.RIGHT_LEFT;
- break;
- case 225:
- st.mOrientation = Orientation.TR_BL;
- break;
- case 270:
- st.mOrientation = Orientation.TOP_BOTTOM;
- break;
- case 315:
- st.mOrientation = Orientation.TL_BR;
- break;
- }
+ st.mAngle = angle % 360;
final TypedValue tv = a.peekValue(R.styleable.GradientDrawableGradient_gradientRadius);
if (tv != null) {
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 977e790..8612e1b 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -816,6 +816,8 @@
*/
@UnsupportedAppUsage
public Builder setInternalLegacyStreamType(int streamType) {
+ mContentType = CONTENT_TYPE_UNKNOWN;
+ mUsage = USAGE_UNKNOWN;
if (AudioProductStrategy.getAudioProductStrategies().size() > 0) {
AudioAttributes attributes =
AudioProductStrategy.getAudioAttributesForStrategyWithLegacyStreamType(
@@ -828,49 +830,52 @@
mTags = attributes.mTags;
mBundle = attributes.mBundle;
mSource = attributes.mSource;
- return this;
}
}
- switch(streamType) {
- case AudioSystem.STREAM_VOICE_CALL:
- mContentType = CONTENT_TYPE_SPEECH;
- break;
- case AudioSystem.STREAM_SYSTEM_ENFORCED:
- mFlags |= FLAG_AUDIBILITY_ENFORCED;
- // intended fall through, attributes in common with STREAM_SYSTEM
- case AudioSystem.STREAM_SYSTEM:
- mContentType = CONTENT_TYPE_SONIFICATION;
- break;
- case AudioSystem.STREAM_RING:
- mContentType = CONTENT_TYPE_SONIFICATION;
- break;
- case AudioSystem.STREAM_MUSIC:
- mContentType = CONTENT_TYPE_MUSIC;
- break;
- case AudioSystem.STREAM_ALARM:
- mContentType = CONTENT_TYPE_SONIFICATION;
- break;
- case AudioSystem.STREAM_NOTIFICATION:
- mContentType = CONTENT_TYPE_SONIFICATION;
- break;
- case AudioSystem.STREAM_BLUETOOTH_SCO:
- mContentType = CONTENT_TYPE_SPEECH;
- mFlags |= FLAG_SCO;
- break;
- case AudioSystem.STREAM_DTMF:
- mContentType = CONTENT_TYPE_SONIFICATION;
- break;
- case AudioSystem.STREAM_TTS:
- mContentType = CONTENT_TYPE_SONIFICATION;
- mFlags |= FLAG_BEACON;
- break;
- case AudioSystem.STREAM_ACCESSIBILITY:
- mContentType = CONTENT_TYPE_SPEECH;
- break;
- default:
- Log.e(TAG, "Invalid stream type " + streamType + " for AudioAttributes");
+ if (mContentType == CONTENT_TYPE_UNKNOWN) {
+ switch (streamType) {
+ case AudioSystem.STREAM_VOICE_CALL:
+ mContentType = CONTENT_TYPE_SPEECH;
+ break;
+ case AudioSystem.STREAM_SYSTEM_ENFORCED:
+ mFlags |= FLAG_AUDIBILITY_ENFORCED;
+ // intended fall through, attributes in common with STREAM_SYSTEM
+ case AudioSystem.STREAM_SYSTEM:
+ mContentType = CONTENT_TYPE_SONIFICATION;
+ break;
+ case AudioSystem.STREAM_RING:
+ mContentType = CONTENT_TYPE_SONIFICATION;
+ break;
+ case AudioSystem.STREAM_MUSIC:
+ mContentType = CONTENT_TYPE_MUSIC;
+ break;
+ case AudioSystem.STREAM_ALARM:
+ mContentType = CONTENT_TYPE_SONIFICATION;
+ break;
+ case AudioSystem.STREAM_NOTIFICATION:
+ mContentType = CONTENT_TYPE_SONIFICATION;
+ break;
+ case AudioSystem.STREAM_BLUETOOTH_SCO:
+ mContentType = CONTENT_TYPE_SPEECH;
+ mFlags |= FLAG_SCO;
+ break;
+ case AudioSystem.STREAM_DTMF:
+ mContentType = CONTENT_TYPE_SONIFICATION;
+ break;
+ case AudioSystem.STREAM_TTS:
+ mContentType = CONTENT_TYPE_SONIFICATION;
+ mFlags |= FLAG_BEACON;
+ break;
+ case AudioSystem.STREAM_ACCESSIBILITY:
+ mContentType = CONTENT_TYPE_SPEECH;
+ break;
+ default:
+ Log.e(TAG, "Invalid stream type " + streamType + " for AudioAttributes");
+ }
}
- mUsage = usageForStreamType(streamType);
+ if (mUsage == USAGE_UNKNOWN) {
+ mUsage = usageForStreamType(streamType);
+ }
return this;
}
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index a56e7f5..f304f7c 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -24,6 +24,7 @@
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.os.Build;
+import android.os.SystemProperties;
import android.util.Log;
import android.util.Pair;
import android.util.Range;
@@ -1105,7 +1106,11 @@
mBitrateRange = Range.create(0, Integer.MAX_VALUE);
mMaxInputChannelCount = MAX_INPUT_CHANNEL_COUNT;
// mBitrateRange = Range.create(1, 320000);
- mSampleRateRanges = new Range[] { Range.create(8000, 96000) };
+ final int minSampleRate = SystemProperties.
+ getInt("ro.mediacodec.min_sample_rate", 7350);
+ final int maxSampleRate = SystemProperties.
+ getInt("ro.mediacodec.max_sample_rate", 192000);
+ mSampleRateRanges = new Range[] { Range.create(minSampleRate, maxSampleRate) };
mSampleRates = null;
}
@@ -1677,6 +1682,13 @@
return "PerformancePoint(" + info + ")";
}
+ @Override
+ public int hashCode() {
+ // only max frame rate must equal between performance points that equal to one
+ // another
+ return mMaxFrameRate;
+ }
+
/**
* Create a detailed performance point with custom max frame rate and macroblock size.
*
diff --git a/media/java/android/media/ThumbnailUtils.java b/media/java/android/media/ThumbnailUtils.java
index 21b194d..5de56c7 100644
--- a/media/java/android/media/ThumbnailUtils.java
+++ b/media/java/android/media/ThumbnailUtils.java
@@ -244,30 +244,77 @@
final Resizer resizer = new Resizer(size, signal);
final String mimeType = MediaFile.getMimeTypeForFile(file.getName());
+ Bitmap bitmap = null;
+ ExifInterface exif = null;
+ int orientation = 0;
+
+ // get orientation
+ if (MediaFile.isExifMimeType(mimeType)) {
+ exif = new ExifInterface(file);
+ switch (exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0)) {
+ case ExifInterface.ORIENTATION_ROTATE_90:
+ orientation = 90;
+ break;
+ case ExifInterface.ORIENTATION_ROTATE_180:
+ orientation = 180;
+ break;
+ case ExifInterface.ORIENTATION_ROTATE_270:
+ orientation = 270;
+ break;
+ }
+ }
+
+ boolean isHeifFile = false;
+
if (mimeType.equals("image/heif")
|| mimeType.equals("image/heif-sequence")
|| mimeType.equals("image/heic")
|| mimeType.equals("image/heic-sequence")) {
+ isHeifFile = true;
try (MediaMetadataRetriever retriever = new MediaMetadataRetriever()) {
retriever.setDataSource(file.getAbsolutePath());
- return retriever.getThumbnailImageAtIndex(-1,
+ bitmap = retriever.getThumbnailImageAtIndex(-1,
new MediaMetadataRetriever.BitmapParams(), size.getWidth(),
size.getWidth() * size.getHeight());
} catch (RuntimeException e) {
throw new IOException("Failed to create thumbnail", e);
}
- } else if (MediaFile.isExifMimeType(mimeType)) {
- final ExifInterface exif = new ExifInterface(file);
+ }
+
+ if (bitmap == null && exif != null) {
final byte[] raw = exif.getThumbnailBytes();
if (raw != null) {
- return ImageDecoder.decodeBitmap(ImageDecoder.createSource(raw), resizer);
+ try {
+ bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(raw), resizer);
+ } catch (ImageDecoder.DecodeException e) {
+ Log.w(TAG, e);
+ }
}
}
// Checkpoint before going deeper
if (signal != null) signal.throwIfCanceled();
- return ImageDecoder.decodeBitmap(ImageDecoder.createSource(file), resizer);
+ if (bitmap == null) {
+ bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(file), resizer);
+ // Use ImageDecoder to do full image decode of heif format file
+ // will have right orientation. Don't rotate the bitmap again.
+ if (isHeifFile) {
+ return bitmap;
+ }
+ }
+
+ // Transform the bitmap if the orientation of the image is not 0.
+ if (orientation != 0 && bitmap != null) {
+ final int width = bitmap.getWidth();
+ final int height = bitmap.getHeight();
+
+ final Matrix m = new Matrix();
+ m.setRotate(orientation, width / 2, height / 2);
+ bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, m, false);
+ }
+
+ return bitmap;
}
/**
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index b2baff5..73c7895 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -23,6 +23,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.app.ActivityThread;
import android.app.INotificationManager;
import android.app.Notification;
@@ -71,6 +72,7 @@
/**
* Notification assistant that provides guidance on notification channel blocking
*/
+@SuppressLint("OverrideAbstract")
public class Assistant extends NotificationAssistantService {
private static final String TAG = "ExtAssistant";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -238,7 +240,7 @@
}
mSingleThreadExecutor.submit(() -> {
NotificationEntry entry =
- new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper);
+ new NotificationEntry(getContext(), mPackageManager, sbn, channel, mSmsHelper);
SmartActionsHelper.SmartSuggestions suggestions = mSmartActionsHelper.suggest(entry);
if (DEBUG) {
Log.d(TAG, String.format(
@@ -295,7 +297,7 @@
}
Ranking ranking = getRanking(sbn.getKey(), rankingMap);
if (ranking != null && ranking.getChannel() != null) {
- NotificationEntry entry = new NotificationEntry(mPackageManager,
+ NotificationEntry entry = new NotificationEntry(getContext(), mPackageManager,
sbn, ranking.getChannel(), mSmsHelper);
String key = getKey(
sbn.getPackageName(), sbn.getUserId(), ranking.getChannel().getId());
diff --git a/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java b/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java
index 84a8a8c..1ffbac9 100644
--- a/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java
+++ b/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java
@@ -28,18 +28,23 @@
import android.app.Person;
import android.app.RemoteInput;
import android.content.ComponentName;
+import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
+import android.graphics.drawable.Icon;
import android.media.AudioAttributes;
import android.media.AudioSystem;
import android.os.Build;
+import android.os.Parcelable;
import android.os.RemoteException;
import android.service.notification.StatusBarNotification;
import android.util.Log;
+import android.util.SparseArray;
import java.util.ArrayList;
import java.util.Objects;
+import java.util.Set;
/**
* Holds data about notifications.
@@ -47,6 +52,10 @@
public class NotificationEntry {
static final String TAG = "NotificationEntry";
+ // Copied from hidden definitions in Notification.TvExtender
+ private static final String EXTRA_TV_EXTENDER = "android.tv.EXTENSIONS";
+
+ private final Context mContext;
private final StatusBarNotification mSbn;
private final IPackageManager mPackageManager;
private int mTargetSdkVersion = Build.VERSION_CODES.N_MR1;
@@ -60,9 +69,10 @@
private final Object mLock = new Object();
- public NotificationEntry(IPackageManager packageManager, StatusBarNotification sbn,
- NotificationChannel channel, SmsHelper smsHelper) {
- mSbn = sbn;
+ public NotificationEntry(Context applicationContext, IPackageManager packageManager,
+ StatusBarNotification sbn, NotificationChannel channel, SmsHelper smsHelper) {
+ mContext = applicationContext;
+ mSbn = cloneStatusBarNotificationLight(sbn);
mChannel = channel;
mPackageManager = packageManager;
mPreChannelsNotification = isPreChannelsNotification();
@@ -71,6 +81,66 @@
mSmsHelper = smsHelper;
}
+ /** Adapted from {@code Notification.lightenPayload}. */
+ @SuppressWarnings("nullness")
+ private static void lightenNotificationPayload(Notification notification) {
+ notification.tickerView = null;
+ notification.contentView = null;
+ notification.bigContentView = null;
+ notification.headsUpContentView = null;
+ notification.largeIcon = null;
+ if (notification.extras != null && !notification.extras.isEmpty()) {
+ final Set<String> keyset = notification.extras.keySet();
+ final int keysetSize = keyset.size();
+ final String[] keys = keyset.toArray(new String[keysetSize]);
+ for (int i = 0; i < keysetSize; i++) {
+ final String key = keys[i];
+ if (EXTRA_TV_EXTENDER.equals(key)
+ || Notification.EXTRA_MESSAGES.equals(key)
+ || Notification.EXTRA_MESSAGING_PERSON.equals(key)
+ || Notification.EXTRA_PEOPLE_LIST.equals(key)) {
+ continue;
+ }
+ final Object obj = notification.extras.get(key);
+ if (obj != null
+ && (obj instanceof Parcelable
+ || obj instanceof Parcelable[]
+ || obj instanceof SparseArray
+ || obj instanceof ArrayList)) {
+ notification.extras.remove(key);
+ }
+ }
+ }
+ }
+
+ /** An interpretation of {@code Notification.cloneInto} with heavy=false. */
+ private Notification cloneNotificationLight(Notification notification) {
+ // We can't just use clone() here because the only way to remove the icons is with the
+ // builder, which we can only create with a Context.
+ Notification lightNotification =
+ Notification.Builder.recoverBuilder(mContext, notification)
+ .setSmallIcon(0)
+ .setLargeIcon((Icon) null)
+ .build();
+ lightenNotificationPayload(lightNotification);
+ return lightNotification;
+ }
+
+ /** Adapted from {@code StatusBarNotification.cloneLight}. */
+ public StatusBarNotification cloneStatusBarNotificationLight(StatusBarNotification sbn) {
+ return new StatusBarNotification(
+ sbn.getPackageName(),
+ sbn.getOpPkg(),
+ sbn.getId(),
+ sbn.getTag(),
+ sbn.getUid(),
+ /*initialPid=*/ 0,
+ /*score=*/ 0,
+ cloneNotificationLight(sbn.getNotification()),
+ sbn.getUser(),
+ sbn.getPostTime());
+ }
+
private boolean isPreChannelsNotification() {
try {
ApplicationInfo info = mPackageManager.getApplicationInfo(
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/AgingHelperTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/AgingHelperTest.java
index 3db275a..a87d57c 100644
--- a/packages/ExtServices/tests/src/android/ext/services/notification/AgingHelperTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/AgingHelperTest.java
@@ -102,7 +102,8 @@
public void testNoSnoozingOnPost() {
NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
StatusBarNotification sbn = generateSbn(channel.getId());
- NotificationEntry entry = new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper);
+ NotificationEntry entry = new NotificationEntry(
+ mContext, mPackageManager, sbn, channel, mSmsHelper);
mAgingHelper.onNotificationPosted(entry);
@@ -113,7 +114,8 @@
public void testPostResetsSnooze() {
NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
StatusBarNotification sbn = generateSbn(channel.getId());
- NotificationEntry entry = new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper);
+ NotificationEntry entry = new NotificationEntry(
+ mContext, mPackageManager, sbn, channel, mSmsHelper);
mAgingHelper.onNotificationPosted(entry);
@@ -124,7 +126,8 @@
public void testSnoozingOnSeen() {
NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
StatusBarNotification sbn = generateSbn(channel.getId());
- NotificationEntry entry = new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper);
+ NotificationEntry entry = new NotificationEntry(
+ mContext, mPackageManager, sbn, channel, mSmsHelper);
entry.setSeen();
when(mCategorizer.getCategory(entry)).thenReturn(NotificationCategorizer.CATEGORY_PEOPLE);
@@ -137,7 +140,8 @@
NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
StatusBarNotification sbn = generateSbn(channel.getId());
- NotificationEntry entry = new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper);
+ NotificationEntry entry = new NotificationEntry(
+ mContext, mPackageManager, sbn, channel, mSmsHelper);
when(mCategorizer.getCategory(entry)).thenReturn(NotificationCategorizer.CATEGORY_PEOPLE);
mAgingHelper.onNotificationSeen(entry);
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
index ee29bc5..012dcc0 100644
--- a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
@@ -468,8 +468,10 @@
@Test
public void testAssistantNeverIncreasesImportanceWhenSuggestingSilent() throws Exception {
StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C3, "min notif!", null);
- Adjustment adjust = mAssistant.createEnqueuedNotificationAdjustment(new NotificationEntry(
- mPackageManager, sbn, P1C3, mSmsHelper), new ArrayList<>(), new ArrayList<>());
+ Adjustment adjust = mAssistant.createEnqueuedNotificationAdjustment(
+ new NotificationEntry(mContext, mPackageManager, sbn, P1C3, mSmsHelper),
+ new ArrayList<>(),
+ new ArrayList<>());
assertEquals(IMPORTANCE_MIN, adjust.getSignals().getInt(Adjustment.KEY_IMPORTANCE));
}
}
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/NotificationEntryTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/NotificationEntryTest.java
index f51e911..c026079 100644
--- a/packages/ExtServices/tests/src/android/ext/services/notification/NotificationEntryTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/NotificationEntryTest.java
@@ -24,6 +24,7 @@
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;
@@ -34,6 +35,8 @@
import android.content.ComponentName;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Icon;
import android.media.AudioAttributes;
import android.os.Build;
import android.os.Process;
@@ -41,9 +44,6 @@
import android.service.notification.StatusBarNotification;
import android.testing.TestableContext;
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -53,6 +53,9 @@
import java.util.ArrayList;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
@RunWith(AndroidJUnit4.class)
public class NotificationEntryTest {
private String mPkg = "pkg";
@@ -113,7 +116,8 @@
people.add(new Person.Builder().setKey("mailto:testing@android.com").build());
sbn.getNotification().extras.putParcelableArrayList(Notification.EXTRA_PEOPLE_LIST, people);
- NotificationEntry entry = new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper);
+ NotificationEntry entry = new NotificationEntry(
+ mContext, mPackageManager, sbn, channel, mSmsHelper);
assertTrue(entry.involvesPeople());
}
@@ -121,7 +125,8 @@
public void testNotPerson() {
NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
StatusBarNotification sbn = generateSbn(channel.getId());
- NotificationEntry entry = new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper);
+ NotificationEntry entry = new NotificationEntry(
+ mContext, mPackageManager, sbn, channel, mSmsHelper);
assertFalse(entry.involvesPeople());
}
@@ -129,7 +134,8 @@
public void testHasPerson_matchesDefaultSmsApp() {
NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
StatusBarNotification sbn = generateSbn(channel.getId(), DEFAULT_SMS_PACKAGE_NAME);
- NotificationEntry entry = new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper);
+ NotificationEntry entry = new NotificationEntry(
+ mContext, mPackageManager, sbn, channel, mSmsHelper);
assertTrue(entry.involvesPeople());
}
@@ -137,7 +143,8 @@
public void testHasPerson_doesntMatchDefaultSmsApp() {
NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
StatusBarNotification sbn = generateSbn(channel.getId(), "abc");
- NotificationEntry entry = new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper);
+ NotificationEntry entry = new NotificationEntry(
+ mContext, mPackageManager, sbn, channel, mSmsHelper);
assertFalse(entry.involvesPeople());
}
@@ -148,8 +155,8 @@
Notification n = new Notification.Builder(mContext, channel.getId())
.setStyle(new Notification.InboxStyle())
.build();
- NotificationEntry entry =
- new NotificationEntry(mPackageManager, generateSbn(n), channel, mSmsHelper);
+ NotificationEntry entry = new NotificationEntry(
+ mContext, mPackageManager, generateSbn(n), channel, mSmsHelper);
assertTrue(entry.hasStyle(Notification.InboxStyle.class));
}
@@ -160,8 +167,8 @@
Notification n = new Notification.Builder(mContext, channel.getId())
.setStyle(new Notification.MessagingStyle(""))
.build();
- NotificationEntry entry =
- new NotificationEntry(mPackageManager, generateSbn(n), channel, mSmsHelper);
+ NotificationEntry entry = new NotificationEntry(
+ mContext, mPackageManager, generateSbn(n), channel, mSmsHelper);
assertTrue(entry.hasStyle(Notification.MessagingStyle.class));
}
@@ -172,8 +179,8 @@
Notification n = new Notification.Builder(mContext, channel.getId())
.setStyle(new Notification.BigPictureStyle())
.build();
- NotificationEntry entry =
- new NotificationEntry(mPackageManager, generateSbn(n), channel, mSmsHelper);
+ NotificationEntry entry = new NotificationEntry(
+ mContext, mPackageManager, generateSbn(n), channel, mSmsHelper);
assertFalse(entry.hasStyle(Notification.InboxStyle.class));
assertFalse(entry.hasStyle(Notification.MessagingStyle.class));
}
@@ -184,7 +191,7 @@
channel.setSound(null, new AudioAttributes.Builder().setUsage(USAGE_ALARM).build());
NotificationEntry entry = new NotificationEntry(
- mPackageManager, generateSbn(channel.getId()), channel, mSmsHelper);
+ mContext, mPackageManager, generateSbn(channel.getId()), channel, mSmsHelper);
assertTrue(entry.isAudioAttributesUsage(USAGE_ALARM));
}
@@ -193,7 +200,7 @@
public void testIsNotAudioAttributes() {
NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
NotificationEntry entry = new NotificationEntry(
- mPackageManager, generateSbn(channel.getId()), channel, mSmsHelper);
+ mContext, mPackageManager, generateSbn(channel.getId()), channel, mSmsHelper);
assertFalse(entry.isAudioAttributesUsage(USAGE_ALARM));
}
@@ -205,8 +212,8 @@
Notification n = new Notification.Builder(mContext, channel.getId())
.setCategory(Notification.CATEGORY_EMAIL)
.build();
- NotificationEntry entry =
- new NotificationEntry(mPackageManager, generateSbn(n), channel, mSmsHelper);
+ NotificationEntry entry = new NotificationEntry(
+ mContext, mPackageManager, generateSbn(n), channel, mSmsHelper);
assertTrue(entry.isCategory(Notification.CATEGORY_EMAIL));
assertFalse(entry.isCategory(Notification.CATEGORY_MESSAGE));
@@ -219,8 +226,8 @@
Notification n = new Notification.Builder(mContext, channel.getId())
.setFlag(FLAG_FOREGROUND_SERVICE, true)
.build();
- NotificationEntry entry =
- new NotificationEntry(mPackageManager, generateSbn(n), channel, mSmsHelper);
+ NotificationEntry entry = new NotificationEntry(
+ mContext, mPackageManager, generateSbn(n), channel, mSmsHelper);
assertTrue(entry.isOngoing());
}
@@ -232,9 +239,28 @@
Notification n = new Notification.Builder(mContext, channel.getId())
.setFlag(FLAG_CAN_COLORIZE, true)
.build();
- NotificationEntry entry =
- new NotificationEntry(mPackageManager, generateSbn(n), channel, mSmsHelper);
+ NotificationEntry entry = new NotificationEntry(
+ mContext, mPackageManager, generateSbn(n), channel, mSmsHelper);
assertFalse(entry.isOngoing());
}
+
+ @Test
+ public void testShrinkNotification() {
+ Notification n = new Notification.Builder(mContext, "")
+ .setLargeIcon(Icon.createWithResource(
+ mContext, android.R.drawable.alert_dark_frame))
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .build();
+ n.largeIcon = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
+ NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
+
+ NotificationEntry entry = new NotificationEntry(
+ mContext, mPackageManager, generateSbn(n), channel, mSmsHelper);
+
+ assertNull(entry.getNotification().getSmallIcon());
+ assertNull(entry.getNotification().getLargeIcon());
+ assertNull(entry.getNotification().largeIcon);
+ assertNull(entry.getNotification().extras.getParcelable(Notification.EXTRA_LARGE_ICON));
+ }
}
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java
index dfa1ea0..69abe87 100644
--- a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java
@@ -46,9 +46,6 @@
import android.view.textclassifier.TextClassifier;
import android.view.textclassifier.TextClassifierEvent;
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
import com.google.common.truth.FailureStrategy;
import com.google.common.truth.Subject;
import com.google.common.truth.SubjectFactory;
@@ -71,9 +68,11 @@
import javax.annotation.Nullable;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
@RunWith(AndroidJUnit4.class)
public class SmartActionsHelperTest {
- private static final String NOTIFICATION_KEY = "key";
private static final String RESULT_ID = "id";
private static final float SCORE = 0.7f;
private static final CharSequence SMART_REPLY = "Home";
@@ -88,7 +87,6 @@
IPackageManager mIPackageManager;
@Mock
private TextClassifier mTextClassifier;
- @Mock
private StatusBarNotification mStatusBarNotification;
@Mock
private SmsHelper mSmsHelper;
@@ -108,9 +106,6 @@
when(mTextClassifier.suggestConversationActions(any(ConversationActions.Request.class)))
.thenReturn(new ConversationActions(Arrays.asList(REPLY_ACTION), RESULT_ID));
- when(mStatusBarNotification.getPackageName()).thenReturn("random.app");
- when(mStatusBarNotification.getUser()).thenReturn(Process.myUserHandle());
- when(mStatusBarNotification.getKey()).thenReturn(NOTIFICATION_KEY);
mNotificationBuilder = new Notification.Builder(mContext, "channel");
mSettings = AssistantSettings.createForTesting(
null, null, Process.myUserHandle().getIdentifier(), null);
@@ -119,10 +114,15 @@
mSmartActionsHelper = new SmartActionsHelper(mContext, mSettings);
}
+ private void setStatusBarNotification(Notification n) {
+ mStatusBarNotification = new StatusBarNotification("random.app", "random.app", 0,
+ "tag", Process.myUid(), Process.myPid(), n, Process.myUserHandle(), null, 0);
+ }
+
@Test
public void testSuggest_notMessageNotification() {
Notification notification = mNotificationBuilder.setContentText(MESSAGE).build();
- when(mStatusBarNotification.getNotification()).thenReturn(notification);
+ setStatusBarNotification(notification);
mSmartActionsHelper.suggest(createNotificationEntry());
@@ -137,7 +137,7 @@
.setContentText(MESSAGE)
.setCategory(Notification.CATEGORY_MESSAGE)
.build();
- when(mStatusBarNotification.getNotification()).thenReturn(notification);
+ setStatusBarNotification(notification);
ConversationActions.Request request = runSuggestAndCaptureRequest();
@@ -154,7 +154,7 @@
mSettings.mGenerateActions = false;
mSettings.mGenerateReplies = false;
Notification notification = createMessageNotification();
- when(mStatusBarNotification.getNotification()).thenReturn(notification);
+ setStatusBarNotification(notification);
mSmartActionsHelper.suggest(createNotificationEntry());
@@ -167,7 +167,7 @@
mSettings.mGenerateReplies = true;
mSettings.mGenerateActions = false;
Notification notification = createMessageNotification();
- when(mStatusBarNotification.getNotification()).thenReturn(notification);
+ setStatusBarNotification(notification);
ConversationActions.Request request = runSuggestAndCaptureRequest();
@@ -184,7 +184,7 @@
mSettings.mGenerateReplies = false;
mSettings.mGenerateActions = true;
Notification notification = createMessageNotification();
- when(mStatusBarNotification.getNotification()).thenReturn(notification);
+ setStatusBarNotification(notification);
ConversationActions.Request request = runSuggestAndCaptureRequest();
@@ -200,7 +200,7 @@
@Test
public void testSuggest_nonMessageStyleMessageNotification() {
Notification notification = createMessageNotification();
- when(mStatusBarNotification.getNotification()).thenReturn(notification);
+ setStatusBarNotification(notification);
List<ConversationActions.Message> messages =
runSuggestAndCaptureRequest().getConversation();
@@ -233,7 +233,7 @@
.setStyle(style)
.setActions(createReplyAction())
.build();
- when(mStatusBarNotification.getNotification()).thenReturn(notification);
+ setStatusBarNotification(notification);
List<ConversationActions.Message> messages =
runSuggestAndCaptureRequest().getConversation();
@@ -288,7 +288,7 @@
.setStyle(style)
.setActions(createReplyAction())
.build();
- when(mStatusBarNotification.getNotification()).thenReturn(notification);
+ setStatusBarNotification(notification);
mSmartActionsHelper.suggest(createNotificationEntry());
@@ -307,7 +307,7 @@
.setStyle(style)
.setActions(createReplyAction())
.build();
- when(mStatusBarNotification.getNotification()).thenReturn(notification);
+ setStatusBarNotification(notification);
mSmartActionsHelper.suggest(createNotificationEntry());
@@ -318,11 +318,11 @@
@Test
public void testOnSuggestedReplySent() {
Notification notification = createMessageNotification();
- when(mStatusBarNotification.getNotification()).thenReturn(notification);
+ setStatusBarNotification(notification);
mSmartActionsHelper.suggest(createNotificationEntry());
- mSmartActionsHelper.onSuggestedReplySent(
- NOTIFICATION_KEY, SMART_REPLY, NotificationAssistantService.SOURCE_FROM_ASSISTANT);
+ mSmartActionsHelper.onSuggestedReplySent(mStatusBarNotification.getKey(), SMART_REPLY,
+ NotificationAssistantService.SOURCE_FROM_ASSISTANT);
ArgumentCaptor<TextClassifierEvent> argumentCaptor =
ArgumentCaptor.forClass(TextClassifierEvent.class);
@@ -338,7 +338,7 @@
@Test
public void testOnSuggestedReplySent_anotherNotification() {
Notification notification = createMessageNotification();
- when(mStatusBarNotification.getNotification()).thenReturn(notification);
+ setStatusBarNotification(notification);
mSmartActionsHelper.suggest(createNotificationEntry());
mSmartActionsHelper.onSuggestedReplySent(
@@ -353,11 +353,11 @@
when(mTextClassifier.suggestConversationActions(any(ConversationActions.Request.class)))
.thenReturn(new ConversationActions(Collections.singletonList(REPLY_ACTION), null));
Notification notification = createMessageNotification();
- when(mStatusBarNotification.getNotification()).thenReturn(notification);
+ setStatusBarNotification(notification);
mSmartActionsHelper.suggest(createNotificationEntry());
- mSmartActionsHelper.onSuggestedReplySent(
- NOTIFICATION_KEY, SMART_REPLY, NotificationAssistantService.SOURCE_FROM_ASSISTANT);
+ mSmartActionsHelper.onSuggestedReplySent(mStatusBarNotification.getKey(), SMART_REPLY,
+ NotificationAssistantService.SOURCE_FROM_ASSISTANT);
verify(mTextClassifier, never()).onTextClassifierEvent(any(TextClassifierEvent.class));
}
@@ -365,10 +365,10 @@
@Test
public void testOnNotificationDirectReply() {
Notification notification = createMessageNotification();
- when(mStatusBarNotification.getNotification()).thenReturn(notification);
+ setStatusBarNotification(notification);
mSmartActionsHelper.suggest(createNotificationEntry());
- mSmartActionsHelper.onNotificationDirectReplied(NOTIFICATION_KEY);
+ mSmartActionsHelper.onNotificationDirectReplied(mStatusBarNotification.getKey());
ArgumentCaptor<TextClassifierEvent> argumentCaptor =
ArgumentCaptor.forClass(TextClassifierEvent.class);
@@ -381,7 +381,7 @@
@Test
public void testOnNotificationExpansionChanged() {
Notification notification = createMessageNotification();
- when(mStatusBarNotification.getNotification()).thenReturn(notification);
+ setStatusBarNotification(notification);
mSmartActionsHelper.suggest(createNotificationEntry());
mSmartActionsHelper.onNotificationExpansionChanged(createNotificationEntry(), true);
@@ -397,7 +397,7 @@
@Test
public void testOnNotificationsSeen_notExpanded() {
Notification notification = createMessageNotification();
- when(mStatusBarNotification.getNotification()).thenReturn(notification);
+ setStatusBarNotification(notification);
mSmartActionsHelper.suggest(createNotificationEntry());
mSmartActionsHelper.onNotificationExpansionChanged(createNotificationEntry(), false);
@@ -409,7 +409,7 @@
@Test
public void testOnNotifications_expanded() {
Notification notification = createMessageNotification();
- when(mStatusBarNotification.getNotification()).thenReturn(notification);
+ setStatusBarNotification(notification);
mSmartActionsHelper.suggest(createNotificationEntry());
mSmartActionsHelper.onNotificationExpansionChanged(createNotificationEntry(), true);
@@ -438,7 +438,7 @@
Collections.singletonList(conversationAction), null));
Notification notification = createMessageNotification();
- when(mStatusBarNotification.getNotification()).thenReturn(notification);
+ setStatusBarNotification(notification);
SmartActionsHelper.SmartSuggestions suggestions =
mSmartActionsHelper.suggest(createNotificationEntry());
@@ -477,7 +477,8 @@
private NotificationEntry createNotificationEntry() {
NotificationChannel channel =
new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_DEFAULT);
- return new NotificationEntry(mIPackageManager, mStatusBarNotification, channel, mSmsHelper);
+ return new NotificationEntry(
+ mContext, mIPackageManager, mStatusBarNotification, channel, mSmsHelper);
}
private Notification createMessageNotification() {
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
index 0ffd471..05c2f24 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
@@ -174,7 +174,7 @@
packageManager.getResourcesForApplication(mActivityPackage);
title = res.getString(mMetaData.getInt(META_DATA_PREFERENCE_TITLE));
} catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) {
- Log.d(TAG, "Couldn't find info", e);
+ Log.w(TAG, "Couldn't find info", e);
}
} else {
title = mMetaData.getString(META_DATA_PREFERENCE_TITLE);
@@ -183,26 +183,16 @@
// Set the preference title to the activity's label if no
// meta-data is found
if (title == null) {
- title = getActivityInfo(context).loadLabel(packageManager);
+ final ActivityInfo activityInfo = getActivityInfo(context);
+ if (activityInfo == null) {
+ return null;
+ }
+ title = activityInfo.loadLabel(packageManager);
}
return title;
}
/**
- * Returns the raw metadata for summary, this is used for comparing 2 summary text without
- * loading the real string.
- */
- public String getSummaryReference() {
- if (mSummaryOverride != null) {
- return mSummaryOverride.toString();
- }
- if (mMetaData != null && mMetaData.containsKey(META_DATA_PREFERENCE_SUMMARY)) {
- return mMetaData.get(META_DATA_PREFERENCE_SUMMARY).toString();
- }
- return null;
- }
-
- /**
* Overrides the summary. This can happen when injected tile wants to provide dynamic summary.
*/
public void overrideSummary(CharSequence summaryOverride) {
@@ -302,7 +292,7 @@
if (iconResId != 0) {
final Icon icon = Icon.createWithResource(activityInfo.packageName, iconResId);
if (isIconTintable(context)) {
- final TypedArray a = context.obtainStyledAttributes(new int[] {
+ final TypedArray a = context.obtainStyledAttributes(new int[]{
android.R.attr.colorControlNormal});
final int tintColor = a.getColor(0, 0);
a.recycle();
@@ -357,6 +347,9 @@
if (infoList != null && !infoList.isEmpty()) {
mActivityInfo = infoList.get(0).activityInfo;
mMetaData = mActivityInfo.metaData;
+ } else {
+ Log.e(TAG, "Cannot find package info for "
+ + intent.getComponent().flattenToString());
}
}
return mActivityInfo;
diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt b/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt
index 257943e..a5b5312 100644
--- a/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt
@@ -44,6 +44,8 @@
// doesn't touch the walls
private val perimeterPath = Path()
private val scaledPerimeter = Path()
+ private val errorPerimeterPath = Path()
+ private val scaledErrorPerimeter = 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()
@@ -242,7 +244,7 @@
}
} else if (powerSaveEnabled) {
// If power save is enabled draw the perimeter path with colorError
- c.drawPath(scaledPerimeter, errorPaint)
+ c.drawPath(scaledErrorPerimeter, errorPaint)
// And draw the plus sign on top of the fill
c.drawPath(scaledPlus, errorPaint)
}
@@ -364,6 +366,7 @@
}
perimeterPath.transform(scaleMatrix, scaledPerimeter)
+ errorPerimeterPath.transform(scaleMatrix, scaledErrorPerimeter)
fillMask.transform(scaleMatrix, scaledFill)
scaledFill.computeBounds(fillRect, true)
boltPath.transform(scaleMatrix, scaledBolt)
@@ -382,8 +385,12 @@
val pathString = context.resources.getString(
com.android.internal.R.string.config_batterymeterPerimeterPath)
perimeterPath.set(PathParser.createPathFromPathData(pathString))
- val b = RectF()
- perimeterPath.computeBounds(b, true)
+ perimeterPath.computeBounds(RectF(), true)
+
+ val errorPathString = context.resources.getString(
+ com.android.internal.R.string.config_batterymeterErrorPerimeterPath)
+ errorPerimeterPath.set(PathParser.createPathFromPathData(errorPathString))
+ errorPerimeterPath.computeBounds(RectF(), true)
val fillMaskString = context.resources.getString(
com.android.internal.R.string.config_batterymeterFillMask)
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 5f2bc4e..d8172a0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -123,7 +123,8 @@
/**
* Synchronization lock for managing concurrency between main and worker threads.
*
- * <p>This lock should be held for all modifications to {@link #mInternalAccessPoints}.
+ * <p>This lock should be held for all modifications to {@link #mInternalAccessPoints} and
+ * {@link #mScanner}.
*/
private final Object mLock = new Object();
@@ -168,6 +169,7 @@
private static final String WIFI_SECURITY_OWE = "OWE";
private static final String WIFI_SECURITY_SUITE_B_192 = "SUITE_B_192";
+ @GuardedBy("mLock")
@VisibleForTesting
Scanner mScanner;
@@ -276,9 +278,11 @@
* <p>Sets {@link #mStaleScanResults} to true.
*/
private void pauseScanning() {
- if (mScanner != null) {
- mScanner.pause();
- mScanner = null;
+ synchronized (mLock) {
+ if (mScanner != null) {
+ mScanner.pause();
+ mScanner = null;
+ }
}
mStaleScanResults = true;
}
@@ -289,12 +293,14 @@
* <p>The score cache should be registered before this method is invoked.
*/
public void resumeScanning() {
- if (mScanner == null) {
- mScanner = new Scanner();
- }
+ synchronized (mLock) {
+ if (mScanner == null) {
+ mScanner = new Scanner();
+ }
- if (isWifiEnabled()) {
- mScanner.resume();
+ if (isWifiEnabled()) {
+ mScanner.resume();
+ }
}
}
@@ -743,7 +749,6 @@
}
private void updateNetworkInfo(NetworkInfo networkInfo) {
-
/* Sticky broadcasts can call this when wifi is disabled */
if (!isWifiEnabled()) {
clearAccessPointsAndConditionallyUpdate();
@@ -880,18 +885,25 @@
* true.
*/
private void updateWifiState(int state) {
+ if (isVerboseLoggingEnabled()) {
+ Log.d(TAG, "updateWifiState: " + state);
+ }
if (state == WifiManager.WIFI_STATE_ENABLED) {
- if (mScanner != null) {
- // We only need to resume if mScanner isn't null because
- // that means we want to be scanning.
- mScanner.resume();
+ synchronized (mLock) {
+ if (mScanner != null) {
+ // We only need to resume if mScanner isn't null because
+ // that means we want to be scanning.
+ mScanner.resume();
+ }
}
} else {
clearAccessPointsAndConditionallyUpdate();
mLastInfo = null;
mLastNetworkInfo = null;
- if (mScanner != null) {
- mScanner.pause();
+ synchronized (mLock) {
+ if (mScanner != null) {
+ mScanner.pause();
+ }
}
mStaleScanResults = true;
}
@@ -919,12 +931,18 @@
private int mRetry = 0;
void resume() {
+ if (isVerboseLoggingEnabled()) {
+ Log.d(TAG, "Scanner resume");
+ }
if (!hasMessages(MSG_SCAN)) {
sendEmptyMessage(MSG_SCAN);
}
}
void pause() {
+ if (isVerboseLoggingEnabled()) {
+ Log.d(TAG, "Scanner pause");
+ }
mRetry = 0;
removeMessages(MSG_SCAN);
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
index d0d1e58..5da6205 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
@@ -23,6 +23,7 @@
import org.robolectric.RuntimeEnvironment;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowPackageManager;
+import org.robolectric.util.ReflectionHelpers;
@RunWith(RobolectricTestRunner.class)
public class TileTest {
@@ -164,4 +165,17 @@
assertThat(tile.mLastUpdateTime).isNotEqualTo(staleTimeStamp);
}
+
+ @Test
+ public void getTitle_noActivity_returnNull() {
+ final ResolveInfo info = new ResolveInfo();
+ info.activityInfo = mActivityInfo;
+ final ShadowPackageManager spm = Shadow.extract(mContext.getPackageManager());
+ spm.removePackage(mActivityInfo.packageName);
+
+ final Tile tile = new Tile(mActivityInfo, "category");
+ ReflectionHelpers.setField(tile, "mActivityInfo", null);
+
+ assertThat(tile.getTitle(RuntimeEnvironment.application)).isNull();
+ }
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActionsPanelPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActionsPanelPlugin.java
index 6be7707..68d2ed7 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActionsPanelPlugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActionsPanelPlugin.java
@@ -26,8 +26,8 @@
* Plugin which provides a "Panel" {@link View} to be rendered inside of the GlobalActions menu.
*
* Implementations should construct a new {@link PanelViewController} with the given
- * {@link Callbacks} instance inside of {@link #onPanelShown(Callbacks)}, and should not hold onto
- * a reference, instead allowing Global Actions to manage the lifetime of the object.
+ * {@link Callbacks} instance inside of {@link #onPanelShown(Callbacks, boolean)}, and should not
+ * hold onto a reference, instead allowing Global Actions to manage the lifetime of the object.
*
* Under this assumption, {@link PanelViewController} represents the lifetime of a single invocation
* of the Global Actions menu. The {@link View} for the Panel is generated when the
@@ -50,9 +50,10 @@
*
* @param callbacks {@link Callbacks} instance that can be used by the Panel to interact with
* the Global Actions menu.
+ * @param deviceLocked Indicates whether or not the device is currently locked.
* @return A {@link PanelViewController} instance used to receive Global Actions events.
*/
- PanelViewController onPanelShown(Callbacks callbacks);
+ PanelViewController onPanelShown(Callbacks callbacks, boolean deviceLocked);
/**
* Provides methods to interact with the Global Actions menu.
@@ -92,5 +93,10 @@
* {@link #getPanelContent()}) is dismissed.
*/
void onDismissed();
+
+ /**
+ * Invoked when the device is either locked or unlocked.
+ */
+ void onDeviceLockStateChanged(boolean locked);
}
}
diff --git a/packages/SystemUI/res/drawable/ic_sysbar_menu.xml b/packages/SystemUI/res/drawable/ic_sysbar_menu.xml
deleted file mode 100644
index d53c95b..0000000
--- a/packages/SystemUI/res/drawable/ic_sysbar_menu.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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="21dp"
- android:height="21dp"
- android:viewportWidth="28"
- android:viewportHeight="28">
-
- <path
- android:fillColor="?attr/singleToneColor"
- android:pathData="M14,9.5c1.24,0,2.25-1.01,2.25-2.25S15.24,5,14,5s-2.25,1.01-2.25,2.25S12.76,9.5,14,9.5z M14,11.75 c-1.24,0-2.25,1.01-2.25,2.25s1.01,2.25,2.25,2.25s2.25-1.01,2.25-2.25S15.24,11.75,14,11.75z M14,18.5 c-1.24,0-2.25,1.01-2.25,2.25S12.76,23,14,23s2.25-1.01,2.25-2.25S15.24,18.5,14,18.5z" />
-</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-land/global_actions_column.xml b/packages/SystemUI/res/layout-land/global_actions_column.xml
new file mode 100644
index 0000000..99a4e13
--- /dev/null
+++ b/packages/SystemUI/res/layout-land/global_actions_column.xml
@@ -0,0 +1,68 @@
+<?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
+ -->
+
+<com.android.systemui.globalactions.GlobalActionsColumnLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@id/global_actions_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:clipToPadding="false"
+ android:theme="@style/qs_theme"
+ android:gravity="center_horizontal | top"
+ android:clipChildren="false"
+>
+ <LinearLayout
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:padding="0dp"
+ android:orientation="horizontal"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ >
+ <!-- Grid of action items -->
+ <LinearLayout
+ android:id="@android:id/list"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_marginTop="@dimen/global_actions_grid_side_margin"
+ android:translationZ="@dimen/global_actions_translate"
+ android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
+ android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
+ android:paddingTop="@dimen/global_actions_grid_vertical_padding"
+ android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
+ android:background="?android:attr/colorBackgroundFloating"
+ />
+ <!-- For separated items-->
+ <LinearLayout
+ android:id="@+id/separated_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="@dimen/global_actions_grid_side_margin"
+ android:layout_marginTop="@dimen/global_actions_grid_side_margin"
+ android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
+ android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
+ android:paddingTop="@dimen/global_actions_grid_vertical_padding"
+ android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
+ android:orientation="horizontal"
+ android:background="?android:attr/colorBackgroundFloating"
+ android:translationZ="@dimen/global_actions_translate"
+ />
+ </LinearLayout>
+
+</com.android.systemui.globalactions.GlobalActionsColumnLayout>
diff --git a/packages/SystemUI/res/layout-land/global_actions_column_seascape.xml b/packages/SystemUI/res/layout-land/global_actions_column_seascape.xml
new file mode 100644
index 0000000..0f86131
--- /dev/null
+++ b/packages/SystemUI/res/layout-land/global_actions_column_seascape.xml
@@ -0,0 +1,68 @@
+<?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
+ -->
+
+<com.android.systemui.globalactions.GlobalActionsColumnLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@id/global_actions_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:clipToPadding="false"
+ android:theme="@style/qs_theme"
+ android:gravity="center_horizontal | bottom"
+ android:clipChildren="false"
+>
+ <LinearLayout
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:padding="0dp"
+ android:orientation="horizontal"
+ >
+ <!-- Grid of action items -->
+ <com.android.systemui.globalactions.ListGridLayout
+ android:id="@android:id/list"
+ android:layout_gravity="bottom|left"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_marginBottom="@dimen/global_actions_grid_side_margin"
+ android:translationZ="@dimen/global_actions_translate"
+ android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
+ android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
+ android:paddingTop="@dimen/global_actions_grid_vertical_padding"
+ android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
+ android:background="?android:attr/colorBackgroundFloating"
+ />
+ <!-- For separated items-->
+ <LinearLayout
+ android:id="@+id/separated_button"
+ android:layout_gravity="top|left"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="@dimen/global_actions_grid_side_margin"
+ android:layout_marginBottom="@dimen/global_actions_grid_side_margin"
+ android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
+ android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
+ android:paddingTop="@dimen/global_actions_grid_vertical_padding"
+ android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
+ android:orientation="horizontal"
+ android:background="?android:attr/colorBackgroundFloating"
+ android:translationZ="@dimen/global_actions_translate"
+ />
+ </LinearLayout>
+
+</com.android.systemui.globalactions.GlobalActionsColumnLayout>
diff --git a/packages/SystemUI/res/layout/bubble_expanded_view.xml b/packages/SystemUI/res/layout/bubble_expanded_view.xml
index 65ede3d..b734125 100644
--- a/packages/SystemUI/res/layout/bubble_expanded_view.xml
+++ b/packages/SystemUI/res/layout/bubble_expanded_view.xml
@@ -33,13 +33,15 @@
android:layout_height="wrap_content"
android:animateLayoutChanges="true">
- <ImageView
+ <com.android.systemui.statusbar.AlphaOptimizedButton
+ style="@android:style/Widget.Material.Button.Borderless"
android:id="@+id/settings_button"
- android:layout_width="@dimen/bubble_header_icon_size"
- android:layout_height="@dimen/bubble_header_icon_size"
- android:src="@drawable/ic_settings"
- android:scaleType="center"
- android:layout_gravity="end"/>
+ android:layout_gravity="end"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:focusable="true"
+ android:text="@string/manage_bubbles_text"
+ android:textColor="?attr/wallpaperTextColor"/>
<include layout="@layout/bubble_permission_view"
android:id="@+id/permission_layout"
diff --git a/packages/SystemUI/res/layout/global_actions_column.xml b/packages/SystemUI/res/layout/global_actions_column.xml
new file mode 100644
index 0000000..b58146b
--- /dev/null
+++ b/packages/SystemUI/res/layout/global_actions_column.xml
@@ -0,0 +1,70 @@
+<?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
+ -->
+
+<com.android.systemui.globalactions.GlobalActionsColumnLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@id/global_actions_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:clipToPadding="false"
+ android:theme="@style/qs_theme"
+ android:gravity="center_vertical | right"
+ android:clipChildren="false"
+>
+ <LinearLayout
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:gravity="top | right"
+ android:orientation="vertical"
+ android:padding="0dp"
+ android:layout_marginTop="@dimen/global_actions_grid_container_bottom_margin"
+ android:layout_marginBottom="@dimen/global_actions_grid_container_bottom_margin"
+ >
+ <!-- Global actions is right-aligned to be physically near power button -->
+ <LinearLayout
+ android:id="@android:id/list"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:gravity="right"
+ android:translationZ="@dimen/global_actions_translate"
+ android:layout_marginRight="@dimen/global_actions_grid_side_margin"
+ android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
+ android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
+ android:paddingTop="@dimen/global_actions_grid_vertical_padding"
+ android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
+ />
+
+ <!-- For separated items-->
+ <LinearLayout
+ android:id="@+id/separated_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/global_actions_grid_side_margin"
+ android:layout_marginRight="@dimen/global_actions_grid_side_margin"
+ android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
+ android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
+ android:paddingTop="@dimen/global_actions_grid_vertical_padding"
+ android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
+ android:orientation="vertical"
+ android:gravity="center"
+ android:translationZ="@dimen/global_actions_translate"
+ />
+ </LinearLayout>
+
+</com.android.systemui.globalactions.GlobalActionsColumnLayout>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 4abe9f0..2871d06 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -494,4 +494,7 @@
<!-- Launcher package name for overlaying icons. -->
<string name="launcher_overlayable_package" translatable="false">com.android.launcher3</string>
+ <!-- ThemePicker package name for overlaying icons. -->
+ <string name="themepicker_overlayable_package" translatable="false">com.android.wallpaper</string>
+
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index cea336c..dc35653 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2414,6 +2414,8 @@
<!-- Text for asking the user whether bubbles (floating app content) should be enabled for an
app. [CHAR LIMIT=NONE] -->
<string name="bubbles_prompt">Allow bubbles from <xliff:g id="app_name" example="YouTube">%1$s</xliff:g>?</string>
+ <!-- The text for the manage bubbles link. [CHAR LIMIT=NONE] -->
+ <string name="manage_bubbles_text">Manage</string>
<!-- Text used for button allowing user to opt out of bubbles [CHAR LIMIT=20] -->
<string name="no_bubbles">Deny</string>
<!-- Text used for button allowing user to approve / enable bubbles [CHAR LIMIT=20] -->
diff --git a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
index 802903d..ad2e002 100644
--- a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
@@ -80,16 +80,6 @@
}
@Override
- public void removeAllItems() {
- if (mList != null) {
- mList.removeAllViews();
- }
- if (mSeparatedView != null) {
- mSeparatedView.removeAllViews();
- }
- }
-
- @Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
updateSettings();
diff --git a/packages/SystemUI/src/com/android/systemui/MultiListLayout.java b/packages/SystemUI/src/com/android/systemui/MultiListLayout.java
index f8287a4..d153fb0 100644
--- a/packages/SystemUI/src/com/android/systemui/MultiListLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/MultiListLayout.java
@@ -45,11 +45,6 @@
protected abstract ViewGroup getListView();
/**
- * Removes all child items from the separated and list views, if they exist.
- */
- protected abstract void removeAllItems();
-
- /**
* Sets the divided view, which may have a differently-colored background.
*/
public abstract void setDivisionView(View v);
@@ -110,6 +105,25 @@
onUpdateList();
}
+ protected void removeAllSeparatedViews() {
+ ViewGroup separated = getSeparatedView();
+ if (separated != null) {
+ separated.removeAllViews();
+ }
+ }
+
+ protected void removeAllListViews() {
+ ViewGroup list = getListView();
+ if (list != null) {
+ list.removeAllViews();
+ }
+ }
+
+ protected void removeAllItems() {
+ removeAllListViews();
+ removeAllSeparatedViews();
+ }
+
protected void onUpdateList() {
removeAllItems();
setSeparatedViewVisibility(mAdapter.hasSeparatedItems());
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 346660d..67f3274 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -40,11 +40,8 @@
import android.graphics.Color;
import android.graphics.Insets;
import android.graphics.Point;
-import android.graphics.drawable.AdaptiveIconDrawable;
-import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.InsetDrawable;
import android.graphics.drawable.ShapeDrawable;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -66,6 +63,7 @@
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.recents.TriangleShape;
+import com.android.systemui.statusbar.AlphaOptimizedButton;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
@@ -80,7 +78,7 @@
private View mPointerView;
private int mPointerMargin;
- private ImageView mSettingsIcon;
+ private AlphaOptimizedButton mSettingsIcon;
// Permission view
private View mPermissionView;
@@ -100,8 +98,6 @@
private int mSettingsIconHeight;
private int mBubbleHeight;
private int mPermissionHeight;
- private int mIconInset;
- private Drawable mSettingsIconDrawable;
private int mPointerWidth;
private int mPointerHeight;
@@ -218,10 +214,7 @@
mSettingsIconHeight = getContext().getResources().getDimensionPixelSize(
R.dimen.bubble_expanded_header_height);
mSettingsIcon = findViewById(R.id.settings_button);
- mIconInset = getResources().getDimensionPixelSize(R.dimen.bubble_icon_inset);
mSettingsIcon.setOnClickListener(this);
- // Save initial drawable to create adaptive icons that will take its place.
- mSettingsIconDrawable = mSettingsIcon.getDrawable();
mPermissionHeight = getContext().getResources().getDimensionPixelSize(
R.dimen.bubble_permission_height);
@@ -377,16 +370,6 @@
int foregroundColor = ta.getColor(1, Color.BLACK /* default */);
ta.recycle();
- // Must clear tint first - otherwise tint updates inconsistently.
- mSettingsIconDrawable.setTintList(null);
- mSettingsIconDrawable.setTint(foregroundColor);
-
- InsetDrawable foreground = new InsetDrawable(mSettingsIconDrawable, mIconInset);
- ColorDrawable background = new ColorDrawable(backgroundColor);
- AdaptiveIconDrawable adaptiveIcon = new AdaptiveIconDrawable(background,
- foreground);
- mSettingsIcon.setImageDrawable(adaptiveIcon);
-
// Update permission prompt color.
mPermissionView.setBackground(createPermissionBackground(backgroundColor));
mPermissionPrompt.setTextColor(foregroundColor);
@@ -649,11 +632,12 @@
}
private Intent getSettingsIntent(String packageName, final int appUid) {
- final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
+ final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_BUBBLE_SETTINGS);
intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
intent.putExtra(Settings.EXTRA_APP_UID, appUid);
intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
return intent;
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsColumnLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsColumnLayout.java
new file mode 100644
index 0000000..5907028
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsColumnLayout.java
@@ -0,0 +1,195 @@
+/*
+ * 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.globalactions;
+
+import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE;
+import static com.android.systemui.util.leak.RotationUtils.ROTATION_NONE;
+import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.R;
+
+/**
+ * Grid-based implementation of the button layout created by the global actions dialog.
+ */
+public class GlobalActionsColumnLayout extends GlobalActionsLayout {
+ private boolean mLastSnap;
+
+ public GlobalActionsColumnLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+
+ post(() -> updateSnap());
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ @VisibleForTesting
+ protected boolean shouldReverseListItems() {
+ int rotation = getCurrentRotation();
+ if (rotation == ROTATION_NONE) {
+ return false;
+ }
+ if (getCurrentLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
+ return rotation == ROTATION_LANDSCAPE;
+ }
+ return rotation == ROTATION_SEASCAPE;
+ }
+
+ @Override
+ public void onUpdateList() {
+ super.onUpdateList();
+ updateChildOrdering();
+ }
+
+ private void updateChildOrdering() {
+ if (shouldReverseListItems()) {
+ getListView().bringToFront();
+ } else {
+ getSeparatedView().bringToFront();
+ }
+ }
+
+ /**
+ * Snap this layout to align with the power button.
+ */
+ @VisibleForTesting
+ protected void snapToPowerButton() {
+ int offset = getPowerButtonOffsetDistance();
+ switch (getCurrentRotation()) {
+ case (ROTATION_LANDSCAPE):
+ setPadding(offset, 0, 0, 0);
+ setGravity(Gravity.LEFT | Gravity.TOP);
+ break;
+ case (ROTATION_SEASCAPE):
+ setPadding(0, 0, offset, 0);
+ setGravity(Gravity.RIGHT | Gravity.BOTTOM);
+ break;
+ default:
+ setPadding(0, offset, 0, 0);
+ setGravity(Gravity.TOP | Gravity.RIGHT);
+ break;
+ }
+ }
+
+ /**
+ * Detach this layout from snapping to the power button and instead center along that edge.
+ */
+ @VisibleForTesting
+ protected void centerAlongEdge() {
+ switch (getCurrentRotation()) {
+ case (ROTATION_LANDSCAPE):
+ setPadding(0, 0, 0, 0);
+ setGravity(Gravity.CENTER_HORIZONTAL | Gravity.TOP);
+ break;
+ case (ROTATION_SEASCAPE):
+ setPadding(0, 0, 0, 0);
+ setGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
+ break;
+ default:
+ setPadding(0, 0, 0, 0);
+ setGravity(Gravity.CENTER_VERTICAL | Gravity.RIGHT);
+ break;
+ }
+ }
+
+ /**
+ * Determines the distance from the top of the screen to the power button.
+ */
+ @VisibleForTesting
+ protected int getPowerButtonOffsetDistance() {
+ return Math.round(getContext().getResources().getDimension(
+ R.dimen.global_actions_top_padding));
+ }
+
+ /**
+ * Check whether there is enough extra space below the dialog such that we can offset the top
+ * of the dialog from the top of the phone to line it up with the power button, then either
+ * snap the dialog to the power button or center it along the edge with snapToPowerButton.
+ */
+ @VisibleForTesting
+ protected boolean shouldSnapToPowerButton() {
+ int offsetSize = getPowerButtonOffsetDistance();
+ int dialogSize;
+ int screenSize;
+ View wrapper = getWrapper();
+ int rotation = getCurrentRotation();
+ if (rotation == ROTATION_NONE) {
+ dialogSize = wrapper.getMeasuredHeight();
+ screenSize = getMeasuredHeight();
+ } else {
+ dialogSize = wrapper.getMeasuredWidth();
+ screenSize = getMeasuredWidth();
+ }
+ return dialogSize + offsetSize < screenSize;
+ }
+
+ @VisibleForTesting
+ protected void updateSnap() {
+ boolean snap = shouldSnapToPowerButton();
+ if (snap != mLastSnap) {
+ if (snap) {
+ snapToPowerButton();
+ } else {
+ centerAlongEdge();
+ }
+ }
+ mLastSnap = snap;
+ }
+
+ @VisibleForTesting
+ protected float getGridItemSize() {
+ return getContext().getResources().getDimension(R.dimen.global_actions_grid_item_height);
+ }
+
+ @VisibleForTesting
+ protected float getAnimationDistance() {
+ return getGridItemSize() / 2;
+ }
+
+ @Override
+ public float getAnimationOffsetX() {
+ if (getCurrentRotation() == ROTATION_NONE) {
+ return getAnimationDistance();
+ }
+ return 0;
+ }
+
+ @Override
+ public float getAnimationOffsetY() {
+ switch (getCurrentRotation()) {
+ case ROTATION_LANDSCAPE:
+ return -getAnimationDistance();
+ case ROTATION_SEASCAPE:
+ return getAnimationDistance();
+ default: // Portrait
+ return 0;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 64511a0..68d792e 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -410,7 +410,8 @@
mActivityStarter
.startPendingIntentDismissingKeyguard(intent);
}
- })
+ },
+ mKeyguardManager.isDeviceLocked())
: null;
ActionsDialog dialog = new ActionsDialog(mContext, mAdapter, panelViewController);
dialog.setCanceledOnTouchOutside(false); // Handled by the custom class.
@@ -1583,13 +1584,20 @@
}
private int getGlobalActionsLayoutId(Context context) {
- if (isForceGridEnabled(context) || shouldUsePanel()) {
- if (RotationUtils.getRotation(context) == RotationUtils.ROTATION_SEASCAPE) {
+ boolean useGridLayout = isForceGridEnabled(context) || shouldUsePanel();
+ if (RotationUtils.getRotation(context) == RotationUtils.ROTATION_SEASCAPE) {
+ if (useGridLayout) {
return com.android.systemui.R.layout.global_actions_grid_seascape;
+ } else {
+ return com.android.systemui.R.layout.global_actions_column_seascape;
}
- return com.android.systemui.R.layout.global_actions_grid;
+ } else {
+ if (useGridLayout) {
+ return com.android.systemui.R.layout.global_actions_grid;
+ } else {
+ return com.android.systemui.R.layout.global_actions_column;
+ }
}
- return com.android.systemui.R.layout.global_actions_wrapped;
}
@Override
@@ -1712,7 +1720,7 @@
}
public void onRotate(int from, int to) {
- if (mShowing && (shouldUsePanel() || isForceGridEnabled(mContext))) {
+ if (mShowing) {
refreshDialog();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java
index 554ed73..e1462d1 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java
@@ -22,58 +22,23 @@
import android.content.Context;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.HardwareBgDrawable;
-import com.android.systemui.MultiListLayout;
-import com.android.systemui.util.leak.RotationUtils;
/**
* Grid-based implementation of the button layout created by the global actions dialog.
*/
-public class GlobalActionsGridLayout extends MultiListLayout {
-
- boolean mBackgroundsSet;
-
+public class GlobalActionsGridLayout extends GlobalActionsLayout {
public GlobalActionsGridLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
- private void setBackgrounds() {
- int gridBackgroundColor = getResources().getColor(
- com.android.systemui.R.color.global_actions_grid_background, null);
- int separatedBackgroundColor = getResources().getColor(
- com.android.systemui.R.color.global_actions_separated_background, null);
- HardwareBgDrawable listBackground = new HardwareBgDrawable(true, true, getContext());
- HardwareBgDrawable separatedBackground = new HardwareBgDrawable(true, true, getContext());
- listBackground.setTint(gridBackgroundColor);
- separatedBackground.setTint(separatedBackgroundColor);
- getListView().setBackground(listBackground);
- getSeparatedView().setBackground(separatedBackground);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
- // backgrounds set only once, the first time onMeasure is called after inflation
- if (getListView() != null && !mBackgroundsSet) {
- setBackgrounds();
- mBackgroundsSet = true;
- }
- }
-
@VisibleForTesting
- protected int getCurrentRotation() {
- return RotationUtils.getRotation(mContext);
- }
-
- @VisibleForTesting
- protected void setupListView(ListGridLayout listView, int itemCount) {
- listView.setExpectedCount(itemCount);
+ protected void setupListView() {
+ ListGridLayout listView = getListView();
+ listView.setExpectedCount(mAdapter.countListItems());
listView.setReverseSublists(shouldReverseSublists());
listView.setReverseItems(shouldReverseListItems());
listView.setSwapRowsAndColumns(shouldSwapRowsAndColumns());
@@ -81,29 +46,8 @@
@Override
public void onUpdateList() {
+ setupListView();
super.onUpdateList();
-
- ViewGroup separatedView = getSeparatedView();
- ListGridLayout listView = getListView();
- setupListView(listView, mAdapter.countListItems());
-
- for (int i = 0; i < mAdapter.getCount(); i++) {
- // generate the view item
- View v;
- boolean separated = mAdapter.shouldBeSeparated(i);
- if (separated) {
- v = mAdapter.getView(i, null, separatedView);
- } else {
- v = mAdapter.getView(i, null, listView);
- }
- Log.d("GlobalActionsGridLayout", "View: " + v);
-
- if (separated) {
- separatedView.addView(v);
- } else {
- listView.addItem(v);
- }
- }
updateSeparatedItemSize();
}
@@ -111,7 +55,8 @@
* If the separated view contains only one item, expand the bounds of that item to take up the
* entire view, so that the whole thing is touch-able.
*/
- private void updateSeparatedItemSize() {
+ @VisibleForTesting
+ protected void updateSeparatedItemSize() {
ViewGroup separated = getSeparatedView();
if (separated.getChildCount() == 0) {
return;
@@ -129,13 +74,24 @@
}
@Override
- protected ViewGroup getSeparatedView() {
- return findViewById(com.android.systemui.R.id.separated_button);
+ protected ListGridLayout getListView() {
+ return (ListGridLayout) super.getListView();
}
@Override
- protected ListGridLayout getListView() {
- return findViewById(android.R.id.list);
+ protected void removeAllListViews() {
+ ListGridLayout list = getListView();
+ if (list != null) {
+ list.removeAllItems();
+ }
+ }
+
+ @Override
+ protected void addToListView(View v, boolean reverse) {
+ ListGridLayout list = getListView();
+ if (list != null) {
+ list.addItem(v);
+ }
}
@Override
@@ -174,12 +130,7 @@
return true;
}
- /**
- * Determines whether the ListGridLayout should reverse the ordering of items within sublists.
- * Used for RTL languages to ensure that items appear in the same positions, without having to
- * override layoutDirection, which breaks Talkback ordering.
- */
- @VisibleForTesting
+ @Override
protected boolean shouldReverseListItems() {
int rotation = getCurrentRotation();
boolean reverse = false; // should we add items to parents in the reverse order?
@@ -187,20 +138,13 @@
|| rotation == ROTATION_SEASCAPE) {
reverse = !reverse; // if we're in portrait or seascape, reverse items
}
- if (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
+ if (getCurrentLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
reverse = !reverse; // if we're in an RTL language, reverse items (again)
}
return reverse;
}
- /**
- * Not ued in this implementation of the Global Actions Menu, but necessary for some others.
- */
- @Override
- public void setDivisionView(View v) {
- // do nothing
- }
-
+ @VisibleForTesting
protected float getAnimationDistance() {
int rows = getListView().getRowCount();
float gridItemSize = getContext().getResources().getDimension(
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsLayout.java
new file mode 100644
index 0000000..f755a93
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsLayout.java
@@ -0,0 +1,146 @@
+/*
+ * 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.globalactions;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.HardwareBgDrawable;
+import com.android.systemui.MultiListLayout;
+import com.android.systemui.R;
+import com.android.systemui.util.leak.RotationUtils;
+
+import java.util.Locale;
+
+/**
+ * Grid-based implementation of the button layout created by the global actions dialog.
+ */
+public abstract class GlobalActionsLayout extends MultiListLayout {
+
+ boolean mBackgroundsSet;
+
+ public GlobalActionsLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ private void setBackgrounds() {
+ int gridBackgroundColor = getResources().getColor(
+ R.color.global_actions_grid_background, null);
+ int separatedBackgroundColor = getResources().getColor(
+ R.color.global_actions_separated_background, null);
+ HardwareBgDrawable listBackground = new HardwareBgDrawable(true, true, getContext());
+ HardwareBgDrawable separatedBackground = new HardwareBgDrawable(true, true, getContext());
+ listBackground.setTint(gridBackgroundColor);
+ separatedBackground.setTint(separatedBackgroundColor);
+ getListView().setBackground(listBackground);
+ getSeparatedView().setBackground(separatedBackground);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ // backgrounds set only once, the first time onMeasure is called after inflation
+ if (getListView() != null && !mBackgroundsSet) {
+ setBackgrounds();
+ mBackgroundsSet = true;
+ }
+ }
+
+ protected void addToListView(View v, boolean reverse) {
+ if (reverse) {
+ getListView().addView(v, 0);
+ } else {
+ getListView().addView(v);
+ }
+ }
+
+ protected void addToSeparatedView(View v, boolean reverse) {
+ if (reverse) {
+ getSeparatedView().addView(v, 0);
+ } else {
+ getSeparatedView().addView(v);
+ }
+ }
+
+ @VisibleForTesting
+ protected int getCurrentLayoutDirection() {
+ return TextUtils.getLayoutDirectionFromLocale(Locale.getDefault());
+ }
+
+ @VisibleForTesting
+ protected int getCurrentRotation() {
+ return RotationUtils.getRotation(mContext);
+ }
+
+ /**
+ * Determines whether the ListGridLayout should reverse the ordering of items within sublists.
+ * Used for RTL languages to ensure that items appear in the same positions, without having to
+ * override layoutDirection, which breaks Talkback ordering.
+ */
+ protected abstract boolean shouldReverseListItems();
+
+ @Override
+ public void onUpdateList() {
+ super.onUpdateList();
+
+ ViewGroup separatedView = getSeparatedView();
+ ViewGroup listView = getListView();
+
+ for (int i = 0; i < mAdapter.getCount(); i++) {
+ // generate the view item
+ View v;
+ boolean separated = mAdapter.shouldBeSeparated(i);
+ if (separated) {
+ v = mAdapter.getView(i, null, separatedView);
+ } else {
+ v = mAdapter.getView(i, null, listView);
+ }
+ if (separated) {
+ addToSeparatedView(v, false);
+ } else {
+ addToListView(v, shouldReverseListItems());
+ }
+ }
+ }
+
+ @Override
+ protected ViewGroup getSeparatedView() {
+ return findViewById(R.id.separated_button);
+ }
+
+ @Override
+ protected ViewGroup getListView() {
+ return findViewById(android.R.id.list);
+ }
+
+ protected View getWrapper() {
+ return getChildAt(0);
+ }
+
+ /**
+ * Not used in this implementation of the Global Actions Menu, but necessary for some others.
+ */
+ @Override
+ public void setDivisionView(View v) {
+ // do nothing
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java
index 477e7d7e..24a4b9e 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java
@@ -30,13 +30,15 @@
import android.util.Log;
/**
- * A helper class that computes histogram and percentile 85 from a bitmap.
- * Percentile 85 will be computed each time the user picks a new image wallpaper.
+ * A helper class that computes threshold from a bitmap.
+ * Threshold will be computed each time the user picks a new image wallpaper.
*/
class ImageProcessHelper {
private static final String TAG = ImageProcessHelper.class.getSimpleName();
- private static final float DEFAULT_PER85 = 0.8f;
- private static final int MSG_UPDATE_PER85 = 1;
+ private static final float DEFAULT_THRESHOLD = 0.8f;
+ private static final float DEFAULT_OTSU_THRESHOLD = 0f;
+ private static final float MAX_THRESHOLD = 0.89f;
+ private static final int MSG_UPDATE_THRESHOLD = 1;
/**
* This color matrix will be applied to each pixel to get luminance from rgb by below formula:
@@ -53,8 +55,8 @@
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
- case MSG_UPDATE_PER85:
- mPer85 = (float) msg.obj;
+ case MSG_UPDATE_THRESHOLD:
+ mThreshold = (float) msg.obj;
return true;
default:
return false;
@@ -62,20 +64,20 @@
}
});
- private float mPer85 = DEFAULT_PER85;
+ private float mThreshold = DEFAULT_THRESHOLD;
- void startComputingPercentile85(Bitmap bitmap) {
- new Per85ComputeTask(mHandler).execute(bitmap);
+ void start(Bitmap bitmap) {
+ new ThresholdComputeTask(mHandler).execute(bitmap);
}
- float getPercentile85() {
- return mPer85;
+ float getThreshold() {
+ return Math.min(mThreshold, MAX_THRESHOLD);
}
- private static class Per85ComputeTask extends AsyncTask<Bitmap, Void, Float> {
+ private static class ThresholdComputeTask extends AsyncTask<Bitmap, Void, Float> {
private Handler mUpdateHandler;
- Per85ComputeTask(Handler handler) {
+ ThresholdComputeTask(Handler handler) {
super(handler);
mUpdateHandler = handler;
}
@@ -84,35 +86,55 @@
protected Float doInBackground(Bitmap... bitmaps) {
Bitmap bitmap = bitmaps[0];
if (bitmap != null) {
- int[] histogram = processHistogram(bitmap);
- return computePercentile85(bitmap, histogram);
+ return new Threshold().compute(bitmap);
}
- Log.e(TAG, "Per85ComputeTask: Can't get bitmap");
- return DEFAULT_PER85;
+ Log.e(TAG, "ThresholdComputeTask: Can't get bitmap");
+ return DEFAULT_THRESHOLD;
}
@Override
protected void onPostExecute(Float result) {
- Message msg = mUpdateHandler.obtainMessage(MSG_UPDATE_PER85, result);
+ Message msg = mUpdateHandler.obtainMessage(MSG_UPDATE_THRESHOLD, result);
mUpdateHandler.sendMessage(msg);
}
+ }
- private int[] processHistogram(Bitmap bitmap) {
+ private static class Threshold {
+ public float compute(Bitmap bitmap) {
+ Bitmap grayscale = toGrayscale(bitmap);
+ int[] histogram = getHistogram(grayscale);
+ boolean isSolidColor = isSolidColor(grayscale, histogram);
+
+ // We will see gray wallpaper during the transition if solid color wallpaper is set,
+ // please refer to b/130360362#comment16.
+ // As a result, we use Percentile85 rather than Otsus if a solid color wallpaper is set.
+ ThresholdAlgorithm algorithm = isSolidColor ? new Percentile85() : new Otsus();
+ return algorithm.compute(grayscale, histogram);
+ }
+
+ private Bitmap toGrayscale(Bitmap bitmap) {
int width = bitmap.getWidth();
int height = bitmap.getHeight();
- Bitmap target = Bitmap.createBitmap(width, height, bitmap.getConfig());
- Canvas canvas = new Canvas(target);
+ Bitmap grayscale = Bitmap.createBitmap(width, height, bitmap.getConfig());
+ Canvas canvas = new Canvas(grayscale);
ColorMatrix cm = new ColorMatrix(LUMINOSITY_MATRIX);
Paint paint = new Paint();
paint.setColorFilter(new ColorMatrixColorFilter(cm));
canvas.drawBitmap(bitmap, new Matrix(), paint);
+ return grayscale;
+ }
+
+ private int[] getHistogram(Bitmap grayscale) {
+ int width = grayscale.getWidth();
+ int height = grayscale.getHeight();
+
// TODO: Fine tune the performance here, tracking on b/123615079.
int[] histogram = new int[256];
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
- int pixel = target.getPixel(col, row);
+ int pixel = grayscale.getPixel(col, row);
int y = Color.red(pixel) + Color.green(pixel) + Color.blue(pixel);
histogram[y]++;
}
@@ -121,8 +143,29 @@
return histogram;
}
- private float computePercentile85(Bitmap bitmap, int[] histogram) {
- float per85 = DEFAULT_PER85;
+ private boolean isSolidColor(Bitmap bitmap, int[] histogram) {
+ boolean solidColor = false;
+ int pixels = bitmap.getWidth() * bitmap.getHeight();
+
+ // In solid color case, only one element of histogram has value,
+ // which is pixel counts and the value of other elements should be 0.
+ for (int value : histogram) {
+ if (value != 0 && value != pixels) {
+ break;
+ }
+ if (value == pixels) {
+ solidColor = true;
+ break;
+ }
+ }
+ return solidColor;
+ }
+ }
+
+ private static class Percentile85 implements ThresholdAlgorithm {
+ @Override
+ public float compute(Bitmap bitmap, int[] histogram) {
+ float per85 = DEFAULT_THRESHOLD;
int pixelCount = bitmap.getWidth() * bitmap.getHeight();
float[] acc = new float[256];
for (int i = 0; i < acc.length; i++) {
@@ -141,4 +184,51 @@
return per85;
}
}
+
+ private static class Otsus implements ThresholdAlgorithm {
+ @Override
+ public float compute(Bitmap bitmap, int[] histogram) {
+ float threshold = DEFAULT_OTSU_THRESHOLD;
+ float maxVariance = 0;
+ float pixelCount = bitmap.getWidth() * bitmap.getHeight();
+ float[] w = new float[2];
+ float[] m = new float[2];
+ float[] u = new float[2];
+
+ for (int i = 0; i < histogram.length; i++) {
+ m[1] += i * histogram[i];
+ }
+
+ w[1] = pixelCount;
+ for (int tonalValue = 0; tonalValue < histogram.length; tonalValue++) {
+ float dU;
+ float variance;
+ float numPixels = histogram[tonalValue];
+ float tmp = numPixels * tonalValue;
+ w[0] += numPixels;
+ w[1] -= numPixels;
+
+ if (w[0] == 0 || w[1] == 0) {
+ continue;
+ }
+
+ m[0] += tmp;
+ m[1] -= tmp;
+ u[0] = m[0] / w[0];
+ u[1] = m[1] / w[1];
+ dU = u[0] - u[1];
+ variance = w[0] * w[1] * dU * dU;
+
+ if (variance > maxVariance) {
+ threshold = (tonalValue + 1f) / histogram.length;
+ maxVariance = variance;
+ }
+ }
+ return threshold;
+ }
+ }
+
+ private interface ThresholdAlgorithm {
+ float compute(Bitmap bitmap, int[] histogram);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
index 464cbe3..5bbfe84 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
@@ -78,8 +78,8 @@
mBitmap = mWallpaperManager.getBitmap();
mBitmapWidth = mBitmap.getWidth();
mBitmapHeight = mBitmap.getHeight();
- // Compute per85 as transition threshold, this is an async work.
- mImageProcessHelper.startComputingPercentile85(mBitmap);
+ // Compute threshold of the image, this is an async work.
+ mImageProcessHelper.start(mBitmap);
mWallpaperManager.forgetLoadedWallpaper();
}
}
@@ -108,13 +108,13 @@
@Override
public void onDrawFrame(GL10 gl) {
- float per85 = mImageProcessHelper.getPercentile85();
+ float threshold = mImageProcessHelper.getThreshold();
float reveal = mImageRevealHelper.getReveal();
glClear(GL_COLOR_BUFFER_BIT);
glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_AOD2OPACITY), 1);
- glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_PER85), per85);
+ glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_PER85), threshold);
glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_REVEAL), reveal);
scaleViewport(reveal);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 9f8ab61..f97be1ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -78,7 +78,7 @@
private static final int MSG_COLLAPSE_PANELS = 4 << MSG_SHIFT;
private static final int MSG_EXPAND_SETTINGS = 5 << MSG_SHIFT;
private static final int MSG_SET_SYSTEMUI_VISIBILITY = 6 << MSG_SHIFT;
- private static final int MSG_TOP_APP_WINDOW_CHANGED = 7 << MSG_SHIFT;
+ private static final int MSG_DISPLAY_READY = 7 << MSG_SHIFT;
private static final int MSG_SHOW_IME_BUTTON = 8 << MSG_SHIFT;
private static final int MSG_TOGGLE_RECENT_APPS = 9 << MSG_SHIFT;
private static final int MSG_PRELOAD_RECENT_APPS = 10 << MSG_SHIFT;
@@ -115,7 +115,6 @@
private static final int MSG_SHOW_CHARGING_ANIMATION = 44 << MSG_SHIFT;
private static final int MSG_SHOW_PINNING_TOAST_ENTER_EXIT = 45 << MSG_SHIFT;
private static final int MSG_SHOW_PINNING_TOAST_ESCAPE = 46 << MSG_SHIFT;
- private static final int MSG_DISPLAY_READY = 47 << MSG_SHIFT;
public static final int FLAG_EXCLUDE_NONE = 0;
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -183,15 +182,6 @@
}
/**
- * Called to notify top app window changes.
- * @see IStatusBar#topAppWindowChanged(int, boolean)
- *
- * @param displayId The id of the display to notify.
- * @param visible {@code true} to show menu button.
- */
- default void topAppWindowChanged(int displayId, boolean visible) { }
-
- /**
* Called to notify IME window status changes.
*
* @param displayId The id of the display to notify.
@@ -480,13 +470,7 @@
}
@Override
- public void topAppWindowChanged(int displayId, boolean menuVisible) {
- synchronized (mLock) {
- mHandler.removeMessages(MSG_TOP_APP_WINDOW_CHANGED);
- mHandler.obtainMessage(MSG_TOP_APP_WINDOW_CHANGED,
- displayId, menuVisible ? 1 : 0, null).sendToTarget();
- }
- }
+ public void topAppWindowChanged(int displayId, boolean menuVisible) { }
@Override
public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
@@ -888,11 +872,6 @@
}
args.recycle();
break;
- case MSG_TOP_APP_WINDOW_CHANGED:
- for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).topAppWindowChanged(msg.arg1, msg.arg2 != 0);
- }
- break;
case MSG_SHOW_IME_BUTTON:
args = (SomeArgs) msg.obj;
handleShowImeButton(args.argi1 /* displayId */, (IBinder) args.arg1 /* token */,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 38eed16..8aa4f03 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -449,13 +449,6 @@
}
@Override
- public void topAppWindowChanged(int displayId, boolean showMenu) {
- if (displayId == mDisplayId && mNavigationBarView != null) {
- mNavigationBarView.setMenuVisibility(showMenu);
- }
- }
-
- @Override
public void setWindowState(
int displayId, @WindowType int window, @WindowVisibleState int state) {
if (displayId == mDisplayId
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 1c03844..6fbb947 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -268,8 +268,6 @@
// Set up the context group of buttons
mContextualButtonGroup = new ContextualButtonGroup(R.id.menu_container);
- final ContextualButton menuButton = new ContextualButton(R.id.menu,
- R.drawable.ic_sysbar_menu);
final ContextualButton imeSwitcherButton = new ContextualButton(R.id.ime_switcher,
R.drawable.ic_ime_switcher_default);
final RotationContextButton rotateSuggestionButton = new RotationContextButton(
@@ -278,7 +276,6 @@
final ContextualButton accessibilityButton =
new ContextualButton(R.id.accessibility_button,
R.drawable.ic_sysbar_accessibility_button);
- mContextualButtonGroup.addButton(menuButton);
mContextualButtonGroup.addButton(imeSwitcherButton);
if (!isGesturalMode) {
mContextualButtonGroup.addButton(rotateSuggestionButton);
@@ -306,7 +303,6 @@
mButtonDispatchers.put(R.id.home, new ButtonDispatcher(R.id.home));
mButtonDispatchers.put(R.id.home_handle, new ButtonDispatcher(R.id.home_handle));
mButtonDispatchers.put(R.id.recent_apps, new ButtonDispatcher(R.id.recent_apps));
- mButtonDispatchers.put(R.id.menu, menuButton);
mButtonDispatchers.put(R.id.ime_switcher, imeSwitcherButton);
mButtonDispatchers.put(R.id.accessibility_button, accessibilityButton);
mButtonDispatchers.put(R.id.rotate_suggestion, rotateSuggestionButton);
@@ -406,10 +402,6 @@
return mButtonDispatchers.get(R.id.recent_apps);
}
- public ButtonDispatcher getMenuButton() {
- return mButtonDispatchers.get(R.id.menu);
- }
-
public ButtonDispatcher getBackButton() {
return mButtonDispatchers.get(R.id.back);
}
@@ -795,10 +787,6 @@
}
}
- public void setMenuVisibility(final boolean show) {
- mContextualButtonGroup.setButtonVisibility(R.id.menu, show);
- }
-
public void setAccessibilityButtonState(final boolean visible, final boolean longClickable) {
mLongClickableAccessibilityButton = longClickable;
getAccessibilityButton().setLongClickable(longClickable);
@@ -1154,16 +1142,14 @@
visibilityToString(getCurrentView().getVisibility()),
getCurrentView().getAlpha()));
- pw.println(String.format(" disabled=0x%08x vertical=%s menu=%s darkIntensity=%.2f",
+ pw.println(String.format(" disabled=0x%08x vertical=%s darkIntensity=%.2f",
mDisabledFlags,
mIsVertical ? "true" : "false",
- getMenuButton().isVisible() ? "true" : "false",
getLightTransitionsController().getCurrentDarkIntensity()));
dumpButton(pw, "back", getBackButton());
dumpButton(pw, "home", getHomeButton());
dumpButton(pw, "rcnt", getRecentsButton());
- dumpButton(pw, "menu", getMenuButton());
dumpButton(pw, "rota", getRotateSuggestionButton());
dumpButton(pw, "a11y", getAccessibilityButton());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 7b3ddf7..1194a1d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -833,6 +833,11 @@
return false;
}
initDownStates(event);
+ // Do not let touches go to shade or QS if the bouncer is visible,
+ // but still let user swipe down to expand the panel, dismissing the bouncer.
+ if (mStatusBar.isBouncerShowing()) {
+ return true;
+ }
if (mBar.panelEnabled() && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
mIsExpansionFromHeadsUp = true;
MetricsLogger.count(mContext, COUNTER_PANEL_OPEN, 1);
@@ -1000,6 +1005,13 @@
if (mBlockTouches || (mQs != null && mQs.isCustomizing())) {
return false;
}
+
+ // Do not allow panel expansion if bouncer is scrimmed, otherwise user would be able to
+ // pull down QS or expand the shade.
+ if (mStatusBar.isBouncerShowingScrimmed()) {
+ return false;
+ }
+
initDownStates(event);
// Make sure the next touch won't the blocked after the current ends.
if (event.getAction() == MotionEvent.ACTION_UP
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 5c16972..1eceff7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -713,7 +713,6 @@
result.mFullscreenStackSysUiVisibility, result.mDockedStackSysUiVisibility,
0xffffffff, result.mFullscreenStackBounds, result.mDockedStackBounds,
result.mNavbarColorManagedByIme);
- topAppWindowChanged(mDisplayId, result.mMenuVisible);
// StatusBarManagerService has a back up of IME token and it's restored here.
setImeWindowStatus(mDisplayId, result.mImeToken, result.mImeWindowVis,
result.mImeBackDisposition, result.mShowImeSwitcher);
@@ -727,11 +726,10 @@
if (DEBUG) {
Log.d(TAG, String.format(
- "init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x",
+ "init: icons=%d disabled=0x%08x lights=0x%08x imeButton=0x%08x",
numIcons,
result.mDisabledFlags1,
result.mSystemUiVisibility,
- result.mMenuVisible ? 1 : 0,
result.mImeWindowVis));
}
@@ -2235,31 +2233,6 @@
return 0 == (mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE);
}
- public void setLightsOn(boolean on) {
- Log.v(TAG, "setLightsOn(" + on + ")");
- if (on) {
- setSystemUiVisibility(mDisplayId, 0, 0, 0, View.SYSTEM_UI_FLAG_LOW_PROFILE,
- mLastFullscreenStackBounds, mLastDockedStackBounds,
- false /* navbarColorManagedByIme */);
- } else {
- setSystemUiVisibility(mDisplayId, View.SYSTEM_UI_FLAG_LOW_PROFILE, 0, 0,
- View.SYSTEM_UI_FLAG_LOW_PROFILE, mLastFullscreenStackBounds,
- mLastDockedStackBounds, false /* navbarColorManagedByIme */);
- }
- }
-
- @Override
- public void topAppWindowChanged(int displayId, boolean showMenu) {
- if (mDisplayId != displayId) return;
- if (SPEW) {
- Log.d(TAG, "display#" + displayId + ": "
- + (showMenu ? "showing" : "hiding") + " the MENU button");
- }
-
- // See above re: lights-out policy for legacy apps.
- if (showMenu) setLightsOn(true);
- }
-
public static String viewInfo(View v) {
return "[(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom()
+ ") " + v.getWidth() + "x" + v.getHeight() + "]";
@@ -4441,6 +4414,13 @@
}
/**
+ * @return Whether the security bouncer from Keyguard is showing.
+ */
+ public boolean isBouncerShowingScrimmed() {
+ return isBouncerShowing() && mStatusBarKeyguardViewManager.bouncerNeedsScrimming();
+ }
+
+ /**
* @return a PackageManger for userId or if userId is < 0 (USER_ALL etc) then
* return PackageManager for mContext
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 7fe8906..ce43253 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -484,7 +484,7 @@
// by a FLAG_DISMISS_KEYGUARD_ACTIVITY.
reset(isOccluding /* hideBouncerWhenShowing*/);
}
- if (animate && !occluded && mShowing) {
+ if (animate && !occluded && mShowing && !mBouncer.isShowing()) {
mStatusBar.animateKeyguardUnoccluding();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 8380b19..89aa797 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -68,7 +68,8 @@
mThemeManager = new ThemeOverlayManager(
mContext.getSystemService(OverlayManager.class),
AsyncTask.THREAD_POOL_EXECUTOR,
- mContext.getString(R.string.launcher_overlayable_package));
+ mContext.getString(R.string.launcher_overlayable_package),
+ mContext.getString(R.string.themepicker_overlayable_package));
final Handler bgHandler = Dependency.get(Dependency.BG_HANDLER);
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_SWITCHED);
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayManager.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayManager.java
index 27e3b2b..930016b 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayManager.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayManager.java
@@ -64,6 +64,9 @@
@VisibleForTesting
static final String OVERLAY_CATEGORY_ICON_LAUNCHER =
"android.theme.customization.icon_pack.launcher";
+ @VisibleForTesting
+ static final String OVERLAY_CATEGORY_ICON_THEME_PICKER =
+ "android.theme.customization.icon_pack.themepicker";
/*
* All theme customization categories used by the system, in order that they should be applied,
@@ -76,7 +79,8 @@
OVERLAY_CATEGORY_COLOR,
OVERLAY_CATEGORY_ICON_ANDROID,
OVERLAY_CATEGORY_ICON_SYSUI,
- OVERLAY_CATEGORY_ICON_SETTINGS);
+ OVERLAY_CATEGORY_ICON_SETTINGS,
+ OVERLAY_CATEGORY_ICON_THEME_PICKER);
/* Categories that need to applied to the current user as well as the system user. */
@VisibleForTesting
@@ -94,12 +98,14 @@
private final OverlayManager mOverlayManager;
private final Executor mExecutor;
private final String mLauncherPackage;
+ private final String mThemePickerPackage;
ThemeOverlayManager(OverlayManager overlayManager, Executor executor,
- String launcherPackage) {
+ String launcherPackage, String themePickerPackage) {
mOverlayManager = overlayManager;
mExecutor = executor;
mLauncherPackage = launcherPackage;
+ mThemePickerPackage = themePickerPackage;
mTargetPackageToCategories.put(ANDROID_PACKAGE, Sets.newHashSet(
OVERLAY_CATEGORY_COLOR, OVERLAY_CATEGORY_FONT,
OVERLAY_CATEGORY_SHAPE, OVERLAY_CATEGORY_ICON_ANDROID));
@@ -109,6 +115,8 @@
Sets.newHashSet(OVERLAY_CATEGORY_ICON_SETTINGS));
mTargetPackageToCategories.put(mLauncherPackage,
Sets.newHashSet(OVERLAY_CATEGORY_ICON_LAUNCHER));
+ mTargetPackageToCategories.put(mThemePickerPackage,
+ Sets.newHashSet(OVERLAY_CATEGORY_ICON_THEME_PICKER));
mCategoryToTargetPackage.put(OVERLAY_CATEGORY_COLOR, ANDROID_PACKAGE);
mCategoryToTargetPackage.put(OVERLAY_CATEGORY_FONT, ANDROID_PACKAGE);
mCategoryToTargetPackage.put(OVERLAY_CATEGORY_SHAPE, ANDROID_PACKAGE);
@@ -116,6 +124,7 @@
mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ICON_SYSUI, SYSUI_PACKAGE);
mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ICON_SETTINGS, SETTINGS_PACKAGE);
mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ICON_LAUNCHER, mLauncherPackage);
+ mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ICON_THEME_PICKER, mThemePickerPackage);
}
/**
diff --git a/packages/SystemUI/tests/res/values/overlayable_icons_test.xml b/packages/SystemUI/tests/res/values/overlayable_icons_test.xml
index 5cc7976..4dc8d45 100644
--- a/packages/SystemUI/tests/res/values/overlayable_icons_test.xml
+++ b/packages/SystemUI/tests/res/values/overlayable_icons_test.xml
@@ -42,7 +42,6 @@
<item>@drawable/ic_power_low</item>
<item>@drawable/ic_power_saver</item>
<item>@drawable/ic_qs_bluetooth_connecting</item>
- <item>@drawable/ic_qs_bluetooth_connecting</item>
<item>@drawable/ic_qs_cancel</item>
<item>@drawable/ic_qs_no_sim</item>
<item>@drawable/ic_qs_wifi_0</item>
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsColumnLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsColumnLayoutTest.java
new file mode 100644
index 0000000..16d665c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsColumnLayoutTest.java
@@ -0,0 +1,201 @@
+/*
+ * 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.globalactions;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.leak.RotationUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link ListGridLayout}.
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class GlobalActionsColumnLayoutTest extends SysuiTestCase {
+
+ private GlobalActionsColumnLayout mColumnLayout;
+
+ @Before
+ public void setUp() throws Exception {
+ mColumnLayout = spy((GlobalActionsColumnLayout)
+ LayoutInflater.from(mContext).inflate(R.layout.global_actions_column, null));
+ }
+
+ @Test
+ public void testShouldReverseListItems() {
+ doReturn(View.LAYOUT_DIRECTION_LTR).when(mColumnLayout).getCurrentLayoutDirection();
+
+ doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mColumnLayout).getCurrentRotation();
+ assertEquals(false, mColumnLayout.shouldReverseListItems());
+
+ doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation();
+ assertEquals(false, mColumnLayout.shouldReverseListItems());
+
+ doReturn(RotationUtils.ROTATION_SEASCAPE).when(mColumnLayout).getCurrentRotation();
+ assertEquals(true, mColumnLayout.shouldReverseListItems());
+
+ doReturn(View.LAYOUT_DIRECTION_RTL).when(mColumnLayout).getCurrentLayoutDirection();
+
+ doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mColumnLayout).getCurrentRotation();
+ assertEquals(true, mColumnLayout.shouldReverseListItems());
+
+ doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation();
+ assertEquals(false, mColumnLayout.shouldReverseListItems());
+
+ doReturn(RotationUtils.ROTATION_SEASCAPE).when(mColumnLayout).getCurrentRotation();
+ assertEquals(false, mColumnLayout.shouldReverseListItems());
+ }
+
+ @Test
+ public void testGetAnimationOffsetX() {
+ doReturn(50f).when(mColumnLayout).getAnimationDistance();
+
+ doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation();
+ assertEquals(50f, mColumnLayout.getAnimationOffsetX(), .01);
+
+ doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mColumnLayout).getCurrentRotation();
+ assertEquals(0, mColumnLayout.getAnimationOffsetX(), .01);
+
+ doReturn(RotationUtils.ROTATION_SEASCAPE).when(mColumnLayout).getCurrentRotation();
+ assertEquals(0, mColumnLayout.getAnimationOffsetX(), .01);
+ }
+
+ @Test
+ public void testGetAnimationOffsetY() {
+ doReturn(50f).when(mColumnLayout).getAnimationDistance();
+
+ doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation();
+ assertEquals(0, mColumnLayout.getAnimationOffsetY(), .01);
+
+ doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mColumnLayout).getCurrentRotation();
+ assertEquals(-50f, mColumnLayout.getAnimationOffsetY(), .01);
+
+ doReturn(RotationUtils.ROTATION_SEASCAPE).when(mColumnLayout).getCurrentRotation();
+ assertEquals(50f, mColumnLayout.getAnimationOffsetY(), .01);
+ }
+
+ @Test
+ public void testSnapToPowerButton_portrait() {
+ doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation();
+ doReturn(50).when(mColumnLayout).getPowerButtonOffsetDistance();
+
+ mColumnLayout.snapToPowerButton();
+ assertEquals(Gravity.TOP | Gravity.RIGHT, mColumnLayout.getGravity());
+ assertEquals(50, mColumnLayout.getPaddingTop(), .01);
+ }
+
+ @Test
+ public void testCenterAlongEdge_portrait() {
+ doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation();
+
+ mColumnLayout.centerAlongEdge();
+ assertEquals(Gravity.CENTER_VERTICAL | Gravity.RIGHT, mColumnLayout.getGravity());
+ assertEquals(0, mColumnLayout.getPaddingTop(), .01);
+ }
+
+ @Test
+ public void testUpdateSnap_initialState() {
+ doReturn(false).when(mColumnLayout).shouldSnapToPowerButton();
+
+ mColumnLayout.updateSnap(); // should do nothing, since snap has not changed from init state
+
+ verify(mColumnLayout, times(0)).snapToPowerButton();
+ verify(mColumnLayout, times(0)).centerAlongEdge();
+ }
+
+ @Test
+ public void testUpdateSnap_snapThenSnap() {
+ doReturn(true).when(mColumnLayout).shouldSnapToPowerButton();
+
+ mColumnLayout.updateSnap(); // should snap to power button
+
+ verify(mColumnLayout, times(1)).snapToPowerButton();
+ verify(mColumnLayout, times(0)).centerAlongEdge();
+
+ mColumnLayout.updateSnap(); // should do nothing, since this is the same state as last time
+
+ verify(mColumnLayout, times(1)).snapToPowerButton();
+ verify(mColumnLayout, times(0)).centerAlongEdge();
+ }
+
+ @Test
+ public void testUpdateSnap_snapThenCenter() {
+ doReturn(true).when(mColumnLayout).shouldSnapToPowerButton();
+
+ mColumnLayout.updateSnap(); // should snap to power button
+
+ verify(mColumnLayout, times(1)).snapToPowerButton();
+ verify(mColumnLayout, times(0)).centerAlongEdge();
+
+ doReturn(false).when(mColumnLayout).shouldSnapToPowerButton();
+
+ mColumnLayout.updateSnap(); // should center to edge
+
+ verify(mColumnLayout, times(1)).snapToPowerButton();
+ verify(mColumnLayout, times(1)).centerAlongEdge();
+ }
+
+ @Test
+ public void testShouldSnapToPowerButton_vertical() {
+ doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation();
+ doReturn(300).when(mColumnLayout).getPowerButtonOffsetDistance();
+ doReturn(1000).when(mColumnLayout).getMeasuredHeight();
+ View wrapper = spy(new View(mContext, null));
+ doReturn(wrapper).when(mColumnLayout).getWrapper();
+ doReturn(500).when(wrapper).getMeasuredHeight();
+
+ assertEquals(true, mColumnLayout.shouldSnapToPowerButton());
+
+ doReturn(600).when(mColumnLayout).getMeasuredHeight();
+
+ assertEquals(false, mColumnLayout.shouldSnapToPowerButton());
+ }
+
+ @Test
+ public void testShouldSnapToPowerButton_horizontal() {
+ doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mColumnLayout).getCurrentRotation();
+ doReturn(300).when(mColumnLayout).getPowerButtonOffsetDistance();
+ doReturn(1000).when(mColumnLayout).getMeasuredWidth();
+ View wrapper = spy(new View(mContext, null));
+ doReturn(wrapper).when(mColumnLayout).getWrapper();
+ doReturn(500).when(wrapper).getMeasuredWidth();
+
+ assertEquals(true, mColumnLayout.shouldSnapToPowerButton());
+
+ doReturn(600).when(mColumnLayout).getMeasuredWidth();
+
+ assertEquals(false, mColumnLayout.shouldSnapToPowerButton());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsGridLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsGridLayoutTest.java
index 3c52e9d..a396f3e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsGridLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsGridLayoutTest.java
@@ -18,12 +18,8 @@
import static junit.framework.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
import android.testing.AndroidTestingRunner;
import android.view.LayoutInflater;
@@ -32,7 +28,6 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.MultiListLayout;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.util.leak.RotationUtils;
@@ -49,61 +44,12 @@
public class GlobalActionsGridLayoutTest extends SysuiTestCase {
private GlobalActionsGridLayout mGridLayout;
- private TestAdapter mAdapter;
private ListGridLayout mListGrid;
- private class TestAdapter extends MultiListLayout.MultiListAdapter {
- @Override
- public void onClickItem(int index) { }
-
- @Override
- public boolean onLongClickItem(int index) {
- return true;
- }
-
- @Override
- public int countSeparatedItems() {
- return -1;
- }
-
- @Override
- public int countListItems() {
- return -1;
- }
-
- @Override
- public boolean shouldBeSeparated(int position) {
- return false;
- }
-
- @Override
- public int getCount() {
- return countSeparatedItems() + countListItems();
- }
-
- @Override
- public Object getItem(int position) {
- return null;
- }
-
- @Override
- public long getItemId(int position) {
- return -1;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- return null;
- }
- }
-
-
@Before
public void setUp() throws Exception {
mGridLayout = spy((GlobalActionsGridLayout)
LayoutInflater.from(mContext).inflate(R.layout.global_actions_grid, null));
- mAdapter = spy(new TestAdapter());
- mGridLayout.setAdapter(mAdapter);
mListGrid = spy(mGridLayout.getListView());
doReturn(mListGrid).when(mGridLayout).getListView();
}
@@ -122,7 +68,7 @@
@Test
public void testShouldReverseListItems() {
- doReturn(View.LAYOUT_DIRECTION_LTR).when(mGridLayout).getLayoutDirection();
+ doReturn(View.LAYOUT_DIRECTION_LTR).when(mGridLayout).getCurrentLayoutDirection();
doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mGridLayout).getCurrentRotation();
assertEquals(false, mGridLayout.shouldReverseListItems());
@@ -133,7 +79,7 @@
doReturn(RotationUtils.ROTATION_SEASCAPE).when(mGridLayout).getCurrentRotation();
assertEquals(true, mGridLayout.shouldReverseListItems());
- doReturn(View.LAYOUT_DIRECTION_RTL).when(mGridLayout).getLayoutDirection();
+ doReturn(View.LAYOUT_DIRECTION_RTL).when(mGridLayout).getCurrentLayoutDirection();
doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mGridLayout).getCurrentRotation();
assertEquals(true, mGridLayout.shouldReverseListItems());
@@ -185,123 +131,26 @@
assertEquals(0f, mGridLayout.getAnimationOffsetY(), .01);
}
- @Test(expected = IllegalStateException.class)
- public void testOnUpdateList_noAdapter() {
- mGridLayout.setAdapter(null);
- mGridLayout.updateList();
- }
-
@Test
- public void testOnUpdateList_noItems() {
- doReturn(0).when(mAdapter).countSeparatedItems();
- doReturn(0).when(mAdapter).countListItems();
- mGridLayout.updateList();
-
- ViewGroup separatedView = mGridLayout.getSeparatedView();
- ListGridLayout listView = mGridLayout.getListView();
-
- assertEquals(0, separatedView.getChildCount());
- assertEquals(View.GONE, separatedView.getVisibility());
-
- verify(mListGrid, times(0)).addItem(any());
- }
-
- @Test
- public void testOnUpdateList_resizesFirstSeparatedItem() {
- doReturn(1).when(mAdapter).countSeparatedItems();
- doReturn(0).when(mAdapter).countListItems();
+ public void testUpdateSeparatedItemSize() {
View firstView = new View(mContext, null);
View secondView = new View(mContext, null);
- doReturn(firstView).when(mAdapter).getView(eq(0), any(), any());
- doReturn(true).when(mAdapter).shouldBeSeparated(0);
+ ViewGroup separatedView = mGridLayout.getSeparatedView();
+ separatedView.addView(firstView);
- mGridLayout.updateList();
+ mGridLayout.updateSeparatedItemSize();
ViewGroup.LayoutParams childParams = firstView.getLayoutParams();
assertEquals(ViewGroup.LayoutParams.MATCH_PARENT, childParams.width);
assertEquals(ViewGroup.LayoutParams.MATCH_PARENT, childParams.height);
- doReturn(2).when(mAdapter).countSeparatedItems();
- doReturn(secondView).when(mAdapter).getView(eq(1), any(), any());
- doReturn(true).when(mAdapter).shouldBeSeparated(1);
+ separatedView.addView(secondView);
- mGridLayout.updateList();
+ mGridLayout.updateSeparatedItemSize();
childParams = firstView.getLayoutParams();
assertEquals(ViewGroup.LayoutParams.WRAP_CONTENT, childParams.width);
assertEquals(ViewGroup.LayoutParams.WRAP_CONTENT, childParams.height);
-
-
- }
-
- @Test
- public void testOnUpdateList_onlySeparatedItems() {
- doReturn(1).when(mAdapter).countSeparatedItems();
- doReturn(0).when(mAdapter).countListItems();
- View testView = new View(mContext, null);
- doReturn(testView).when(mAdapter).getView(eq(0), any(), any());
- doReturn(true).when(mAdapter).shouldBeSeparated(0);
-
- mGridLayout.updateList();
-
- verify(mListGrid, times(0)).addItem(any());
- }
-
- @Test
- public void testOnUpdateList_oneSeparatedOneList() {
- doReturn(1).when(mAdapter).countSeparatedItems();
- doReturn(1).when(mAdapter).countListItems();
- View view1 = new View(mContext, null);
- View view2 = new View(mContext, null);
-
- doReturn(view1).when(mAdapter).getView(eq(0), any(), any());
- doReturn(true).when(mAdapter).shouldBeSeparated(0);
-
- doReturn(view2).when(mAdapter).getView(eq(1), any(), any());
- doReturn(false).when(mAdapter).shouldBeSeparated(1);
-
- mGridLayout.updateList();
-
- ViewGroup separatedView = mGridLayout.getSeparatedView();
-
- assertEquals(1, separatedView.getChildCount());
- assertEquals(View.VISIBLE, separatedView.getVisibility());
- assertEquals(view1, separatedView.getChildAt(0));
-
- verify(mListGrid, times(1)).addItem(view2);
- }
-
- @Test
- public void testOnUpdateList_fourInList() {
- doReturn(0).when(mAdapter).countSeparatedItems();
- doReturn(4).when(mAdapter).countListItems();
- View view1 = new View(mContext, null);
- View view2 = new View(mContext, null);
- View view3 = new View(mContext, null);
- View view4 = new View(mContext, null);
-
- doReturn(view1).when(mAdapter).getView(eq(0), any(), any());
- doReturn(false).when(mAdapter).shouldBeSeparated(0);
-
- doReturn(view2).when(mAdapter).getView(eq(1), any(), any());
- doReturn(false).when(mAdapter).shouldBeSeparated(1);
-
- doReturn(view3).when(mAdapter).getView(eq(2), any(), any());
- doReturn(false).when(mAdapter).shouldBeSeparated(2);
-
- doReturn(view4).when(mAdapter).getView(eq(3), any(), any());
- doReturn(false).when(mAdapter).shouldBeSeparated(3);
-
- mGridLayout.updateList();
-
- ViewGroup separatedView = mGridLayout.getSeparatedView();
- assertEquals(0, separatedView.getChildCount());
- assertEquals(View.GONE, separatedView.getVisibility());
-
- verify(mListGrid, times(1)).addItem(view1);
- verify(mListGrid, times(1)).addItem(view2);
- verify(mListGrid, times(1)).addItem(view3);
- verify(mListGrid, times(1)).addItem(view4);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsLayoutTest.java
new file mode 100644
index 0000000..16dcd65
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsLayoutTest.java
@@ -0,0 +1,303 @@
+/*
+ * 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.globalactions;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.testing.AndroidTestingRunner;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.MultiListLayout;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+
+/**
+ * Tests for {@link ListGridLayout}.
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class GlobalActionsLayoutTest extends SysuiTestCase {
+
+ private TestLayout mLayout;
+ private TestAdapter mAdapter;
+
+ private class TestAdapter extends MultiListLayout.MultiListAdapter {
+ @Override
+ public void onClickItem(int index) { }
+
+ @Override
+ public boolean onLongClickItem(int index) {
+ return true;
+ }
+
+ @Override
+ public int countSeparatedItems() {
+ return -1;
+ }
+
+ @Override
+ public int countListItems() {
+ return -1;
+ }
+
+ @Override
+ public boolean shouldBeSeparated(int position) {
+ return false;
+ }
+
+ @Override
+ public int getCount() {
+ return countSeparatedItems() + countListItems();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return null;
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return -1;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ return null;
+ }
+ }
+
+ private class TestLayout extends GlobalActionsLayout {
+ ArrayList<View> mSeparatedViews = new ArrayList<>();
+ ArrayList<View> mListViews = new ArrayList<>();
+ boolean mSeparatedViewVisible = false;
+
+ TestLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected boolean shouldReverseListItems() {
+ return false;
+ }
+
+ @Override
+ public float getAnimationOffsetX() {
+ return 0;
+ }
+
+ @Override
+ public float getAnimationOffsetY() {
+ return 0;
+ }
+
+ @Override
+ protected void addToListView(View v, boolean reverse) {
+ if (reverse) {
+ mListViews.add(0, v);
+ } else {
+ mListViews.add(v);
+ }
+ }
+
+ @Override
+ protected void addToSeparatedView(View v, boolean reverse) {
+ if (reverse) {
+ mSeparatedViews.add(0, v);
+ } else {
+ mSeparatedViews.add(v);
+ }
+ }
+
+ @Override
+ protected void setSeparatedViewVisibility(boolean visible) {
+ mSeparatedViewVisible = visible;
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mLayout = spy(new TestLayout(mContext, null));
+ mAdapter = spy(new TestAdapter());
+ mLayout.setAdapter(mAdapter);
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testOnUpdateList_noAdapter() {
+ mLayout.setAdapter(null);
+ mLayout.updateList();
+ }
+
+ @Test
+ public void testOnUpdateList_noItems() {
+ doReturn(0).when(mAdapter).countSeparatedItems();
+ doReturn(0).when(mAdapter).countListItems();
+ mLayout.updateList();
+
+ assertEquals(0, mLayout.mSeparatedViews.size());
+ assertEquals(0, mLayout.mListViews.size());
+
+ assertEquals(false, mLayout.mSeparatedViewVisible);
+ }
+
+ @Test
+ public void testOnUpdateList_oneSeparatedOneList() {
+ doReturn(1).when(mAdapter).countSeparatedItems();
+ doReturn(1).when(mAdapter).countListItems();
+ View view1 = new View(mContext, null);
+ View view2 = new View(mContext, null);
+
+ doReturn(view1).when(mAdapter).getView(eq(0), any(), any());
+ doReturn(true).when(mAdapter).shouldBeSeparated(0);
+
+ doReturn(view2).when(mAdapter).getView(eq(1), any(), any());
+ doReturn(false).when(mAdapter).shouldBeSeparated(1);
+
+ mLayout.updateList();
+
+ assertEquals(1, mLayout.mSeparatedViews.size());
+ assertEquals(1, mLayout.mListViews.size());
+ assertEquals(view1, mLayout.mSeparatedViews.get(0));
+ assertEquals(view2, mLayout.mListViews.get(0));
+ }
+
+
+ @Test
+ public void testOnUpdateList_twoSeparatedItems() {
+ doReturn(2).when(mAdapter).countSeparatedItems();
+ doReturn(0).when(mAdapter).countListItems();
+ View view1 = new View(mContext, null);
+ View view2 = new View(mContext, null);
+
+ doReturn(view1).when(mAdapter).getView(eq(0), any(), any());
+ doReturn(true).when(mAdapter).shouldBeSeparated(0);
+ doReturn(view2).when(mAdapter).getView(eq(1), any(), any());
+ doReturn(true).when(mAdapter).shouldBeSeparated(1);
+
+ mLayout.updateList();
+
+ assertEquals(2, mLayout.mSeparatedViews.size());
+ assertEquals(0, mLayout.mListViews.size());
+
+ assertEquals(view1, mLayout.mSeparatedViews.get(0));
+ assertEquals(view2, mLayout.mSeparatedViews.get(1));
+
+ // if separated view has items in it, should be made visible
+ assertEquals(true, mLayout.mSeparatedViewVisible);
+ }
+
+ @Test
+ public void testOnUpdateList_twoSeparatedItems_reverse() {
+ doReturn(2).when(mAdapter).countSeparatedItems();
+ doReturn(0).when(mAdapter).countListItems();
+ doReturn(true).when(mLayout).shouldReverseListItems();
+ View view1 = new View(mContext, null);
+ View view2 = new View(mContext, null);
+
+ doReturn(view1).when(mAdapter).getView(eq(0), any(), any());
+ doReturn(true).when(mAdapter).shouldBeSeparated(0);
+
+ doReturn(view2).when(mAdapter).getView(eq(1), any(), any());
+ doReturn(true).when(mAdapter).shouldBeSeparated(1);
+
+ mLayout.updateList();
+
+ assertEquals(2, mLayout.mSeparatedViews.size());
+ assertEquals(0, mLayout.mListViews.size());
+
+ // separated view items are not reversed in current implementation, and this is intentional!
+ assertEquals(view1, mLayout.mSeparatedViews.get(0));
+ assertEquals(view2, mLayout.mSeparatedViews.get(1));
+ }
+
+ @Test
+ public void testOnUpdateList_fourInList() {
+ doReturn(0).when(mAdapter).countSeparatedItems();
+ doReturn(4).when(mAdapter).countListItems();
+ View view1 = new View(mContext, null);
+ View view2 = new View(mContext, null);
+ View view3 = new View(mContext, null);
+ View view4 = new View(mContext, null);
+
+ doReturn(view1).when(mAdapter).getView(eq(0), any(), any());
+ doReturn(false).when(mAdapter).shouldBeSeparated(0);
+
+ doReturn(view2).when(mAdapter).getView(eq(1), any(), any());
+ doReturn(false).when(mAdapter).shouldBeSeparated(1);
+
+ doReturn(view3).when(mAdapter).getView(eq(2), any(), any());
+ doReturn(false).when(mAdapter).shouldBeSeparated(2);
+
+ doReturn(view4).when(mAdapter).getView(eq(3), any(), any());
+ doReturn(false).when(mAdapter).shouldBeSeparated(3);
+
+ mLayout.updateList();
+
+ assertEquals(0, mLayout.mSeparatedViews.size());
+ assertEquals(4, mLayout.mListViews.size());
+ assertEquals(view1, mLayout.mListViews.get(0));
+ assertEquals(view2, mLayout.mListViews.get(1));
+ assertEquals(view3, mLayout.mListViews.get(2));
+ assertEquals(view4, mLayout.mListViews.get(3));
+ }
+
+ @Test
+ public void testOnUpdateList_fourInList_reverse() {
+ doReturn(0).when(mAdapter).countSeparatedItems();
+ doReturn(4).when(mAdapter).countListItems();
+ doReturn(true).when(mLayout).shouldReverseListItems();
+ View view1 = new View(mContext, null);
+ View view2 = new View(mContext, null);
+ View view3 = new View(mContext, null);
+ View view4 = new View(mContext, null);
+
+ doReturn(view1).when(mAdapter).getView(eq(0), any(), any());
+ doReturn(false).when(mAdapter).shouldBeSeparated(0);
+
+ doReturn(view2).when(mAdapter).getView(eq(1), any(), any());
+ doReturn(false).when(mAdapter).shouldBeSeparated(1);
+
+ doReturn(view3).when(mAdapter).getView(eq(2), any(), any());
+ doReturn(false).when(mAdapter).shouldBeSeparated(2);
+
+ doReturn(view4).when(mAdapter).getView(eq(3), any(), any());
+ doReturn(false).when(mAdapter).shouldBeSeparated(3);
+
+ mLayout.updateList();
+
+ assertEquals(0, mLayout.mSeparatedViews.size());
+ assertEquals(4, mLayout.mListViews.size());
+ assertEquals(view1, mLayout.mListViews.get(3));
+ assertEquals(view2, mLayout.mListViews.get(2));
+ assertEquals(view3, mLayout.mListViews.get(1));
+ assertEquals(view4, mLayout.mListViews.get(0));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index 2bde5f6..b049632 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -128,20 +128,6 @@
}
@Test
- public void testTopAppWindowChanged() {
- mCommandQueue.topAppWindowChanged(DEFAULT_DISPLAY, true);
- waitForIdleSync();
- verify(mCallbacks).topAppWindowChanged(eq(DEFAULT_DISPLAY), eq(true));
- }
-
- @Test
- public void testTopAppWindowChangedForSecondaryDisplay() {
- mCommandQueue.topAppWindowChanged(SECONDARY_DISPLAY, true);
- waitForIdleSync();
- verify(mCallbacks).topAppWindowChanged(eq(SECONDARY_DISPLAY), eq(true));
- }
-
- @Test
public void testShowImeButton() {
mCommandQueue.setImeWindowStatus(DEFAULT_DISPLAY, null, 1, 2, true);
waitForIdleSync();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 1c6e3b0..f50cf5a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -19,6 +19,7 @@
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -196,6 +197,17 @@
verify(mBouncer, never()).setExpansion(anyFloat());
}
+ @Test
+ public void setOccluded_animatesPanelExpansion_onlyIfBouncerHidden() {
+ mStatusBarKeyguardViewManager.setOccluded(false /* occluded */, true /* animated */);
+ verify(mStatusBar).animateKeyguardUnoccluding();
+
+ when(mBouncer.isShowing()).thenReturn(true);
+ clearInvocations(mStatusBar);
+ mStatusBarKeyguardViewManager.setOccluded(false /* occluded */, true /* animated */);
+ verify(mStatusBar, never()).animateKeyguardUnoccluding();
+ }
+
private class TestableStatusBarKeyguardViewManager extends StatusBarKeyguardViewManager {
public TestableStatusBarKeyguardViewManager(Context context,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayManagerTest.java
index 4659afc..c99deb6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayManagerTest.java
@@ -22,6 +22,7 @@
import static com.android.systemui.theme.ThemeOverlayManager.OVERLAY_CATEGORY_ICON_LAUNCHER;
import static com.android.systemui.theme.ThemeOverlayManager.OVERLAY_CATEGORY_ICON_SETTINGS;
import static com.android.systemui.theme.ThemeOverlayManager.OVERLAY_CATEGORY_ICON_SYSUI;
+import static com.android.systemui.theme.ThemeOverlayManager.OVERLAY_CATEGORY_ICON_THEME_PICKER;
import static com.android.systemui.theme.ThemeOverlayManager.OVERLAY_CATEGORY_SHAPE;
import static com.android.systemui.theme.ThemeOverlayManager.SETTINGS_PACKAGE;
import static com.android.systemui.theme.ThemeOverlayManager.SYSTEM_USER_CATEGORIES;
@@ -74,6 +75,7 @@
}
}
+ private static final String THEMEPICKER_PACKAGE = "com.android.wallpaper";
private static final String LAUNCHER_PACKAGE = "com.android.launcher3";
private static final UserHandle TEST_USER = UserHandle.of(5);
private static final Set<UserHandle> TEST_USER_HANDLES = Sets.newHashSet(TEST_USER);
@@ -87,7 +89,7 @@
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
mManager = new ThemeOverlayManager(mOverlayManager, MoreExecutors.directExecutor(),
- LAUNCHER_PACKAGE);
+ LAUNCHER_PACKAGE, THEMEPICKER_PACKAGE);
when(mOverlayManager.getOverlayInfosForTarget(ANDROID_PACKAGE, UserHandle.SYSTEM))
.thenReturn(Lists.newArrayList(
createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_COLOR,
@@ -124,6 +126,12 @@
LAUNCHER_PACKAGE, OVERLAY_CATEGORY_ICON_LAUNCHER, false),
createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_ICON_LAUNCHER,
LAUNCHER_PACKAGE, OVERLAY_CATEGORY_ICON_LAUNCHER, true)));
+ when(mOverlayManager.getOverlayInfosForTarget(THEMEPICKER_PACKAGE, UserHandle.SYSTEM))
+ .thenReturn(Lists.newArrayList(
+ createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_ICON_THEME_PICKER,
+ THEMEPICKER_PACKAGE, OVERLAY_CATEGORY_ICON_THEME_PICKER, false),
+ createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_ICON_THEME_PICKER,
+ THEMEPICKER_PACKAGE, OVERLAY_CATEGORY_ICON_THEME_PICKER, true)));
}
@Test
@@ -222,6 +230,8 @@
verify(mOverlayManager, never()).getOverlayInfosForTarget(SYSUI_PACKAGE, UserHandle.SYSTEM);
verify(mOverlayManager, never()).getOverlayInfosForTarget(LAUNCHER_PACKAGE,
UserHandle.SYSTEM);
+ verify(mOverlayManager, never()).getOverlayInfosForTarget(THEMEPICKER_PACKAGE,
+ UserHandle.SYSTEM);
}
private static OverlayInfo createOverlayInfo(String packageName, String targetPackageName,
diff --git a/packages/overlays/Android.mk b/packages/overlays/Android.mk
index b9b3a61..2675e90 100644
--- a/packages/overlays/Android.mk
+++ b/packages/overlays/Android.mk
@@ -32,14 +32,17 @@
IconPackCircularLauncherOverlay \
IconPackCircularSettingsOverlay \
IconPackCircularSystemUIOverlay \
+ IconPackCircularThemePickerOverlay \
IconPackFilledAndroidOverlay \
IconPackFilledLauncherOverlay \
IconPackFilledSettingsOverlay \
IconPackFilledSystemUIOverlay \
+ IconPackFilledThemePickerOverlay \
IconPackRoundedAndroidOverlay \
IconPackRoundedLauncherOverlay \
IconPackRoundedSettingsOverlay \
IconPackRoundedSystemUIOverlay \
+ IconPackRoundedThemePickerUIOverlay \
IconShapeRoundedRectOverlay \
IconShapeSquareOverlay \
IconShapeSquircleOverlay \
diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/values/config.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/values/config.xml
new file mode 100644
index 0000000..ae5cc2b
--- /dev/null
+++ b/packages/overlays/IconPackCircularAndroidOverlay/res/values/config.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * 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.
+ */
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_batterymeterPerimeterPath" translatable="false">
+ M 8,1 C 8,0.45 7.55,0 7,0 H 5 C 4.45,0 4,0.45 4,1 H 3 C 1.34,1 0,2.34 0,4 V 17 C 0,18.66 1.34,20 3,20 H 9 C 10.66,20 12,18.66 12,17 V 4 C 12,2.34 10.66,1 9,1 Z M 10.5,4 V 17 C 10.5,17.83 9.83,18.5 9,18.5 H 3 C 2.17,18.5 1.5,17.83 1.5,17 V 4 C 1.5,3.17 2.17,2.5 3,2.5 H 9 C 9.83,2.5 10.5,3.17 10.5,4 Z
+ </string>
+ <string name="config_batterymeterFillMask" translatable="false">
+ M 10.5,4 V 17 C 10.5,17.83 9.83,18.5 9,18.5 H 3 C 2.17,18.5 1.5,17.83 1.5,17 V 4 C 1.5,3.17 2.17,2.5 3,2.5 H 9 C 9.83,2.5 10.5,3.17 10.5,4 Z
+ </string>
+ <string name="config_batterymeterBoltPath" translatable="false">
+ M 8.08,9.5 H 7 V 5.99 C 7,5.73 6.65,5.64 6.53,5.87 L 3.7,11.13 C 3.61,11.3 3.73,11.5 3.92,11.5 H 5 V 15.01 C 5,15.27 5.35,15.36 5.47,15.13 L 8.3,9.87 C 8.39,9.7 8.27,9.5 8.08,9.5 Z
+ </string>
+ <string name="config_batterymeterPowersavePath" translatable="false">
+ M 3.75,11.25 H 5.25 V 12.75 C 5.25,13.16 5.59,13.5 6,13.5 6.41,13.5 6.75,13.16 6.75,12.75 V 11.25 H 8.25 C 8.66,11.25 9,10.91 9,10.5 9,10.09 8.6601,9.75 8.25,9.75 H 6.75 V 8.25 C 6.75,7.84 6.41,7.5 6,7.5 5.59,7.5 5.25,7.84 5.25,8.25 V 9.75 H 3.75 C 3.34,9.75 3,10.09 3,10.5 3,10.91 3.34,11.25 3.75,11.25 Z
+ </string>
+</resources>
diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/Android.mk b/packages/overlays/IconPackCircularThemePickerOverlay/Android.mk
new file mode 100644
index 0000000..412c26f
--- /dev/null
+++ b/packages/overlays/IconPackCircularThemePickerOverlay/Android.mk
@@ -0,0 +1,31 @@
+#
+# 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.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_RRO_THEME := IconPackCircularThemePicker
+LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := IconPackCircularThemePickerOverlay
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/AndroidManifest.xml b/packages/overlays/IconPackCircularThemePickerOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..eae7de8
--- /dev/null
+++ b/packages/overlays/IconPackCircularThemePickerOverlay/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.theme.icon_pack.circular.themepicker"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <overlay android:targetPackage="com.android.wallpaper" android:category="android.theme.customization.icon_pack.themepicker" android:priority="1"/>
+ <application android:label="Circular" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_add_24px.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_add_24px.xml
new file mode 100644
index 0000000..900aaa0
--- /dev/null
+++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_add_24px.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="24dp" >
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20.25,11.25h-7.5v-7.5C12.75,3.34,12.41,3,12,3s-0.75,0.34-0.75,0.75v7.5h-7.5C3.34,11.25,3,11.59,3,12 s0.34,0.75,0.75,0.75h7.5v7.5c0,0.41,0.34,0.75,0.75,0.75s0.75-0.34,0.75-0.75v-7.5h7.5c0.41,0,0.75-0.34,0.75-0.75 S20.66,11.25,20.25,11.25z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_close_24px.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_close_24px.xml
new file mode 100644
index 0000000..ddfb980
--- /dev/null
+++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_close_24px.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="24dp" >
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20.03,3.97c-0.29-0.29-0.77-0.29-1.06,0L12,10.94L5.03,3.97c-0.29-0.29-0.77-0.29-1.06,0s-0.29,0.77,0,1.06L10.94,12 l-6.97,6.97c-0.29,0.29-0.29,0.77,0,1.06c0.15,0.15,0.34,0.22,0.53,0.22s0.38-0.07,0.53-0.22L12,13.06l6.97,6.97 c0.15,0.15,0.34,0.22,0.53,0.22s0.38-0.07,0.53-0.22c0.29-0.29,0.29-0.77,0-1.06L13.06,12l6.97-6.97 C20.32,4.74,20.32,4.26,20.03,3.97z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_colorize_24px.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_colorize_24px.xml
new file mode 100644
index 0000000..f572af6
--- /dev/null
+++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_colorize_24px.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="24dp" >
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M17.59,3.59l-2.97,2.97l-2.09-2.09c-0.29-0.29-0.77-0.29-1.06,0c-0.29,0.29-0.29,0.77,0,1.06l1.5,1.5l-9.68,9.68 C3.11,16.89,3,17.15,3,17.41V21h3.59c0.27,0,0.52-0.11,0.71-0.29l9.68-9.68l1.5,1.5c0.15,0.15,0.34,0.22,0.53,0.22 c0.19,0,0.38-0.07,0.53-0.22c0.29-0.29,0.29-0.77,0-1.06l-2.09-2.09l2.97-2.97c0.78-0.78,0.78-2.05,0-2.83 C19.63,2.8,18.37,2.8,17.59,3.59z M6.38,19.5H4.5v-1.88l9.5-9.5L15.88,10L6.38,19.5z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_delete_24px.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_delete_24px.xml
new file mode 100644
index 0000000..a87186b
--- /dev/null
+++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_delete_24px.xml
@@ -0,0 +1,31 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="24dp" >
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M9,20h6c1.66,0,3-1.34,3-3V6h0.5c0.41,0,0.75-0.34,0.75-0.75S18.91,4.5,18.5,4.5H18h-3l-1-1h-4l-1,1H6H5.5 c-0.41,0-0.75,0.34-0.75,0.75S5.09,6,5.5,6H6v11C6,18.66,7.34,20,9,20z M16.5,6v11c0,0.83-0.67,1.5-1.5,1.5H9 c-0.83,0-1.5-0.67-1.5-1.5V6H16.5z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M13.97,16c0.41,0,0.75-0.34,0.75-0.75v-6.5c0-0.41-0.34-0.75-0.75-0.75s-0.75,0.34-0.75,0.75v6.5 C13.22,15.66,13.55,16,13.97,16z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M10,16c0.41,0,0.75-0.34,0.75-0.75v-6.5C10.75,8.34,10.41,8,10,8S9.25,8.34,9.25,8.75v6.5C9.25,15.66,9.59,16,10,16z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_font.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_font.xml
new file mode 100644
index 0000000..edaf3c7
--- /dev/null
+++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_font.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="24dp" >
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M18,2H6C3.8,2,2,3.8,2,6v12c0,2.2,1.8,4,4,4h12c2.2,0,4-1.8,4-4V6C22,3.8,20.2,2,18,2z M20.5,18c0,1.38-1.12,2.5-2.5,2.5H6 c-1.38,0-2.5-1.12-2.5-2.5V6c0-1.38,1.12-2.5,2.5-2.5h12c1.38,0,2.5,1.12,2.5,2.5V18z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M10.69,6L6.2,18h2.5l1-2.87h4.59L15.3,18h2.5L13.29,6H10.69z M10.43,13.06l1.51-4.46h0.13l1.49,4.46H10.43z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_clock.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_clock.xml
new file mode 100644
index 0000000..2884d71
--- /dev/null
+++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_clock.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="24dp" >
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M22,12c0-5.52-4.48-10-10-10C6.48,2,2,6.48,2,12s4.48,10,10,10C17.52,22,22,17.52,22,12z M12,20.5 c-4.69,0-8.5-3.81-8.5-8.5S7.31,3.5,12,3.5c4.69,0,8.5,3.81,8.5,8.5S16.69,20.5,12,20.5z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M12.75,11.69V5.78c0-0.41-0.34-0.75-0.75-0.75s-0.75,0.34-0.75,0.75v6.53l3.78,3.78c0.15,0.15,0.34,0.22,0.53,0.22 s0.38-0.07,0.53-0.22c0.29-0.29,0.29-0.77,0-1.06L12.75,11.69z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_grid.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_grid.xml
new file mode 100644
index 0000000..d50dbd4
--- /dev/null
+++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_grid.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="24dp" >
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M2.75,16.75C2.34,16.75,2,17.09,2,17.5s0.34,0.75,0.75,0.75h3v3C5.75,21.66,6.09,22,6.5,22s0.75-0.34,0.75-0.75v-3h4v3 c0,0.41,0.34,0.75,0.75,0.75s0.75-0.34,0.75-0.75v-3h4v3c0,0.41,0.34,0.75,0.75,0.75s0.75-0.34,0.75-0.75v-3h3 c0.41,0,0.75-0.34,0.75-0.75s-0.34-0.75-0.75-0.75h-3v-4h3c0.41,0,0.75-0.34,0.75-0.75s-0.34-0.75-0.75-0.75h-3v-4h3 C21.66,7.25,22,6.91,22,6.5s-0.34-0.75-0.75-0.75h-3v-3C18.25,2.34,17.91,2,17.5,2s-0.75,0.34-0.75,0.75v3h-4v-3 C12.75,2.34,12.41,2,12,2s-0.75,0.34-0.75,0.75v3h-4v-3C7.25,2.34,6.91,2,6.5,2S5.75,2.34,5.75,2.75v3h-3C2.34,5.75,2,6.09,2,6.5 s0.34,0.75,0.75,0.75h3v4h-3C2.34,11.25,2,11.59,2,12s0.34,0.75,0.75,0.75h3v4H2.75z M16.75,16.75h-4v-4h4V16.75z M16.75,7.25v4h-4 v-4H16.75z M7.25,7.25h4v4h-4V7.25z M7.25,12.75h4v4h-4V12.75z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_theme.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_theme.xml
new file mode 100644
index 0000000..7375bc9
--- /dev/null
+++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_theme.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="24dp" >
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M8,14h1v5c0,1.66,1.34,3,3,3s3-1.34,3-3v-5h1c1.66,0,3-1.34,3-3V2H5v9C5,12.66,6.34,14,8,14z M16,12.5h-2.5V14v5 c0,0.83-0.67,1.5-1.5,1.5s-1.5-0.67-1.5-1.5v-5v-1.5H8c-0.83,0-1.5-0.67-1.5-1.5v-0.5h11V11C17.5,11.83,16.83,12.5,16,12.5z M9,3.5 v3c0,0.41,0.34,0.75,0.75,0.75S10.5,6.91,10.5,6.5v-3h2.75v3c0,0.41,0.34,0.75,0.75,0.75s0.75-0.34,0.75-0.75v-3h2.75V9h-11V3.5H9z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_wallpaper.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_wallpaper.xml
new file mode 100644
index 0000000..bdb7442
--- /dev/null
+++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_wallpaper.xml
@@ -0,0 +1,40 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="24dp" >
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M 16 6.75 C 16.6903559373 6.75 17.25 7.30964406271 17.25 8 C 17.25 8.69035593729 16.6903559373 9.25 16 9.25 C 15.3096440627 9.25 14.75 8.69035593729 14.75 8 C 14.75 7.30964406271 15.3096440627 6.75 16 6.75 Z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M11,3.76c0-0.41-0.34-0.75-0.75-0.75H6.84C4.72,3.01,3,4.74,3,6.85v3.4C3,10.66,3.34,11,3.75,11s0.75-0.34,0.75-0.75v-3.4 c0-1.29,1.05-2.33,2.34-2.33h3.41C10.66,4.51,11,4.18,11,3.76z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M10.25,19.5H6.84c-1.29,0-2.34-1.05-2.34-2.34v-3.41C4.5,13.34,4.16,13,3.75,13S3,13.34,3,13.75v3.41 C3,19.28,4.72,21,6.84,21h3.41c0.41,0,0.75-0.34,0.75-0.75S10.66,19.5,10.25,19.5z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20.25,13c-0.41,0-0.75,0.34-0.75,0.75v3.41c0,1.29-1.05,2.34-2.34,2.34h-3.41c-0.41,0-0.75,0.34-0.75,0.75 S13.34,21,13.75,21h3.41c2.12,0,3.84-1.72,3.84-3.84v-3.41C21,13.34,20.66,13,20.25,13z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M17.16,3h-3.41C13.34,3,13,3.34,13,3.75s0.34,0.75,0.75,0.75h3.41c1.29,0,2.34,1.05,2.34,2.34v3.41 c0,0.41,0.34,0.75,0.75,0.75S21,10.66,21,10.25V6.84C21,4.72,19.28,3,17.16,3z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M12.89,15.29l-1.41-1.82c-0.8-1.04-2.37-1.03-3.17,0.01l-2.7,3.51l12.83-0.01l-1.88-2.35c-0.79-0.99-2.29-1-3.1-0.03 L12.89,15.29z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_shapes_24px.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_shapes_24px.xml
new file mode 100644
index 0000000..b7e6bf9
--- /dev/null
+++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_shapes_24px.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="24dp" >
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M19,9h-1.5v1.5H19c0.83,0,1.5,0.67,1.5,1.5v7c0,0.83-0.67,1.5-1.5,1.5h-8c-0.83,0-1.5-0.67-1.5-1.5v-1.5H8V19 c0,1.66,1.34,3,3,3h8c1.66,0,3-1.34,3-3v-7C22,10.34,20.66,9,19,9z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M9,16c3.87,0,7-3.13,7-7c0-3.87-3.13-7-7-7C5.13,2,2,5.13,2,9C2,12.87,5.13,16,9,16z M3.5,9c0-3.03,2.47-5.5,5.5-5.5 s5.5,2.47,5.5,5.5s-2.47,5.5-5.5,5.5S3.5,12.03,3.5,9z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_tune.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_tune.xml
new file mode 100644
index 0000000..9c88211
--- /dev/null
+++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_tune.xml
@@ -0,0 +1,40 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="20dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="20dp" >
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20.25,4.75h-2.4C17.55,4.02,16.84,3.5,16,3.5c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2c0.84,0,1.55-0.52,1.85-1.25h2.4 C20.66,6.25,21,5.91,21,5.5S20.66,4.75,20.25,4.75z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M11.98,4.75H3.75C3.34,4.75,3,5.09,3,5.5s0.34,0.75,0.75,0.75h8.23c0.01,0,0.01,0,0.02,0V4.75 C11.99,4.75,11.99,4.75,11.98,4.75z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20.25,17.75h-5.4c-0.3-0.73-1.01-1.25-1.85-1.25c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2c0.84,0,1.55-0.52,1.85-1.25h5.4 c0.41,0,0.75-0.34,0.75-0.75S20.66,17.75,20.25,17.75z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M8.98,17.75H3.75C3.34,17.75,3,18.09,3,18.5s0.34,0.75,0.75,0.75h5.23c0.01,0,0.01,0,0.02,0v-1.49 C8.99,17.75,8.99,17.75,8.98,17.75z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M7,10c-0.84,0-1.55,0.52-1.85,1.25h-1.4C3.34,11.25,3,11.59,3,12s0.34,0.75,0.75,0.75h1.4C5.45,13.48,6.16,14,7,14 c1.1,0,2-0.9,2-2C9,10.9,8.1,10,7,10z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20.25,11.25h-9.23c-0.01,0-0.01,0-0.02,0v1.49c0.01,0,0.01,0,0.02,0h9.23c0.41,0,0.75-0.34,0.75-0.75 S20.66,11.25,20.25,11.25z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_wifi_24px.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_wifi_24px.xml
new file mode 100644
index 0000000..fde9965
--- /dev/null
+++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_wifi_24px.xml
@@ -0,0 +1,34 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="24dp" >
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M 12 17 C 12.8284271247 17 13.5 17.6715728753 13.5 18.5 C 13.5 19.3284271247 12.8284271247 20 12 20 C 11.1715728753 20 10.5 19.3284271247 10.5 18.5 C 10.5 17.6715728753 11.1715728753 17 12 17 Z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M19.42,11.84c-0.19,0-0.38-0.07-0.53-0.22C17.05,9.77,14.6,8.75,12,8.75s-5.05,1.02-6.89,2.86 c-0.29,0.29-0.77,0.29-1.06,0c-0.29-0.29-0.29-0.77,0-1.06C6.17,8.43,9,7.25,12,7.25s5.83,1.17,7.95,3.3 c0.29,0.29,0.29,0.77,0,1.06C19.8,11.76,19.61,11.84,19.42,11.84z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M22.61,8.65c-0.19,0-0.38-0.07-0.53-0.22C19.38,5.74,15.81,4.25,12,4.25S4.62,5.74,1.92,8.43c-0.29,0.29-0.77,0.29-1.06,0 s-0.29-0.77,0-1.06C3.84,4.39,7.79,2.75,12,2.75s8.16,1.64,11.14,4.61c0.29,0.29,0.29,0.77,0,1.06 C22.99,8.57,22.8,8.65,22.61,8.65z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M16.25,15c-0.19,0-0.38-0.07-0.53-0.22c-1-0.99-2.32-1.53-3.73-1.53s-2.73,0.54-3.73,1.53c-0.29,0.29-0.77,0.29-1.06-0.01 s-0.29-0.77,0.01-1.06c1.28-1.27,2.98-1.96,4.78-1.96s3.5,0.7,4.78,1.96c0.29,0.29,0.3,0.77,0.01,1.06 C16.64,14.93,16.45,15,16.25,15z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackFilledAndroidOverlay/res/values/config.xml b/packages/overlays/IconPackFilledAndroidOverlay/res/values/config.xml
new file mode 100644
index 0000000..6b59b62
--- /dev/null
+++ b/packages/overlays/IconPackFilledAndroidOverlay/res/values/config.xml
@@ -0,0 +1,36 @@
+<?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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_batterymeterPerimeterPath" translatable="false">
+ M 4,0 V 2 H 2.33 C 1.6,2 1,2.6 1,3.33 V 18.66 C 1,19.4 1.6,20 2.33,20 H 9.66 C 10.4,20 11,19.4 11,18.67 V 3.33 C 11,2.6 10.4,2 9.67,2 H 8 V 0 Z
+ </string>
+ <string name="config_batterymeterErrorPerimeterPath" translatable="false">
+ M 3.5,0 V 0.5 1.5 H 2.3301 C 1.3261,1.5 0.5,2.3261 0.5,3.3301 V 18.16 C 0.5,19.17 1.3261,20 2.3301,20 H 9.6602 C 10.67,20 11.5,19.174 11.5,18.17 V 3.3301 C 11.5,2.3261 10.674,1.5 9.6699,1.5 H 8.5 V 0 Z M 9.1698,2.9999 C 9.6259,2.9999 9.9999,3.374 9.9999,3.83 V 17.67 C 9.9999,18.126 9.6299,18.5 9.1601,18.5 H 2.83 C 2.3741,18.5 2,18.13 2,17.66 V 3.83 C 2,3.374 2.3741,2.9999 2.83,2.9999 Z
+ </string>
+ <string name="config_batterymeterFillMask" translatable="false">
+ M 4,0 V 2 H 2.33 C 1.6,2 1,2.6 1,3.33 V 18.66 C 1,19.4 1.6,20 2.33,20 H 9.66 C 10.4,20 11,19.4 11,18.67 V 3.33 C 11,2.6 10.4,2 9.67,2 H 8 V 0 Z
+ </string>
+ <string name="config_batterymeterBoltPath" translatable="false">
+ M 8.58,10 C 8.77,10 8.89,10.2 8.8,10.37 L 5.94,15.74 C 5.7,16.19 5,16.02 5,15.5 V 12 H 3.42 C 3.23,12 3.11,11.8 3.2,11.63 L 6.06,6.26 C 6.3,5.81 7,5.98 7,6.5 V 10 Z
+ </string>
+ <string name="config_batterymeterPowersavePath" translatable="false">
+ M 9,11 C 9,11.55 8.55,12 8,12 H 7 V 13 C 7,13.55 6.55,14 6,14 5.45,14 5,13.55 5,13 V 12 H 4 C 3.45,12 3,11.55 3,11 3,10.45 3.45,10.005 4,10 H 5 V 9 C 5,8.45 5.45,8 6,8 6.55,8 7,8.45 7,9 V 10 H 8 C 8.55,10 9,10.45 9,11 Z
+ </string>
+ <bool name="config_batterymeterDualTone">true</bool>
+</resources>
diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/Android.mk b/packages/overlays/IconPackFilledThemePickerOverlay/Android.mk
new file mode 100644
index 0000000..6d15603
--- /dev/null
+++ b/packages/overlays/IconPackFilledThemePickerOverlay/Android.mk
@@ -0,0 +1,31 @@
+#
+# 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.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_RRO_THEME := IconPackFilledThemePicker
+LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := IconPackFilledThemePickerOverlay
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/AndroidManifest.xml b/packages/overlays/IconPackFilledThemePickerOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..35023ab
--- /dev/null
+++ b/packages/overlays/IconPackFilledThemePickerOverlay/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.theme.icon_pack.filled.themepicker"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <overlay android:targetPackage="com.android.wallpaper" android:category="android.theme.customization.icon_pack.themepicker" android:priority="1"/>
+ <application android:label="Filled" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_add_24px.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_add_24px.xml
new file mode 100644
index 0000000..1768723
--- /dev/null
+++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_add_24px.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="24dp" >
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M5,13h6v6c0,0.55,0.45,1,1,1s1-0.45,1-1v-6h6c0.55,0,1-0.45,1-1s-0.45-1-1-1h-6V5c0-0.55-0.45-1-1-1s-1,0.45-1,1v6H5 c-0.55,0-1,0.45-1,1S4.45,13,5,13z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_close_24px.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_close_24px.xml
new file mode 100644
index 0000000..4bfff2c
--- /dev/null
+++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_close_24px.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="24dp" >
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M5.7,18.3c0.39,0.39,1.02,0.39,1.41,0L12,13.41l4.89,4.89c0.39,0.39,1.02,0.39,1.41,0s0.39-1.02,0-1.41L13.41,12l4.89-4.89 c0.38-0.38,0.38-1.02,0-1.4c-0.39-0.39-1.02-0.39-1.41,0c0,0,0,0,0,0L12,10.59L7.11,5.7c-0.39-0.39-1.02-0.39-1.41,0 s-0.39,1.02,0,1.41L10.59,12L5.7,16.89C5.31,17.28,5.31,17.91,5.7,18.3z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_colorize_24px.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_colorize_24px.xml
new file mode 100644
index 0000000..aa3a925
--- /dev/null
+++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_colorize_24px.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="24dp" >
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M11.21,5.2L11.21,5.2c-0.39,0.39-0.39,1.02,0,1.41l0.71,0.71l-8.63,8.63C3.11,16.14,3,16.4,3,16.66V20c0,0.55,0.45,1,1,1 h3.34c0.27,0,0.52-0.11,0.71-0.29l8.63-8.63l0.72,0.72c0.39,0.39,1.02,0.39,1.41,0c0.39-0.39,0.39-1.02,0-1.41l-1.21-1.22L20,7.75 c0.78-0.78,0.78-2.05,0-2.83L19.08,4c-0.78-0.78-2.05-0.78-2.83,0l-2.41,2.41L12.61,5.2C12.23,4.81,11.6,4.81,11.21,5.2z M14.98,10.94L6.92,19H5v-1.92l8.06-8.06L14.98,10.94z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_delete_24px.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_delete_24px.xml
new file mode 100644
index 0000000..94c6311
--- /dev/null
+++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_delete_24px.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="24dp" >
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M18,4h-2.5l-0.71-0.71C14.61,3.11,14.35,3,14.09,3H9.9C9.64,3,9.38,3.11,9.2,3.29L8.49,4h-2.5c-0.55,0-1,0.45-1,1 s0.45,1,1,1h12c0.55,0,1-0.45,1-1C19,4.45,18.55,4,18,4z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M6,19c0,1.1,0.9,2,2,2h8c1.1,0,2-0.9,2-2V7H6V19z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_font.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_font.xml
new file mode 100644
index 0000000..7603823
--- /dev/null
+++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_font.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="24dp" >
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M 12.07 8.6 L 11.94 8.6 L 10.43 13.06 L 13.56 13.06 Z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M19,2H5C3.35,2,2,3.35,2,5v14c0,1.65,1.35,3,3,3h14c1.65,0,3-1.35,3-3V5C22,3.35,20.65,2,19,2z M15.3,18l-1.01-2.87H9.7 L8.7,18H6.2l4.49-12h2.6l4.51,12H15.3z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_clock.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_clock.xml
new file mode 100644
index 0000000..11260159
--- /dev/null
+++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_clock.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="24dp" >
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M11.99,22C17.52,22,22,17.52,22,12c0-5.52-4.48-10-10.01-10C6.47,2,2,6.48,2,12C2,17.52,6.47,22,11.99,22z M11,6.82 c0-0.4,0.25-0.72,0.75-0.72s0.75,0.32,0.75,0.72v5.43l3.87,2.3c0.01,0,0.01,0.01,0.02,0.01c0.33,0.21,0.44,0.64,0.23,0.98 c-0.2,0.34-0.64,0.44-0.98,0.24L11,13V6.82z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_grid.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_grid.xml
new file mode 100644
index 0000000..0397b6c
--- /dev/null
+++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_grid.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="24dp" >
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M22,7c0.55,0,1-0.45,1-1s-0.45-1-1-1h-3V2c0-0.55-0.45-1-1-1s-1,0.45-1,1v3h-4V2c0-0.55-0.45-1-1-1s-1,0.45-1,1v3H7V2 c0-0.55-0.45-1-1-1S5,1.45,5,2v3H2C1.45,5,1,5.45,1,6s0.45,1,1,1h3v4H2c-0.55,0-1,0.45-1,1s0.45,1,1,1h3v4H2c-0.55,0-1,0.45-1,1 s0.45,1,1,1h3v3c0,0.55,0.45,1,1,1s1-0.45,1-1v-3h4v3c0,0.55,0.45,1,1,1s1-0.45,1-1v-3h4v3c0,0.55,0.45,1,1,1s1-0.45,1-1v-3h3 c0.55,0,1-0.45,1-1s-0.45-1-1-1h-3v-4h3c0.55,0,1-0.45,1-1s-0.45-1-1-1h-3V7H22z M11,17H7v-4h4V17z M11,11H7V7h4V11z M17,17h-4v-4 h4V17z M17,11h-4V7h4V11z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_theme.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_theme.xml
new file mode 100644
index 0000000..6f0462c
--- /dev/null
+++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_theme.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="24dp" >
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M18,2H6C5.45,2,5,2.45,5,3v9c0,1.65,1.35,3,3,3h2v6c0,0.55,0.45,1,1,1h2c0.55,0,1-0.45,1-1v-6h2c1.65,0,3-1.35,3-3V3 C19,2.45,18.55,2,18,2z M17,9H7V4h2v2c0,0.55,0.45,1,1,1s1-0.45,1-1V4h2v2c0,0.55,0.45,1,1,1c0.55,0,1-0.45,1-1V4h2V9z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_wallpaper.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_wallpaper.xml
new file mode 100644
index 0000000..ea195ca
--- /dev/null
+++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_wallpaper.xml
@@ -0,0 +1,40 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="24dp" >
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M 15.5 7 C 16.3284271247 7 17 7.67157287525 17 8.5 C 17 9.32842712475 16.3284271247 10 15.5 10 C 14.6715728753 10 14 9.32842712475 14 8.5 C 14 7.67157287525 14.6715728753 7 15.5 7 Z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M3.5,11h1C4.78,11,5,10.78,5,10.5V5h5.5C10.78,5,11,4.78,11,4.5v-1C11,3.22,10.78,3,10.5,3H5C3.9,3,3,3.9,3,5v5.5 C3,10.78,3.22,11,3.5,11z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M19,3h-5.5C13.22,3,13,3.22,13,3.5v1C13,4.78,13.22,5,13.5,5H19v5.5c0,0.28,0.22,0.5,0.5,0.5h1c0.28,0,0.5-0.22,0.5-0.5V5 C21,3.9,20.1,3,19,3z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20.5,13h-1c-0.28,0-0.5,0.22-0.5,0.5V19h-5.5c-0.28,0-0.5,0.22-0.5,0.5v1c0,0.28,0.22,0.5,0.5,0.5H19c1.1,0,2-0.9,2-2 v-5.5C21,13.22,20.78,13,20.5,13z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M10.5,19H5v-5.5C5,13.22,4.78,13,4.5,13h-1C3.22,13,3,13.22,3,13.5V19c0,1.1,0.9,2,2,2h5.5c0.28,0,0.5-0.22,0.5-0.5v-1 C11,19.22,10.78,19,10.5,19z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M10.78,12.98c-0.4-0.5-1.16-0.5-1.56,0l-2.57,3.21C6.39,16.51,6.62,17,7.04,17H17c0.41,0,0.65-0.47,0.4-0.8l-1.6-2.13 c-0.4-0.53-1.2-0.53-1.6,0l-1.23,1.64L10.78,12.98z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_shapes_24px.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_shapes_24px.xml
new file mode 100644
index 0000000..cea09b56
--- /dev/null
+++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_shapes_24px.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="24dp" >
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20,9h-3v2h3v9H10v-2H8v2c0,1.1,0.9,2,2,2h10c1.1,0,2-0.9,2-2v-9C22,9.9,21.1,9,20,9z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M15,9c0-1.07-0.25-2.09-0.68-3C13.2,3.64,10.79,2,8,2C4.13,2,1,5.13,1,9c0,2.79,1.64,5.2,4,6.32C5.91,15.75,6.93,16,8,16 C11.87,16,15,12.87,15,9z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_tune.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_tune.xml
new file mode 100644
index 0000000..ae03b51
--- /dev/null
+++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_tune.xml
@@ -0,0 +1,40 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="20dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="20dp" >
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M8,9c-0.55,0-1,0.45-1,1v1H4c-0.55,0-1,0.45-1,1c0,0.55,0.45,1,1,1h3v1c0,0.55,0.45,1,1,1s1-0.45,1-1v-4 C9,9.45,8.55,9,8,9z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20,5h-3V4c0-0.55-0.45-1-1-1s-1,0.45-1,1v4c0,0.55,0.45,1,1,1s1-0.45,1-1V7h3c0.55,0,1-0.45,1-1C21,5.45,20.55,5,20,5z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20,11h-9v2h9c0.55,0,1-0.45,1-1C21,11.45,20.55,11,20,11z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20,17h-7v-1c0-0.55-0.45-1-1-1s-1,0.45-1,1v4c0,0.55,0.45,1,1,1s1-0.45,1-1v-1h7c0.55,0,1-0.45,1-1S20.55,17,20,17z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M4,7h9V5H4C3.45,5,3,5.45,3,6C3,6.55,3.45,7,4,7z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M3,18c0,0.55,0.45,1,1,1h5v-2H4C3.45,17,3,17.45,3,18z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_wifi_24px.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_wifi_24px.xml
new file mode 100644
index 0000000..03e142e
--- /dev/null
+++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_wifi_24px.xml
@@ -0,0 +1,31 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="24dp" >
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M11.29,19.29c0.39,0.39,1.03,0.4,1.42,0L14,18c0.47-0.47,0.38-1.28-0.22-1.58C13.25,16.15,12.64,16,12,16 c-0.64,0-1.24,0.15-1.77,0.41c-0.59,0.29-0.69,1.11-0.22,1.58L11.29,19.29z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M17.6,14.39l0.71-0.71c0.42-0.42,0.39-1.12-0.08-1.5C16.52,10.82,14.35,10,12,10c-2.34,0-4.5,0.81-6.21,2.17 c-0.47,0.37-0.51,1.07-0.09,1.49l0.71,0.71c0.35,0.36,0.92,0.39,1.32,0.08C8.91,13.54,10.39,13,12,13c1.61,0,3.1,0.55,4.29,1.47 C16.69,14.78,17.25,14.75,17.6,14.39z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M21.83,10.16l0.71-0.71c0.42-0.42,0.38-1.09-0.06-1.48C19.68,5.5,16.01,4,12,4C8.01,4,4.36,5.49,1.56,7.94 C1.12,8.33,1.08,9,1.49,9.41l0.71,0.71c0.37,0.37,0.96,0.4,1.35,0.06C5.81,8.2,8.77,7,12,7c3.25,0,6.22,1.22,8.49,3.22 C20.88,10.56,21.47,10.53,21.83,10.16z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedAndroidOverlay/res/values/config.xml b/packages/overlays/IconPackRoundedAndroidOverlay/res/values/config.xml
new file mode 100644
index 0000000..ebcac82
--- /dev/null
+++ b/packages/overlays/IconPackRoundedAndroidOverlay/res/values/config.xml
@@ -0,0 +1,33 @@
+<?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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_batterymeterPerimeterPath" translatable="false">
+ M 11,1.505 H 8 V 0.995 C 8,0.445 7.55,-0.005 7,-0.005 H 5 C 4.45,-0.005 4,0.445 4,0.995 V 1.505 H 1 C 0.45,1.505 0,1.955 0,2.505 V 19.005 C 0,19.555 0.45,20.005 1,20.005 H 11 C 11.55,20.005 12,19.555 12,19.005 V 2.505 C 12,1.955 11.543,1.505 11,1.505 Z M 10.5,18.505 H 1.5 V 3.005 H 10.5 Z
+ </string>
+
+ <string name="config_batterymeterFillMask" translatable="false">
+ M 10.5,18.505 H 1.5 V 3.005 H 10.5 Z
+ </string>
+ <string name="config_batterymeterBoltPath" translatable="false">
+ M 3.92,11.5 H 5 V 15.01 C 5,15.17 5.13,15.26 5.25,15.26 5.33,15.26 5.42,15.22 5.47,15.13 L 8.3,9.87 C 8.39,9.7 8.27,9.5 8.08,9.5 H 7 V 5.99 C 7,5.83 6.87,5.74 6.75,5.74 6.67,5.74 6.58,5.78 6.53,5.87 L 3.7,11.13 C 3.61,11.3 3.73,11.5 3.92,11.5 Z
+ </string>
+ <string name="config_batterymeterPowersavePath" translatable="false">
+ M 3.75,11.25 H 5.25 V 12.75 C 5.25,13.16 5.59,13.5 6,13.5 6.41,13.5 6.75,13.16 6.75,12.75 V 11.25 H 8.25 C 8.66,11.25 9,10.91 9,10.5 9,10.09 8.66,9.7499 8.25,9.7499 H 6.75 V 8.2499 C 6.75,7.8399 6.41,7.4999 6,7.4999 5.59,7.4999 5.2794,7.841 5.25,8.2499 V 9.7499 H 3.75 C 3.34,9.7499 3,10.09 3,10.5 3,10.91 3.3401,11.25 3.75,11.25 Z
+ </string>
+</resources>
diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/Android.mk b/packages/overlays/IconPackRoundedThemePickerOverlay/Android.mk
new file mode 100644
index 0000000..ae48186
--- /dev/null
+++ b/packages/overlays/IconPackRoundedThemePickerOverlay/Android.mk
@@ -0,0 +1,31 @@
+#
+# 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.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_RRO_THEME := IconPackRoundedThemePicker
+LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := IconPackRoundedThemePickerOverlay
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/AndroidManifest.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..9a90a05
--- /dev/null
+++ b/packages/overlays/IconPackRoundedThemePickerOverlay/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.theme.icon_pack.rounded.themepicker"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <overlay android:targetPackage="com.android.wallpaper" android:category="android.theme.customization.icon_pack.themepicker" android:priority="1"/>
+ <application android:label="Rounded" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_add_24px.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_add_24px.xml
new file mode 100644
index 0000000..707369a
--- /dev/null
+++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_add_24px.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="24dp" >
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M4.67,12.75h6.58v6.58C11.25,19.7,11.59,20,12,20s0.75-0.3,0.75-0.67v-6.58h6.58C19.7,12.75,20,12.41,20,12 s-0.3-0.75-0.67-0.75h-6.58V4.67C12.75,4.3,12.41,4,12,4s-0.75,0.3-0.75,0.67v6.58H4.67C4.3,11.25,4,11.59,4,12 S4.3,12.75,4.67,12.75z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_close_24px.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_close_24px.xml
new file mode 100644
index 0000000..1dca14d
--- /dev/null
+++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_close_24px.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="24dp" >
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M18.78,5.22c-0.29-0.29-0.77-0.29-1.06,0L12,10.94L6.28,5.22c-0.29-0.29-0.77-0.29-1.06,0s-0.29,0.77,0,1.06L10.94,12 l-5.72,5.72c-0.29,0.29-0.29,0.77,0,1.06C5.37,18.93,5.56,19,5.75,19s0.38-0.07,0.53-0.22L12,13.06l5.72,5.72 c0.15,0.15,0.34,0.22,0.53,0.22s0.38-0.07,0.53-0.22c0.29-0.29,0.29-0.77,0-1.06L13.06,12l5.72-5.72 C19.07,5.99,19.07,5.51,18.78,5.22z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_colorize_24px.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_colorize_24px.xml
new file mode 100644
index 0000000..5c21b23
--- /dev/null
+++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_colorize_24px.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="24dp" >
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M11.47,4.47c-0.29,0.29-0.29,0.77,0,1.06l1.5,1.5l-9.68,9.68C3.11,16.89,3,17.15,3,17.42l0,2.58c0,0.55,0.45,1,1,1L6.58,21 c0,0,0,0,0,0c0.27,0,0.52-0.11,0.71-0.29l9.68-9.68l1.5,1.5c0.15,0.15,0.34,0.22,0.53,0.22c0.19,0,0.38-0.07,0.53-0.22 c0.29-0.29,0.29-0.77,0-1.06l-2.09-2.09l2.97-2.97c0.78-0.78,0.78-2.05,0-2.83c-0.78-0.78-2.05-0.78-2.83,0l-2.97,2.97l-2.09-2.09 C12.24,4.17,11.77,4.17,11.47,4.47z M6.38,19.5L4.5,19.49l0-1.87l9-9l1.88,1.88L6.38,19.5z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_delete_24px.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_delete_24px.xml
new file mode 100644
index 0000000..48a430f
--- /dev/null
+++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_delete_24px.xml
@@ -0,0 +1,31 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="24dp" >
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20,4h-1h-4c0-0.55-0.45-1-1-1h-4C9.45,3,9,3.45,9,4H5H4C3.59,4,3.25,4.34,3.25,4.75S3.59,5.5,4,5.5h1V18 c0,1.66,1.34,3,3,3h8c1.66,0,3-1.34,3-3V5.5h1c0.41,0,0.75-0.34,0.75-0.75S20.41,4,20,4z M17.5,18c0,0.83-0.67,1.5-1.5,1.5H8 c-0.83,0-1.5-0.67-1.5-1.5V5.5h11V18z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M14.25,8c-0.41,0-0.75,0.34-0.75,0.75v7.5c0,0.41,0.34,0.75,0.75,0.75S15,16.66,15,16.25v-7.5C15,8.34,14.66,8,14.25,8z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M9.75,8C9.34,8,9,8.34,9,8.75v7.5C9,16.66,9.34,17,9.75,17s0.75-0.34,0.75-0.75v-7.5C10.5,8.34,10.16,8,9.75,8z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_font.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_font.xml
new file mode 100644
index 0000000..bbae929
--- /dev/null
+++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_font.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="24dp" >
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20,2H4C2.9,2,2,2.9,2,4v16c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V4C22,2.9,21.1,2,20,2z M20.5,20c0,0.27-0.23,0.5-0.5,0.5H4 c-0.27,0-0.5-0.23-0.5-0.5V4c0-0.27,0.23-0.5,0.5-0.5h16c0.27,0,0.5,0.23,0.5,0.5V20z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M10.69,6L6.2,18h2.5l1-2.87h4.59L15.3,18h2.5L13.29,6H10.69z M10.43,13.06l1.51-4.46h0.13l1.49,4.46H10.43z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_clock.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_clock.xml
new file mode 100644
index 0000000..9c9d663
--- /dev/null
+++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_clock.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="24dp" >
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M19.07,4.93c-3.91-3.9-10.24-3.9-14.14,0.01s-3.9,10.24,0.01,14.14s10.24,3.9,14.14-0.01C20.95,17.2,22,14.65,22,12 C22,9.35,20.94,6.81,19.07,4.93z M18,18c-1.6,1.59-3.76,2.48-6.02,2.48c-4.69-0.01-8.49-3.83-8.48-8.52 c0.01-4.69,3.83-8.49,8.52-8.48c4.69,0.01,8.49,3.83,8.48,8.52C20.49,14.25,19.6,16.41,18,18z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M12.75,11.69V6.75C12.75,6.34,12.41,6,12,6s-0.75,0.34-0.75,0.75V12c0,0.2,0.08,0.39,0.22,0.53l3.25,3.25 c0.15,0.15,0.34,0.22,0.53,0.22s0.38-0.07,0.53-0.22c0.29-0.29,0.29-0.77,0-1.06L12.75,11.69z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_grid.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_grid.xml
new file mode 100644
index 0000000..c81ca1e
--- /dev/null
+++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_grid.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="24dp" >
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20.25,7C20.66,7,21,6.66,21,6.25S20.66,5.5,20.25,5.5H18.5V3.75C18.5,3.34,18.16,3,17.75,3S17,3.34,17,3.75V5.5h-4.25 V3.75C12.75,3.34,12.41,3,12,3s-0.75,0.34-0.75,0.75V5.5H7V3.75C7,3.34,6.66,3,6.25,3S5.5,3.34,5.5,3.75V5.5H3.75 C3.34,5.5,3,5.84,3,6.25S3.34,7,3.75,7H5.5v4.25H3.75C3.34,11.25,3,11.59,3,12s0.34,0.75,0.75,0.75H5.5V17H3.75 C3.34,17,3,17.34,3,17.75s0.34,0.75,0.75,0.75H5.5v1.75C5.5,20.66,5.84,21,6.25,21S7,20.66,7,20.25V18.5h4.25v1.75 c0,0.41,0.34,0.75,0.75,0.75s0.75-0.34,0.75-0.75V18.5H17v1.75c0,0.41,0.34,0.75,0.75,0.75s0.75-0.34,0.75-0.75V18.5h1.75 c0.41,0,0.75-0.34,0.75-0.75S20.66,17,20.25,17H18.5v-4.25h1.75c0.41,0,0.75-0.34,0.75-0.75s-0.34-0.75-0.75-0.75H18.5V7H20.25z M7,7h4.25v4.25H7V7z M7,17v-4.25h4.25V17H7z M17,17h-4.25v-4.25H17V17z M17,11.25h-4.25V7H17V11.25z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_theme.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_theme.xml
new file mode 100644
index 0000000..32d154b
--- /dev/null
+++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_theme.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="24dp" >
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M11.5,22h1c1.1,0,2-0.9,2-2v-6H16c1.66,0,3-1.34,3-3V3c0-0.55-0.45-1-1-1H6C5.45,2,5,2.45,5,3v8c0,1.66,1.34,3,3,3h1.5v6 C9.5,21.1,10.4,22,11.5,22z M9,3.5v2.75C9,6.66,9.34,7,9.75,7s0.75-0.34,0.75-0.75V3.5h3v2.75C13.5,6.66,13.84,7,14.25,7 S15,6.66,15,6.25V3.5h2.5V9h-11V3.5H9z M8,12.5c-0.83,0-1.5-0.67-1.5-1.5v-0.5h11V11c0,0.83-0.67,1.5-1.5,1.5h-3V14v6 c0,0.28-0.22,0.5-0.5,0.5h-1c-0.28,0-0.5-0.22-0.5-0.5v-6v-1.5H8z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_wallpaper.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_wallpaper.xml
new file mode 100644
index 0000000..21daf9d
--- /dev/null
+++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_wallpaper.xml
@@ -0,0 +1,40 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="24dp" >
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M 16 6.75 C 16.6903559373 6.75 17.25 7.30964406271 17.25 8 C 17.25 8.69035593729 16.6903559373 9.25 16 9.25 C 15.3096440627 9.25 14.75 8.69035593729 14.75 8 C 14.75 7.30964406271 15.3096440627 6.75 16 6.75 Z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M3.75,10.75c0.41,0,0.75-0.34,0.75-0.75V4.75c0-0.14,0.11-0.25,0.25-0.25H10c0.41,0,0.75-0.34,0.75-0.75S10.41,3,10,3 H4.75C3.79,3,3,3.79,3,4.75V10C3,10.41,3.34,10.75,3.75,10.75z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M10,19.5H4.75c-0.14,0-0.25-0.11-0.25-0.25V14c0-0.41-0.34-0.75-0.75-0.75S3,13.59,3,14v5.25C3,20.21,3.79,21,4.75,21H10 c0.41,0,0.75-0.34,0.75-0.75S10.41,19.5,10,19.5z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20.25,13.25c-0.41,0-0.75,0.34-0.75,0.75v5.25c0,0.14-0.11,0.25-0.25,0.25H14c-0.41,0-0.75,0.34-0.75,0.75 S13.59,21,14,21h5.25c0.96,0,1.75-0.79,1.75-1.75V14C21,13.59,20.66,13.25,20.25,13.25z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M19.25,3H14c-0.41,0-0.75,0.34-0.75,0.75S13.59,4.5,14,4.5h5.25c0.14,0,0.25,0.11,0.25,0.25V10 c0,0.41,0.34,0.75,0.75,0.75S21,10.41,21,10V4.75C21,3.79,20.21,3,19.25,3z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M13.73,12.37l-2.6,3.35l-1.74-2.1c-0.2-0.25-0.58-0.24-0.78,0.01l-1.99,2.56C6.36,16.52,6.59,17,7.01,17h9.98 c0.41,0,0.65-0.47,0.4-0.8l-2.87-3.83C14.32,12.11,13.93,12.11,13.73,12.37z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_shapes_24px.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_shapes_24px.xml
new file mode 100644
index 0000000..19ce4e3
--- /dev/null
+++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_shapes_24px.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="24dp" >
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20,9h-2.51c0,0.1,0.01,0.2,0.01,0.3v1.2H20c0.28,0,0.5,0.22,0.5,0.5v9c0,0.28-0.22,0.5-0.5,0.5H10 c-0.28,0-0.5-0.22-0.5-0.5v-2.5H8V20c0,1.1,0.9,2,2,2h10c1.1,0,2-0.9,2-2v-9C22,9.9,21.1,9,20,9z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M16,9c0-3.87-3.13-7-7-7C5.13,2,2,5.13,2,9c0,3.87,3.13,7,7,7C12.87,16,16,12.87,16,9z M3.5,9c0-3.03,2.47-5.5,5.5-5.5 s5.5,2.47,5.5,5.5s-2.47,5.5-5.5,5.5S3.5,12.03,3.5,9z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_tune.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_tune.xml
new file mode 100644
index 0000000..2a56cc5
--- /dev/null
+++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_tune.xml
@@ -0,0 +1,40 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="20dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="20dp" >
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M3.75,4.75C3.34,4.75,3,5.09,3,5.5s0.34,0.75,0.75,0.75H14v-1.5H3.75z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20.25,4.75H17v-1.5c0-0.41-0.34-0.75-0.75-0.75S15.5,2.84,15.5,3.25v4.5c0,0.41,0.34,0.75,0.75,0.75S17,8.16,17,7.75v-1.5 h3.25C20.66,6.25,21,5.91,21,5.5S20.66,4.75,20.25,4.75z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20.25,11.25H10v1.5h10.25c0.41,0,0.75-0.34,0.75-0.75S20.66,11.25,20.25,11.25z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M3.75,17.75C3.34,17.75,3,18.09,3,18.5s0.34,0.75,0.75,0.75H10v-1.5H3.75z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20.25,17.75H13v-1.5c0-0.41-0.34-0.75-0.75-0.75s-0.75,0.34-0.75,0.75v4.5c0,0.41,0.34,0.75,0.75,0.75S13,21.16,13,20.75 v-1.5h7.25c0.41,0,0.75-0.34,0.75-0.75S20.66,17.75,20.25,17.75z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M8.5,9.75C8.5,9.34,8.16,9,7.75,9S7,9.34,7,9.75v1.5H3.75C3.34,11.25,3,11.59,3,12s0.34,0.75,0.75,0.75H7v1.5 C7,14.66,7.34,15,7.75,15s0.75-0.34,0.75-0.75V9.75z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_wifi_24px.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_wifi_24px.xml
new file mode 100644
index 0000000..0a1c305
--- /dev/null
+++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_wifi_24px.xml
@@ -0,0 +1,34 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="24dp" >
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M12,2.75C7.95,2.69,4.05,4.3,1.22,7.2C0.96,7.5,0.97,7.95,1.24,8.23C1.53,8.53,2,8.54,2.3,8.25c2.55-2.61,6.05-4.06,9.7-4 c3.65-0.06,7.17,1.4,9.72,4.02c0.28,0.27,0.73,0.28,1.03,0.01c0.31-0.28,0.33-0.75,0.05-1.06C19.96,4.32,16.06,2.69,12,2.75z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M15.78,14.82c0.05,0.06,0.1,0.11,0.17,0.15c0.34,0.23,0.81,0.14,1.04-0.21s0.14-0.81-0.21-1.04 c-2.64-2.64-6.91-2.64-9.55,0c-0.27,0.29-0.27,0.73,0,1.02c0.28,0.3,0.76,0.32,1.06,0.04h0.03c0,0,0,0,0.01-0.01 c2.05-2.05,5.37-2.04,7.42,0.01C15.75,14.8,15.76,14.81,15.78,14.82z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M 12 17 C 12.8284271247 17 13.5 17.6715728753 13.5 18.5 C 13.5 19.3284271247 12.8284271247 20 12 20 C 11.1715728753 20 10.5 19.3284271247 10.5 18.5 C 10.5 17.6715728753 11.1715728753 17 12 17 Z" />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20.03,11.79c0.3-0.29,0.3-0.77,0.01-1.06h-0.01c-2.12-2.18-5.01-3.44-8.04-3.5c-3.04,0.06-5.93,1.32-8.05,3.5 c-0.29,0.3-0.28,0.77,0.01,1.06c0.3,0.29,0.77,0.28,1.06-0.01c1.85-1.88,4.36-2.96,7-3c2.62,0.05,5.11,1.13,6.95,3 C19.25,12.07,19.73,12.08,20.03,11.79z" />
+</vector>
\ No newline at end of file
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index a7921b5..4399e42 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -161,8 +161,14 @@
@Override // from PerUserSystemService
@GuardedBy("mLock")
protected boolean updateLocked(boolean disabled) {
- destroyLocked();
final boolean disabledStateChanged = super.updateLocked(disabled);
+ if (disabledStateChanged) {
+ // update session content capture enabled state.
+ for (int i = 0; i < mSessions.size(); i++) {
+ mSessions.valueAt(i).setContentCaptureEnabledLocked(!disabled);
+ }
+ }
+ destroyLocked();
updateRemoteServiceLocked(disabled);
return disabledStateChanged;
}
@@ -542,7 +548,8 @@
Slog.v(TAG, "setContentCaptureWhitelist(" + (packages == null
? "null_packages" : packages.size() + " packages")
+ ", " + (activities == null
- ? "null_activities" : activities.size() + " activities") + ")");
+ ? "null_activities" : activities.size() + " activities") + ")"
+ + " for user " + mUserId);
}
mMaster.mGlobalContentCaptureOptions.setWhitelist(mUserId, packages, activities);
}
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
index d38dfd4..2643db1 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
@@ -16,6 +16,8 @@
package com.android.server.contentcapture;
import static android.service.contentcapture.ContentCaptureService.setClientState;
+import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_FALSE;
+import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_TRUE;
import static android.view.contentcapture.ContentCaptureSession.NO_SESSION_ID;
import static android.view.contentcapture.ContentCaptureSession.STATE_ACTIVE;
import static android.view.contentcapture.ContentCaptureSession.STATE_DISABLED;
@@ -24,13 +26,16 @@
import android.annotation.NonNull;
import android.content.ComponentName;
+import android.os.Bundle;
import android.os.IBinder;
+import android.os.RemoteException;
import android.service.contentcapture.ContentCaptureService;
import android.service.contentcapture.SnapshotData;
import android.util.LocalLog;
import android.util.Slog;
import android.view.contentcapture.ContentCaptureContext;
import android.view.contentcapture.ContentCaptureSessionId;
+import android.view.contentcapture.MainContentCaptureSession;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.IResultReceiver;
@@ -108,6 +113,20 @@
}
/**
+ * Changes the {@link ContentCaptureService} enabled state.
+ */
+ @GuardedBy("mLock")
+ public void setContentCaptureEnabledLocked(boolean enabled) {
+ try {
+ final Bundle extras = new Bundle();
+ extras.putBoolean(MainContentCaptureSession.EXTRA_ENABLED_STATE, true);
+ mSessionStateReceiver.send(enabled ? RESULT_CODE_TRUE : RESULT_CODE_FALSE, extras);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Error async reporting result to client: " + e);
+ }
+ }
+
+ /**
* Notifies the {@link ContentCaptureService} of a snapshot of an activity.
*/
@GuardedBy("mLock")
diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java
index 9b70272..7709311 100644
--- a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java
+++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java
@@ -28,6 +28,7 @@
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
+import android.graphics.ColorSpace;
import android.graphics.GraphicBuffer;
import android.os.Bundle;
import android.os.RemoteException;
@@ -99,11 +100,17 @@
ActivityManager.TaskSnapshot snapshot =
mActivityTaskManagerInternal.getTaskSnapshot(taskId, false);
GraphicBuffer snapshotBuffer = null;
+ int colorSpaceId = 0;
if (snapshot != null) {
snapshotBuffer = snapshot.getSnapshot();
+ ColorSpace colorSpace = snapshot.getColorSpace();
+ if (colorSpace != null) {
+ colorSpaceId = colorSpace.getId();
+ }
}
- service.provideContextImage(taskId, snapshotBuffer, imageContextRequestExtras);
+ service.provideContextImage(taskId, snapshotBuffer, colorSpaceId,
+ imageContextRequestExtras);
}
}
diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/RemoteContentSuggestionsService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/RemoteContentSuggestionsService.java
index 4b36352..a8b7b81 100644
--- a/services/contentsuggestions/java/com/android/server/contentsuggestions/RemoteContentSuggestionsService.java
+++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/RemoteContentSuggestionsService.java
@@ -68,9 +68,9 @@
}
void provideContextImage(int taskId, @Nullable GraphicBuffer contextImage,
- @NonNull Bundle imageContextRequestExtras) {
+ int colorSpaceId, @NonNull Bundle imageContextRequestExtras) {
scheduleAsyncRequest((s) -> s.provideContextImage(taskId, contextImage,
- imageContextRequestExtras));
+ colorSpaceId, imageContextRequestExtras));
}
void suggestContentSelections(
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 11ddceb..4d0d3d2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2185,24 +2185,38 @@
"hidden_api_access_statslog_sampling_rate";
public void onPropertiesChanged(DeviceConfig.Properties properties) {
- int logSampleRate = properties.getInt(HIDDEN_API_ACCESS_LOG_SAMPLING_RATE, 0x0);
- if (logSampleRate < 0 || logSampleRate > 0x10000) {
- logSampleRate = -1;
- }
- if (logSampleRate != -1 && logSampleRate != mLogSampleRate) {
+ int logSampleRate = properties.getInt(HIDDEN_API_ACCESS_LOG_SAMPLING_RATE,
+ mLogSampleRate);
+ int statslogSampleRate = properties.getInt(HIDDEN_API_ACCESS_STATSLOG_SAMPLING_RATE,
+ mStatslogSampleRate);
+ setSampleRates(logSampleRate, statslogSampleRate);
+ }
+
+ private void setSampleRates(int logSampleRate, int statslogSampleRate) {
+ if (logSampleRate >= 0 && logSampleRate <= 0x10000
+ && logSampleRate != mLogSampleRate) {
mLogSampleRate = logSampleRate;
ZYGOTE_PROCESS.setHiddenApiAccessLogSampleRate(mLogSampleRate);
}
- int statslogSampleRate =
- properties.getInt(HIDDEN_API_ACCESS_STATSLOG_SAMPLING_RATE, 0);
- if (statslogSampleRate < 0 || statslogSampleRate > 0x10000) {
- statslogSampleRate = -1;
- }
- if (statslogSampleRate != -1 && statslogSampleRate != mStatslogSampleRate) {
+ if (statslogSampleRate >= 0 && statslogSampleRate <= 0x10000
+ && statslogSampleRate != mStatslogSampleRate) {
mStatslogSampleRate = statslogSampleRate;
ZYGOTE_PROCESS.setHiddenApiAccessStatslogSampleRate(mStatslogSampleRate);
}
+
+ }
+
+ /**
+ * Set initial sampling rates from DeviceConfig. This is required after each restart,
+ * if they never get updated.
+ */
+ private void initializeSampleRates() {
+ int logSampleRate = DeviceConfig.getInt(DeviceConfig.NAMESPACE_APP_COMPAT,
+ HIDDEN_API_ACCESS_LOG_SAMPLING_RATE, 0);
+ int statslogSampleRate = DeviceConfig.getInt(DeviceConfig.NAMESPACE_APP_COMPAT,
+ HIDDEN_API_ACCESS_STATSLOG_SAMPLING_RATE, 0);
+ setSampleRates(logSampleRate, statslogSampleRate);
}
public HiddenApiSettings(Handler handler, Context context) {
@@ -2219,6 +2233,7 @@
Settings.Global.getUriFor(Settings.Global.HIDDEN_API_POLICY),
false,
this);
+ initializeSampleRates();
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_APP_COMPAT,
mContext.getMainExecutor(), this);
update();
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 043daef..7abfcea 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -38,6 +38,7 @@
import static android.os.Process.setThreadPriority;
import static android.os.Process.setThreadScheduler;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKUP;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
@@ -60,8 +61,8 @@
import android.app.ActivityManager;
import android.app.usage.UsageEvents;
import android.content.Context;
-import android.os.Binder;
import android.os.Debug;
+import android.os.Handler;
import android.os.IBinder;
import android.os.PowerManagerInternal;
import android.os.Process;
@@ -78,6 +79,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.procstats.ProcessStats;
import com.android.server.LocalServices;
+import com.android.server.ServiceThread;
import com.android.server.wm.ActivityServiceConnectionsHolder;
import com.android.server.wm.WindowProcessController;
@@ -148,6 +150,12 @@
/** Track all uids that have actively running processes. */
ActiveUids mActiveUids;
+ /**
+ * The handler to execute {@link #setProcessGroup} (it may be heavy if the process has many
+ * threads) for reducing the time spent in {@link #applyOomAdjLocked}.
+ */
+ private final Handler mProcessGroupHandler;
+
private final ArraySet<BroadcastQueue> mTmpBroadcastQueue = new ArraySet();
private final ActivityManagerService mService;
@@ -161,6 +169,28 @@
mLocalPowerManager = LocalServices.getService(PowerManagerInternal.class);
mConstants = mService.mConstants;
mAppCompact = new AppCompactor(mService);
+
+ // The process group is usually critical to the response time of foreground app, so the
+ // setter should apply it as soon as possible.
+ final ServiceThread adjusterThread = new ServiceThread(TAG, TOP_APP_PRIORITY_BOOST,
+ false /* allowIo */);
+ adjusterThread.start();
+ Process.setThreadGroupAndCpuset(adjusterThread.getThreadId(), THREAD_GROUP_TOP_APP);
+ mProcessGroupHandler = new Handler(adjusterThread.getLooper(), msg -> {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "setProcessGroup");
+ final int pid = msg.arg1;
+ final int group = msg.arg2;
+ try {
+ setProcessGroup(pid, group);
+ } catch (Exception e) {
+ if (DEBUG_ALL) {
+ Slog.w(TAG, "Failed setting process group of " + pid + " to " + group, e);
+ }
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ }
+ return true;
+ });
}
void initSettings() {
@@ -1726,9 +1756,9 @@
processGroup = THREAD_GROUP_DEFAULT;
break;
}
- long oldId = Binder.clearCallingIdentity();
+ mProcessGroupHandler.sendMessage(mProcessGroupHandler.obtainMessage(
+ 0 /* unused */, app.pid, processGroup));
try {
- setProcessGroup(app.pid, processGroup);
if (curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
// do nothing if we already switched to RT
if (oldSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
@@ -1791,13 +1821,9 @@
}
}
} catch (Exception e) {
- if (false) {
- Slog.w(TAG, "Failed setting process group of " + app.pid
- + " to " + app.getCurrentSchedulingGroup());
- Slog.w(TAG, "at location", e);
+ if (DEBUG_ALL) {
+ Slog.w(TAG, "Failed setting thread priority of " + app.pid, e);
}
- } finally {
- Binder.restoreCallingIdentity(oldId);
}
}
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index c56d8ea..d04aa89 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -3292,7 +3292,7 @@
pw.println(" Starts a given operation for a particular application.");
pw.println(" stop [--user <USER_ID>] <PACKAGE | UID> <OP> ");
pw.println(" Stops a given operation for a particular application.");
- pw.println(" set [--user <USER_ID>] <--uid PACKAGE | PACKAGE | UID> <OP> <MODE>");
+ pw.println(" set [--user <USER_ID>] <[--uid] PACKAGE | UID> <OP> <MODE>");
pw.println(" Set the mode for a particular application and operation.");
pw.println(" get [--user <USER_ID>] <PACKAGE | UID> [<OP>]");
pw.println(" Return the mode for a particular application and optional operation.");
@@ -3305,12 +3305,11 @@
pw.println(" read-settings");
pw.println(" Read the last written settings, replacing current state in RAM.");
pw.println(" options:");
- pw.println(" <PACKAGE> an Android package name.");
+ pw.println(" <PACKAGE> an Android package name or its UID if prefixed by --uid");
pw.println(" <OP> an AppOps operation.");
pw.println(" <MODE> one of allow, ignore, deny, or default");
pw.println(" <USER_ID> the user id under which the package is installed. If --user is not");
pw.println(" specified, the current user is assumed.");
- pw.println(" --uid PACKAGE refer to the UID of the package");
}
static int onShellCommand(Shell shell, String cmd) {
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
index 74b7221..1e0f205 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
@@ -63,11 +63,11 @@
*/
public abstract boolean shouldFrameworkHandleLockout();
- public AuthenticationClient(Context context, Metrics metrics,
+ public AuthenticationClient(Context context, Constants constants,
BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token,
BiometricServiceBase.ServiceListener listener, int targetUserId, int groupId, long opId,
boolean restricted, String owner, int cookie, boolean requireConfirmation) {
- super(context, metrics, daemon, halDeviceId, token, listener, targetUserId, groupId,
+ super(context, constants, daemon, halDeviceId, token, listener, targetUserId, groupId,
restricted, owner, cookie);
mOpId = opId;
mRequireConfirmation = requireConfirmation;
@@ -126,7 +126,7 @@
final BiometricServiceBase.ServiceListener listener = getListener();
- mMetricsLogger.action(mMetrics.actionBiometricAuth(), authenticated);
+ mMetricsLogger.action(mConstants.actionBiometricAuth(), authenticated);
boolean result = false;
try {
@@ -225,7 +225,7 @@
final int result = getDaemonWrapper().authenticate(mOpId, getGroupId());
if (result != 0) {
Slog.w(getLogTag(), "startAuthentication failed, result=" + result);
- mMetricsLogger.histogram(mMetrics.tagAuthStartError(), result);
+ mMetricsLogger.histogram(mConstants.tagAuthStartError(), result);
onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
return result;
diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
index 3f856d3..d3c62be 100644
--- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
+++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
@@ -138,7 +138,7 @@
/**
* @return the metrics constants for a biometric implementation.
*/
- protected abstract Metrics getMetrics();
+ protected abstract Constants getConstants();
/**
* @param userId
@@ -220,7 +220,7 @@
public AuthenticationClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
IBinder token, ServiceListener listener, int targetUserId, int groupId, long opId,
boolean restricted, String owner, int cookie, boolean requireConfirmation) {
- super(context, getMetrics(), daemon, halDeviceId, token, listener, targetUserId,
+ super(context, getConstants(), daemon, halDeviceId, token, listener, targetUserId,
groupId, opId, restricted, owner, cookie, requireConfirmation);
}
@@ -283,7 +283,7 @@
IBinder token, ServiceListener listener, int userId, int groupId,
byte[] cryptoToken, boolean restricted, String owner,
final int[] disabledFeatures) {
- super(context, getMetrics(), daemon, halDeviceId, token, listener,
+ super(context, getConstants(), daemon, halDeviceId, token, listener,
userId, groupId, cryptoToken, restricted, owner, getBiometricUtils(),
disabledFeatures);
}
@@ -302,7 +302,7 @@
DaemonWrapper daemon, long halDeviceId, IBinder token,
ServiceListener listener, int templateId, int groupId, int userId,
boolean restricted, String owner) {
- super(context, getMetrics(), daemon, halDeviceId, token, listener, templateId, groupId,
+ super(context, getConstants(), daemon, halDeviceId, token, listener, templateId, groupId,
userId, restricted, owner, getBiometricUtils());
}
@@ -329,7 +329,7 @@
ServiceListener listener, int groupId, int userId, boolean restricted,
String owner, List<? extends BiometricAuthenticator.Identifier> enrolledList,
BiometricUtils utils) {
- super(context, getMetrics(), daemon, halDeviceId, token, listener, groupId, userId,
+ super(context, getConstants(), daemon, halDeviceId, token, listener, groupId, userId,
restricted, owner);
mEnrolledList = enrolledList;
mUtils = utils;
@@ -655,7 +655,7 @@
@Override
public void serviceDied(long cookie) {
Slog.e(getTag(), "HAL died");
- mMetricsLogger.count(getMetrics().tagHalDied(), 1);
+ mMetricsLogger.count(getConstants().tagHalDied(), 1);
mHALDeathCount++;
mCurrentUserId = UserHandle.USER_NULL;
handleError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
@@ -845,7 +845,7 @@
}
mHandler.post(() -> {
- mMetricsLogger.histogram(getMetrics().tagAuthToken(), opId != 0L ? 1 : 0);
+ mMetricsLogger.histogram(getConstants().tagAuthToken(), opId != 0L ? 1 : 0);
// Get performance stats object for this user.
HashMap<Integer, PerformanceStats> pmap
diff --git a/services/core/java/com/android/server/biometrics/ClientMonitor.java b/services/core/java/com/android/server/biometrics/ClientMonitor.java
index 0065580..942e050 100644
--- a/services/core/java/com/android/server/biometrics/ClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/ClientMonitor.java
@@ -63,7 +63,7 @@
private final int mCookie;
protected final MetricsLogger mMetricsLogger;
- protected final Metrics mMetrics;
+ protected final Constants mConstants;
protected boolean mAlreadyCancelled;
protected boolean mAlreadyDone;
@@ -80,12 +80,12 @@
* permission
* @param owner name of the client that owns this
*/
- public ClientMonitor(Context context, Metrics metrics,
+ public ClientMonitor(Context context, Constants constants,
BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token,
BiometricServiceBase.ServiceListener listener, int userId, int groupId,
boolean restricted, String owner, int cookie) {
mContext = context;
- mMetrics = metrics;
+ mConstants = constants;
mDaemon = daemon;
mHalDeviceId = halDeviceId;
mToken = token;
@@ -108,7 +108,7 @@
}
protected String getLogTag() {
- return mMetrics.logTag();
+ return mConstants.logTag();
}
public int getCookie() {
@@ -145,6 +145,31 @@
public abstract boolean onEnumerationResult(
BiometricAuthenticator.Identifier identifier, int remaining);
+ public int[] getAcquireIgnorelist() {
+ return new int[0];
+ }
+ public int[] getAcquireVendorIgnorelist() {
+ return new int[0];
+ }
+
+ private boolean blacklistContains(int acquiredInfo, int vendorCode) {
+ if (acquiredInfo == mConstants.acquireVendorCode()) {
+ for (int i = 0; i < getAcquireVendorIgnorelist().length; i++) {
+ if (getAcquireVendorIgnorelist()[i] == vendorCode) {
+ if (DEBUG) Slog.v(getLogTag(), "Ignoring vendor message: " + vendorCode);
+ return true;
+ }
+ }
+ } else {
+ for (int i = 0; i < getAcquireIgnorelist().length; i++) {
+ if (getAcquireIgnorelist()[i] == acquiredInfo) {
+ if (DEBUG) Slog.v(getLogTag(), "Ignoring message: " + acquiredInfo);
+ return true;
+ }
+ }
+ }
+ return false;
+ }
public boolean isAlreadyDone() {
return mAlreadyDone;
@@ -160,7 +185,7 @@
super.logOnAcquired(mContext, acquiredInfo, vendorCode, getTargetUserId());
if (DEBUG) Slog.v(getLogTag(), "Acquired: " + acquiredInfo + " " + vendorCode);
try {
- if (mListener != null) {
+ if (mListener != null && !blacklistContains(acquiredInfo, vendorCode)) {
mListener.onAcquired(getHalDeviceId(), acquiredInfo, vendorCode);
}
return false; // acquisition continues...
diff --git a/services/core/java/com/android/server/biometrics/Metrics.java b/services/core/java/com/android/server/biometrics/Constants.java
similarity index 94%
rename from services/core/java/com/android/server/biometrics/Metrics.java
rename to services/core/java/com/android/server/biometrics/Constants.java
index 02e44e9..874fd42 100644
--- a/services/core/java/com/android/server/biometrics/Metrics.java
+++ b/services/core/java/com/android/server/biometrics/Constants.java
@@ -16,7 +16,7 @@
package com.android.server.biometrics;
-public interface Metrics {
+public interface Constants {
/** The log tag */
String logTag();
@@ -31,4 +31,6 @@
/** Integers for MetricsLogger.action() */
int actionBiometricAuth();
int actionBiometricEnroll();
+
+ int acquireVendorCode();
}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/biometrics/EnrollClient.java b/services/core/java/com/android/server/biometrics/EnrollClient.java
index e656d98..854528f 100644
--- a/services/core/java/com/android/server/biometrics/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/EnrollClient.java
@@ -40,12 +40,12 @@
public abstract boolean shouldVibrate();
- public EnrollClient(Context context, Metrics metrics,
+ public EnrollClient(Context context, Constants constants,
BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token,
BiometricServiceBase.ServiceListener listener, int userId, int groupId,
byte[] cryptoToken, boolean restricted, String owner, BiometricUtils utils,
final int[] disabledFeatures) {
- super(context, metrics, daemon, halDeviceId, token, listener, userId, groupId, restricted,
+ super(context, constants, daemon, halDeviceId, token, listener, userId, groupId, restricted,
owner, 0 /* cookie */);
mBiometricUtils = utils;
mCryptoToken = Arrays.copyOf(cryptoToken, cryptoToken.length);
@@ -78,7 +78,7 @@
if (shouldVibrate()) {
vibrateSuccess();
}
- mMetricsLogger.action(mMetrics.actionBiometricEnroll());
+ mMetricsLogger.action(mConstants.actionBiometricEnroll());
try {
final BiometricServiceBase.ServiceListener listener = getListener();
if (listener != null) {
@@ -105,7 +105,7 @@
disabledFeatures);
if (result != 0) {
Slog.w(getLogTag(), "startEnroll failed, result=" + result);
- mMetricsLogger.histogram(mMetrics.tagEnrollStartError(), result);
+ mMetricsLogger.histogram(mConstants.tagEnrollStartError(), result);
onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
return result;
diff --git a/services/core/java/com/android/server/biometrics/EnumerateClient.java b/services/core/java/com/android/server/biometrics/EnumerateClient.java
index 44ac037..f889d2b 100644
--- a/services/core/java/com/android/server/biometrics/EnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/EnumerateClient.java
@@ -30,11 +30,11 @@
* A class to keep track of the enumeration state for a given client.
*/
public abstract class EnumerateClient extends ClientMonitor {
- public EnumerateClient(Context context, Metrics metrics,
+ public EnumerateClient(Context context, Constants constants,
BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token,
BiometricServiceBase.ServiceListener listener, int groupId, int userId,
boolean restricted, String owner) {
- super(context, metrics, daemon, halDeviceId, token, listener, userId, groupId, restricted,
+ super(context, constants, daemon, halDeviceId, token, listener, userId, groupId, restricted,
owner, 0 /* cookie */);
}
@@ -55,7 +55,7 @@
if (result != 0) {
Slog.w(getLogTag(), "start enumerate for user " + getTargetUserId()
+ " failed, result=" + result);
- mMetricsLogger.histogram(mMetrics.tagEnumerateStartError(), result);
+ mMetricsLogger.histogram(mConstants.tagEnumerateStartError(), result);
onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
return result;
diff --git a/services/core/java/com/android/server/biometrics/RemovalClient.java b/services/core/java/com/android/server/biometrics/RemovalClient.java
index a18f336..bccab7b 100644
--- a/services/core/java/com/android/server/biometrics/RemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/RemovalClient.java
@@ -33,11 +33,11 @@
private final int mBiometricId;
private final BiometricUtils mBiometricUtils;
- public RemovalClient(Context context, Metrics metrics,
+ public RemovalClient(Context context, Constants constants,
BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token,
BiometricServiceBase.ServiceListener listener, int biometricId, int groupId, int userId,
boolean restricted, String owner, BiometricUtils utils) {
- super(context, metrics, daemon, halDeviceId, token, listener, userId, groupId, restricted,
+ super(context, constants, daemon, halDeviceId, token, listener, userId, groupId, restricted,
owner, 0 /* cookie */);
mBiometricId = biometricId;
mBiometricUtils = utils;
@@ -60,7 +60,7 @@
if (result != 0) {
Slog.w(getLogTag(), "startRemove with id = " + mBiometricId + " failed, result=" +
result);
- mMetricsLogger.histogram(mMetrics.tagRemoveStartError(), result);
+ mMetricsLogger.histogram(mConstants.tagRemoveStartError(), result);
onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
return result;
diff --git a/services/core/java/com/android/server/biometrics/face/FaceMetrics.java b/services/core/java/com/android/server/biometrics/face/FaceConstants.java
similarity index 86%
rename from services/core/java/com/android/server/biometrics/face/FaceMetrics.java
rename to services/core/java/com/android/server/biometrics/face/FaceConstants.java
index 1c5cd5a..143eed5 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceMetrics.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceConstants.java
@@ -16,10 +16,12 @@
package com.android.server.biometrics.face;
-import com.android.internal.logging.nano.MetricsProto;
-import com.android.server.biometrics.Metrics;
+import android.hardware.face.FaceManager;
-public class FaceMetrics implements Metrics {
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.server.biometrics.Constants;
+
+public class FaceConstants implements Constants {
@Override
public String logTag() {
return FaceService.TAG;
@@ -64,4 +66,9 @@
public int actionBiometricEnroll() {
return MetricsProto.MetricsEvent.ACTION_FACE_ENROLL;
}
+
+ @Override
+ public int acquireVendorCode() {
+ return FaceManager.FACE_ACQUIRED_VENDOR;
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
index f5a96c7..37c15a0 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -65,7 +65,7 @@
import com.android.server.biometrics.BiometricServiceBase;
import com.android.server.biometrics.BiometricUtils;
import com.android.server.biometrics.EnumerateClient;
-import com.android.server.biometrics.Metrics;
+import com.android.server.biometrics.Constants;
import com.android.server.biometrics.RemovalClient;
import org.json.JSONArray;
@@ -131,6 +131,26 @@
}
@Override
+ public int[] getAcquireIgnorelist() {
+ if (isBiometricPrompt()) {
+ return mBiometricPromptIgnoreList;
+ } else {
+ // Keyguard
+ return mKeyguardIgnoreList;
+ }
+ }
+
+ @Override
+ public int[] getAcquireVendorIgnorelist() {
+ if (isBiometricPrompt()) {
+ return mBiometricPromptIgnoreListVendor;
+ } else {
+ // Keyguard
+ return mKeyguardIgnoreListVendor;
+ }
+ }
+
+ @Override
public boolean onAcquired(int acquireInfo, int vendorCode) {
if (acquireInfo == FaceManager.FACE_ACQUIRED_RECALIBRATE) {
@@ -205,6 +225,17 @@
final EnrollClientImpl client = new EnrollClientImpl(getContext(), mDaemonWrapper,
mHalDeviceId, token, new ServiceListenerImpl(receiver), mCurrentUserId,
0 /* groupId */, cryptoToken, restricted, opPackageName, disabledFeatures) {
+
+ @Override
+ public int[] getAcquireIgnorelist() {
+ return mEnrollIgnoreList;
+ }
+
+ @Override
+ public int[] getAcquireVendorIgnorelist() {
+ return mEnrollIgnoreListVendor;
+ }
+
@Override
public boolean shouldVibrate() {
return false;
@@ -293,7 +324,7 @@
}
final boolean restricted = isRestricted();
- final RemovalClient client = new RemovalClient(getContext(), getMetrics(),
+ final RemovalClient client = new RemovalClient(getContext(), getConstants(),
mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), faceId,
0 /* groupId */, userId, restricted, token.toString(), getBiometricUtils()) {
@Override
@@ -310,7 +341,7 @@
checkPermission(MANAGE_BIOMETRIC);
final boolean restricted = isRestricted();
- final EnumerateClient client = new EnumerateClient(getContext(), getMetrics(),
+ final EnumerateClient client = new EnumerateClient(getContext(), getConstants(),
mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), userId,
userId, restricted, getContext().getOpPackageName()) {
@Override
@@ -638,13 +669,20 @@
}
}
- private final FaceMetrics mFaceMetrics = new FaceMetrics();
+ private final FaceConstants mFaceConstants = new FaceConstants();
@GuardedBy("this")
private IBiometricsFace mDaemon;
// One of the AuthenticationClient constants
private int mCurrentUserLockoutMode;
+ private int[] mBiometricPromptIgnoreList;
+ private int[] mBiometricPromptIgnoreListVendor;
+ private int[] mKeyguardIgnoreList;
+ private int[] mKeyguardIgnoreListVendor;
+ private int[] mEnrollIgnoreList;
+ private int[] mEnrollIgnoreListVendor;
+
/**
* Receives callbacks from the HAL.
*/
@@ -830,6 +868,19 @@
public FaceService(Context context) {
super(context);
+
+ mBiometricPromptIgnoreList = getContext().getResources()
+ .getIntArray(R.array.config_face_acquire_biometricprompt_ignorelist);
+ mBiometricPromptIgnoreListVendor = getContext().getResources()
+ .getIntArray(R.array.config_face_acquire_vendor_biometricprompt_ignorelist);
+ mKeyguardIgnoreList = getContext().getResources()
+ .getIntArray(R.array.config_face_acquire_keyguard_ignorelist);
+ mKeyguardIgnoreListVendor = getContext().getResources()
+ .getIntArray(R.array.config_face_acquire_vendor_keyguard_ignorelist);
+ mEnrollIgnoreList = getContext().getResources()
+ .getIntArray(R.array.config_face_acquire_enroll_ignorelist);
+ mEnrollIgnoreListVendor = getContext().getResources()
+ .getIntArray(R.array.config_face_acquire_vendor_enroll_ignorelist);
}
@Override
@@ -855,8 +906,8 @@
}
@Override
- protected Metrics getMetrics() {
- return mFaceMetrics;
+ protected Constants getConstants() {
+ return mFaceConstants;
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintMetrics.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintConstants.java
similarity index 85%
rename from services/core/java/com/android/server/biometrics/fingerprint/FingerprintMetrics.java
rename to services/core/java/com/android/server/biometrics/fingerprint/FingerprintConstants.java
index a1115c8..bdaff71 100644
--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintMetrics.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintConstants.java
@@ -16,10 +16,12 @@
package com.android.server.biometrics.fingerprint;
-import com.android.internal.logging.nano.MetricsProto;
-import com.android.server.biometrics.Metrics;
+import android.hardware.fingerprint.FingerprintManager;
-public class FingerprintMetrics implements Metrics {
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.server.biometrics.Constants;
+
+public class FingerprintConstants implements Constants {
@Override
public String logTag() {
@@ -65,4 +67,9 @@
public int actionBiometricEnroll() {
return MetricsProto.MetricsEvent.ACTION_FINGERPRINT_ENROLL;
}
+
+ @Override
+ public int acquireVendorCode() {
+ return FingerprintManager.FINGERPRINT_ACQUIRED_VENDOR;
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
index 6ebeaf9..d91670d 100644
--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
@@ -68,7 +68,7 @@
import com.android.server.biometrics.BiometricUtils;
import com.android.server.biometrics.ClientMonitor;
import com.android.server.biometrics.EnumerateClient;
-import com.android.server.biometrics.Metrics;
+import com.android.server.biometrics.Constants;
import com.android.server.biometrics.RemovalClient;
import org.json.JSONArray;
@@ -284,7 +284,7 @@
}
final boolean restricted = isRestricted();
- final RemovalClient client = new RemovalClient(getContext(), getMetrics(),
+ final RemovalClient client = new RemovalClient(getContext(), getConstants(),
mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
fingerId, groupId, userId, restricted, token.toString(), getBiometricUtils()) {
@Override
@@ -301,7 +301,7 @@
checkPermission(MANAGE_FINGERPRINT);
final boolean restricted = isRestricted();
- final EnumerateClient client = new EnumerateClient(getContext(), getMetrics(),
+ final EnumerateClient client = new EnumerateClient(getContext(), getConstants(),
mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), userId,
userId, restricted, getContext().getOpPackageName()) {
@Override
@@ -557,7 +557,7 @@
}
}
- private final FingerprintMetrics mFingerprintMetrics = new FingerprintMetrics();
+ private final FingerprintConstants mFingerprintConstants = new FingerprintConstants();
private final CopyOnWriteArrayList<IFingerprintClientActiveCallback> mClientActiveCallbacks =
new CopyOnWriteArrayList<>();
@@ -736,8 +736,8 @@
}
@Override
- protected Metrics getMetrics() {
- return mFingerprintMetrics;
+ protected Constants getConstants() {
+ return mFingerprintConstants;
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/iris/IrisService.java b/services/core/java/com/android/server/biometrics/iris/IrisService.java
index cb8a772..2817315 100644
--- a/services/core/java/com/android/server/biometrics/iris/IrisService.java
+++ b/services/core/java/com/android/server/biometrics/iris/IrisService.java
@@ -23,7 +23,7 @@
import com.android.server.biometrics.AuthenticationClient;
import com.android.server.biometrics.BiometricServiceBase;
import com.android.server.biometrics.BiometricUtils;
-import com.android.server.biometrics.Metrics;
+import com.android.server.biometrics.Constants;
import java.util.List;
@@ -75,7 +75,7 @@
}
@Override
- protected Metrics getMetrics() {
+ protected Constants getConstants() {
return null;
}
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index ba4dcdb..998ee1e 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -1104,11 +1104,13 @@
@Override
public void addStatusChangeListener(int mask, ISyncStatusObserver callback) {
+ final int callingUid = Binder.getCallingUid();
long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
if (syncManager != null && callback != null) {
- syncManager.getSyncStorageEngine().addStatusChangeListener(mask, callback);
+ syncManager.getSyncStorageEngine().addStatusChangeListener(
+ mask, UserHandle.getUserId(callingUid), callback);
}
} finally {
restoreCallingIdentity(identityToken);
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 9f80a83..7e79a12 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -3375,7 +3375,8 @@
}
scheduleSyncOperationH(op);
- mSyncStorageEngine.reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
+ mSyncStorageEngine.reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS,
+ target.userId);
}
/**
@@ -3877,7 +3878,8 @@
EventLog.writeEvent(2720,
syncOperation.toEventLog(SyncStorageEngine.EVENT_STOP));
mSyncStorageEngine.stopSyncEvent(rowId, elapsedTime,
- resultMessage, downstreamActivity, upstreamActivity);
+ resultMessage, downstreamActivity, upstreamActivity,
+ syncOperation.target.userId);
}
}
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index 6b441a0..c7a3f4b 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -43,6 +43,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.EventLog;
import android.util.Log;
@@ -54,6 +55,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.IntPair;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -588,9 +590,10 @@
return mSyncRandomOffset;
}
- public void addStatusChangeListener(int mask, ISyncStatusObserver callback) {
+ public void addStatusChangeListener(int mask, int userId, ISyncStatusObserver callback) {
synchronized (mAuthorities) {
- mChangeListeners.register(callback, mask);
+ final long cookie = IntPair.of(userId, mask);
+ mChangeListeners.register(callback, cookie);
}
}
@@ -622,14 +625,16 @@
}
}
- void reportChange(int which) {
+ void reportChange(int which, int callingUserId) {
ArrayList<ISyncStatusObserver> reports = null;
synchronized (mAuthorities) {
int i = mChangeListeners.beginBroadcast();
while (i > 0) {
i--;
- Integer mask = (Integer)mChangeListeners.getBroadcastCookie(i);
- if ((which & mask.intValue()) == 0) {
+ final long cookie = (long) mChangeListeners.getBroadcastCookie(i);
+ final int userId = IntPair.first(cookie);
+ final int mask = IntPair.second(cookie);
+ if ((which & mask) == 0 || callingUserId != userId) {
continue;
}
if (reports == null) {
@@ -719,7 +724,7 @@
new Bundle(),
syncExemptionFlag, callingUid, callingPid);
}
- reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
+ reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, userId);
queueBackup();
}
@@ -787,7 +792,7 @@
requestSync(aInfo, SyncOperation.REASON_IS_SYNCABLE, new Bundle(),
ContentResolver.SYNC_EXEMPTION_NONE, callingUid, callingPid);
}
- reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
+ reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, target.userId);
}
public Pair<Long, Long> getBackoff(EndPoint info) {
@@ -833,7 +838,7 @@
}
}
if (changed) {
- reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
+ reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, info.userId);
}
}
@@ -871,7 +876,7 @@
}
public void clearAllBackoffsLocked() {
- boolean changed = false;
+ final ArraySet<Integer> changedUserIds = new ArraySet<>();
synchronized (mAuthorities) {
// Clear backoff for all sync adapters.
for (AccountInfo accountInfo : mAccounts.values()) {
@@ -888,14 +893,14 @@
}
authorityInfo.backoffTime = NOT_IN_BACKOFF_MODE;
authorityInfo.backoffDelay = NOT_IN_BACKOFF_MODE;
- changed = true;
+ changedUserIds.add(accountInfo.accountAndUser.userId);
}
}
}
}
- if (changed) {
- reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
+ for (int i = changedUserIds.size() - 1; i > 0; i--) {
+ reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, changedUserIds.valueAt(i));
}
}
@@ -921,7 +926,7 @@
}
authority.delayUntil = delayUntil;
}
- reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
+ reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, info.userId);
}
/**
@@ -964,7 +969,7 @@
new Bundle(),
syncExemptionFlag, callingUid, callingPid);
}
- reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
+ reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, userId);
mContext.sendBroadcast(ContentResolver.ACTION_SYNC_CONN_STATUS_CHANGED);
queueBackup();
}
@@ -1015,7 +1020,7 @@
SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
status.pending = pendingValue;
}
- reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING);
+ reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING, info.userId);
}
/**
@@ -1103,7 +1108,7 @@
activeSyncContext.mStartTime);
getCurrentSyncs(authorityInfo.target.userId).add(syncInfo);
}
- reportActiveChange();
+ reportActiveChange(activeSyncContext.mSyncOperation.target.userId);
return syncInfo;
}
@@ -1120,14 +1125,14 @@
getCurrentSyncs(userId).remove(syncInfo);
}
- reportActiveChange();
+ reportActiveChange(userId);
}
/**
* To allow others to send active change reports, to poke clients.
*/
- public void reportActiveChange() {
- reportChange(ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE);
+ public void reportActiveChange(int userId) {
+ reportChange(ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE, userId);
}
/**
@@ -1162,12 +1167,12 @@
if (Log.isLoggable(TAG, Log.VERBOSE)) Slog.v(TAG, "returning historyId " + id);
}
- reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS);
+ reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS, op.target.userId);
return id;
}
public void stopSyncEvent(long historyId, long elapsedTime, String resultMessage,
- long downstreamActivity, long upstreamActivity) {
+ long downstreamActivity, long upstreamActivity, int userId) {
synchronized (mAuthorities) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Slog.v(TAG, "stopSyncEvent: historyId=" + historyId);
@@ -1307,7 +1312,7 @@
}
}
- reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS);
+ reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS, userId);
}
/**
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 55191db..cdcb641 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -26,6 +26,7 @@
import static android.app.NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED;
import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED;
import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED;
+import static android.app.NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_MIN;
import static android.app.NotificationManager.IMPORTANCE_NONE;
@@ -111,6 +112,7 @@
import android.app.NotificationManager.Policy;
import android.app.PendingIntent;
import android.app.Person;
+import android.app.RemoteInput;
import android.app.StatusBarManager;
import android.app.UriGrantsManager;
import android.app.admin.DeviceAdminInfo;
@@ -3754,7 +3756,7 @@
pkg, userId, true, granted);
getContext().sendBroadcastAsUser(new Intent(
- NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
+ ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
.setPackage(pkg)
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT),
UserHandle.of(userId), null);
@@ -3914,7 +3916,7 @@
userId, true, granted);
getContext().sendBroadcastAsUser(new Intent(
- NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
+ ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
.setPackage(listener.getPackageName())
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
UserHandle.of(userId), null);
@@ -3930,7 +3932,9 @@
public void setNotificationAssistantAccessGrantedForUser(ComponentName assistant,
int userId, boolean granted) {
checkCallerIsSystemOrSystemUiOrShell();
- mAssistants.setUserSet(userId, true);
+ for (UserInfo ui : mUm.getEnabledProfiles(userId)) {
+ mAssistants.setUserSet(ui.id, true);
+ }
final long identity = Binder.clearCallingIdentity();
try {
setNotificationAssistantAccessGrantedForUserInternal(assistant, userId, granted);
@@ -4144,30 +4148,36 @@
@VisibleForTesting
protected void setNotificationAssistantAccessGrantedForUserInternal(
- ComponentName assistant, int userId, boolean granted) {
- if (assistant == null) {
- ComponentName allowedAssistant = CollectionUtils.firstOrNull(
- mAssistants.getAllowedComponents(userId));
- if (allowedAssistant != null) {
- setNotificationAssistantAccessGrantedForUserInternal(
- allowedAssistant, userId, false);
+ ComponentName assistant, int baseUserId, boolean granted) {
+ List<UserInfo> users = mUm.getEnabledProfiles(baseUserId);
+ if (users != null) {
+ for (UserInfo user : users) {
+ int userId = user.id;
+ if (assistant == null) {
+ ComponentName allowedAssistant = CollectionUtils.firstOrNull(
+ mAssistants.getAllowedComponents(userId));
+ if (allowedAssistant != null) {
+ setNotificationAssistantAccessGrantedForUserInternal(
+ allowedAssistant, userId, false);
+ }
+ continue;
+ }
+ if (!granted || mAllowedManagedServicePackages.test(assistant.getPackageName(),
+ userId, mAssistants.getRequiredPermission())) {
+ mConditionProviders.setPackageOrComponentEnabled(assistant.flattenToString(),
+ userId, false, granted);
+ mAssistants.setPackageOrComponentEnabled(assistant.flattenToString(),
+ userId, true, granted);
+
+ getContext().sendBroadcastAsUser(
+ new Intent(ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
+ .setPackage(assistant.getPackageName())
+ .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
+ UserHandle.of(userId), null);
+
+ handleSavePolicyFile();
+ }
}
- return;
- }
- if (!granted || mAllowedManagedServicePackages.test(assistant.getPackageName(), userId,
- mAssistants.getRequiredPermission())) {
- mConditionProviders.setPackageOrComponentEnabled(assistant.flattenToString(),
- userId, false, granted);
- mAssistants.setPackageOrComponentEnabled(assistant.flattenToString(),
- userId, true, granted);
-
- getContext().sendBroadcastAsUser(new Intent(
- NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
- .setPackage(assistant.getPackageName())
- .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
- UserHandle.of(userId), null);
-
- handleSavePolicyFile();
}
}
@@ -4834,17 +4844,35 @@
: null;
boolean isForegroundCall = CATEGORY_CALL.equals(notification.category)
&& (notification.flags & FLAG_FOREGROUND_SERVICE) != 0;
- // OR message style (which always has a person)
+ // OR message style (which always has a person) with any remote input
Class<? extends Notification.Style> style = notification.getNotificationStyle();
boolean isMessageStyle = Notification.MessagingStyle.class.equals(style);
- boolean notificationAppropriateToBubble = isMessageStyle
+ boolean notificationAppropriateToBubble =
+ (isMessageStyle && hasValidRemoteInput(notification))
|| (peopleList != null && !peopleList.isEmpty() && isForegroundCall);
+
// OR something that was previously a bubble & still exists
boolean bubbleUpdate = oldRecord != null
&& (oldRecord.getNotification().flags & FLAG_BUBBLE) != 0;
return canBubble && (notificationAppropriateToBubble || appIsForeground || bubbleUpdate);
}
+ private boolean hasValidRemoteInput(Notification n) {
+ // Also check for inline reply
+ Notification.Action[] actions = n.actions;
+ if (actions != null) {
+ // Get the remote inputs
+ for (int i = 0; i < actions.length; i++) {
+ Notification.Action action = actions[i];
+ RemoteInput[] inputs = action.getRemoteInputs();
+ if (inputs != null && inputs.length > 0) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
private void doChannelWarningToast(CharSequence toastText) {
Binder.withCleanCallingIdentity(() -> {
final int defaultWarningEnabled = Build.IS_DEBUGGABLE ? 1 : 0;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c0f09d2..6f8439b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2923,28 +2923,50 @@
// Remove disable package settings for updated system apps that were
// removed via an OTA. If the update is no longer present, remove the
// app completely. Otherwise, revoke their system privileges.
- for (String deletedAppName : possiblyDeletedUpdatedSystemApps) {
- PackageParser.Package deletedPkg = mPackages.get(deletedAppName);
- mSettings.removeDisabledSystemPackageLPw(deletedAppName);
+ for (int i = possiblyDeletedUpdatedSystemApps.size() - 1; i >= 0; --i) {
+ final String packageName = possiblyDeletedUpdatedSystemApps.get(i);
+ final PackageParser.Package pkg = mPackages.get(packageName);
final String msg;
- if (deletedPkg == null) {
+
+ // remove from the disabled system list; do this first so any future
+ // scans of this package are performed without this state
+ mSettings.removeDisabledSystemPackageLPw(packageName);
+
+ if (pkg == null) {
// should have found an update, but, we didn't; remove everything
- msg = "Updated system package " + deletedAppName
+ msg = "Updated system package " + packageName
+ " no longer exists; removing its data";
// Actual deletion of code and data will be handled by later
// reconciliation step
} else {
// found an update; revoke system privileges
- msg = "Updated system package + " + deletedAppName
- + " no longer exists; revoking system privileges";
+ msg = "Updated system package " + packageName
+ + " no longer exists; rescanning package on data";
- // Don't do anything if a stub is removed from the system image. If
- // we were to remove the uncompressed version from the /data partition,
- // this is where it'd be done.
+ // NOTE: We don't do anything special if a stub is removed from the
+ // system image. But, if we were [like removing the uncompressed
+ // version from the /data partition], this is where it'd be done.
- final PackageSetting deletedPs = mSettings.mPackages.get(deletedAppName);
- deletedPkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;
- deletedPs.pkgFlags &= ~ApplicationInfo.FLAG_SYSTEM;
+ // remove the package from the system and re-scan it without any
+ // special privileges
+ removePackageLI(pkg, true);
+ try {
+ final File codePath = new File(pkg.applicationInfo.getCodePath());
+ scanPackageTracedLI(codePath, 0, scanFlags, 0, null);
+ } catch (PackageManagerException e) {
+ Slog.e(TAG, "Failed to parse updated, ex-system package: "
+ + e.getMessage());
+ }
+ }
+
+ // one final check. if we still have a package setting [ie. it was
+ // previously scanned and known to the system], but, we don't have
+ // a package [ie. there was an error scanning it from the /data
+ // partition], completely remove the package data.
+ final PackageSetting ps = mSettings.mPackages.get(packageName);
+ if (ps != null && mPackages.get(packageName) == null) {
+ removePackageDataLIF(ps, null, null, 0, false);
+
}
logCriticalInfo(Log.WARN, msg);
}
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 803ab2d..bb9f674 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -44,13 +44,11 @@
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.storage.IStorageManager;
import android.util.Slog;
import android.util.SparseArray;
import android.util.apk.ApkSignatureVerifier;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.content.PackageHelper;
import com.android.internal.os.BackgroundThread;
import java.io.File;
@@ -255,21 +253,6 @@
}
}
- // Make sure we start a filesystem checkpoint on the next boot.
- try {
- IStorageManager storageManager = PackageHelper.getStorageManager();
- if (storageManager.supportsCheckpoint()) {
- storageManager.startCheckpoint(1 /* numRetries */);
- }
- } catch (Exception e) { // TODO(b/130190815) make a RemoteException again
- // While StorageManager lives in the same process, the native implementation
- // it calls through lives in 'vold'; so, this call can fail if 'vold' isn't
- // reachable.
- // Since we can live without filesystem checkpointing, just warn in this case
- // and continue.
- Slog.w(TAG, "Could not start filesystem checkpoint:", e);
- }
-
session.setStagedSessionReady();
if (sessionContainsApex(session)
&& !mApexManager.markStagedSessionReady(session.sessionId)) {
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index db2c742..1682858 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -649,8 +649,7 @@
// hasn't actually been updated.
onPackageReplaced(apexPackageName);
}
-
- mPackageHealthObserver.onBootCompleted();
+ mPackageHealthObserver.onBootCompletedAsync();
});
}
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index 748a661..bcef66c 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -32,6 +32,7 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.os.PowerManager;
+import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.Slog;
import android.util.StatsLog;
@@ -49,9 +50,12 @@
import java.io.PrintWriter;
import java.util.Collections;
import java.util.List;
+import java.util.concurrent.TimeUnit;
/**
- * {@code PackageHealthObserver} for {@code RollbackManagerService}.
+ * {@link PackageHealthObserver} for {@link RollbackManagerService}.
+ * This class monitors crashes and triggers RollbackManager rollback accordingly.
+ * It also monitors native crashes for some short while after boot.
*
* @hide
*/
@@ -59,12 +63,21 @@
private static final String TAG = "RollbackPackageHealthObserver";
private static final String NAME = "rollback-observer";
private static final int INVALID_ROLLBACK_ID = -1;
+ // TODO: make the following values configurable via DeviceConfig
+ private static final long NATIVE_CRASH_POLLING_INTERVAL_MILLIS =
+ TimeUnit.SECONDS.toMillis(30);
+ private static final long NUMBER_OF_NATIVE_CRASH_POLLS = 10;
+
private final Context mContext;
private final Handler mHandler;
private final File mLastStagedRollbackIdFile;
+ // this field is initialized in the c'tor and then only accessed from mHandler thread, so
+ // no need to guard with a lock
+ private long mNumberOfNativeCrashPollsRemaining;
RollbackPackageHealthObserver(Context context) {
mContext = context;
+ mNumberOfNativeCrashPollsRemaining = NUMBER_OF_NATIVE_CRASH_POLLS;
HandlerThread handlerThread = new HandlerThread("RollbackPackageHealthObserver");
handlerThread.start();
mHandler = handlerThread.getThreadHandler();
@@ -76,8 +89,6 @@
@Override
public int onHealthCheckFailed(VersionedPackage failedPackage) {
- VersionedPackage moduleMetadataPackage = getModuleMetadataPackage();
-
if (getAvailableRollback(mContext.getSystemService(RollbackManager.class), failedPackage)
== null) {
// Don't handle the notification, no rollbacks available for the package
@@ -145,16 +156,29 @@
PackageWatchdog.getInstance(mContext).startObservingHealth(this, packages, durationMs);
}
- /** Verifies the rollback state after a reboot. */
- public void onBootCompleted() {
+ /** Verifies the rollback state after a reboot and schedules polling for sometime after reboot
+ * to check for native crashes and mitigate them if needed.
+ */
+ public void onBootCompletedAsync() {
+ mHandler.post(()->onBootCompleted());
+ }
+
+ private void onBootCompleted() {
+ RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
+ PackageInstaller packageInstaller = mContext.getPackageManager().getPackageInstaller();
+ String moduleMetadataPackageName = getModuleMetadataPackageName();
+ VersionedPackage newModuleMetadataPackage = getModuleMetadataPackage();
+
+ if (getAvailableRollback(rollbackManager, newModuleMetadataPackage) != null) {
+ scheduleCheckAndMitigateNativeCrashes();
+ }
+
int rollbackId = popLastStagedRollbackId();
if (rollbackId == INVALID_ROLLBACK_ID) {
// No staged rollback before reboot
return;
}
- RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
- PackageInstaller packageInstaller = mContext.getPackageManager().getPackageInstaller();
RollbackInfo rollback = null;
for (RollbackInfo info : rollbackManager.getRecentlyCommittedRollbacks()) {
if (rollbackId == info.getRollbackId()) {
@@ -168,14 +192,12 @@
return;
}
- String moduleMetadataPackageName = getModuleMetadataPackageName();
-
// Use the version of the metadata package that was installed before
// we rolled back for logging purposes.
- VersionedPackage moduleMetadataPackage = null;
+ VersionedPackage oldModuleMetadataPackage = null;
for (PackageRollbackInfo packageRollback : rollback.getPackages()) {
if (packageRollback.getPackageName().equals(moduleMetadataPackageName)) {
- moduleMetadataPackage = packageRollback.getVersionRolledBackFrom();
+ oldModuleMetadataPackage = packageRollback.getVersionRolledBackFrom();
break;
}
}
@@ -187,12 +209,12 @@
return;
}
if (sessionInfo.isStagedSessionApplied()) {
- logEvent(moduleMetadataPackage,
+ logEvent(oldModuleMetadataPackage,
StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS);
} else if (sessionInfo.isStagedSessionReady()) {
// TODO: What do for staged session ready but not applied
} else {
- logEvent(moduleMetadataPackage,
+ logEvent(oldModuleMetadataPackage,
StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE);
}
}
@@ -320,4 +342,34 @@
moduleMetadataPackage.getPackageName(), moduleMetadataPackage.getVersionCode());
}
}
+
+ /**
+ * This method should be only called on mHandler thread, since it modifies
+ * {@link #mNumberOfNativeCrashPollsRemaining} and we want to keep this class lock free.
+ */
+ private void checkAndMitigateNativeCrashes() {
+ mNumberOfNativeCrashPollsRemaining--;
+ // Check if native watchdog reported a crash
+ if ("1".equals(SystemProperties.get("ro.init.updatable_crashing"))) {
+ execute(getModuleMetadataPackage());
+ // we stop polling after an attempt to execute rollback, regardless of whether the
+ // attempt succeeds or not
+ } else {
+ if (mNumberOfNativeCrashPollsRemaining > 0) {
+ mHandler.postDelayed(() -> checkAndMitigateNativeCrashes(),
+ NATIVE_CRASH_POLLING_INTERVAL_MILLIS);
+ }
+ }
+ }
+
+ /**
+ * Since this method can eventually trigger a RollbackManager rollback, it should be called
+ * only once boot has completed {@code onBootCompleted} and not earlier, because the install
+ * session must be entirely completed before we try to rollback.
+ */
+ private void scheduleCheckAndMitigateNativeCrashes() {
+ Slog.i(TAG, "Scheduling " + mNumberOfNativeCrashPollsRemaining + " polls to check "
+ + "and mitigate native crashes");
+ mHandler.post(()->checkAndMitigateNativeCrashes());
+ }
}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 6ce42ec..b6a5be8 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -1108,6 +1108,7 @@
// the window manager is still looking for where to put it.
// We will do the work when we get a focus change callback.
// TODO(b/112273690): Support multiple displays
+ // TODO(b/129098348): Support embedded displays
if (mService.getDefaultDisplayContentLocked().mCurrentFocus == null) {
return;
}
@@ -1400,7 +1401,28 @@
if (w.isVisibleLw()) {
outWindows.put(mTempLayer++, w);
}
- }, false /* traverseTopToBottom */ );
+ }, false /* traverseTopToBottom */);
+ mService.mRoot.forAllWindows(w -> {
+ final WindowState win = findRootDisplayParentWindow(w);
+ if (win != null && win.getDisplayContent().isDefaultDisplay && w.isVisibleLw()) {
+ // TODO(b/129098348): insert windows on child displays into outWindows based on
+ // root-display-parent window.
+ outWindows.put(mTempLayer++, w);
+ }
+ }, false /* traverseTopToBottom */);
+ }
+
+ private WindowState findRootDisplayParentWindow(WindowState win) {
+ WindowState displayParentWindow = win.getDisplayContent().getParentWindow();
+ if (displayParentWindow == null) {
+ return null;
+ }
+ WindowState candidate = displayParentWindow;
+ while (candidate != null) {
+ displayParentWindow = candidate;
+ candidate = displayParentWindow.getDisplayContent().getParentWindow();
+ }
+ return displayParentWindow;
}
private class MyHandler extends Handler {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 4278860..802683a 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -916,8 +916,12 @@
}
}
+ static boolean isResolverActivity(String className) {
+ return ResolverActivity.class.getName().equals(className);
+ }
+
boolean isResolverActivity() {
- return ResolverActivity.class.getName().equals(mActivityComponent.getClassName());
+ return isResolverActivity(mActivityComponent.getClassName());
}
boolean isResolverOrChildActivity() {
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 7eac07c..919141c 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -171,7 +171,12 @@
void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason, int displayId) {
final ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
- options.setLaunchActivityType(ACTIVITY_TYPE_HOME);
+ if (!ActivityRecord.isResolverActivity(aInfo.name)) {
+ // The resolver activity shouldn't be put in home stack because when the foreground is
+ // standard type activity, the resolver activity should be put on the top of current
+ // foreground instead of bring home stack to front.
+ options.setLaunchActivityType(ACTIVITY_TYPE_HOME);
+ }
options.setLaunchDisplayId(displayId);
mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason)
.setOutActivity(tmpOutRecord)
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 4ef8753..b6840fa 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1038,6 +1038,12 @@
}
}
}
+ // don't abort if the callingUid has SYSTEM_ALERT_WINDOW permission
+ if (mService.hasSystemAlertWindowPermission(callingUid, callingPid, callingPackage)) {
+ Slog.w(TAG, "Background activity start for " + callingPackage
+ + " allowed because SYSTEM_ALERT_WINDOW permission is granted.");
+ return false;
+ }
// anything that has fallen through would currently be aborted
Slog.w(TAG, "Background activity start [callingPackage: " + callingPackage
+ "; callingUid: " + callingUid
@@ -2075,8 +2081,8 @@
final ActivityRecord curTop = (focusStack == null)
? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop);
final TaskRecord topTask = curTop != null ? curTop.getTaskRecord() : null;
- differentTopTask = topTask != null
- && (topTask != intentActivity.getTaskRecord() || topTask != focusStack.topTask());
+ differentTopTask = topTask != intentActivity.getTaskRecord()
+ || (focusStack != null && topTask != focusStack.topTask());
} else {
// The existing task should always be different from those in other displays.
differentTopTask = true;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 7bc9600..4a6aa33 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -108,12 +108,10 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_VISIBILITY;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_ACTIVITY_ID;
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT;
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA;
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
-import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_TASK_ID;
import static com.android.server.wm.ActivityTaskManagerService.H.REPORT_TIME_TRACKER_MSG;
import static com.android.server.wm.ActivityTaskManagerService.UiHandler.DISMISS_DIALOG_UI_MSG;
import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
@@ -136,6 +134,7 @@
import android.app.ActivityThread;
import android.app.AlertDialog;
import android.app.AppGlobals;
+import android.app.AppOpsManager;
import android.app.Dialog;
import android.app.IActivityController;
import android.app.IActivityTaskManager;
@@ -216,7 +215,6 @@
import android.util.ArrayMap;
import android.util.EventLog;
import android.util.Log;
-import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
@@ -875,6 +873,16 @@
return getUserManager().hasUserRestriction(restriction, userId);
}
+ boolean hasSystemAlertWindowPermission(int callingUid, int callingPid, String callingPackage) {
+ final int mode = getAppOpsService().noteOperation(AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
+ callingUid, callingPackage);
+ if (mode == AppOpsManager.MODE_DEFAULT) {
+ return checkPermission(Manifest.permission.SYSTEM_ALERT_WINDOW, callingPid, callingUid)
+ == PERMISSION_GRANTED;
+ }
+ return mode == AppOpsManager.MODE_ALLOWED;
+ }
+
protected RecentTasks createRecentTasks() {
return new RecentTasks(this, mStackSupervisor);
}
diff --git a/services/core/java/com/android/server/wm/AppWindowThumbnail.java b/services/core/java/com/android/server/wm/AppWindowThumbnail.java
index 6318486..4d972dca 100644
--- a/services/core/java/com/android/server/wm/AppWindowThumbnail.java
+++ b/services/core/java/com/android/server/wm/AppWindowThumbnail.java
@@ -129,7 +129,7 @@
mSurfaceAnimator.startAnimation(t, new LocalAnimationAdapter(
new WindowAnimationSpec(anim, position,
mAppToken.getDisplayContent().mAppTransition.canSkipFirstFrame(),
- mAppToken.getWindowCornerRadiusForAnimation()),
+ mAppToken.getDisplayContent().getWindowCornerRadius()),
mAppToken.mWmService.mSurfaceAnimationRunner), false /* hidden */);
}
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index a3cef7f..81d6898 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -78,6 +78,7 @@
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
import static com.android.server.wm.WindowManagerService.logWithStack;
+import static com.android.server.wm.WindowState.LEGACY_POLICY_VISIBILITY;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
@@ -638,8 +639,8 @@
// If we are being set visible, and the starting window is not yet displayed,
// then make sure it doesn't get displayed.
if (startingWindow != null && !startingWindow.isDrawnLw()) {
- startingWindow.mPolicyVisibility = false;
- startingWindow.mPolicyVisibilityAfterAnim = false;
+ startingWindow.clearPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY);
+ startingWindow.mLegacyPolicyVisibilityAfterAnim = false;
}
// We are becoming visible, so better freeze the screen with the windows that are
@@ -1932,7 +1933,7 @@
+ ", isAnimationSet=" + isSelfAnimating());
if (!w.isDrawnLw()) {
Slog.v(TAG, "Not displayed: s=" + winAnimator.mSurfaceController
- + " pv=" + w.mPolicyVisibility
+ + " pv=" + w.isVisibleByPolicy()
+ " mDrawState=" + winAnimator.drawStateToString()
+ " ph=" + w.isParentWindowHidden() + " th=" + hiddenRequested
+ " a=" + isSelfAnimating());
@@ -2557,7 +2558,7 @@
getDisplayContent().mAppTransition.canSkipFirstFrame(),
appStackClipMode,
true /* isAppAnimation */,
- getWindowCornerRadiusForAnimation()),
+ getDisplayContent().getWindowCornerRadius()),
mWmService.mSurfaceAnimationRunner);
if (a.getZAdjustment() == Animation.ZORDER_TOP) {
mNeedsZBoost = true;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 41292d2..6a5c84a 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -144,6 +144,7 @@
import android.graphics.Bitmap;
import android.graphics.Insets;
import android.graphics.Matrix;
+import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
@@ -541,6 +542,10 @@
private final InsetsStateController mInsetsStateController;
+ /** @see #getParentWindow() */
+ private WindowState mParentWindow;
+
+ private Point mLocationInParentWindow = new Point();
private SurfaceControl mParentSurfaceControl;
private InputWindowHandle mPortalWindowHandle;
@@ -549,6 +554,9 @@
// Last systemUiVisibility we dispatched to windows.
private int mLastDispatchedSystemUiVisibility = 0;
+ /** Corner radius that windows should have in order to match the display. */
+ private final float mWindowCornerRadius;
+
private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
WindowStateAnimator winAnimator = w.mWinAnimator;
final AppWindowToken atoken = w.mAppToken;
@@ -909,6 +917,7 @@
if (mWmService.mSystemReady) {
mDisplayPolicy.systemReady();
}
+ mWindowCornerRadius = mDisplayPolicy.getWindowCornerRadius();
mDividerControllerLocked = new DockedStackDividerController(service, this);
mPinnedStackControllerLocked = new PinnedStackController(service, this);
@@ -953,6 +962,10 @@
return mDisplayId;
}
+ float getWindowCornerRadius() {
+ return mWindowCornerRadius;
+ }
+
WindowToken getWindowToken(IBinder binder) {
return mTokenMap.get(binder);
}
@@ -4562,7 +4575,7 @@
token2.mOwnerCanManageAppTokens) ? -1 : 1;
private final Predicate<WindowState> mGetOrientingWindow = w -> {
- if (!w.isVisibleLw() || !w.mPolicyVisibilityAfterAnim) {
+ if (!w.isVisibleLw() || !w.mLegacyPolicyVisibilityAfterAnim) {
return false;
}
final int req = w.mAttrs.screenOrientation;
@@ -4923,11 +4936,14 @@
/**
* Re-parent the DisplayContent's top surfaces, {@link #mWindowingLayer} and
- * {@link #mOverlayLayer} to the specified surfaceControl.
+ * {@link #mOverlayLayer} to the specified SurfaceControl.
*
+ * @param win The window which owns the SurfaceControl. This indicates the z-order of the
+ * windows of this display against the windows on the parent display.
* @param sc The new SurfaceControl, where the DisplayContent's surfaces will be re-parented to.
*/
- void reparentDisplayContent(SurfaceControl sc) {
+ void reparentDisplayContent(WindowState win, SurfaceControl sc) {
+ mParentWindow = win;
mParentSurfaceControl = sc;
if (mPortalWindowHandle == null) {
mPortalWindowHandle = createPortalWindowHandle(sc.toString());
@@ -4936,6 +4952,41 @@
.reparent(mWindowingLayer, sc).reparent(mOverlayLayer, sc);
}
+ /**
+ * Get the window which owns the surface that this DisplayContent is re-parented to.
+ *
+ * @return the parent window.
+ */
+ WindowState getParentWindow() {
+ return mParentWindow;
+ }
+
+ /**
+ * Update the location of this display in the parent window. This enables windows in this
+ * display to compute the global transformation matrix.
+ *
+ * @param win The parent window of this display.
+ * @param x The x coordinate in the parent window.
+ * @param y The y coordinate in the parent window.
+ */
+ void updateLocation(WindowState win, int x, int y) {
+ if (mParentWindow != win) {
+ throw new IllegalArgumentException(
+ "The given window is not the parent window of this display.");
+ }
+ if (mLocationInParentWindow.x != x || mLocationInParentWindow.y != y) {
+ mLocationInParentWindow.x = x;
+ mLocationInParentWindow.y = y;
+ if (mWmService.mAccessibilityController != null) {
+ mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
+ }
+ }
+ }
+
+ Point getLocationInParentWindow() {
+ return mLocationInParentWindow;
+ }
+
@VisibleForTesting
SurfaceControl getWindowingLayer() {
return mWindowingLayer;
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 32d0b32..26430fb 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -25,6 +25,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.res.Configuration.UI_MODE_TYPE_CAR;
import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
+import static android.view.Display.TYPE_BUILT_IN;
import static android.view.InsetsState.TYPE_TOP_BAR;
import static android.view.InsetsState.TYPE_TOP_GESTURES;
import static android.view.InsetsState.TYPE_TOP_TAPPABLE_ELEMENT;
@@ -157,6 +158,7 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.internal.util.ScreenShapeHelper;
import com.android.internal.util.ScreenshotHelper;
import com.android.internal.util.function.TriConsumer;
@@ -2870,6 +2872,16 @@
- statusBarHeight;
}
+ /**
+ * Return corner radius in pixels that should be used on windows in order to cover the display.
+ * The radius is only valid for built-in displays since the one who configures window corner
+ * radius cannot know the corner radius of non-built-in display.
+ */
+ float getWindowCornerRadius() {
+ return mDisplayContent.getDisplay().getType() == TYPE_BUILT_IN
+ ? ScreenDecorationsUtils.getWindowCornerRadius(mContext.getResources()) : 0f;
+ }
+
boolean isShowingDreamLw() {
return mShowingDream;
}
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index f67b11b..402ec59 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -29,12 +29,11 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.util.proto.ProtoOutputStream;
+import android.view.InsetsSource;
+import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
-import android.view.InsetsSource;
-import android.view.InsetsSourceControl;
-import android.view.ViewRootImpl;
import com.android.internal.util.function.TriConsumer;
import com.android.internal.util.function.pooled.PooledLambda;
@@ -142,7 +141,7 @@
mStateController.notifyControlChanged(mControllingWin);
}
}
- setServerVisible(mWin.wouldBeVisibleIfPolicyIgnored() && mWin.mPolicyVisibility
+ setServerVisible(mWin.wouldBeVisibleIfPolicyIgnored() && mWin.isVisibleByPolicy()
&& !mWin.mGivenInsetsPending);
}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index b33f8c7..34273f3 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -426,6 +426,16 @@
}
@Override
+ public void reparentDisplayContent(IWindow window, SurfaceControl sc, int displayId) {
+ mService.reparentDisplayContent(window, sc, displayId);
+ }
+
+ @Override
+ public void updateDisplayContentLocation(IWindow window, int x, int y, int displayId) {
+ mService.updateDisplayContentLocation(window, x, y, displayId);
+ }
+
+ @Override
public void updateTapExcludeRegion(IWindow window, int regionId, Region region) {
final long identity = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 4eddb30..7ac887e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -240,7 +240,6 @@
import com.android.internal.os.IResultReceiver;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.IShortcutService;
-import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.LatencyTracker;
@@ -793,9 +792,6 @@
final DisplayManager mDisplayManager;
final ActivityTaskManagerService mAtmService;
- /** Corner radius that windows should have in order to match the display. */
- final float mWindowCornerRadius;
-
/** Indicates whether this device supports wide color gamut / HDR rendering */
private boolean mHasWideColorGamutSupport;
private boolean mHasHdrSupport;
@@ -1020,7 +1016,6 @@
mInputManager = inputManager; // Must be before createDisplayContentLocked.
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
mDisplayWindowSettings = new DisplayWindowSettings(this);
- mWindowCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context.getResources());
mTransactionFactory = transactionFactory;
mTransaction = mTransactionFactory.make();
@@ -1880,7 +1875,8 @@
// We need to report touchable region changes to accessibility.
if (mAccessibilityController != null
- && w.getDisplayContent().getDisplayId() == DEFAULT_DISPLAY) {
+ && (w.getDisplayContent().getDisplayId() == DEFAULT_DISPLAY
+ || w.getDisplayContent().getParentWindow() != null)) {
mAccessibilityController.onSomeWindowResizedOrMovedLocked();
}
}
@@ -2012,7 +2008,8 @@
}
if (((attrChanges & LayoutParams.ACCESSIBILITY_TITLE_CHANGED) != 0)
&& (mAccessibilityController != null)
- && (win.getDisplayId() == DEFAULT_DISPLAY)) {
+ && (win.getDisplayId() == DEFAULT_DISPLAY
+ || win.getDisplayContent().getParentWindow() != null)) {
// No move or resize, but the controller checks for title changes as well
mAccessibilityController.onSomeWindowResizedOrMovedLocked();
}
@@ -5300,7 +5297,7 @@
": removed=" + win.mRemoved + " visible=" + win.isVisibleLw() +
" mHasSurface=" + win.mHasSurface +
" drawState=" + win.mWinAnimator.mDrawState);
- if (win.mRemoved || !win.mHasSurface || !win.mPolicyVisibility) {
+ if (win.mRemoved || !win.mHasSurface || !win.isVisibleByPolicy()) {
// Window has been removed or hidden; no draw will now happen, so stop waiting.
if (DEBUG_SCREEN_ON) Slog.w(TAG_WM, "Aborted waiting for drawn: " + win);
mWaitingForDrawn.remove(win);
@@ -6703,6 +6700,61 @@
}
}
+ private void checkCallerOwnsDisplay(int displayId) {
+ final Display display = mDisplayManager.getDisplay(displayId);
+ if (display == null) {
+ throw new IllegalArgumentException(
+ "Cannot find display for non-existent displayId: " + displayId);
+ }
+
+ final int callingUid = Binder.getCallingUid();
+ final int displayOwnerUid = display.getOwnerUid();
+ if (callingUid != displayOwnerUid) {
+ throw new SecurityException("The caller doesn't own the display.");
+ }
+ }
+
+ /** @see Session#reparentDisplayContent(IWindow, SurfaceControl, int) */
+ void reparentDisplayContent(IWindow client, SurfaceControl sc, int displayId) {
+ checkCallerOwnsDisplay(displayId);
+
+ synchronized (mGlobalLock) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ final WindowState win = windowForClientLocked(null, client, false);
+ if (win == null) {
+ Slog.w(TAG_WM, "Bad requesting window " + client);
+ return;
+ }
+ getDisplayContentOrCreate(displayId, null).reparentDisplayContent(win, sc);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+
+ /** @see Session#updateDisplayContentLocation(IWindow, int, int, int) */
+ void updateDisplayContentLocation(IWindow client, int x, int y, int displayId) {
+ checkCallerOwnsDisplay(displayId);
+
+ synchronized (mGlobalLock) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ final WindowState win = windowForClientLocked(null, client, false);
+ if (win == null) {
+ Slog.w(TAG_WM, "Bad requesting window " + client);
+ return;
+ }
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+ if (displayContent != null) {
+ displayContent.updateLocation(win, x, y);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+
/**
* Update a tap exclude region in the window identified by the provided id. Touches down on this
* region will not:
@@ -7538,31 +7590,6 @@
}
@Override
- public void reparentDisplayContent(int displayId, SurfaceControl sc) {
- final Display display = mDisplayManager.getDisplay(displayId);
- if (display == null) {
- throw new IllegalArgumentException(
- "Can't reparent display for non-existent displayId: " + displayId);
- }
-
- final int callingUid = Binder.getCallingUid();
- final int displayOwnerUid = display.getOwnerUid();
- if (callingUid != displayOwnerUid) {
- throw new SecurityException("Only owner of the display can reparent surfaces to it.");
- }
-
- synchronized (mGlobalLock) {
- long token = Binder.clearCallingIdentity();
- try {
- DisplayContent displayContent = getDisplayContentOrCreate(displayId, null);
- displayContent.reparentDisplayContent(sc);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
- }
-
- @Override
public boolean injectInputAfterTransactionsApplied(InputEvent ev, int mode) {
boolean shouldWaitForAnimToComplete = false;
if (ev instanceof KeyEvent) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index e39cd56..8cbad2d 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -251,18 +251,33 @@
int mSeq;
int mViewVisibility;
int mSystemUiVisibility;
+
/**
- * The visibility of the window based on policy like {@link WindowManagerPolicy}.
+ * The visibility flag of the window based on policy like {@link WindowManagerPolicy}.
* Normally set by calling {@link #showLw} and {@link #hideLw}.
+ *
+ * TODO: b/131253938 This will eventually be split into individual visibility policy flags.
*/
- boolean mPolicyVisibility = true;
+ static final int LEGACY_POLICY_VISIBILITY = 1;
/**
- * What {@link #mPolicyVisibility} should be set to after a transition animation.
- * For example, {@link #mPolicyVisibility} might true during an exit animation to hide it and
- * then set to the value of {@link #mPolicyVisibilityAfterAnim} which is false after the exit
- * animation is done.
+ * The visibility flag that determines whether this window is visible for the current user.
*/
- boolean mPolicyVisibilityAfterAnim = true;
+ private static final int VISIBLE_FOR_USER = 1 << 1;
+ private static final int POLICY_VISIBILITY_ALL = VISIBLE_FOR_USER | LEGACY_POLICY_VISIBILITY;
+ /**
+ * The Bitwise-or of flags that contribute to visibility of the WindowState
+ */
+ private int mPolicyVisibility = POLICY_VISIBILITY_ALL;
+
+ /**
+ * Whether {@link #LEGACY_POLICY_VISIBILITY} flag should be set after a transition animation.
+ * For example, {@link #LEGACY_POLICY_VISIBILITY} might be set during an exit animation to hide
+ * it and then unset when the value of {@link #mLegacyPolicyVisibilityAfterAnim} is false
+ * after the exit animation is done.
+ *
+ * TODO: b/131253938 Determine whether this can be changed to use a visibility flag instead.
+ */
+ boolean mLegacyPolicyVisibilityAfterAnim = true;
// overlay window is hidden because the owning app is suspended
private boolean mHiddenWhileSuspended;
private boolean mAppOpVisibility = true;
@@ -1414,13 +1429,33 @@
@Override
boolean isVisible() {
- return wouldBeVisibleIfPolicyIgnored() && mPolicyVisibility
+ return wouldBeVisibleIfPolicyIgnored() && isVisibleByPolicy()
// If we don't have a provider, this window isn't used as a window generating
// insets, so nobody can hide it over the inset APIs.
&& (mInsetProvider == null || mInsetProvider.isClientVisible());
}
/**
+ * Ensures that all the policy visibility bits are set.
+ * @return {@code true} if all flags about visiblity are set
+ */
+ boolean isVisibleByPolicy() {
+ return (mPolicyVisibility & POLICY_VISIBILITY_ALL) == POLICY_VISIBILITY_ALL;
+ }
+
+ void clearPolicyVisibilityFlag(int policyVisibilityFlag) {
+ mPolicyVisibility &= ~policyVisibilityFlag;
+ }
+
+ void setPolicyVisibilityFlag(int policyVisibilityFlag) {
+ mPolicyVisibility |= policyVisibilityFlag;
+ }
+
+ private boolean isLegacyPolicyVisibility() {
+ return (mPolicyVisibility & LEGACY_POLICY_VISIBILITY) != 0;
+ }
+
+ /**
* @return {@code true} if the window would be visible if we'd ignore policy visibility,
* {@code false} otherwise.
*/
@@ -1470,7 +1505,7 @@
boolean isVisibleOrAdding() {
final AppWindowToken atoken = mAppToken;
return (mHasSurface || (!mRelayoutCalled && mViewVisibility == View.VISIBLE))
- && mPolicyVisibility && !isParentWindowHidden()
+ && isVisibleByPolicy() && !isParentWindowHidden()
&& (atoken == null || !atoken.hiddenRequested)
&& !mAnimatingExit && !mDestroying;
}
@@ -1481,7 +1516,7 @@
* being visible.
*/
boolean isOnScreen() {
- if (!mHasSurface || mDestroying || !mPolicyVisibility) {
+ if (!mHasSurface || mDestroying || !isVisibleByPolicy()) {
return false;
}
final AppWindowToken atoken = mAppToken;
@@ -1522,7 +1557,7 @@
}
final boolean parentAndClientVisible = !isParentWindowHidden()
&& mViewVisibility == View.VISIBLE && !mToken.isHidden();
- return mHasSurface && mPolicyVisibility && !mDestroying
+ return mHasSurface && isVisibleByPolicy() && !mDestroying
&& (parentAndClientVisible || isAnimating());
}
@@ -1551,7 +1586,7 @@
@Override
public boolean isDisplayedLw() {
final AppWindowToken atoken = mAppToken;
- return isDrawnLw() && mPolicyVisibility
+ return isDrawnLw() && isVisibleByPolicy()
&& ((!isParentWindowHidden() && (atoken == null || !atoken.hiddenRequested))
|| isAnimating());
}
@@ -2057,8 +2092,8 @@
Slog.i(TAG_WM, " mSurfaceController=" + mWinAnimator.mSurfaceController
+ " relayoutCalled=" + mRelayoutCalled
+ " viewVis=" + mViewVisibility
- + " policyVis=" + mPolicyVisibility
- + " policyVisAfterAnim=" + mPolicyVisibilityAfterAnim
+ + " policyVis=" + isVisibleByPolicy()
+ + " policyVisAfterAnim=" + mLegacyPolicyVisibilityAfterAnim
+ " parentHidden=" + isParentWindowHidden()
+ " exiting=" + mAnimatingExit + " destroying=" + mDestroying);
if (mAppToken != null) {
@@ -2192,7 +2227,9 @@
if (isHiddenFromUserLocked()) {
if (DEBUG_VISIBILITY) Slog.w(TAG_WM, "user changing, hiding " + this
+ ", attrs=" + mAttrs.type + ", belonging to " + mOwnerUid);
- hideLw(false);
+ clearPolicyVisibilityFlag(VISIBLE_FOR_USER);
+ } else {
+ setPolicyVisibilityFlag(VISIBLE_FOR_USER);
}
}
@@ -2284,13 +2321,17 @@
}
void checkPolicyVisibilityChange() {
- if (mPolicyVisibility != mPolicyVisibilityAfterAnim) {
+ if (isLegacyPolicyVisibility() != mLegacyPolicyVisibilityAfterAnim) {
if (DEBUG_VISIBILITY) {
Slog.v(TAG, "Policy visibility changing after anim in " +
- mWinAnimator + ": " + mPolicyVisibilityAfterAnim);
+ mWinAnimator + ": " + mLegacyPolicyVisibilityAfterAnim);
}
- mPolicyVisibility = mPolicyVisibilityAfterAnim;
- if (!mPolicyVisibility) {
+ if (mLegacyPolicyVisibilityAfterAnim) {
+ setPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY);
+ } else {
+ clearPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY);
+ }
+ if (!isVisibleByPolicy()) {
mWinAnimator.hide("checkPolicyVisibilityChange");
if (isFocused()) {
if (DEBUG_FOCUS_LIGHT) Slog.i(TAG,
@@ -2531,7 +2572,7 @@
}
boolean showLw(boolean doAnimation, boolean requestAnim) {
- if (mPolicyVisibility && mPolicyVisibilityAfterAnim) {
+ if (isLegacyPolicyVisibility() && mLegacyPolicyVisibilityAfterAnim) {
// Already showing.
return false;
}
@@ -2558,18 +2599,18 @@
if (DEBUG_VISIBILITY) Slog.v(TAG, "Policy visibility true: " + this);
if (doAnimation) {
if (DEBUG_VISIBILITY) Slog.v(TAG, "doAnimation: mPolicyVisibility="
- + mPolicyVisibility + " animating=" + isAnimating());
+ + isLegacyPolicyVisibility() + " animating=" + isAnimating());
if (!mToken.okToAnimate()) {
doAnimation = false;
- } else if (mPolicyVisibility && !isAnimating()) {
+ } else if (isLegacyPolicyVisibility() && !isAnimating()) {
// Check for the case where we are currently visible and
// not animating; we do not want to do animation at such a
// point to become visible when we already are.
doAnimation = false;
}
}
- mPolicyVisibility = true;
- mPolicyVisibilityAfterAnim = true;
+ setPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY);
+ mLegacyPolicyVisibilityAfterAnim = true;
if (doAnimation) {
mWinAnimator.applyAnimationLocked(TRANSIT_ENTER, true);
}
@@ -2593,7 +2634,8 @@
doAnimation = false;
}
}
- boolean current = doAnimation ? mPolicyVisibilityAfterAnim : mPolicyVisibility;
+ boolean current =
+ doAnimation ? mLegacyPolicyVisibilityAfterAnim : isLegacyPolicyVisibility();
if (!current) {
// Already hiding.
return false;
@@ -2604,11 +2646,11 @@
doAnimation = false;
}
}
- mPolicyVisibilityAfterAnim = false;
+ mLegacyPolicyVisibilityAfterAnim = false;
final boolean isFocused = isFocused();
if (!doAnimation) {
if (DEBUG_VISIBILITY) Slog.v(TAG, "Policy visibility false: " + this);
- mPolicyVisibility = false;
+ clearPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY);
// Window is no longer visible -- make sure if we were waiting
// for it to be displayed before enabling the display, that
// we allow the display to be enabled now.
@@ -3100,7 +3142,8 @@
}
//TODO (multidisplay): Accessibility supported only for the default display.
- if (mWmService.mAccessibilityController != null && getDisplayId() == DEFAULT_DISPLAY) {
+ if (mWmService.mAccessibilityController != null && (getDisplayId() == DEFAULT_DISPLAY
+ || getDisplayContent().getParentWindow() != null)) {
mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
}
@@ -3443,11 +3486,11 @@
pw.println(prefix + "mSeq=" + mSeq
+ " mSystemUiVisibility=0x" + Integer.toHexString(mSystemUiVisibility));
}
- if (!mPolicyVisibility || !mPolicyVisibilityAfterAnim || !mAppOpVisibility
+ if (!isVisibleByPolicy() || !mLegacyPolicyVisibilityAfterAnim || !mAppOpVisibility
|| isParentWindowHidden() || mPermanentlyHidden || mForceHideNonSystemOverlayWindow
|| mHiddenWhileSuspended) {
- pw.println(prefix + "mPolicyVisibility=" + mPolicyVisibility
- + " mPolicyVisibilityAfterAnim=" + mPolicyVisibilityAfterAnim
+ pw.println(prefix + "mPolicyVisibility=" + isVisibleByPolicy()
+ + " mLegacyPolicyVisibilityAfterAnim=" + mLegacyPolicyVisibilityAfterAnim
+ " mAppOpVisibility=" + mAppOpVisibility
+ " parentHidden=" + isParentWindowHidden()
+ " mPermanentlyHidden=" + mPermanentlyHidden
@@ -3904,7 +3947,7 @@
+ ": mDrawState=" + mWinAnimator.drawStateToString()
+ " readyForDisplay=" + isReadyForDisplay()
+ " starting=" + (mAttrs.type == TYPE_APPLICATION_STARTING)
- + " during animation: policyVis=" + mPolicyVisibility
+ + " during animation: policyVis=" + isVisibleByPolicy()
+ " parentHidden=" + isParentWindowHidden()
+ " tok.hiddenRequested="
+ (mAppToken != null && mAppToken.hiddenRequested)
@@ -4165,7 +4208,8 @@
}
//TODO (multidisplay): Accessibility is supported only for the default display.
- if (mWmService.mAccessibilityController != null && getDisplayId() == DEFAULT_DISPLAY) {
+ if (mWmService.mAccessibilityController != null && (getDisplayId() == DEFAULT_DISPLAY
+ || getDisplayContent().getParentWindow() != null)) {
mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
}
@@ -4313,7 +4357,7 @@
+ ", animating=" + isAnimating());
if (!isDrawnLw()) {
Slog.v(TAG, "Not displayed: s=" + mWinAnimator.mSurfaceController
- + " pv=" + mPolicyVisibility
+ + " pv=" + isVisibleByPolicy()
+ " mDrawState=" + mWinAnimator.mDrawState
+ " ph=" + isParentWindowHidden()
+ " th=" + (mAppToken != null ? mAppToken.hiddenRequested : false)
@@ -4555,7 +4599,7 @@
anim.scaleCurrentDuration(mWmService.getWindowAnimationScaleLocked());
final AnimationAdapter adapter = new LocalAnimationAdapter(
new WindowAnimationSpec(anim, mSurfacePosition, false /* canSkipFirstFrame */,
- mToken.getWindowCornerRadiusForAnimation()),
+ getDisplayContent().getWindowCornerRadius()),
mWmService.mSurfaceAnimationRunner);
startAnimation(mPendingTransaction, adapter);
commitPendingTransaction();
@@ -4604,6 +4648,18 @@
int x = mSurfacePosition.x;
int y = mSurfacePosition.y;
+ // We might be on a display which has been re-parented to a view in another window, so here
+ // computes the global location of our display.
+ DisplayContent dc = getDisplayContent();
+ while (dc != null && dc.getParentWindow() != null) {
+ final WindowState displayParent = dc.getParentWindow();
+ x += displayParent.mWindowFrames.mFrame.left - displayParent.mAttrs.surfaceInsets.left
+ + (dc.getLocationInParentWindow().x * displayParent.mGlobalScale + 0.5f);
+ y += displayParent.mWindowFrames.mFrame.top - displayParent.mAttrs.surfaceInsets.top
+ + (dc.getLocationInParentWindow().y * displayParent.mGlobalScale + 0.5f);
+ dc = displayParent.getDisplayContent();
+ }
+
// If changed, also adjust transformFrameToSurfacePosition
final WindowContainer parent = getParent();
if (isChildWindow()) {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 780d471..20e1ac6 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -256,7 +256,7 @@
mWin.checkPolicyVisibilityChange();
final DisplayContent displayContent = mWin.getDisplayContent();
- if (mAttrType == LayoutParams.TYPE_STATUS_BAR && mWin.mPolicyVisibility) {
+ if (mAttrType == LayoutParams.TYPE_STATUS_BAR && mWin.isVisibleByPolicy()) {
// Upon completion of a not-visible to visible status bar animation a relayout is
// required.
if (displayContent != null) {
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index f65f0ab..f0b9c62 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -345,8 +345,4 @@
mOwnerCanManageAppTokens);
return mOwnerCanManageAppTokens && (layer > navLayer);
}
-
- float getWindowCornerRadiusForAnimation() {
- return mDisplayContent.isDefaultDisplay ? mWmService.mWindowCornerRadius : 0;
- }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c5a2068..22231c0 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -8034,6 +8034,7 @@
throw new IllegalArgumentException("Component " + who
+ " not installed for userId:" + userHandle);
}
+
final boolean hasIncompatibleAccountsOrNonAdb =
hasIncompatibleAccountsOrNonAdbNoLock(userHandle, who);
synchronized (getLockObject()) {
@@ -8539,9 +8540,30 @@
return;
}
enforceCanManageProfileAndDeviceOwners();
- if ((mIsWatch || hasUserSetupCompleted(userHandle)) && !isCallerWithSystemUid()) {
- throw new IllegalStateException("Cannot set the profile owner on a user which is "
- + "already set-up");
+
+ if ((mIsWatch || hasUserSetupCompleted(userHandle))) {
+ if (!isCallerWithSystemUid()) {
+ throw new IllegalStateException("Cannot set the profile owner on a user which is "
+ + "already set-up");
+ }
+
+ if (!mIsWatch) {
+ // Only the default supervision profile owner can be set as profile owner after SUW
+ final String supervisor = mContext.getResources().getString(
+ com.android.internal.R.string
+ .config_defaultSupervisionProfileOwnerComponent);
+ if (supervisor == null) {
+ throw new IllegalStateException("Unable to set profile owner post-setup, no"
+ + "default supervisor profile owner defined");
+ }
+
+ final ComponentName supervisorComponent = ComponentName.unflattenFromString(
+ supervisor);
+ if (!owner.equals(supervisorComponent)) {
+ throw new IllegalStateException("Unable to set non-default profile owner"
+ + " post-setup " + owner);
+ }
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 6ec864c..f4a6231 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -16,6 +16,7 @@
package com.android.server.usage;
+import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_START;
import static android.app.usage.UsageEvents.Event.NOTIFICATION_SEEN;
import static android.app.usage.UsageEvents.Event.SLICE_PINNED;
import static android.app.usage.UsageEvents.Event.SLICE_PINNED_PRIV;
@@ -821,6 +822,41 @@
}
@Test
+ public void testInitialForegroundServiceTimeout() throws Exception {
+ setChargingState(mController, false);
+
+ mInjector.mElapsedRealtime = 1 * RARE_THRESHOLD + 100;
+ // Make sure app is in NEVER bucket
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
+ REASON_MAIN_FORCED, mInjector.mElapsedRealtime);
+ mController.checkIdleStates(USER_ID);
+ assertBucket(STANDBY_BUCKET_NEVER);
+
+ mInjector.mElapsedRealtime += 100;
+
+ // Trigger a FOREGROUND_SERVICE_START and verify bucket
+ reportEvent(mController, FOREGROUND_SERVICE_START, mInjector.mElapsedRealtime, PACKAGE_1);
+ mController.checkIdleStates(USER_ID);
+ assertBucket(STANDBY_BUCKET_ACTIVE);
+
+ // Verify it's still in ACTIVE close to end of timeout
+ mInjector.mElapsedRealtime += mController.mInitialForegroundServiceStartTimeoutMillis - 100;
+ mController.checkIdleStates(USER_ID);
+ assertBucket(STANDBY_BUCKET_ACTIVE);
+
+ // Verify bucket moves to RARE after timeout
+ mInjector.mElapsedRealtime += 200;
+ mController.checkIdleStates(USER_ID);
+ assertBucket(STANDBY_BUCKET_RARE);
+
+ // Trigger a FOREGROUND_SERVICE_START again
+ reportEvent(mController, FOREGROUND_SERVICE_START, mInjector.mElapsedRealtime, PACKAGE_1);
+ mController.checkIdleStates(USER_ID);
+ // Bucket should not be immediately elevated on subsequent service starts
+ assertBucket(STANDBY_BUCKET_RARE);
+ }
+
+ @Test
public void testPredictionNotOverridden() throws Exception {
reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
assertBucket(STANDBY_BUCKET_ACTIVE);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index cbca087..8d56bc4 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -62,6 +62,7 @@
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -85,6 +86,7 @@
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Person;
+import android.app.RemoteInput;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.usage.UsageStatsManagerInternal;
import android.companion.ICompanionDeviceManager;
@@ -96,6 +98,7 @@
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
+import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.drawable.Icon;
@@ -327,6 +330,8 @@
LocalServices.removeServiceForTest(WindowManagerInternal.class);
LocalServices.addService(WindowManagerInternal.class, mWindowManagerInternal);
+ doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any());
+
mService = new TestableNotificationManagerService(mContext);
// Use this testable looper.
@@ -375,20 +380,16 @@
when(mAssistants.isAdjustmentAllowed(anyString())).thenReturn(true);
- try {
- mService.init(mTestableLooper.getLooper(),
- mPackageManager, mPackageManagerClient, mockLightsManager,
- mListeners, mAssistants, mConditionProviders,
- mCompanionMgr, mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager,
- mGroupHelper, mAm, mAppUsageStats,
- mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal,
- mAppOpsManager, mUm);
- mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
- } catch (SecurityException e) {
- if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
- throw e;
- }
- }
+
+ mService.init(mTestableLooper.getLooper(),
+ mPackageManager, mPackageManagerClient, mockLightsManager,
+ mListeners, mAssistants, mConditionProviders,
+ mCompanionMgr, mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager,
+ mGroupHelper, mAm, mAppUsageStats,
+ mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal,
+ mAppOpsManager, mUm);
+ mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
+
mService.setAudioManager(mAudioManager);
// Tests call directly into the Binder.
@@ -2080,14 +2081,8 @@
public void testSetListenerAccessForUser() throws Exception {
UserHandle user = UserHandle.of(10);
ComponentName c = ComponentName.unflattenFromString("package/Component");
- try {
- mBinderService.setNotificationListenerAccessGrantedForUser(
- c, user.getIdentifier(), true);
- } catch (SecurityException e) {
- if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
- throw e;
- }
- }
+ mBinderService.setNotificationListenerAccessGrantedForUser(c, user.getIdentifier(), true);
+
verify(mContext, times(1)).sendBroadcastAsUser(any(), eq(user), any());
verify(mListeners, times(1)).setPackageOrComponentEnabled(
@@ -2101,15 +2096,14 @@
@Test
public void testSetAssistantAccessForUser() throws Exception {
UserHandle user = UserHandle.of(10);
+ List<UserInfo> uis = new ArrayList<>();
+ UserInfo ui = new UserInfo();
+ ui.id = 10;
+ uis.add(ui);
ComponentName c = ComponentName.unflattenFromString("package/Component");
- try {
- mBinderService.setNotificationAssistantAccessGrantedForUser(
- c, user.getIdentifier(), true);
- } catch (SecurityException e) {
- if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
- throw e;
- }
- }
+ when(mUm.getEnabledProfiles(10)).thenReturn(uis);
+
+ mBinderService.setNotificationAssistantAccessGrantedForUser(c, user.getIdentifier(), true);
verify(mContext, times(1)).sendBroadcastAsUser(any(), eq(user), any());
verify(mAssistants, times(1)).setPackageOrComponentEnabled(
@@ -2150,14 +2144,8 @@
public void testSetDndAccessForUser() throws Exception {
UserHandle user = UserHandle.of(10);
ComponentName c = ComponentName.unflattenFromString("package/Component");
- try {
- mBinderService.setNotificationPolicyAccessGrantedForUser(
- c.getPackageName(), user.getIdentifier(), true);
- } catch (SecurityException e) {
- if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
- throw e;
- }
- }
+ mBinderService.setNotificationPolicyAccessGrantedForUser(
+ c.getPackageName(), user.getIdentifier(), true);
verify(mContext, times(1)).sendBroadcastAsUser(any(), eq(user), any());
verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
@@ -2171,13 +2159,7 @@
@Test
public void testSetListenerAccess() throws Exception {
ComponentName c = ComponentName.unflattenFromString("package/Component");
- try {
- mBinderService.setNotificationListenerAccessGranted(c, true);
- } catch (SecurityException e) {
- if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
- throw e;
- }
- }
+ mBinderService.setNotificationListenerAccessGranted(c, true);
verify(mListeners, times(1)).setPackageOrComponentEnabled(
c.flattenToString(), 0, true, true);
@@ -2189,14 +2171,14 @@
@Test
public void testSetAssistantAccess() throws Exception {
+ List<UserInfo> uis = new ArrayList<>();
+ UserInfo ui = new UserInfo();
+ ui.id = 0;
+ uis.add(ui);
+ when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis);
ComponentName c = ComponentName.unflattenFromString("package/Component");
- try {
- mBinderService.setNotificationAssistantAccessGranted(c, true);
- } catch (SecurityException e) {
- if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
- throw e;
- }
- }
+
+ mBinderService.setNotificationAssistantAccessGranted(c, true);
verify(mAssistants, times(1)).setPackageOrComponentEnabled(
c.flattenToString(), 0, true, true);
@@ -2207,19 +2189,44 @@
}
@Test
+ public void testSetAssistantAccess_multiProfile() throws Exception {
+ List<UserInfo> uis = new ArrayList<>();
+ UserInfo ui = new UserInfo();
+ ui.id = 0;
+ uis.add(ui);
+ UserInfo ui10 = new UserInfo();
+ ui10.id = 10;
+ uis.add(ui10);
+ when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis);
+ ComponentName c = ComponentName.unflattenFromString("package/Component");
+
+ mBinderService.setNotificationAssistantAccessGranted(c, true);
+
+ verify(mAssistants, times(1)).setPackageOrComponentEnabled(
+ c.flattenToString(), 0, true, true);
+ verify(mAssistants, times(1)).setPackageOrComponentEnabled(
+ c.flattenToString(), 10, true, true);
+ verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
+ c.flattenToString(), 0, false, true);
+ verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
+ c.flattenToString(), 10, false, true);
+ verify(mListeners, never()).setPackageOrComponentEnabled(
+ any(), anyInt(), anyBoolean(), anyBoolean());
+ }
+
+ @Test
public void testSetAssistantAccess_nullWithAllowedAssistant() throws Exception {
ArrayList<ComponentName> componentList = new ArrayList<>();
ComponentName c = ComponentName.unflattenFromString("package/Component");
componentList.add(c);
when(mAssistants.getAllowedComponents(anyInt())).thenReturn(componentList);
+ List<UserInfo> uis = new ArrayList<>();
+ UserInfo ui = new UserInfo();
+ ui.id = 0;
+ uis.add(ui);
+ when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis);
- try {
- mBinderService.setNotificationAssistantAccessGranted(null, true);
- } catch (SecurityException e) {
- if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
- throw e;
- }
- }
+ mBinderService.setNotificationAssistantAccessGranted(null, true);
verify(mAssistants, times(1)).setPackageOrComponentEnabled(
c.flattenToString(), 0, true, false);
@@ -2231,23 +2238,23 @@
@Test
public void testSetAssistantAccessForUser_nullWithAllowedAssistant() throws Exception {
- UserHandle user = UserHandle.of(10);
+ List<UserInfo> uis = new ArrayList<>();
+ UserInfo ui = new UserInfo();
+ ui.id = 10;
+ uis.add(ui);
+ UserHandle user = ui.getUserHandle();
ArrayList<ComponentName> componentList = new ArrayList<>();
ComponentName c = ComponentName.unflattenFromString("package/Component");
componentList.add(c);
when(mAssistants.getAllowedComponents(anyInt())).thenReturn(componentList);
+ when(mUm.getEnabledProfiles(10)).thenReturn(uis);
- try {
- mBinderService.setNotificationAssistantAccessGrantedForUser(
- null, user.getIdentifier(), true);
- } catch (SecurityException e) {
- if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
- throw e;
- }
- }
+ mBinderService.setNotificationAssistantAccessGrantedForUser(
+ null, user.getIdentifier(), true);
verify(mAssistants, times(1)).setPackageOrComponentEnabled(
c.flattenToString(), user.getIdentifier(), true, false);
+ verify(mAssistants).setUserSet(10, true);
verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
c.flattenToString(), user.getIdentifier(), false, false);
verify(mListeners, never()).setPackageOrComponentEnabled(
@@ -2255,15 +2262,44 @@
}
@Test
+ public void testSetAssistantAccessForUser_workProfile_nullWithAllowedAssistant()
+ throws Exception {
+ List<UserInfo> uis = new ArrayList<>();
+ UserInfo ui = new UserInfo();
+ ui.id = 0;
+ uis.add(ui);
+ UserInfo ui10 = new UserInfo();
+ ui10.id = 10;
+ uis.add(ui10);
+ UserHandle user = ui.getUserHandle();
+ ArrayList<ComponentName> componentList = new ArrayList<>();
+ ComponentName c = ComponentName.unflattenFromString("package/Component");
+ componentList.add(c);
+ when(mAssistants.getAllowedComponents(anyInt())).thenReturn(componentList);
+ when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis);
+
+ mBinderService.setNotificationAssistantAccessGrantedForUser(
+ null, user.getIdentifier(), true);
+
+ verify(mAssistants, times(1)).setPackageOrComponentEnabled(
+ c.flattenToString(), user.getIdentifier(), true, false);
+ verify(mAssistants, times(1)).setPackageOrComponentEnabled(
+ c.flattenToString(), ui10.id, true, false);
+ verify(mAssistants).setUserSet(0, true);
+ verify(mAssistants).setUserSet(10, true);
+ verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
+ c.flattenToString(), user.getIdentifier(), false, false);
+ verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
+ c.flattenToString(), ui10.id, false, false);
+ verify(mListeners, never()).setPackageOrComponentEnabled(
+ any(), anyInt(), anyBoolean(), anyBoolean());
+ }
+
+ @Test
public void testSetDndAccess() throws Exception {
ComponentName c = ComponentName.unflattenFromString("package/Component");
- try {
- mBinderService.setNotificationPolicyAccessGranted(c.getPackageName(), true);
- } catch (SecurityException e) {
- if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
- throw e;
- }
- }
+
+ mBinderService.setNotificationPolicyAccessGranted(c.getPackageName(), true);
verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
c.getPackageName(), 0, true, true);
@@ -2291,6 +2327,12 @@
public void testSetAssistantAccess_doesNothingOnLowRam() throws Exception {
when(mActivityManager.isLowRamDevice()).thenReturn(true);
ComponentName c = ComponentName.unflattenFromString("package/Component");
+ List<UserInfo> uis = new ArrayList<>();
+ UserInfo ui = new UserInfo();
+ ui.id = 0;
+ uis.add(ui);
+ when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis);
+
mBinderService.setNotificationAssistantAccessGranted(c, true);
verify(mListeners, never()).setPackageOrComponentEnabled(
@@ -2320,13 +2362,8 @@
when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(true);
when(mActivityManager.isLowRamDevice()).thenReturn(true);
ComponentName c = ComponentName.unflattenFromString("package/Component");
- try {
- mBinderService.setNotificationListenerAccessGranted(c, true);
- } catch (SecurityException e) {
- if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
- throw e;
- }
- }
+
+ mBinderService.setNotificationListenerAccessGranted(c, true);
verify(mListeners, times(1)).setPackageOrComponentEnabled(
c.flattenToString(), 0, true, true);
@@ -2341,13 +2378,13 @@
when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(true);
when(mActivityManager.isLowRamDevice()).thenReturn(true);
ComponentName c = ComponentName.unflattenFromString("package/Component");
- try {
- mBinderService.setNotificationAssistantAccessGranted(c, true);
- } catch (SecurityException e) {
- if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
- throw e;
- }
- }
+ List<UserInfo> uis = new ArrayList<>();
+ UserInfo ui = new UserInfo();
+ ui.id = 0;
+ uis.add(ui);
+ when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis);
+
+ mBinderService.setNotificationAssistantAccessGranted(c, true);
verify(mListeners, never()).setPackageOrComponentEnabled(
anyString(), anyInt(), anyBoolean(), anyBoolean());
@@ -2362,13 +2399,8 @@
when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(true);
when(mActivityManager.isLowRamDevice()).thenReturn(true);
ComponentName c = ComponentName.unflattenFromString("package/Component");
- try {
- mBinderService.setNotificationPolicyAccessGranted(c.getPackageName(), true);
- } catch (SecurityException e) {
- if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
- throw e;
- }
- }
+
+ mBinderService.setNotificationPolicyAccessGranted(c.getPackageName(), true);
verify(mListeners, never()).setPackageOrComponentEnabled(
anyString(), anyInt(), anyBoolean(), anyBoolean());
@@ -4492,6 +4524,13 @@
Person person = new Person.Builder()
.setName("bubblebot")
.build();
+ // It needs remote input to be bubble-able
+ RemoteInput remoteInput = new RemoteInput.Builder("reply_key").setLabel("reply").build();
+ PendingIntent inputIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+ Icon icon = Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon);
+ Notification.Action replyAction = new Notification.Action.Builder(icon, "Reply",
+ inputIntent).addRemoteInput(remoteInput)
+ .build();
// Make it messaging style
Notification.Builder nb = new Notification.Builder(mContext,
mTestNotificationChannel.getId())
@@ -4504,6 +4543,7 @@
.addMessage("Is it me you're looking for?",
SystemClock.currentThreadTimeMillis(), person)
)
+ .setActions(replyAction)
.setSmallIcon(android.R.drawable.sym_def_app_icon);
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, null, mUid, 0,
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 45d5219..5803385 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -79,7 +79,6 @@
import androidx.test.filters.SmallTest;
import com.android.server.wm.LaunchParamsController.LaunchParamsModifier;
-import com.android.server.wm.TaskRecord.TaskRecordFactory;
import org.junit.Before;
import org.junit.Test;
@@ -330,21 +329,14 @@
any(), any(), any(), anyInt(), anyInt(), anyInt(), any(),
anyBoolean(), anyBoolean(), any(), any(), any());
- // instrument the stack and task used.
- final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().createStack(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- final TaskRecord task = new TaskBuilder(mSupervisor)
- .setCreateStack(false)
- .build();
-
- // use factory that only returns spy task.
- final TaskRecordFactory factory = mock(TaskRecordFactory.class);
- TaskRecord.setTaskRecordFactory(factory);
-
- // return task when created.
- doReturn(task).when(factory).create(any(), anyInt(), any(), any(), any(), any());
+ // Use factory that only returns spy task.
+ mockTaskRecordFactory();
if (mockGetLaunchStack) {
+ // Instrument the stack and task used.
+ final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
// Direct starter to use spy stack.
doReturn(stack).when(mRootActivityContainer)
.getLaunchStack(any(), any(), any(), anyBoolean());
@@ -707,6 +699,36 @@
}
/**
+ * This test ensures that {@link ActivityStarter#setTargetStackAndMoveToFrontIfNeeded} will
+ * move the existing task to front if the current focused stack doesn't have running task.
+ */
+ @Test
+ public void testBringTaskToFrontWhenFocusedStackIsFinising() {
+ // Put 2 tasks in the same stack (simulate the behavior of home stack).
+ final ActivityRecord activity = new ActivityBuilder(mService)
+ .setCreateTask(true).build();
+ new ActivityBuilder(mService)
+ .setStack(activity.getActivityStack())
+ .setCreateTask(true).build();
+
+ // Create a top finishing activity.
+ final ActivityRecord finishingTopActivity = new ActivityBuilder(mService)
+ .setCreateTask(true).build();
+ finishingTopActivity.getActivityStack().moveToFront("finishingTopActivity");
+
+ assertEquals(finishingTopActivity, mRootActivityContainer.topRunningActivity());
+ finishingTopActivity.finishing = true;
+
+ // Launch the bottom task of the target stack.
+ prepareStarter(FLAG_ACTIVITY_NEW_TASK, false /* mockGetLaunchStack */)
+ .setReason("testBringTaskToFrontWhenTopStackIsFinising")
+ .setIntent(activity.intent)
+ .execute();
+ // The hierarchies of the activity should move to front.
+ assertEquals(activity, mRootActivityContainer.topRunningActivity());
+ }
+
+ /**
* This test ensures that when starting an existing single task activity on secondary display
* which is not the top focused display, it should deliver new intent to the activity and not
* create a new stack.
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index afd4ec1..09b511a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -41,6 +41,7 @@
import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
+import android.app.AppOpsManager;
import android.app.IApplicationThread;
import android.content.ComponentName;
import android.content.Context;
@@ -71,6 +72,7 @@
import com.android.server.appop.AppOpsService;
import com.android.server.firewall.IntentFirewall;
import com.android.server.uri.UriGrantsManagerInternal;
+import com.android.server.wm.TaskRecord.TaskRecordFactory;
import com.android.server.wm.utils.MockTracker;
import org.junit.After;
@@ -159,6 +161,19 @@
}
/**
+ * Delegates task creation to {@link #TaskBuilder} to avoid the dependency of window hierarchy
+ * when starting activity in unit tests.
+ */
+ void mockTaskRecordFactory() {
+ final TaskRecord task = new TaskBuilder(mSupervisor).setCreateStack(false).build();
+ final TaskRecordFactory factory = mock(TaskRecordFactory.class);
+ TaskRecord.setTaskRecordFactory(factory);
+ doReturn(task).when(factory).create(any() /* service */, anyInt() /* taskId */,
+ any() /* info */, any() /* intent */, any() /* voiceSession */,
+ any() /* voiceInteractor */);
+ }
+
+ /**
* Builder for creating new activities.
*/
protected static class ActivityBuilder {
@@ -413,12 +428,18 @@
ActivityStackSupervisor mTestStackSupervisor;
ActivityDisplay mDefaultDisplay;
+ AppOpsService mAppOpsService;
TestActivityTaskManagerService(Context context) {
super(context);
spyOn(this);
mUgmInternal = mock(UriGrantsManagerInternal.class);
+ mAppOpsService = mock(AppOpsService.class);
+
+ // Make sure permission checks aren't overridden.
+ doReturn(AppOpsManager.MODE_DEFAULT)
+ .when(mAppOpsService).noteOperation(anyInt(), anyInt(), anyString());
mSupportsMultiWindow = true;
mSupportsMultiDisplay = true;
@@ -482,6 +503,11 @@
}
@Override
+ AppOpsService getAppOpsService() {
+ return mAppOpsService;
+ }
+
+ @Override
void updateCpuStats() {
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index 2d1dd39..20379a2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -105,6 +105,7 @@
}
@Test
+ @FlakyTest(bugId = 131005232)
public void testTransitWithinTask() {
synchronized (mWm.mGlobalLock) {
final AppWindowToken opening = createAppWindowToken(mDisplayContent,
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index 9cdea9a..5fc7f44 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -65,6 +65,7 @@
}
@Test
+ @FlakyTest(bugId = 131005232)
public void testKeyguardOverride() {
mWm.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false /* alwaysKeepCurrent */);
mWm.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */);
@@ -72,6 +73,7 @@
}
@Test
+ @FlakyTest(bugId = 131005232)
public void testKeyguardKeep() {
mWm.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */);
mWm.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false /* alwaysKeepCurrent */);
@@ -172,6 +174,7 @@
}
@Test
+ @FlakyTest(bugId = 131005232)
public void testLoadAnimationSafely() {
DisplayContent dc = createNewDisplay(Display.STATE_ON);
assertNull(dc.mAppTransition.loadAnimationSafely(
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 623559e..70d9b5f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
@@ -30,6 +30,7 @@
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;
@@ -65,6 +66,7 @@
}
@Test
+ @FlakyTest(bugId = 131005232)
public void clipAfterAnim_boundsLayerIsCreated() {
mToken.mNeedsAnimationBoundsLayer = true;
@@ -86,6 +88,7 @@
}
@Test
+ @FlakyTest(bugId = 131005232)
public void clipAfterAnim_boundsLayerIsDestroyed() {
mToken.mNeedsAnimationBoundsLayer = true;
mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
@@ -116,6 +119,7 @@
}
@Test
+ @FlakyTest(bugId = 131005232)
public void clipNoneAnim_boundsLayerIsNotCreated() {
mToken.mNeedsAnimationBoundsLayer = false;
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index 064b553..7600d4a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -150,6 +150,7 @@
}
@Test
+ @FlakyTest(bugId = 131005232)
public void testLandscapeSeascapeRotationByApp() {
// Some plumbing to get the service ready for rotation updates.
mWm.mDisplayReady = true;
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index bac1ecd5..b2084f8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -25,7 +25,6 @@
import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
import static android.view.Display.DEFAULT_DISPLAY;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
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.never;
@@ -71,10 +70,10 @@
import java.util.List;
/**
- * Tests for the {@link ActivityStackSupervisor} class.
+ * Tests for the {@link RootActivityContainer} class.
*
* Build/Install/Run:
- * atest WmTests:ActivityStackSupervisorTests
+ * atest WmTests:RootActivityContainerTests
*/
@MediumTest
@Presubmit
@@ -399,17 +398,15 @@
*/
@Test
public void testStartHomeOnAllDisplays() {
+ mockResolveHomeActivity();
+
// Create secondary displays.
final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay());
mRootActivityContainer.addChild(secondDisplay, POSITION_TOP);
doReturn(true).when(secondDisplay).supportsSystemDecorations();
// Create mock tasks and other necessary mocks.
- TaskBuilder taskBuilder = new TaskBuilder(mService.mStackSupervisor).setCreateStack(false);
- final TaskRecord.TaskRecordFactory factory = mock(TaskRecord.TaskRecordFactory.class);
- TaskRecord.setTaskRecordFactory(factory);
- doAnswer(i -> taskBuilder.build()).when(factory)
- .create(any(), anyInt(), any(), any(), any(), any());
+ mockTaskRecordFactory();
doReturn(true).when(mRootActivityContainer)
.ensureVisibilityAndConfig(any(), anyInt(), anyBoolean(), anyBoolean());
doReturn(true).when(mRootActivityContainer).canStartHomeOnDisplay(
@@ -510,6 +507,26 @@
}
/**
+ * Tests that when starting {@link #ResolverActivity} for home, it should use the standard
+ * activity type (in a new stack) so the order of back stack won't be broken.
+ */
+ @Test
+ public void testStartResolverActivityForHome() {
+ final ActivityInfo info = new ActivityInfo();
+ info.applicationInfo = new ApplicationInfo();
+ info.applicationInfo.packageName = "android";
+ info.name = ResolverActivity.class.getName();
+ doReturn(info).when(mRootActivityContainer).resolveHomeActivity(anyInt(), any());
+ mockTaskRecordFactory();
+
+ mRootActivityContainer.startHomeOnDisplay(0 /* userId */, "test", DEFAULT_DISPLAY);
+ final ActivityRecord resolverActivity = mRootActivityContainer.topRunningActivity();
+
+ assertEquals(info, resolverActivity.info);
+ assertEquals(ACTIVITY_TYPE_STANDARD, resolverActivity.getActivityStack().getActivityType());
+ }
+
+ /**
* Tests that secondary home should be selected if default home not set.
*/
@Test
@@ -542,13 +559,7 @@
*/
@Test
public void testResolveSecondaryHomeActivityWhenDefaultHomeNotSupportMultiDisplay() {
- final Intent defaultHomeIntent = mService.getHomeIntent();
- final ActivityInfo aInfoDefault = new ActivityInfo();
- aInfoDefault.name = "fakeHomeActivity";
- aInfoDefault.applicationInfo = new ApplicationInfo();
- aInfoDefault.applicationInfo.packageName = "fakeHomePackage";
- doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
- refEq(defaultHomeIntent));
+ mockResolveHomeActivity();
final List<ResolveInfo> resolutions = new ArrayList<>();
doReturn(resolutions).when(mRootActivityContainer).resolveActivities(anyInt(), any());
@@ -575,13 +586,7 @@
*/
@Test
public void testResolveSecondaryHomeActivityWhenDefaultHomeSupportMultiDisplay() {
- final Intent homeIntent = mService.getHomeIntent();
- final ActivityInfo aInfoDefault = new ActivityInfo();
- aInfoDefault.name = "fakeHomeActivity";
- aInfoDefault.applicationInfo = new ApplicationInfo();
- aInfoDefault.applicationInfo.packageName = "fakeHomePackage";
- doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
- refEq(homeIntent));
+ final ActivityInfo aInfoDefault = mockResolveHomeActivity();
final List<ResolveInfo> resolutions = new ArrayList<>();
final ResolveInfo infoFake1 = new ResolveInfo();
@@ -612,13 +617,7 @@
*/
@Test
public void testResolveSecondaryHomeActivityWhenOtherActivitySupportMultiDisplay() {
- final Intent homeIntent = mService.getHomeIntent();
- final ActivityInfo aInfoDefault = new ActivityInfo();
- aInfoDefault.name = "fakeHomeActivity";
- aInfoDefault.applicationInfo = new ApplicationInfo();
- aInfoDefault.applicationInfo.packageName = "fakeHomePackage";
- doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
- refEq(homeIntent));
+ mockResolveHomeActivity();
final List<ResolveInfo> resolutions = new ArrayList<>();
final ResolveInfo infoFake1 = new ResolveInfo();
@@ -646,4 +645,19 @@
resolvedInfo.first.applicationInfo.packageName);
assertEquals(infoFake1.activityInfo.name, resolvedInfo.first.name);
}
+
+ /**
+ * Mock {@link RootActivityContainerTests#resolveHomeActivity} for returning consistent activity
+ * info for test cases (the original implementation will resolve from the real package manager).
+ */
+ private ActivityInfo mockResolveHomeActivity() {
+ final Intent homeIntent = mService.getHomeIntent();
+ final ActivityInfo aInfoDefault = new ActivityInfo();
+ aInfoDefault.name = "fakeHomeActivity";
+ aInfoDefault.applicationInfo = new ApplicationInfo();
+ aInfoDefault.applicationInfo.packageName = "fakeHomePackage";
+ doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
+ refEq(homeIntent));
+ return aInfoDefault;
+ }
}
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 715353e..3a8d3b7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -476,4 +476,47 @@
root.prepareWindowToDisplayDuringRelayout(wasVisible /*wasVisible*/);
verify(sPowerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString());
}
+
+ @Test
+ public void testVisibilityChangeSwitchUser() {
+ final WindowState window = createWindow(null, TYPE_APPLICATION, "app");
+ window.mHasSurface = true;
+ window.setShowToOwnerOnlyLocked(true);
+
+ mWm.mCurrentUserId = 1;
+ window.switchUser();
+ assertFalse(window.isVisible());
+ assertFalse(window.isVisibleByPolicy());
+
+ mWm.mCurrentUserId = 0;
+ window.switchUser();
+ assertTrue(window.isVisible());
+ assertTrue(window.isVisibleByPolicy());
+ }
+
+ @Test
+ public void testGetTransformationMatrix() {
+ synchronized (mWm.mGlobalLock) {
+ final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0");
+ win0.getFrameLw().offsetTo(1, 0);
+
+ final DisplayContent dc = createNewDisplay();
+ dc.reparentDisplayContent(win0, win0.getSurfaceControl());
+ dc.updateLocation(win0, 2, 0);
+
+ final float[] values = new float[9];
+ final Matrix matrix = new Matrix();
+ final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
+ final WindowState win1 = createWindow(null, TYPE_APPLICATION, dc, "win1");
+ win1.mHasSurface = true;
+ win1.mSurfaceControl = mock(SurfaceControl.class);
+ win1.getFrameLw().offsetTo(3, 0);
+ win1.updateSurfacePosition(t);
+ win1.getTransformationMatrix(values, matrix);
+
+ matrix.getValues(values);
+ assertEquals(6f, values[Matrix.MTRANS_X], 0f);
+ assertEquals(0f, values[Matrix.MTRANS_Y], 0f);
+ }
+ }
}
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 4b33e16..7786627 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -27,6 +27,7 @@
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_DOZE;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_NON_DOZE;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_START;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_FOREGROUND_SERVICE_START;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_NOTIFICATION_SEEN;
@@ -238,6 +239,11 @@
long mUnexemptedSyncScheduledTimeoutMillis;
/** Maximum time a system interaction should keep the buckets elevated. */
long mSystemInteractionTimeoutMillis;
+ /**
+ * Maximum time a foreground service start should keep the buckets elevated if the service
+ * start is the first usage of the app
+ */
+ long mInitialForegroundServiceStartTimeoutMillis;
/** The length of time phone must be charging before considered stable enough to run jobs */
long mStableChargingThresholdMillis;
@@ -877,7 +883,8 @@
|| event.mEventType == UsageEvents.Event.USER_INTERACTION
|| event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN
|| event.mEventType == UsageEvents.Event.SLICE_PINNED
- || event.mEventType == UsageEvents.Event.SLICE_PINNED_PRIV)) {
+ || event.mEventType == UsageEvents.Event.SLICE_PINNED_PRIV
+ || event.mEventType == UsageEvents.Event.FOREGROUND_SERVICE_START)) {
final AppUsageHistory appHistory = mAppIdleHistory.getAppUsageHistory(
event.mPackage, userId, elapsedRealtime);
@@ -898,6 +905,13 @@
STANDBY_BUCKET_ACTIVE, subReason,
0, elapsedRealtime + mSystemInteractionTimeoutMillis);
nextCheckTime = mSystemInteractionTimeoutMillis;
+ } else if (event.mEventType == UsageEvents.Event.FOREGROUND_SERVICE_START) {
+ // Only elevate bucket if this is the first usage of the app
+ if (prevBucket != STANDBY_BUCKET_NEVER) return;
+ mAppIdleHistory.reportUsage(appHistory, event.mPackage,
+ STANDBY_BUCKET_ACTIVE, subReason,
+ 0, elapsedRealtime + mInitialForegroundServiceStartTimeoutMillis);
+ nextCheckTime = mInitialForegroundServiceStartTimeoutMillis;
} else {
mAppIdleHistory.reportUsage(appHistory, event.mPackage,
STANDBY_BUCKET_ACTIVE, subReason,
@@ -930,6 +944,8 @@
case UsageEvents.Event.NOTIFICATION_SEEN: return REASON_SUB_USAGE_NOTIFICATION_SEEN;
case UsageEvents.Event.SLICE_PINNED: return REASON_SUB_USAGE_SLICE_PINNED;
case UsageEvents.Event.SLICE_PINNED_PRIV: return REASON_SUB_USAGE_SLICE_PINNED_PRIV;
+ case UsageEvents.Event.FOREGROUND_SERVICE_START:
+ return REASON_SUB_USAGE_FOREGROUND_SERVICE_START;
default: return 0;
}
}
@@ -1509,6 +1525,9 @@
pw.print(" mSystemInteractionTimeoutMillis=");
TimeUtils.formatDuration(mSystemInteractionTimeoutMillis, pw);
pw.println();
+ pw.print(" mInitialForegroundServiceStartTimeoutMillis=");
+ TimeUtils.formatDuration(mInitialForegroundServiceStartTimeoutMillis, pw);
+ pw.println();
pw.print(" mPredictionTimeoutMillis=");
TimeUtils.formatDuration(mPredictionTimeoutMillis, pw);
@@ -1848,6 +1867,8 @@
"unexempted_sync_scheduled_duration";
private static final String KEY_SYSTEM_INTERACTION_HOLD_DURATION =
"system_interaction_duration";
+ private static final String KEY_INITIAL_FOREGROUND_SERVICE_START_HOLD_DURATION =
+ "initial_foreground_service_start_duration";
private static final String KEY_STABLE_CHARGING_THRESHOLD = "stable_charging_threshold";
public static final long DEFAULT_STRONG_USAGE_TIMEOUT = 1 * ONE_HOUR;
public static final long DEFAULT_NOTIFICATION_TIMEOUT = 12 * ONE_HOUR;
@@ -1859,6 +1880,7 @@
public static final long DEFAULT_EXEMPTED_SYNC_START_TIMEOUT = 10 * ONE_MINUTE;
public static final long DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT = 10 * ONE_MINUTE;
public static final long DEFAULT_STABLE_CHARGING_THRESHOLD = 10 * ONE_MINUTE;
+ public static final long DEFAULT_INITIAL_FOREGROUND_SERVICE_START_TIMEOUT = 30 * ONE_MINUTE;
private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -1964,6 +1986,12 @@
mSystemInteractionTimeoutMillis = mParser.getDurationMillis(
KEY_SYSTEM_INTERACTION_HOLD_DURATION,
COMPRESS_TIME ? ONE_MINUTE : DEFAULT_SYSTEM_INTERACTION_TIMEOUT);
+
+ mInitialForegroundServiceStartTimeoutMillis = mParser.getDurationMillis(
+ KEY_INITIAL_FOREGROUND_SERVICE_START_HOLD_DURATION,
+ COMPRESS_TIME ? ONE_MINUTE :
+ DEFAULT_INITIAL_FOREGROUND_SERVICE_START_TIMEOUT);
+
mStableChargingThresholdMillis = mParser.getDurationMillis(
KEY_STABLE_CHARGING_THRESHOLD,
COMPRESS_TIME ? ONE_MINUTE : DEFAULT_STABLE_CHARGING_THRESHOLD);
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index a814c03..5cd46ca 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -301,4 +301,9 @@
void setTestAutoModeApp(String packageName);
+ /**
+ * @see TelecomServiceImpl#setTestDefaultDialer
+ */
+ void setTestDefaultDialer(in String packageName);
+
}
diff --git a/telephony/java/android/telephony/UiccSlotInfo.java b/telephony/java/android/telephony/UiccSlotInfo.java
index 785d7ae..9a8d7cd 100644
--- a/telephony/java/android/telephony/UiccSlotInfo.java
+++ b/telephony/java/android/telephony/UiccSlotInfo.java
@@ -140,6 +140,10 @@
return mIsEuicc;
}
+ /**
+ * Returns the ICCID of a the UICC in the given slot, or the EID if it is an eUICC. Note that if
+ * the value is unavailble this will return null.
+ */
public String getCardId() {
return mCardId;
}
diff --git a/tools/bit/main.cpp b/tools/bit/main.cpp
index 1a91f52..d80c2e7 100644
--- a/tools/bit/main.cpp
+++ b/tools/bit/main.cpp
@@ -290,8 +290,14 @@
m_currentAction->target->name.c_str(), className.c_str(),
testName.c_str(), g_escapeEndColor);
- string stack = get_bundle_string(results, &found, "stack", NULL);
- if (found) {
+ bool stackFound;
+ string stack = get_bundle_string(results, &stackFound, "stack", NULL);
+ if (status.has_logcat()) {
+ const string logcat = status.logcat();
+ if (logcat.length() > 0) {
+ printf("%s\n", logcat.c_str());
+ }
+ } else if (stackFound) {
printf("%s\n", stack.c_str());
}
}