Merge "Fix spurious "Timeout waiting for provider""
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index 8a9c774..b905273 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -606,10 +606,6 @@
return hasLateConstraint;
}
- private static boolean kindofEqualsBundle(BaseBundle a, BaseBundle b) {
- return (a == b) || (a != null && a.kindofEquals(b));
- }
-
@Override
public boolean equals(Object o) {
if (!(o instanceof JobInfo)) {
@@ -620,11 +616,11 @@
return false;
}
// XXX won't be correct if one is parcelled and the other not.
- if (!kindofEqualsBundle(extras, j.extras)) {
+ if (!BaseBundle.kindofEquals(extras, j.extras)) {
return false;
}
// XXX won't be correct if one is parcelled and the other not.
- if (!kindofEqualsBundle(transientExtras, j.transientExtras)) {
+ if (!BaseBundle.kindofEquals(transientExtras, j.transientExtras)) {
return false;
}
// XXX for now we consider two different clip data objects to be different,
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index cf7f380..789f20b 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -1648,11 +1648,11 @@
pw.println();
}
}
- if (job.getExtras() != null && !job.getExtras().maybeIsEmpty()) {
+ if (job.getExtras() != null && !job.getExtras().isDefinitelyEmpty()) {
pw.print(prefix); pw.print(" Extras: ");
pw.println(job.getExtras().toShortString());
}
- if (job.getTransientExtras() != null && !job.getTransientExtras().maybeIsEmpty()) {
+ if (job.getTransientExtras() != null && !job.getTransientExtras().isDefinitelyEmpty()) {
pw.print(prefix); pw.print(" Transient extras: ");
pw.println(job.getTransientExtras().toShortString());
}
@@ -1869,10 +1869,10 @@
job.getTriggerContentMaxDelay());
}
}
- if (job.getExtras() != null && !job.getExtras().maybeIsEmpty()) {
+ if (job.getExtras() != null && !job.getExtras().isDefinitelyEmpty()) {
job.getExtras().dumpDebug(proto, JobStatusDumpProto.JobInfo.EXTRAS);
}
- if (job.getTransientExtras() != null && !job.getTransientExtras().maybeIsEmpty()) {
+ if (job.getTransientExtras() != null && !job.getTransientExtras().isDefinitelyEmpty()) {
job.getTransientExtras().dumpDebug(proto, JobStatusDumpProto.JobInfo.TRANSIENT_EXTRAS);
}
if (job.getClipData() != null) {
diff --git a/apex/statsd/Android.bp b/apex/statsd/Android.bp
index baa1c25..1f9f18c 100644
--- a/apex/statsd/Android.bp
+++ b/apex/statsd/Android.bp
@@ -20,8 +20,6 @@
apex_defaults {
native_shared_libs: [
- "libstatssocket",
- "libstatspull",
"libstats_jni",
],
// binaries: ["vold"],
@@ -30,7 +28,6 @@
"service-statsd",
],
// prebuilts: ["my_prebuilt"],
- compile_multilib: "both",
name: "com.android.os.statsd-defaults",
key: "com.android.os.statsd.key",
certificate: ":com.android.os.statsd.certificate",
@@ -77,4 +74,4 @@
//TODO (b/148620413): remove platform.
"//apex_available:platform",
],
-}
+}
\ No newline at end of file
diff --git a/apex/statsd/aidl/Android.bp b/apex/statsd/aidl/Android.bp
index 4ccdd7e..7c93bc7 100644
--- a/apex/statsd/aidl/Android.bp
+++ b/apex/statsd/aidl/Android.bp
@@ -38,10 +38,6 @@
},
ndk: {
enabled: true,
- apex_available: [
- "com.android.os.statsd",
- ],
}
-
- },
+ }
}
diff --git a/api/current.txt b/api/current.txt
index 6a918d9..c2bbb6d 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6426,9 +6426,13 @@
public class StatusBarManager {
}
- public final class SyncNotedAppOp {
+ public final class SyncNotedAppOp implements android.os.Parcelable {
+ ctor public SyncNotedAppOp(@IntRange(from=0L) int, @Nullable String);
+ method public int describeContents();
method @Nullable public String getFeatureId();
method @NonNull public String getOp();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.SyncNotedAppOp> CREATOR;
}
@Deprecated public class TabActivity extends android.app.ActivityGroup {
@@ -30816,7 +30820,7 @@
public class AudioGroup {
ctor @Deprecated public AudioGroup();
- ctor public AudioGroup(@Nullable android.content.Context);
+ ctor public AudioGroup(@NonNull android.content.Context);
method public void clear();
method public int getMode();
method public android.net.rtp.AudioStream[] getStreams();
@@ -43048,6 +43052,7 @@
method @NonNull public android.service.autofill.FillResponse build();
method @NonNull public android.service.autofill.FillResponse.Builder disableAutofill(long);
method @NonNull public android.service.autofill.FillResponse.Builder setAuthentication(@NonNull android.view.autofill.AutofillId[], @Nullable android.content.IntentSender, @Nullable android.widget.RemoteViews);
+ method @NonNull public android.service.autofill.FillResponse.Builder setAuthentication(@NonNull android.view.autofill.AutofillId[], @Nullable android.content.IntentSender, @Nullable android.widget.RemoteViews, @Nullable android.service.autofill.InlinePresentation);
method @NonNull public android.service.autofill.FillResponse.Builder setClientState(@Nullable android.os.Bundle);
method @NonNull public android.service.autofill.FillResponse.Builder setFieldClassificationIds(@NonNull android.view.autofill.AutofillId...);
method @NonNull public android.service.autofill.FillResponse.Builder setFlags(int);
@@ -46894,7 +46899,7 @@
}
public final class CellIdentityGsm extends android.telephony.CellIdentity {
- method @NonNull public java.util.List<java.lang.String> getAdditionalPlmns();
+ method @NonNull public java.util.Set<java.lang.String> getAdditionalPlmns();
method public int getArfcn();
method public int getBsic();
method public int getCid();
@@ -46910,7 +46915,7 @@
}
public final class CellIdentityLte extends android.telephony.CellIdentity {
- method @NonNull public java.util.List<java.lang.String> getAdditionalPlmns();
+ method @NonNull public java.util.Set<java.lang.String> getAdditionalPlmns();
method @NonNull public java.util.List<java.lang.Integer> getBands();
method public int getBandwidth();
method public int getCi();
@@ -46928,7 +46933,7 @@
}
public final class CellIdentityNr extends android.telephony.CellIdentity {
- method @NonNull public java.util.List<java.lang.String> getAdditionalPlmns();
+ method @NonNull public java.util.Set<java.lang.String> getAdditionalPlmns();
method @NonNull public java.util.List<java.lang.Integer> getBands();
method @Nullable public String getMccString();
method @Nullable public String getMncString();
@@ -46941,7 +46946,7 @@
}
public final class CellIdentityTdscdma extends android.telephony.CellIdentity {
- method @NonNull public java.util.List<java.lang.String> getAdditionalPlmns();
+ method @NonNull public java.util.Set<java.lang.String> getAdditionalPlmns();
method public int getCid();
method @Nullable public android.telephony.ClosedSubscriberGroupInfo getClosedSubscriberGroupInfo();
method public int getCpid();
@@ -46955,7 +46960,7 @@
}
public final class CellIdentityWcdma extends android.telephony.CellIdentity {
- method @NonNull public java.util.List<java.lang.String> getAdditionalPlmns();
+ method @NonNull public java.util.Set<java.lang.String> getAdditionalPlmns();
method public int getCid();
method @Nullable public android.telephony.ClosedSubscriberGroupInfo getClosedSubscriberGroupInfo();
method public int getLac();
@@ -47245,7 +47250,7 @@
method @Nullable public android.telephony.CellIdentity getCellIdentity();
method public int getDomain();
method public int getNrState();
- method @NonNull public String getRegisteredPlmn();
+ method @Nullable public String getRegisteredPlmn();
method public int getTransportType();
method public boolean isRegistered();
method public boolean isRoaming();
diff --git a/api/system-current.txt b/api/system-current.txt
index 3b1ab45..c6d4eb3 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -18,6 +18,7 @@
field public static final String ACCESS_SHARED_LIBRARIES = "android.permission.ACCESS_SHARED_LIBRARIES";
field public static final String ACCESS_SHORTCUTS = "android.permission.ACCESS_SHORTCUTS";
field public static final String ACCESS_SURFACE_FLINGER = "android.permission.ACCESS_SURFACE_FLINGER";
+ field public static final String ACCESS_TV_DESCRAMBLER = "android.permission.ACCESS_TV_DESCRAMBLER";
field public static final String ACCESS_TV_TUNER = "android.permission.ACCESS_TV_TUNER";
field public static final String ACCESS_VIBRATOR_STATE = "android.permission.ACCESS_VIBRATOR_STATE";
field public static final String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING";
@@ -364,6 +365,7 @@
}
public class AppOpsManager {
+ method @Nullable @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public android.app.RuntimeAppOpAccessMessage collectRuntimeAppOpAccessMessage();
method @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public void getHistoricalOps(@NonNull android.app.AppOpsManager.HistoricalOpsRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>);
method public static String[] getOpStrs();
method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.app.AppOpsManager.PackageOps> getOpsForPackage(int, @NonNull String, @Nullable java.lang.String...);
@@ -680,6 +682,19 @@
method public void setNotificationAssistantAccessGranted(@Nullable android.content.ComponentName, boolean);
}
+ public final class RuntimeAppOpAccessMessage implements android.os.Parcelable {
+ ctor public RuntimeAppOpAccessMessage(@IntRange(from=0L) int, @IntRange(from=0L) int, @NonNull String, @Nullable String, @NonNull String, int);
+ method public int describeContents();
+ method @Nullable public String getFeatureId();
+ method @NonNull public String getMessage();
+ method @NonNull public String getOp();
+ method @NonNull public String getPackageName();
+ method public int getSamplingStrategy();
+ method @IntRange(from=0L) public int getUid();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.RuntimeAppOpAccessMessage> CREATOR;
+ }
+
public class SearchManager implements android.content.DialogInterface.OnCancelListener android.content.DialogInterface.OnDismissListener {
method public void launchAssist(@Nullable android.os.Bundle);
}
@@ -4877,7 +4892,7 @@
method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public static android.media.tv.tuner.DemuxCapabilities getDemuxCapabilities(@NonNull android.content.Context);
method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.frontend.FrontendInfo getFrontendInfo();
method @Nullable public android.media.tv.tuner.frontend.FrontendStatus getFrontendStatus(@NonNull int[]);
- method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.Descrambler openDescrambler();
+ method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_DESCRAMBLER) public android.media.tv.tuner.Descrambler openDescrambler();
method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.dvr.DvrPlayback openDvrPlayback(long, @Nullable java.util.concurrent.Executor, @Nullable android.media.tv.tuner.dvr.OnPlaybackStatusChangedListener);
method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.dvr.DvrRecorder openDvrRecorder(long, @Nullable java.util.concurrent.Executor, @Nullable android.media.tv.tuner.dvr.OnRecordStatusChangedListener);
method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.filter.Filter openFilter(int, int, long, @Nullable java.util.concurrent.Executor, @Nullable android.media.tv.tuner.filter.FilterCallback);
@@ -9680,6 +9695,7 @@
field public static final String ACTION_REQUEST_ENABLE_CONTENT_CAPTURE = "android.settings.REQUEST_ENABLE_CONTENT_CAPTURE";
field public static final String ACTION_SHOW_ADMIN_SUPPORT_DETAILS = "android.settings.SHOW_ADMIN_SUPPORT_DETAILS";
field public static final String ACTION_TETHER_PROVISIONING_UI = "android.settings.TETHER_PROVISIONING_UI";
+ field public static final String ACTION_TETHER_SETTINGS = "android.settings.TETHER_SETTINGS";
}
public static final class Settings.Global extends android.provider.Settings.NameValueTable {
@@ -14063,6 +14079,14 @@
}
+package android.view.inline {
+
+ public final class InlinePresentationSpec implements android.os.Parcelable {
+ method @Nullable public String getStyle();
+ }
+
+}
+
package android.webkit {
public abstract class CookieManager {
diff --git a/api/test-current.txt b/api/test-current.txt
index 4c8bb02..7284fe6 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -164,6 +164,7 @@
public class AppOpsManager {
method @RequiresPermission("android.permission.MANAGE_APPOPS") public void addHistoricalOps(@NonNull android.app.AppOpsManager.HistoricalOps);
method @RequiresPermission("android.permission.MANAGE_APPOPS") public void clearHistory();
+ method @Nullable @RequiresPermission("android.permission.GET_APP_OPS_STATS") public android.app.RuntimeAppOpAccessMessage collectRuntimeAppOpAccessMessage();
method @RequiresPermission("android.permission.GET_APP_OPS_STATS") public void getHistoricalOps(@NonNull android.app.AppOpsManager.HistoricalOpsRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>);
method @RequiresPermission("android.permission.MANAGE_APPOPS") public void getHistoricalOpsFromDiskRaw(@NonNull android.app.AppOpsManager.HistoricalOpsRequest, @Nullable java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>);
method public static int getNumOps();
@@ -447,6 +448,19 @@
method public android.graphics.Rect getSourceRectHint();
}
+ public final class RuntimeAppOpAccessMessage implements android.os.Parcelable {
+ ctor public RuntimeAppOpAccessMessage(@IntRange(from=0L) int, @IntRange(from=0L) int, @NonNull String, @Nullable String, @NonNull String, int);
+ method public int describeContents();
+ method @Nullable public String getFeatureId();
+ method @NonNull public String getMessage();
+ method @NonNull public String getOp();
+ method @NonNull public String getPackageName();
+ method public int getSamplingStrategy();
+ method @IntRange(from=0L) public int getUid();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.RuntimeAppOpAccessMessage> CREATOR;
+ }
+
public class StatusBarManager {
method public void collapsePanels();
method public void expandNotificationsPanel();
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 1bcf44e..956fd29 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -120,14 +120,13 @@
"libstatslog",
"libstatsmetadata",
"libsysutils",
- // TODO(b/145923087): move to shared when statsd is moved to the apex
- "libstatssocket",
"libutils",
],
shared_libs: [
"libbinder",
"libincident",
"liblog",
+ "libstatssocket",
"statsd-aidl-cpp",
],
}
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 23a4437..27c4f4e 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -8000,6 +8000,18 @@
// of this pull. If this number grows prohibitively large, then this can
// cause jank due to resource contention.
optional int32 event_connection_count = 6;
+ // Set of timings measured from when SurfaceFlinger began compositing a
+ // frame, until the frame was requested to be presented to the display. This
+ // measures SurfaceFlinger's total CPU walltime on the critical path per
+ // frame.
+ optional FrameTimingHistogram frame_duration = 7
+ [(android.os.statsd.log_mode) = MODE_BYTES];
+ // Set of timings measured from when SurfaceFlinger first began using the
+ // GPU to composite a frame, until the GPU has finished compositing that
+ // frame. This measures the total additional time SurfaceFlinger needed to
+ // perform due to falling back into GPU composition.
+ optional FrameTimingHistogram render_engine_timing = 8
+ [(android.os.statsd.log_mode) = MODE_BYTES];
}
/**
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 103eb0c..1ef1ab2 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -290,9 +290,9 @@
}
bool LogEvent::writeBytes(const string& value) {
- if (mContext) {
+ /* if (mContext) {
return android_log_write_char_array(mContext, value.c_str(), value.length()) >= 0;
- }
+ }*/
return false;
}
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index a67bef4..ea449b0 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -20,7 +20,6 @@
#include <android/util/ProtoOutputStream.h>
#include <private/android_logger.h>
-#include <stats_event_list.h>
#include <stats_event.h>
#include <string>
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 395a196..5df67bc 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1907,6 +1907,7 @@
if (!mAutoFillIgnoreFirstResumePause) {
View focus = getCurrentFocus();
if (focus != null && focus.canNotifyAutofillEnterExitEvent()) {
+ // TODO(b/148815880): Bring up keyboard if resumed from inline authentication.
// TODO: in Activity killed/recreated case, i.e. SessionLifecycleTest#
// testDatasetVisibleWhileAutofilledAppIsLifecycled: the View's initial
// window visibility after recreation is INVISIBLE in onResume() and next frame
@@ -8467,7 +8468,7 @@
/** @hide */
@Override
public final void autofillClientAuthenticate(int authenticationId, IntentSender intent,
- Intent fillInIntent) {
+ Intent fillInIntent, boolean authenticateInline) {
try {
startIntentSenderForResultInner(intent, AUTO_FILL_AUTH_WHO_PREFIX,
authenticationId, fillInIntent, 0, 0, null);
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 1de68ba..f6bbc68 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -16,6 +16,10 @@
package android.app;
+import static android.util.StatsLogInternal.RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__DEFAULT;
+import static android.util.StatsLogInternal.RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__RARELY_USED;
+import static android.util.StatsLogInternal.RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__UNIFORM;
+
import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
@@ -48,6 +52,8 @@
import android.os.Process;
import android.os.RemoteCallback;
import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
import android.os.UserManager;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -63,6 +69,7 @@
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsNotedCallback;
import com.android.internal.app.IAppOpsService;
+import com.android.internal.app.MessageSamplingConfig;
import com.android.internal.os.RuntimeInit;
import com.android.internal.os.ZygoteInit;
import com.android.internal.util.ArrayUtils;
@@ -141,6 +148,13 @@
@UnsupportedAppUsage
final IAppOpsService mService;
+ /**
+ * Service for the application context, to be used by static methods via
+ * {@link #getService()}
+ */
+ @GuardedBy("sLock")
+ static IAppOpsService sService;
+
@GuardedBy("mModeWatchers")
private final ArrayMap<OnOpChangedListener, IAppOpsCallback> mModeWatchers =
new ArrayMap<>();
@@ -159,6 +173,50 @@
@GuardedBy("sLock")
private static @Nullable AppOpsCollector sNotedAppOpsCollector;
+ /**
+ * Additional collector that collect accesses and forwards a few of them them via
+ * {@link IAppOpsService#reportRuntimeAppOpAccessMessageAndGetConfig}.
+ */
+ private static AppOpsCollector sMessageCollector =
+ new AppOpsCollector() {
+ @Override
+ public void onNoted(@NonNull SyncNotedAppOp op) {
+ reportStackTraceIfNeeded(op);
+ }
+
+ @Override
+ public void onAsyncNoted(@NonNull AsyncNotedAppOp asyncOp) {
+ // collected directly in AppOpsService
+ }
+
+ @Override
+ public void onSelfNoted(@NonNull SyncNotedAppOp op) {
+ reportStackTraceIfNeeded(op);
+ }
+
+ private void reportStackTraceIfNeeded(@NonNull SyncNotedAppOp op) {
+ if (sConfig.getSampledOpCode() == OP_NONE
+ && sConfig.getExpirationTimeSinceBootMillis()
+ >= SystemClock.elapsedRealtime()) {
+ return;
+ }
+
+ MessageSamplingConfig config = sConfig;
+ if (leftCircularDistance(strOpToOp(op.getOp()), config.getSampledOpCode(),
+ _NUM_OP) <= config.getAcceptableLeftDistance()
+ || config.getExpirationTimeSinceBootMillis()
+ < SystemClock.elapsedRealtime()) {
+ String stackTrace = getFormattedStackTrace();
+ try {
+ sConfig = getService().reportRuntimeAppOpAccessMessageAndGetConfig(
+ ActivityThread.currentOpPackageName(), op, stackTrace);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ }
+ };
+
static IBinder sClientId;
/**
@@ -550,7 +608,6 @@
})
public @interface OpFlags {}
-
/** @hide */
public static final String getFlagName(@OpFlags int flag) {
switch (flag) {
@@ -569,6 +626,18 @@
}
}
+ /**
+ * Strategies used for message sampling
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"RUNTIME_APP_OPS_ACCESS__SAMPLING_STRATEGY__"}, value = {
+ RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__DEFAULT,
+ RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__UNIFORM,
+ RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__RARELY_USED
+ })
+ public @interface SamplingStrategy {}
+
private static final int UID_STATE_OFFSET = 31;
private static final int FLAGS_MASK = 0xFFFFFFFF;
@@ -2225,6 +2294,10 @@
}
}
+ /** Config used to control app ops access messages sampling */
+ private static MessageSamplingConfig sConfig =
+ new MessageSamplingConfig(OP_NONE, 0, 0);
+
/** @hide */
public static final String KEY_HISTORICAL_OPS = "historical_ops";
@@ -7268,6 +7341,17 @@
}
}
+ /** @hide */
+ private static IAppOpsService getService() {
+ synchronized (sLock) {
+ if (sService == null) {
+ sService = IAppOpsService.Stub.asInterface(
+ ServiceManager.getService(Context.APP_OPS_SERVICE));
+ }
+ return sService;
+ }
+ }
+
/**
* @deprecated use {@link #startOp(String, int, String, String, String)} instead
*/
@@ -7614,6 +7698,7 @@
sNotedAppOpsCollector.onSelfNoted(new SyncNotedAppOp(op, featureId));
}
}
+ sMessageCollector.onSelfNoted(new SyncNotedAppOp(op, featureId));
}
/**
@@ -7764,6 +7849,10 @@
}
}
}
+ for (int code = notedAppOps.nextSetBit(0); code != -1;
+ code = notedAppOps.nextSetBit(code + 1)) {
+ sMessageCollector.onNoted(new SyncNotedAppOp(code, featureId));
+ }
}
}
}
@@ -7958,10 +8047,13 @@
StringBuilder sb = new StringBuilder();
for (int i = firstInteresting; i <= lastInteresting; i++) {
- sb.append(trace[i]);
- if (i != lastInteresting) {
+ if (i != firstInteresting) {
sb.append('\n');
}
+ if (sb.length() + trace[i].toString().length() > 600) {
+ break;
+ }
+ sb.append(trace[i]);
}
return sb.toString();
@@ -8089,6 +8181,22 @@
}
/**
+ * Pulls current AppOps access report and picks package and op to watch for next access report
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ @RequiresPermission(Manifest.permission.GET_APP_OPS_STATS)
+ public @Nullable RuntimeAppOpAccessMessage collectRuntimeAppOpAccessMessage() {
+ try {
+ return mService.collectRuntimeAppOpAccessMessage();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns all supported operation names.
* @hide
*/
@@ -8297,4 +8405,12 @@
return AppOpsManager.MODE_DEFAULT;
}
+
+ /**
+ * Calculate left circular distance for two numbers modulo size.
+ * @hide
+ */
+ public static int leftCircularDistance(int from, int to, int size) {
+ return (to + size - from) % size;
+ }
}
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 180507c..3c475c1 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -315,11 +315,6 @@
void positionTaskInStack(int taskId, int stackId, int position);
void reportSizeConfigurations(in IBinder token, in int[] horizontalSizeConfiguration,
in int[] verticalSizeConfigurations, in int[] smallestWidthConfigurations);
- /**
- * Dismisses split-screen multi-window mode.
- * {@param toTop} If true the current primary split-screen stack will be placed or left on top.
- */
- void dismissSplitScreenMode(boolean toTop);
/**
* Dismisses PiP
diff --git a/core/java/android/app/NotificationHistory.java b/core/java/android/app/NotificationHistory.java
index f26e628..d16120d 100644
--- a/core/java/android/app/NotificationHistory.java
+++ b/core/java/android/app/NotificationHistory.java
@@ -21,6 +21,7 @@
import android.graphics.drawable.Icon;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.TextUtils;
import java.util.ArrayList;
import java.util.Arrays;
@@ -51,6 +52,7 @@
private String mTitle;
private String mText;
private Icon mIcon;
+ private String mConversationId;
private HistoricalNotification() {}
@@ -94,6 +96,10 @@
return mPackage + "|" + mUid + "|" + mPostedTimeMs;
}
+ public String getConversationId() {
+ return mConversationId;
+ }
+
@Override
public String toString() {
return "HistoricalNotification{" +
@@ -104,6 +110,7 @@
", mTitle='" + mTitle + '\'' +
", mText='" + mText + '\'' +
", mIcon=" + mIcon +
+ ", mConversationId=" + mConversationId +
'}';
}
@@ -123,6 +130,7 @@
Objects.equals(getChannelId(), that.getChannelId()) &&
Objects.equals(getTitle(), that.getTitle()) &&
Objects.equals(getText(), that.getText()) &&
+ Objects.equals(getConversationId(), that.getConversationId()) &&
iconsAreSame;
}
@@ -130,7 +138,7 @@
public int hashCode() {
return Objects.hash(getPackage(), getChannelName(), getChannelId(), getUid(),
getUserId(),
- getPostedTimeMs(), getTitle(), getText(), getIcon());
+ getPostedTimeMs(), getTitle(), getText(), getIcon(), getConversationId());
}
public static final class Builder {
@@ -143,6 +151,7 @@
private String mTitle;
private String mText;
private Icon mIcon;
+ private String mConversationId;
public Builder() {}
@@ -191,6 +200,11 @@
return this;
}
+ public Builder setConversationId(String conversationId) {
+ mConversationId = conversationId;
+ return this;
+ }
+
public HistoricalNotification build() {
HistoricalNotification n = new HistoricalNotification();
n.mPackage = mPackage;
@@ -202,6 +216,7 @@
n.mTitle = mTitle;
n.mText = mText;
n.mIcon = mIcon;
+ n.mConversationId = mConversationId;
return n;
}
}
@@ -299,6 +314,9 @@
mStringsToWrite.add(notification.getPackage());
mStringsToWrite.add(notification.getChannelName());
mStringsToWrite.add(notification.getChannelId());
+ if (!TextUtils.isEmpty(notification.getConversationId())) {
+ mStringsToWrite.add(notification.getConversationId());
+ }
}
}
@@ -423,9 +441,17 @@
channelIdIndex = -1;
}
+ final int conversationIdIndex;
+ if (!TextUtils.isEmpty(notification.getConversationId())) {
+ conversationIdIndex = findStringIndex(notification.getConversationId());
+ } else {
+ conversationIdIndex = -1;
+ }
+
p.writeInt(packageIndex);
p.writeInt(channelNameIndex);
p.writeInt(channelIdIndex);
+ p.writeInt(conversationIdIndex);
p.writeInt(notification.getUid());
p.writeInt(notification.getUserId());
p.writeLong(notification.getPostedTimeMs());
@@ -461,6 +487,13 @@
notificationOut.setChannelId(null);
}
+ final int conversationIdIndex = p.readInt();
+ if (conversationIdIndex >= 0) {
+ notificationOut.setConversationId(mStringPool[conversationIdIndex]);
+ } else {
+ notificationOut.setConversationId(null);
+ }
+
notificationOut.setUid(p.readInt());
notificationOut.setUserId(p.readInt());
notificationOut.setPostedTimeMs(p.readLong());
diff --git a/core/java/android/app/RuntimeAppOpAccessMessage.aidl b/core/java/android/app/RuntimeAppOpAccessMessage.aidl
new file mode 100644
index 0000000..68e8819
--- /dev/null
+++ b/core/java/android/app/RuntimeAppOpAccessMessage.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+parcelable RuntimeAppOpAccessMessage;
diff --git a/core/java/android/app/RuntimeAppOpAccessMessage.java b/core/java/android/app/RuntimeAppOpAccessMessage.java
new file mode 100644
index 0000000..a81b8e7
--- /dev/null
+++ b/core/java/android/app/RuntimeAppOpAccessMessage.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.annotations.Immutable;
+import com.android.internal.util.DataClass;
+
+/**
+ * Message for noted runtime permission access.
+ * @hide
+ */
+@Immutable
+@TestApi
+@SystemApi
+/*@DataClass(genConstructor = false)
+@DataClass.Suppress("getOpCode")*/
+public final class RuntimeAppOpAccessMessage implements Parcelable {
+ /** Uid of package for which runtime app op access message was collected */
+ private final @IntRange(from = 0L) int mUid;
+ /** Op code of operation access which was collected */
+ private final @IntRange(from = 0L, to = AppOpsManager._NUM_OP - 1) int mOpCode;
+ /** Name of package for which runtime app op access message was collected */
+ private final @NonNull String mPackageName;
+ /** Feature of package for which runtime app op access message was collected */
+ private final @Nullable String mFeatureId;
+ /** Message collected (including stacktrace for synchronous ops) */
+ private final @NonNull String mMessage;
+ /** Sampling strategy used to collect this message. */
+ private final @AppOpsManager.SamplingStrategy int mSamplingStrategy;
+
+ public @NonNull String getOp() {
+ return AppOpsManager.opToPublicName(mOpCode);
+ }
+
+ /**
+ * Creates a new RuntimeAppOpAccessMessage.
+ *
+ * @param uid
+ * Uid of package for which runtime app op access message was collected
+ * @param opCode
+ * Op code of operation access which was collected
+ * @param packageName
+ * Name of package for which runtime app op access message was collected
+ * @param featureId
+ * Feature of package for which runtime app op access message was collected
+ * @param message
+ * Message collected (including stacktrace for synchronous ops)
+ * @param samplingStrategy
+ * Sampling strategy used to collect this message.
+ */
+ @DataClass.Generated.Member
+ public RuntimeAppOpAccessMessage(
+ @IntRange(from = 0L) int uid,
+ @IntRange(from = 0L) int opCode,
+ @NonNull String packageName,
+ @Nullable String featureId,
+ @NonNull String message,
+ @AppOpsManager.SamplingStrategy int samplingStrategy) {
+ this.mUid = uid;
+ com.android.internal.util.AnnotationValidations.validate(
+ IntRange.class, null, mUid,
+ "from", 0L);
+ this.mOpCode = opCode;
+ com.android.internal.util.AnnotationValidations.validate(
+ IntRange.class, null, mOpCode,
+ "from", 0L,
+ "to", AppOpsManager._NUM_OP - 1);
+ this.mPackageName = packageName;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mPackageName);
+ this.mFeatureId = featureId;
+ this.mMessage = message;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mMessage);
+ this.mSamplingStrategy = samplingStrategy;
+ com.android.internal.util.AnnotationValidations.validate(
+ AppOpsManager.SamplingStrategy.class, null, mSamplingStrategy);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+
+
+
+ // Code below generated by codegen v1.0.14.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/app/RuntimeAppOpAccessMessage.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /**
+ * Uid of package for which runtime app op access message was collected
+ */
+ @DataClass.Generated.Member
+ public @IntRange(from = 0L) int getUid() {
+ return mUid;
+ }
+
+ /**
+ * Name of package for which runtime app op access message was collected
+ */
+ @DataClass.Generated.Member
+ public @NonNull String getPackageName() {
+ return mPackageName;
+ }
+
+ /**
+ * Feature of package for which runtime app op access message was collected
+ */
+ @DataClass.Generated.Member
+ public @Nullable String getFeatureId() {
+ return mFeatureId;
+ }
+
+ /**
+ * Message collected (including stacktrace for synchronous ops)
+ */
+ @DataClass.Generated.Member
+ public @NonNull String getMessage() {
+ return mMessage;
+ }
+
+ /**
+ * Sampling strategy used to collect this message.
+ */
+ @DataClass.Generated.Member
+ public @AppOpsManager.SamplingStrategy int getSamplingStrategy() {
+ return mSamplingStrategy;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ byte flg = 0;
+ if (mFeatureId != null) flg |= 0x8;
+ dest.writeByte(flg);
+ dest.writeInt(mUid);
+ dest.writeInt(mOpCode);
+ dest.writeString(mPackageName);
+ if (mFeatureId != null) dest.writeString(mFeatureId);
+ dest.writeString(mMessage);
+ dest.writeInt(mSamplingStrategy);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ RuntimeAppOpAccessMessage(@NonNull Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ byte flg = in.readByte();
+ int uid = in.readInt();
+ int opCode = in.readInt();
+ String packageName = in.readString();
+ String featureId = (flg & 0x8) == 0 ? null : in.readString();
+ String message = in.readString();
+ int samplingStrategy = in.readInt();
+
+ this.mUid = uid;
+ com.android.internal.util.AnnotationValidations.validate(
+ IntRange.class, null, mUid,
+ "from", 0L);
+ this.mOpCode = opCode;
+ com.android.internal.util.AnnotationValidations.validate(
+ IntRange.class, null, mOpCode,
+ "from", 0L,
+ "to", AppOpsManager._NUM_OP - 1);
+ this.mPackageName = packageName;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mPackageName);
+ this.mFeatureId = featureId;
+ this.mMessage = message;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mMessage);
+ this.mSamplingStrategy = samplingStrategy;
+ com.android.internal.util.AnnotationValidations.validate(
+ AppOpsManager.SamplingStrategy.class, null, mSamplingStrategy);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<RuntimeAppOpAccessMessage> CREATOR
+ = new Parcelable.Creator<RuntimeAppOpAccessMessage>() {
+ @Override
+ public RuntimeAppOpAccessMessage[] newArray(int size) {
+ return new RuntimeAppOpAccessMessage[size];
+ }
+
+ @Override
+ public RuntimeAppOpAccessMessage createFromParcel(@NonNull Parcel in) {
+ return new RuntimeAppOpAccessMessage(in);
+ }
+ };
+
+ /*@DataClass.Generated(
+ time = 1581517099127L,
+ codegenVersion = "1.0.14",
+ sourceFile = "frameworks/base/core/java/android/app/RuntimeAppOpAccessMessage.java",
+ inputSignatures = "private final @android.annotation.IntRange(from=0L) int mUid\nprivate final @android.annotation.IntRange(from=0L, to=AppOpsManager._NUM_OP - 1) int mOpCode\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mFeatureId\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.app.AppOpsManager.SamplingStrategy int mSamplingStrategy\npublic @android.annotation.NonNull java.lang.String getOp()\nclass RuntimeAppOpAccessMessage extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false)")*/
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/app/SyncNotedAppOp.aidl b/core/java/android/app/SyncNotedAppOp.aidl
new file mode 100644
index 0000000..ab062d2
--- /dev/null
+++ b/core/java/android/app/SyncNotedAppOp.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+parcelable SyncNotedAppOp;
diff --git a/core/java/android/app/SyncNotedAppOp.java b/core/java/android/app/SyncNotedAppOp.java
index 065d5de..aa11b95 100644
--- a/core/java/android/app/SyncNotedAppOp.java
+++ b/core/java/android/app/SyncNotedAppOp.java
@@ -19,8 +19,10 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.os.Parcelable;
import com.android.internal.annotations.Immutable;
+import com.android.internal.util.DataClass;
/**
* Description of an app-op that was noted for the current process.
@@ -32,48 +34,154 @@
* itself}.
*/
@Immutable
-public final class SyncNotedAppOp {
- private final int mOpCode;
+/*@DataClass(
+ genEqualsHashCode = true,
+ genConstructor = false
+)
+@DataClass.Suppress("getOpCode")*/
+public final class SyncNotedAppOp implements Parcelable {
+
+ /** op code of synchronous appop noted */
+ private final @IntRange(from = 0L, to = AppOpsManager._NUM_OP - 1) int mOpCode;
+ /** featureId of synchronous appop noted */
private final @Nullable String mFeatureId;
/**
+ * Creates a new SyncNotedAppOp.
+ *
+ * @param opCode
+ * op code of synchronous appop noted
+ * @param featureId
+ * featureId of synchronous appop noted
+ */
+ public SyncNotedAppOp(@IntRange(from = 0L) int opCode, @Nullable String featureId) {
+ this.mOpCode = opCode;
+ com.android.internal.util.AnnotationValidations.validate(
+ IntRange.class, null, mOpCode,
+ "from", 0,
+ "to", AppOpsManager._NUM_OP - 1);
+ this.mFeatureId = featureId;
+ }
+
+ /**
* @return The op that was noted.
*/
public @NonNull String getOp() {
return AppOpsManager.opToPublicName(mOpCode);
}
+ // Code below generated by codegen v1.0.14.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/app/SyncNotedAppOp.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
/**
- * @return The {@link android.content.Context#createFeatureContext Feature} in the app
+ * featureId of synchronous appop noted
*/
+ @DataClass.Generated.Member
public @Nullable String getFeatureId() {
return mFeatureId;
}
- /**
- * Create a new sync op description
- *
- * @param opCode The op that was noted
- *
- * @hide
- */
- public SyncNotedAppOp(@IntRange(from = 0, to = AppOpsManager._NUM_OP - 1) int opCode,
- @Nullable String featureId) {
- mOpCode = opCode;
- mFeatureId = featureId;
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(SyncNotedAppOp other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ SyncNotedAppOp that = (SyncNotedAppOp) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && mOpCode == that.mOpCode
+ && java.util.Objects.equals(mFeatureId, that.mFeatureId);
}
@Override
- public boolean equals(Object other) {
- if (!(other instanceof SyncNotedAppOp)) {
- return false;
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + mOpCode;
+ _hash = 31 * _hash + java.util.Objects.hashCode(mFeatureId);
+ return _hash;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ byte flg = 0;
+ if (mFeatureId != null) flg |= 0x2;
+ dest.writeByte(flg);
+ dest.writeInt(mOpCode);
+ if (mFeatureId != null) dest.writeString(mFeatureId);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ SyncNotedAppOp(@NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ byte flg = in.readByte();
+ int opCode = in.readInt();
+ String featureId = (flg & 0x2) == 0 ? null : in.readString();
+
+ this.mOpCode = opCode;
+ com.android.internal.util.AnnotationValidations.validate(
+ IntRange.class, null, mOpCode,
+ "from", 0,
+ "to", AppOpsManager._NUM_OP - 1);
+ this.mFeatureId = featureId;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<SyncNotedAppOp> CREATOR
+ = new Parcelable.Creator<SyncNotedAppOp>() {
+ @Override
+ public SyncNotedAppOp[] newArray(int size) {
+ return new SyncNotedAppOp[size];
}
- return mOpCode == ((SyncNotedAppOp) other).mOpCode;
- }
+ @Override
+ public SyncNotedAppOp createFromParcel(@NonNull android.os.Parcel in) {
+ return new SyncNotedAppOp(in);
+ }
+ };
- @Override
- public int hashCode() {
- return mOpCode;
- }
+ /*@DataClass.Generated(
+ time = 1579188889960L,
+ codegenVersion = "1.0.14",
+ sourceFile = "frameworks/base/core/java/android/app/SyncNotedAppOp.java",
+ inputSignatures = "private final @android.annotation.IntRange(from=0L, to=AppOpsManager._NUM_OP - 1) int mOpCode\nprivate final @android.annotation.Nullable java.lang.String mFeatureId\npublic @android.annotation.NonNull java.lang.String getOp()\npublic @android.annotation.SystemApi int getOpCode()\nclass SyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genConstructor=false)")*/
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 4a5a23a..40b81dd 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -11580,13 +11580,16 @@
}
/**
- * Sets the set of package names that are allowed to request user consent for cross-profile
- * communication.
+ * Sets the set of admin-whitelisted package names that are allowed to request user consent for
+ * cross-profile communication.
*
* <p>Assumes that the caller is a profile owner and is the given {@code admin}.
*
* <p>Previous calls are overridden by each subsequent call to this method.
*
+ * <p>Note that other apps may be able to request user consent for cross-profile communication
+ * if they have been explicitly whitelisted by the OEM.
+ *
* <p>When previously-set cross-profile packages are missing from {@code packageNames}, the
* app-op for {@code INTERACT_ACROSS_PROFILES} will be reset for those packages. This will not
* occur for packages that are whitelisted by the OEM.
@@ -11608,15 +11611,18 @@
/**
* Returns the set of package names that the admin has previously set as allowed to request user
- * consent for cross-profile communication, via {@link
- * #setCrossProfilePackages(ComponentName, Set)}.
+ * consent for cross-profile communication, via {@link #setCrossProfilePackages(ComponentName,
+ * Set)}.
*
* <p>Assumes that the caller is a profile owner and is the given {@code admin}.
*
+ * <p>Note that other apps not included in the returned set may be able to request user consent
+ * for cross-profile communication if they have been explicitly whitelisted by the OEM.
+ *
* @param admin the {@link DeviceAdminReceiver} this request is associated with
* @return the set of package names the admin has previously set as allowed to request user
- * consent for cross-profile communication, via {@link
- * #setCrossProfilePackages(ComponentName, Set)}
+ * consent for cross-profile communication, via {@link #setCrossProfilePackages(ComponentName,
+ * Set)}
*/
public @NonNull Set<String> getCrossProfilePackages(@NonNull ComponentName admin) {
throwIfParentInstance("getCrossProfilePackages");
@@ -11634,18 +11640,17 @@
* Returns the combined set of the following:
* <ul>
* <li>The package names that the admin has previously set as allowed to request user consent
- * for cross-profile communication, via {@link
- * #setCrossProfilePackages(ComponentName, Set)}.</li>
+ * for cross-profile communication, via {@link #setCrossProfilePackages(ComponentName,
+ * Set)}.</li>
* <li>The default package names set by the OEM that are allowed to request user consent for
- * cross-profile communication without being explicitly enabled by the admin, via
- * {@link com.android.internal.R.array#cross_profile_apps} and
- * {@link com.android.internal.R.array#vendor_cross_profile_apps}.</li>
+ * cross-profile communication without being explicitly enabled by the admin, via {@link
+ * com.android.internal.R.array#cross_profile_apps} and {@link com.android.internal.R.array
+ * #vendor_cross_profile_apps}.</li>
* </ul>
*
* @return the combined set of whitelisted package names set via
- * {@link #setCrossProfilePackages(ComponentName, Set)},
- * {@link com.android.internal.R.array#cross_profile_apps},
- * and {@link com.android.internal.R.array#vendor_cross_profile_apps}.
+ * {@link #setCrossProfilePackages(ComponentName, Set)}, {@link com.android.internal.R.array
+ * #cross_profile_apps}, and {@link com.android.internal.R.array#vendor_cross_profile_apps}.
*
* @hide
*/
@@ -11668,9 +11673,9 @@
/**
* Returns the default package names set by the OEM that are allowed to request user consent for
- * cross-profile communication without being explicitly enabled by the admin, via
- * {@link com.android.internal.R.array#cross_profile_apps} and
- * {@link com.android.internal.R.array#vendor_cross_profile_apps}.
+ * cross-profile communication without being explicitly enabled by the admin, via {@link
+ * com.android.internal.R.array#cross_profile_apps} and {@link com.android.internal.R.array
+ * #vendor_cross_profile_apps}.
*
* @hide
*/
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 0f88c90..2e591ca 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -6767,7 +6767,7 @@
this.mClipData = new ClipData(o.mClipData);
}
} else {
- if (o.mExtras != null && !o.mExtras.maybeIsEmpty()) {
+ if (o.mExtras != null && !o.mExtras.isDefinitelyEmpty()) {
this.mExtras = Bundle.STRIPPED;
}
diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java
index 50841c3..eb1da67 100644
--- a/core/java/android/content/pm/CrossProfileApps.java
+++ b/core/java/android/content/pm/CrossProfileApps.java
@@ -47,8 +47,15 @@
public class CrossProfileApps {
/**
- * Broadcast signalling that the receiving app's ability to interact across profiles has
- * changed, as defined by the return value of {@link #canInteractAcrossProfiles()}.
+ * Broadcast signalling that the receiving app's permission to interact across profiles has
+ * changed. This includes the user, admin, or OEM changing their consent such that the
+ * permission for the app to interact across profiles has changed.
+ *
+ * <p>This broadcast is not sent when other circumstances result in a change to being able to
+ * interact across profiles in practice, such as the profile being turned off or removed, apps
+ * being uninstalled, etc. The methods {@link #canInteractAcrossProfiles()} and {@link
+ * #canRequestInteractAcrossProfiles()} can be used by apps prior to attempting to interact
+ * across profiles or attempting to request user consent to interact across profiles.
*
* <p>Apps that have set the {@code android:crossProfile} manifest attribute to {@code true}
* can receive this broadcast in manifest broadcast receivers. Otherwise, it can only be
@@ -99,8 +106,11 @@
/**
* Starts the specified activity of the caller package in the specified profile.
*
- * <p>The caller must have the {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES}
- * permission and both the caller and target user profiles must be in the same profile group.
+ * <p>The caller must have the {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES},
+ * {@code android.Manifest.permission#INTERACT_ACROSS_USERS}, or {@code
+ * android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission. Both the caller and
+ * target user profiles must be in the same profile group. The target user must be a valid user
+ * returned from {@link #getTargetUserProfiles()}.
*
* @param intent The intent to launch. A component in the caller package must be specified.
* @param targetUser The {@link UserHandle} of the profile; must be one of the users returned by
@@ -219,10 +229,11 @@
}
/**
- * Returns whether the calling package can request to interact across profiles.
+ * Returns whether the calling package can request user consent to interact across profiles.
*
- * <p>The package's current ability to interact across profiles can be checked with
- * {@link #canInteractAcrossProfiles()}.
+ * <p>If {@code true}, user consent can be obtained via {@link
+ * #createRequestInteractAcrossProfilesIntent()}. The package can then listen to {@link
+ * #ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED} broadcasts.
*
* <p>Specifically, returns whether the following are all true:
* <ul>
@@ -235,6 +246,10 @@
* </li>
* </ul>
*
+ * <p>Note that user consent could already be granted if given a return value of {@code true}.
+ * The package's current ability to interact across profiles can be checked with {@link
+ * #canInteractAcrossProfiles()}.
+ *
* @return true if the calling package can request to interact across profiles.
*/
public boolean canRequestInteractAcrossProfiles() {
@@ -247,10 +262,7 @@
/**
* Returns whether the calling package can interact across profiles.
- *
- * <p>The package's current ability to request to interact across profiles can be checked with
- * {@link #canRequestInteractAcrossProfiles()}.
- *
+
* <p>Specifically, returns whether the following are all true:
* <ul>
* <li>{@link #getTargetUserProfiles()} returns a non-empty list for the calling user.</li>
@@ -262,6 +274,11 @@
* </li>
* </ul>
*
+ * <p>If {@code false}, the package's current ability to request user consent to interact across
+ * profiles can be checked with {@link #canRequestInteractAcrossProfiles()}. If {@code true},
+ * user consent can be obtained via {@link #createRequestInteractAcrossProfilesIntent()}. The
+ * package can then listen to {@link #ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED} broadcasts.
+ *
* @return true if the calling package can interact across profiles.
* @throws SecurityException if {@code mContext.getPackageName()} does not belong to the
* calling UID.
@@ -276,11 +293,15 @@
/**
* Returns an {@link Intent} to open the settings page that allows the user to decide whether
- * the calling app can interact across profiles. The current state is given by
- * {@link #canInteractAcrossProfiles()}.
+ * the calling app can interact across profiles.
*
* <p>Returns {@code null} if {@link #canRequestInteractAcrossProfiles()} is {@code false}.
*
+ * <p>Note that the user may already have given consent and the app may already be able to
+ * interact across profiles, even if {@link #canRequestInteractAcrossProfiles()} is {@code
+ * true}. The current ability to interact across profiles is given by {@link
+ * #canInteractAcrossProfiles()}.
+ *
* @return an {@link Intent} to open the settings page that allows the user to decide whether
* the app can interact across profiles
*
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 25b84c5..65f45d8 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -80,7 +80,7 @@
* Display category: Presentation displays.
* <p>
* This category can be used to identify secondary displays that are suitable for
- * use as presentation displays such as HDMI or Wireless displays. Applications
+ * use as presentation displays such as external or wireless displays. Applications
* may automatically project their content to presentation displays to provide
* richer second screen experiences.
* </p>
@@ -100,7 +100,7 @@
* When this flag is set, the virtual display is public.
* </p><p>
* A public virtual display behaves just like most any other display that is connected
- * to the system such as an HDMI or Wireless display. Applications can open
+ * to the system such as an external or wireless display. Applications can open
* windows on the display and the system may mirror the contents of other displays
* onto it.
* </p><p>
@@ -364,7 +364,7 @@
addAllDisplaysLocked(mTempDisplays, displayIds);
} else if (category.equals(DISPLAY_CATEGORY_PRESENTATION)) {
addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_WIFI);
- addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_HDMI);
+ addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_EXTERNAL);
addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_OVERLAY);
addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_VIRTUAL);
}
diff --git a/core/java/android/inputmethodservice/InlineSuggestionSession.java b/core/java/android/inputmethodservice/InlineSuggestionSession.java
new file mode 100644
index 0000000..edae06a
--- /dev/null
+++ b/core/java/android/inputmethodservice/InlineSuggestionSession.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.inputmethodservice;
+
+import static android.inputmethodservice.InputMethodService.DEBUG;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.inputmethod.InlineSuggestionsRequest;
+import android.view.inputmethod.InlineSuggestionsResponse;
+
+import com.android.internal.view.IInlineSuggestionsRequestCallback;
+import com.android.internal.view.IInlineSuggestionsResponseCallback;
+
+import java.lang.ref.WeakReference;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+/**
+ * Maintains an active inline suggestion session.
+ *
+ * <p>
+ * Each session corresponds to one inline suggestion request, but there may be multiple callbacks
+ * with the inline suggestions response.
+ */
+class InlineSuggestionSession {
+
+ private static final String TAG = InlineSuggestionSession.class.getSimpleName();
+
+ private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true);
+
+ @NonNull
+ private final ComponentName mComponentName;
+ @NonNull
+ private final IInlineSuggestionsRequestCallback mCallback;
+ @NonNull
+ private final InlineSuggestionsResponseCallbackImpl mResponseCallback;
+ @NonNull
+ private final Supplier<String> mClientPackageNameSupplier;
+ @NonNull
+ private final Supplier<InlineSuggestionsRequest> mRequestSupplier;
+ @NonNull
+ private final Supplier<IBinder> mHostInputTokenSupplier;
+ @NonNull
+ private final Consumer<InlineSuggestionsResponse> mResponseConsumer;
+
+ private volatile boolean mInvalidated = false;
+
+ InlineSuggestionSession(@NonNull ComponentName componentName,
+ @NonNull IInlineSuggestionsRequestCallback callback,
+ @NonNull Supplier<String> clientPackageNameSupplier,
+ @NonNull Supplier<InlineSuggestionsRequest> requestSupplier,
+ @NonNull Supplier<IBinder> hostInputTokenSupplier,
+ @NonNull Consumer<InlineSuggestionsResponse> responseConsumer) {
+ mComponentName = componentName;
+ mCallback = callback;
+ mResponseCallback = new InlineSuggestionsResponseCallbackImpl(this);
+ mClientPackageNameSupplier = clientPackageNameSupplier;
+ mRequestSupplier = requestSupplier;
+ mHostInputTokenSupplier = hostInputTokenSupplier;
+ mResponseConsumer = responseConsumer;
+
+ makeInlineSuggestionsRequest();
+ }
+
+ /**
+ * This needs to be called before creating a new session, such that the later response callbacks
+ * will be discarded.
+ */
+ void invalidateSession() {
+ mInvalidated = true;
+ }
+
+ /**
+ * Sends an {@link InlineSuggestionsRequest} obtained from {@cocde supplier} to the current
+ * Autofill Session through
+ * {@link IInlineSuggestionsRequestCallback#onInlineSuggestionsRequest}.
+ */
+ private void makeInlineSuggestionsRequest() {
+ try {
+ final InlineSuggestionsRequest request = mRequestSupplier.get();
+ if (request == null) {
+ if (DEBUG) {
+ Log.d(TAG, "onCreateInlineSuggestionsRequest() returned null request");
+ }
+ mCallback.onInlineSuggestionsUnsupported();
+ } else {
+ request.setHostInputToken(mHostInputTokenSupplier.get());
+ mCallback.onInlineSuggestionsRequest(request, mResponseCallback);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "makeInlinedSuggestionsRequest() remote exception:" + e);
+ }
+ }
+
+ private void handleOnInlineSuggestionsResponse(@NonNull InlineSuggestionsResponse response) {
+ if (mInvalidated) {
+ if (DEBUG) {
+ Log.d(TAG, "handleOnInlineSuggestionsResponse() called on invalid session");
+ }
+ return;
+ }
+ // TODO(b/149522488): checking the current focused input field to make sure we don't send
+ // inline responses for previous input field
+ if (!mComponentName.getPackageName().equals(mClientPackageNameSupplier.get())) {
+ if (DEBUG) {
+ Log.d(TAG, "handleOnInlineSuggestionsResponse() called on the wrong package name");
+ }
+ return;
+ }
+ mResponseConsumer.accept(response);
+ }
+
+ /**
+ * Internal implementation of {@link IInlineSuggestionsResponseCallback}.
+ */
+ static final class InlineSuggestionsResponseCallbackImpl
+ extends IInlineSuggestionsResponseCallback.Stub {
+ private final WeakReference<InlineSuggestionSession> mInlineSuggestionSession;
+
+ private InlineSuggestionsResponseCallbackImpl(
+ InlineSuggestionSession inlineSuggestionSession) {
+ mInlineSuggestionSession = new WeakReference<>(inlineSuggestionSession);
+ }
+
+ @Override
+ public void onInlineSuggestionsResponse(InlineSuggestionsResponse response)
+ throws RemoteException {
+ final InlineSuggestionSession session = mInlineSuggestionSession.get();
+ if (session != null) {
+ session.mHandler.sendMessage(obtainMessage(
+ InlineSuggestionSession::handleOnInlineSuggestionsResponse, session,
+ response));
+ }
+ }
+ }
+}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 49e1d5e..b1aa67e 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -22,8 +22,6 @@
import static android.view.WindowInsets.Type.navigationBars;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
-import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
-
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.AnyThread;
@@ -51,7 +49,6 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.SystemClock;
@@ -102,13 +99,11 @@
import com.android.internal.inputmethod.InputMethodPrivilegedOperations;
import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
import com.android.internal.view.IInlineSuggestionsRequestCallback;
-import com.android.internal.view.IInlineSuggestionsResponseCallback;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.lang.ref.WeakReference;
import java.util.Collections;
/**
@@ -450,7 +445,7 @@
final int[] mTmpLocation = new int[2];
@Nullable
- private InlineSuggestionsRequestInfo mInlineSuggestionsRequestInfo = null;
+ private InlineSuggestionSession mInlineSuggestionSession;
private boolean mAutomotiveHideNavBarForKeyboard;
private boolean mIsAutomotive;
@@ -465,8 +460,6 @@
*/
private IBinder mCurShowInputToken;
- private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true);
-
final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer = info -> {
onComputeInsets(mTmpInsets);
if (isExtractViewShown()) {
@@ -538,7 +531,7 @@
if (DEBUG) {
Log.d(TAG, "InputMethodService received onCreateInlineSuggestionsRequest()");
}
- handleOnCreateInlineSuggestionsRequest(componentName, autofillId, cb);
+ handleOnCreateInlineSuggestionsRequest(componentName, cb);
}
/**
@@ -770,39 +763,9 @@
return false;
}
- /**
- * Sends an {@link InlineSuggestionsRequest} obtained from
- * {@link #onCreateInlineSuggestionsRequest()} to the current Autofill Session through
- * {@link IInlineSuggestionsRequestCallback#onInlineSuggestionsRequest}.
- */
- private void makeInlineSuggestionsRequest() {
- if (mInlineSuggestionsRequestInfo == null) {
- Log.w(TAG, "makeInlineSuggestionsRequest() called with null requestInfo cache");
- return;
- }
-
- final IInlineSuggestionsRequestCallback requestCallback =
- mInlineSuggestionsRequestInfo.mCallback;
- try {
- final InlineSuggestionsRequest request = onCreateInlineSuggestionsRequest();
- if (request == null) {
- Log.w(TAG, "onCreateInlineSuggestionsRequest() returned null request");
- requestCallback.onInlineSuggestionsUnsupported();
- } else {
- final IInlineSuggestionsResponseCallback inlineSuggestionsResponseCallback =
- new InlineSuggestionsResponseCallbackImpl(this,
- mInlineSuggestionsRequestInfo.mComponentName,
- mInlineSuggestionsRequestInfo.mFocusedId);
- requestCallback.onInlineSuggestionsRequest(request,
- inlineSuggestionsResponseCallback);
- }
- } catch (RemoteException e) {
- Log.w(TAG, "makeInlinedSuggestionsRequest() remote exception:" + e);
- }
- }
-
+ @MainThread
private void handleOnCreateInlineSuggestionsRequest(@NonNull ComponentName componentName,
- @NonNull AutofillId autofillId, @NonNull IInlineSuggestionsRequestCallback callback) {
+ @NonNull IInlineSuggestionsRequestCallback callback) {
if (!mInputStarted) {
try {
Log.w(TAG, "onStartInput() not called yet");
@@ -813,24 +776,32 @@
return;
}
- mInlineSuggestionsRequestInfo = new InlineSuggestionsRequestInfo(componentName, autofillId,
- callback);
-
- makeInlineSuggestionsRequest();
+ if (mInlineSuggestionSession != null) {
+ mInlineSuggestionSession.invalidateSession();
+ }
+ mInlineSuggestionSession = new InlineSuggestionSession(componentName, callback,
+ this::getEditorInfoPackageName, this::onCreateInlineSuggestionsRequest,
+ this::getHostInputToken, this::onInlineSuggestionsResponse);
}
- private void handleOnInlineSuggestionsResponse(@NonNull ComponentName componentName,
- @NonNull AutofillId autofillId, @NonNull InlineSuggestionsResponse response) {
- if (!mInlineSuggestionsRequestInfo.validate(componentName)) {
- if (DEBUG) {
- Log.d(TAG,
- "Response component=" + componentName + " differs from request component="
- + mInlineSuggestionsRequestInfo.mComponentName
- + ", ignoring response");
- }
- return;
+ @Nullable
+ private String getEditorInfoPackageName() {
+ if (mInputEditorInfo != null) {
+ return mInputEditorInfo.packageName;
}
- onInlineSuggestionsResponse(response);
+ return null;
+ }
+
+ /**
+ * Returns the {@link IBinder} input token from the host view root.
+ */
+ @Nullable
+ private IBinder getHostInputToken() {
+ ViewRootImpl viewRoot = null;
+ if (mRootView != null) {
+ viewRoot = mRootView.getViewRootImpl();
+ }
+ return viewRoot == null ? null : viewRoot.getInputToken();
}
private void notifyImeHidden() {
@@ -851,63 +822,6 @@
}
/**
- * Internal implementation of {@link IInlineSuggestionsResponseCallback}.
- */
- private static final class InlineSuggestionsResponseCallbackImpl
- extends IInlineSuggestionsResponseCallback.Stub {
- private final WeakReference<InputMethodService> mInputMethodService;
-
- private final ComponentName mRequestComponentName;
- private final AutofillId mRequestAutofillId;
-
- private InlineSuggestionsResponseCallbackImpl(InputMethodService inputMethodService,
- ComponentName componentName, AutofillId autofillId) {
- mInputMethodService = new WeakReference<>(inputMethodService);
- mRequestComponentName = componentName;
- mRequestAutofillId = autofillId;
- }
-
- @Override
- public void onInlineSuggestionsResponse(InlineSuggestionsResponse response)
- throws RemoteException {
- final InputMethodService service = mInputMethodService.get();
- if (service != null) {
- service.mHandler.sendMessage(obtainMessage(
- InputMethodService::handleOnInlineSuggestionsResponse, service,
- mRequestComponentName, mRequestAutofillId, response));
- }
- }
- }
-
- /**
- * Information about incoming requests from Autofill Frameworks for inline suggestions.
- */
- private static final class InlineSuggestionsRequestInfo {
- final ComponentName mComponentName;
- final AutofillId mFocusedId;
- final IInlineSuggestionsRequestCallback mCallback;
-
- InlineSuggestionsRequestInfo(ComponentName componentName, AutofillId focusedId,
- IInlineSuggestionsRequestCallback callback) {
- this.mComponentName = componentName;
- this.mFocusedId = focusedId;
- this.mCallback = callback;
- }
-
- /**
- * Returns whether the cached {@link ComponentName} matches the passed in activity.
- */
- public boolean validate(ComponentName componentName) {
- final boolean result = componentName.equals(mComponentName);
- if (DEBUG && !result) {
- Log.d(TAG, "Cached request info ComponentName=" + mComponentName
- + " differs from received ComponentName=" + componentName);
- }
- return result;
- }
- }
-
- /**
* Concrete implementation of
* {@link AbstractInputMethodService.AbstractInputMethodSessionImpl} that provides
* all of the standard behavior for an input method session.
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index 6453af8..81deba4 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -365,12 +365,16 @@
}
/**
+ * This method returns true when the parcel is 'definitely' empty.
+ * That is, it may return false for an empty parcel. But will never return true for a non-empty
+ * one.
+ *
* @hide this should probably be the implementation of isEmpty(). To do that we
* need to ensure we always use the special empty parcel form when the bundle is
* empty. (This may already be the case, but to be safe we'll do this later when
* we aren't trying to stabilize.)
*/
- public boolean maybeIsEmpty() {
+ public boolean isDefinitelyEmpty() {
if (isParcelled()) {
return isEmptyParcel();
} else {
@@ -402,6 +406,9 @@
if (other == null) {
return false;
}
+ if (isDefinitelyEmpty() && other.isDefinitelyEmpty()) {
+ return true;
+ }
if (isParcelled() != other.isParcelled()) {
// Big kind-of here!
return false;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 37efec3..a8f88d4 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2055,6 +2055,19 @@
public static final String ACTION_MANAGE_DOMAIN_URLS = "android.settings.MANAGE_DOMAIN_URLS";
/**
+ * Activity Action: Show screen that let user select enable (or disable) tethering.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing
+ *
+ * @hide
+ */
+ @SystemApi
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_TETHER_SETTINGS = "android.settings.TETHER_SETTINGS";
+
+ /**
* Broadcast to trigger notification of asking user to enable MMS.
* Need to specify {@link #EXTRA_ENABLE_MMS_DATA_REQUEST_REASON} and {@link #EXTRA_SUB_ID}.
*
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 7032825..e8e1223 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -75,6 +75,7 @@
private final @Nullable SaveInfo mSaveInfo;
private final @Nullable Bundle mClientState;
private final @Nullable RemoteViews mPresentation;
+ private final @Nullable InlinePresentation mInlinePresentation;
private final @Nullable RemoteViews mHeader;
private final @Nullable RemoteViews mFooter;
private final @Nullable IntentSender mAuthentication;
@@ -95,6 +96,7 @@
mSaveInfo = builder.mSaveInfo;
mClientState = builder.mClientState;
mPresentation = builder.mPresentation;
+ mInlinePresentation = builder.mInlinePresentation;
mHeader = builder.mHeader;
mFooter = builder.mFooter;
mAuthentication = builder.mAuthentication;
@@ -131,6 +133,11 @@
}
/** @hide */
+ public @Nullable InlinePresentation getInlinePresentation() {
+ return mInlinePresentation;
+ }
+
+ /** @hide */
public @Nullable RemoteViews getHeader() {
return mHeader;
}
@@ -219,6 +226,7 @@
private SaveInfo mSaveInfo;
private Bundle mClientState;
private RemoteViews mPresentation;
+ private InlinePresentation mInlinePresentation;
private RemoteViews mHeader;
private RemoteViews mFooter;
private IntentSender mAuthentication;
@@ -318,6 +326,67 @@
}
/**
+ * Triggers a custom UI before before autofilling the screen with any data set in this
+ * response.
+ *
+ * <p><b>Note:</b> Although the name of this method suggests that it should be used just for
+ * authentication flow, it can be used for other advanced flows; see {@link AutofillService}
+ * for examples.
+ *
+ * <p>This method is similar to
+ * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)}, but also accepts
+ * an {@link InlinePresentation} presentation which is required for authenticating through
+ * the inline autofill flow.
+ *
+ * <p><b>Note:</b> {@link #setHeader(RemoteViews)} or {@link #setFooter(RemoteViews)} does
+ * not work with {@link InlinePresentation}.</p>
+ *
+ * @param authentication Intent to an activity with your authentication flow.
+ * @param presentation The presentation to visualize the response.
+ * @param inlinePresentation The inlinePresentation to visualize the response inline.
+ * @param ids id of Views that when focused will display the authentication UI.
+ *
+ * @return This builder.
+ *
+ * @throws IllegalArgumentException if any of the following occurs:
+ * <ul>
+ * <li>{@code ids} is {@code null}</li>
+ * <li>{@code ids} is empty</li>
+ * <li>{@code ids} contains a {@code null} element</li>
+ * <li>both {@code authentication} and {@code presentation} are {@code null}</li>
+ * <li>both {@code authentication} and {@code presentation} are non-{@code null}</li>
+ * <li>both {@code authentication} and {@code inlinePresentation} are {@code null}</li>
+ * <li>both {@code authentication} and {@code inlinePresentation} are
+ * non-{@code null}</li>
+ * </ul>
+ *
+ * @throws IllegalStateException if a {@link #setHeader(RemoteViews) header} or a
+ * {@link #setFooter(RemoteViews) footer} are already set for this builder.
+ *
+ * @see android.app.PendingIntent#getIntentSender()
+ */
+ @NonNull
+ public Builder setAuthentication(@NonNull AutofillId[] ids,
+ @Nullable IntentSender authentication, @Nullable RemoteViews presentation,
+ @Nullable InlinePresentation inlinePresentation) {
+ throwIfDestroyed();
+ throwIfDisableAutofillCalled();
+ if (mHeader != null || mFooter != null) {
+ throw new IllegalStateException("Already called #setHeader() or #setFooter()");
+ }
+
+ if (authentication == null ^ (presentation == null && inlinePresentation == null)) {
+ throw new IllegalArgumentException("authentication and presentation "
+ + "(dropdown or inline), must be both non-null or null");
+ }
+ mAuthentication = authentication;
+ mPresentation = presentation;
+ mInlinePresentation = inlinePresentation;
+ mAuthenticationIds = assertValid(ids);
+ return this;
+ }
+
+ /**
* Specifies views that should not trigger new
* {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal,
* FillCallback)} requests.
@@ -644,6 +713,8 @@
break;
}
}
+ } else if (mInlinePresentation != null) {
+ mSupportsInlineSuggestions = true;
}
mDestroyed = true;
@@ -691,6 +762,9 @@
if (mPresentation != null) {
builder.append(", hasPresentation");
}
+ if (mInlinePresentation != null) {
+ builder.append(", hasInlinePresentation");
+ }
if (mHeader != null) {
builder.append(", hasHeader");
}
@@ -740,6 +814,7 @@
parcel.writeParcelableArray(mAuthenticationIds, flags);
parcel.writeParcelable(mAuthentication, flags);
parcel.writeParcelable(mPresentation, flags);
+ parcel.writeParcelable(mInlinePresentation, flags);
parcel.writeParcelable(mHeader, flags);
parcel.writeParcelable(mFooter, flags);
parcel.writeParcelable(mUserData, flags);
@@ -774,8 +849,10 @@
AutofillId.class);
final IntentSender authentication = parcel.readParcelable(null);
final RemoteViews presentation = parcel.readParcelable(null);
+ final InlinePresentation inlinePresentation = parcel.readParcelable(null);
if (authenticationIds != null) {
- builder.setAuthentication(authenticationIds, authentication, presentation);
+ builder.setAuthentication(authenticationIds, authentication, presentation,
+ inlinePresentation);
}
final RemoteViews header = parcel.readParcelable(null);
if (header != null) {
diff --git a/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl b/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl
index decdcf5..c389b1a 100644
--- a/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl
+++ b/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl
@@ -16,6 +16,7 @@
package android.service.autofill;
+import android.os.IBinder;
import android.service.autofill.IInlineSuggestionUiCallback;
import android.service.autofill.InlinePresentation;
@@ -25,6 +26,7 @@
* @hide
*/
oneway interface IInlineSuggestionRenderService {
- void renderSuggestion(in IInlineSuggestionUiCallback callback, in InlinePresentation presentation,
- int width, int height);
+ void renderSuggestion(in IInlineSuggestionUiCallback callback,
+ in InlinePresentation presentation, int width, int height,
+ in IBinder hostInputToken);
}
diff --git a/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl b/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl
index a55a2ce..210f95f 100644
--- a/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl
+++ b/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl
@@ -16,6 +16,7 @@
package android.service.autofill;
+import android.os.IBinder;
import android.view.SurfaceControl;
/**
@@ -24,6 +25,8 @@
* @hide
*/
oneway interface IInlineSuggestionUiCallback {
- void autofill();
+ void onAutofill();
void onContent(in SurfaceControl surface);
+ void onError();
+ void onTransferTouchFocusToImeWindow(in IBinder sourceInputToken, int displayId);
}
diff --git a/core/java/android/service/autofill/InlineSuggestionRenderService.java b/core/java/android/service/autofill/InlineSuggestionRenderService.java
index 2593aab..4aafb63 100644
--- a/core/java/android/service/autofill/InlineSuggestionRenderService.java
+++ b/core/java/android/service/autofill/InlineSuggestionRenderService.java
@@ -24,11 +24,16 @@
import android.app.Service;
import android.app.slice.Slice;
import android.content.Intent;
+import android.graphics.PixelFormat;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
+import android.os.RemoteException;
import android.util.Log;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
import android.view.View;
+import android.view.WindowManager;
/**
* A service that renders an inline presentation given the {@link InlinePresentation} containing
@@ -55,8 +60,40 @@
private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true);
private void handleRenderSuggestion(IInlineSuggestionUiCallback callback,
- InlinePresentation presentation, int width, int height) {
- //TODO(b/146453086): implementation in ExtService
+ InlinePresentation presentation, int width, int height, IBinder hostInputToken) {
+ if (hostInputToken == null) {
+ try {
+ callback.onError();
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException calling onError()");
+ }
+ return;
+ }
+ final SurfaceControlViewHost host = new SurfaceControlViewHost(this, this.getDisplay(),
+ hostInputToken);
+ final SurfaceControl surface = host.getSurfacePackage().getSurfaceControl();
+
+ final View suggestionView = onRenderSuggestion(presentation, width, height);
+
+ final InlineSuggestionRoot suggestionRoot = new InlineSuggestionRoot(this, callback);
+ suggestionRoot.addView(suggestionView);
+ suggestionRoot.setOnClickListener((v) -> {
+ try {
+ callback.onAutofill();
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException calling onAutofill()");
+ }
+ });
+
+ WindowManager.LayoutParams lp =
+ new WindowManager.LayoutParams(width, height,
+ WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.TRANSPARENT);
+ host.addView(suggestionRoot, lp);
+ try {
+ callback.onContent(surface);
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException calling onContent(" + surface + ")");
+ }
}
@Override
@@ -66,11 +103,12 @@
return new IInlineSuggestionRenderService.Stub() {
@Override
public void renderSuggestion(@NonNull IInlineSuggestionUiCallback callback,
- @NonNull InlinePresentation presentation, int width, int height) {
+ @NonNull InlinePresentation presentation, int width, int height,
+ @Nullable IBinder hostInputToken) {
mHandler.sendMessage(obtainMessage(
InlineSuggestionRenderService::handleRenderSuggestion,
InlineSuggestionRenderService.this, callback, presentation,
- width, height));
+ width, height, hostInputToken));
}
}.asBinder();
}
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionRoot.java b/core/java/android/service/autofill/InlineSuggestionRoot.java
similarity index 65%
rename from services/autofill/java/com/android/server/autofill/ui/InlineSuggestionRoot.java
rename to core/java/android/service/autofill/InlineSuggestionRoot.java
index e813dae..bdcc253 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionRoot.java
+++ b/core/java/android/service/autofill/InlineSuggestionRoot.java
@@ -14,39 +14,39 @@
* limitations under the License.
*/
-package com.android.server.autofill.ui;
+package android.service.autofill;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
import android.content.Context;
+import android.os.RemoteException;
import android.util.Log;
import android.util.MathUtils;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import android.widget.FrameLayout;
-import com.android.server.LocalServices;
-import com.android.server.wm.WindowManagerInternal;
-
/**
* This class is the root view for an inline suggestion. It is responsible for
* detecting the click on the item and to also transfer input focus to the IME
* window if we detect the user is scrolling.
+ *
+ * @hide
*/
- // TODO(b/146453086) Move to ExtServices and add @SystemApi to transfer touch focus
@SuppressLint("ViewConstructor")
-class InlineSuggestionRoot extends FrameLayout {
- private static final String LOG_TAG = InlineSuggestionRoot.class.getSimpleName();
+public class InlineSuggestionRoot extends FrameLayout {
+ private static final String TAG = "InlineSuggestionRoot";
- private final @NonNull Runnable mOnErrorCallback;
+ private final @NonNull IInlineSuggestionUiCallback mCallback;
private final int mTouchSlop;
private float mDownX;
private float mDownY;
- InlineSuggestionRoot(@NonNull Context context, @NonNull Runnable onErrorCallback) {
+ public InlineSuggestionRoot(@NonNull Context context,
+ @NonNull IInlineSuggestionUiCallback callback) {
super(context);
- mOnErrorCallback = onErrorCallback;
+ mCallback = callback;
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
setFocusable(false);
}
@@ -64,20 +64,15 @@
final float distance = MathUtils.dist(mDownX, mDownY,
event.getX(), event.getY());
if (distance > mTouchSlop) {
- transferTouchFocusToImeWindow();
+ try {
+ mCallback.onTransferTouchFocusToImeWindow(getViewRootImpl().getInputToken(),
+ getContext().getDisplayId());
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException transferring touch focus to IME");
+ }
}
} break;
}
return super.onTouchEvent(event);
}
-
- private void transferTouchFocusToImeWindow() {
- final WindowManagerInternal windowManagerInternal = LocalServices.getService(
- WindowManagerInternal.class);
- if (!windowManagerInternal.transferTouchFocusToImeWindow(getViewRootImpl().getInputToken(),
- getContext().getDisplayId())) {
- Log.e(LOG_TAG, "Cannot transfer touch focus from suggestion to IME");
- mOnErrorCallback.run();
- }
- }
}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index f61217d..d1edf58 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -67,7 +67,7 @@
* </ul>
* </p><p>
* A logical display does not necessarily represent a particular physical display device
- * such as the built-in screen or an external monitor. The contents of a logical
+ * such as the internal display or an external display. The contents of a logical
* display may be presented on one or more physical displays according to the devices
* that are currently attached and whether mirroring has been enabled.
* </p>
@@ -104,8 +104,7 @@
private int mCachedAppHeightCompat;
/**
- * The default Display id, which is the id of the built-in primary display
- * assuming there is one.
+ * The default Display id, which is the id of the primary display assuming there is one.
*/
public static final int DEFAULT_DISPLAY = 0;
@@ -188,7 +187,7 @@
* Display flag: Indicates that the display is a presentation display.
* <p>
* This flag identifies secondary displays that are suitable for
- * use as presentation displays such as HDMI or Wireless displays. Applications
+ * use as presentation displays such as external or wireless displays. Applications
* may automatically project their content to presentation displays to provide
* richer second screen experiences.
* </p>
@@ -257,17 +256,17 @@
public static final int TYPE_UNKNOWN = 0;
/**
- * Display type: Built-in display.
+ * Display type: Physical display connected through an internal port.
* @hide
*/
- public static final int TYPE_BUILT_IN = 1;
+ public static final int TYPE_INTERNAL = 1;
/**
- * Display type: HDMI display.
+ * Display type: Physical display connected through an external port.
* @hide
*/
@UnsupportedAppUsage
- public static final int TYPE_HDMI = 2;
+ public static final int TYPE_EXTERNAL = 2;
/**
* Display type: WiFi display.
@@ -562,8 +561,8 @@
* @return The display type.
*
* @see #TYPE_UNKNOWN
- * @see #TYPE_BUILT_IN
- * @see #TYPE_HDMI
+ * @see #TYPE_INTERNAL
+ * @see #TYPE_EXTERNAL
* @see #TYPE_WIFI
* @see #TYPE_OVERLAY
* @see #TYPE_VIRTUAL
@@ -1251,10 +1250,10 @@
switch (type) {
case TYPE_UNKNOWN:
return "UNKNOWN";
- case TYPE_BUILT_IN:
- return "BUILT_IN";
- case TYPE_HDMI:
- return "HDMI";
+ case TYPE_INTERNAL:
+ return "INTERNAL";
+ case TYPE_EXTERNAL:
+ return "EXTERNAL";
case TYPE_WIFI:
return "WIFI";
case TYPE_OVERLAY:
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index fe9e36e..cf48c52 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -321,14 +321,12 @@
public static final int FX_SURFACE_NORMAL = 0x00000000;
/**
- * Surface creation flag: Creates a Dim surface.
- * Everything behind this surface is dimmed by the amount specified
- * in {@link Transaction#setAlpha(SurfaceControl, float)}. It is an error to lock a Dim
- * surface, since it doesn't have a backing store.
+ * Surface creation flag: Creates a effect surface which
+ * represents a solid color and or shadows.
*
* @hide
*/
- public static final int FX_SURFACE_DIM = 0x00020000;
+ public static final int FX_SURFACE_EFFECT = 0x00020000;
/**
* Surface creation flag: Creates a container surface.
@@ -739,11 +737,11 @@
*/
public Builder setColorLayer() {
unsetBufferSize();
- return setFlags(FX_SURFACE_DIM, FX_SURFACE_MASK);
+ return setFlags(FX_SURFACE_EFFECT, FX_SURFACE_MASK);
}
private boolean isColorLayerSet() {
- return (mFlags & FX_SURFACE_DIM) == FX_SURFACE_DIM;
+ return (mFlags & FX_SURFACE_EFFECT) == FX_SURFACE_EFFECT;
}
/**
@@ -1285,12 +1283,15 @@
* @hide
*/
public static final class DisplayInfo {
+ public boolean isInternal;
public float density;
public boolean secure;
@Override
public String toString() {
- return "DisplayInfo{density=" + density + ", secure=" + secure + "}";
+ return "DisplayInfo{isInternal=" + isInternal
+ + ", density=" + density
+ + ", secure=" + secure + "}";
}
}
@@ -2075,6 +2076,7 @@
private final ArrayMap<SurfaceControl, Point> mResizedSurfaces = new ArrayMap<>();
Runnable mFreeNativeResources;
+ private static final float[] INVALID_COLOR = {-1, -1, -1};
/**
* @hide
@@ -2530,8 +2532,9 @@
}
/**
- * Sets a color for the Surface.
- * @param color A float array with three values to represent r, g, b in range [0..1]
+ * Fills the surface with the specified color.
+ * @param color A float array with three values to represent r, g, b in range [0..1]. An
+ * invalid color will remove the color fill.
* @hide
*/
@UnsupportedAppUsage
@@ -2542,6 +2545,16 @@
}
/**
+ * Removes color fill.
+ * @hide
+ */
+ public Transaction unsetColor(SurfaceControl sc) {
+ checkPreconditions(sc);
+ nativeSetColor(mNativeObject, sc.mNativeObject, INVALID_COLOR);
+ return this;
+ }
+
+ /**
* Sets the security of the surface. Setting the flag is equivalent to creating the
* Surface with the {@link #SECURE} flag.
* @hide
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index c159f89..e9a8a73 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -86,9 +86,10 @@
import java.util.Objects;
import java.util.Set;
-//TODO: use java.lang.ref.Cleaner once Android supports Java 9
import sun.misc.Cleaner;
+//TODO: use java.lang.ref.Cleaner once Android supports Java 9
+
/**
* <p>The {@link AutofillManager} class provides ways for apps and custom views to
* integrate with the Autofill Framework lifecycle.
@@ -547,7 +548,7 @@
* @param fillInIntent The authentication fill-in intent.
*/
void autofillClientAuthenticate(int authenticationId, IntentSender intent,
- Intent fillInIntent);
+ Intent fillInIntent, boolean authenticateInline);
/**
* Tells the client this manager has state to be reset.
@@ -2070,7 +2071,7 @@
}
private void authenticate(int sessionId, int authenticationId, IntentSender intent,
- Intent fillInIntent) {
+ Intent fillInIntent, boolean authenticateInline) {
synchronized (mLock) {
if (sessionId == mSessionId) {
final AutofillClient client = getClient();
@@ -2078,7 +2079,8 @@
// clear mOnInvisibleCalled and we will see if receive onInvisibleForAutofill()
// before onAuthenticationResult()
mOnInvisibleCalled = false;
- client.autofillClientAuthenticate(authenticationId, intent, fillInIntent);
+ client.autofillClientAuthenticate(authenticationId, intent, fillInIntent,
+ authenticateInline);
}
}
}
@@ -3250,10 +3252,11 @@
@Override
public void authenticate(int sessionId, int authenticationId, IntentSender intent,
- Intent fillInIntent) {
+ Intent fillInIntent, boolean authenticateInline) {
final AutofillManager afm = mAfm.get();
if (afm != null) {
- afm.post(() -> afm.authenticate(sessionId, authenticationId, intent, fillInIntent));
+ afm.post(() -> afm.authenticate(sessionId, authenticationId, intent, fillInIntent,
+ authenticateInline));
}
}
diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
index 84949c8..3903665 100644
--- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
@@ -50,7 +50,7 @@
* Authenticates a fill response or a data set.
*/
void authenticate(int sessionId, int authenticationId, in IntentSender intent,
- in Intent fillInIntent);
+ in Intent fillInIntent, boolean authenticateInline);
/**
* Sets the views to track. If saveOnAllViewsInvisible is set and all these view are invisible
diff --git a/core/java/android/view/inline/InlinePresentationSpec.java b/core/java/android/view/inline/InlinePresentationSpec.java
index a85b5f1..406e599 100644
--- a/core/java/android/view/inline/InlinePresentationSpec.java
+++ b/core/java/android/view/inline/InlinePresentationSpec.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.os.Parcelable;
import android.util.Size;
@@ -55,6 +56,7 @@
/**
* @hide
*/
+ @SystemApi
public @Nullable String getStyle() {
return mStyle;
}
@@ -281,10 +283,10 @@
}
@DataClass.Generated(
- time = 1577145109444L,
+ time = 1581117017522L,
codegenVersion = "1.0.14",
sourceFile = "frameworks/base/core/java/android/view/inline/InlinePresentationSpec.java",
- inputSignatures = "private final @android.annotation.NonNull android.util.Size mMinSize\nprivate final @android.annotation.NonNull android.util.Size mMaxSize\nprivate final @android.annotation.Nullable java.lang.String mStyle\nprivate static java.lang.String defaultStyle()\npublic @android.annotation.Nullable java.lang.String getStyle()\nclass InlinePresentationSpec extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nclass BaseBuilder extends java.lang.Object implements []")
+ inputSignatures = "private final @android.annotation.NonNull android.util.Size mMinSize\nprivate final @android.annotation.NonNull android.util.Size mMaxSize\nprivate final @android.annotation.Nullable java.lang.String mStyle\nprivate static java.lang.String defaultStyle()\npublic @android.annotation.SystemApi @android.annotation.Nullable java.lang.String getStyle()\nclass InlinePresentationSpec extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/inputmethod/InlineSuggestion.java b/core/java/android/view/inputmethod/InlineSuggestion.java
index a32ea4b..57269c4 100644
--- a/core/java/android/view/inputmethod/InlineSuggestion.java
+++ b/core/java/android/view/inputmethod/InlineSuggestion.java
@@ -286,7 +286,7 @@
};
@DataClass.Generated(
- time = 1578972138081L,
+ time = 1581377984320L,
codegenVersion = "1.0.14",
sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestion.java",
inputSignatures = "private static final java.lang.String TAG\nprivate final @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo mInfo\nprivate final @android.annotation.Nullable com.android.internal.view.inline.IInlineContentProvider mContentProvider\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestion newInlineSuggestion(android.view.inputmethod.InlineSuggestionInfo)\npublic void inflate(android.content.Context,android.util.Size,java.util.concurrent.Executor,java.util.function.Consumer<android.view.View>)\nclass InlineSuggestion extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
index 860ce90..be9370a 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityThread;
+import android.os.IBinder;
import android.os.Parcelable;
import android.view.inline.InlinePresentationSpec;
@@ -59,6 +60,14 @@
private @NonNull String mHostPackageName;
/**
+ * The host input token of the IME that made the request. This will be set by the system for
+ * safety reasons.
+ *
+ * @hide
+ */
+ private @Nullable IBinder mHostInputToken;
+
+ /**
* @hide
* @see {@link #mHostPackageName}.
*/
@@ -66,6 +75,14 @@
mHostPackageName = hostPackageName;
}
+ /**
+ * @hide
+ * @see {@link #mHostInputToken}.
+ */
+ public void setHostInputToken(IBinder hostInputToken) {
+ mHostInputToken = hostInputToken;
+ }
+
private void onConstructed() {
Preconditions.checkState(mMaxSuggestionCount >= mPresentationSpecs.size());
}
@@ -78,11 +95,17 @@
return ActivityThread.currentPackageName();
}
+ private static IBinder defaultHostInputToken() {
+ return null;
+ }
+
/** @hide */
abstract static class BaseBuilder {
abstract Builder setPresentationSpecs(@NonNull List<InlinePresentationSpec> value);
abstract Builder setHostPackageName(@Nullable String value);
+
+ abstract Builder setHostInputToken(IBinder hostInputToken);
}
@@ -104,7 +127,8 @@
/* package-private */ InlineSuggestionsRequest(
int maxSuggestionCount,
@NonNull List<InlinePresentationSpec> presentationSpecs,
- @NonNull String hostPackageName) {
+ @NonNull String hostPackageName,
+ @Nullable IBinder hostInputToken) {
this.mMaxSuggestionCount = maxSuggestionCount;
this.mPresentationSpecs = presentationSpecs;
com.android.internal.util.AnnotationValidations.validate(
@@ -112,6 +136,7 @@
this.mHostPackageName = hostPackageName;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mHostPackageName);
+ this.mHostInputToken = hostInputToken;
onConstructed();
}
@@ -145,6 +170,17 @@
return mHostPackageName;
}
+ /**
+ * The host input token of the IME that made the request. This will be set by the system for
+ * safety reasons.
+ *
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public @Nullable IBinder getHostInputToken() {
+ return mHostInputToken;
+ }
+
@Override
@DataClass.Generated.Member
public String toString() {
@@ -154,7 +190,8 @@
return "InlineSuggestionsRequest { " +
"maxSuggestionCount = " + mMaxSuggestionCount + ", " +
"presentationSpecs = " + mPresentationSpecs + ", " +
- "hostPackageName = " + mHostPackageName +
+ "hostPackageName = " + mHostPackageName + ", " +
+ "hostInputToken = " + mHostInputToken +
" }";
}
@@ -173,7 +210,8 @@
return true
&& mMaxSuggestionCount == that.mMaxSuggestionCount
&& java.util.Objects.equals(mPresentationSpecs, that.mPresentationSpecs)
- && java.util.Objects.equals(mHostPackageName, that.mHostPackageName);
+ && java.util.Objects.equals(mHostPackageName, that.mHostPackageName)
+ && java.util.Objects.equals(mHostInputToken, that.mHostInputToken);
}
@Override
@@ -186,6 +224,7 @@
_hash = 31 * _hash + mMaxSuggestionCount;
_hash = 31 * _hash + java.util.Objects.hashCode(mPresentationSpecs);
_hash = 31 * _hash + java.util.Objects.hashCode(mHostPackageName);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mHostInputToken);
return _hash;
}
@@ -195,9 +234,13 @@
// You can override field parcelling by defining methods like:
// void parcelFieldName(Parcel dest, int flags) { ... }
+ byte flg = 0;
+ if (mHostInputToken != null) flg |= 0x8;
+ dest.writeByte(flg);
dest.writeInt(mMaxSuggestionCount);
dest.writeParcelableList(mPresentationSpecs, flags);
dest.writeString(mHostPackageName);
+ if (mHostInputToken != null) dest.writeStrongBinder(mHostInputToken);
}
@Override
@@ -211,10 +254,12 @@
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
+ byte flg = in.readByte();
int maxSuggestionCount = in.readInt();
List<InlinePresentationSpec> presentationSpecs = new ArrayList<>();
in.readParcelableList(presentationSpecs, InlinePresentationSpec.class.getClassLoader());
String hostPackageName = in.readString();
+ IBinder hostInputToken = (flg & 0x8) == 0 ? null : in.readStrongBinder();
this.mMaxSuggestionCount = maxSuggestionCount;
this.mPresentationSpecs = presentationSpecs;
@@ -223,6 +268,7 @@
this.mHostPackageName = hostPackageName;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mHostPackageName);
+ this.mHostInputToken = hostInputToken;
onConstructed();
}
@@ -251,6 +297,7 @@
private int mMaxSuggestionCount;
private @NonNull List<InlinePresentationSpec> mPresentationSpecs;
private @NonNull String mHostPackageName;
+ private @Nullable IBinder mHostInputToken;
private long mBuilderFieldsSet = 0L;
@@ -320,10 +367,25 @@
return this;
}
+ /**
+ * The host input token of the IME that made the request. This will be set by the system for
+ * safety reasons.
+ *
+ * @hide
+ */
+ @DataClass.Generated.Member
+ @Override
+ @NonNull Builder setHostInputToken(@Nullable IBinder value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x8;
+ mHostInputToken = value;
+ return this;
+ }
+
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull InlineSuggestionsRequest build() {
checkNotUsed();
- mBuilderFieldsSet |= 0x8; // Mark builder used
+ mBuilderFieldsSet |= 0x10; // Mark builder used
if ((mBuilderFieldsSet & 0x1) == 0) {
mMaxSuggestionCount = defaultMaxSuggestionCount();
@@ -331,15 +393,19 @@
if ((mBuilderFieldsSet & 0x4) == 0) {
mHostPackageName = defaultHostPackageName();
}
+ if ((mBuilderFieldsSet & 0x8) == 0) {
+ mHostInputToken = defaultHostInputToken();
+ }
InlineSuggestionsRequest o = new InlineSuggestionsRequest(
mMaxSuggestionCount,
mPresentationSpecs,
- mHostPackageName);
+ mHostPackageName,
+ mHostInputToken);
return o;
}
private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x8) != 0) {
+ if ((mBuilderFieldsSet & 0x10) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
@@ -347,10 +413,10 @@
}
@DataClass.Generated(
- time = 1578948035951L,
+ time = 1581555687721L,
codegenVersion = "1.0.14",
sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java",
- inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.view.inline.InlinePresentationSpec> mPresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\npublic void setHostPackageName(java.lang.String)\nprivate void onConstructed()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setPresentationSpecs(java.util.List<android.view.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nclass BaseBuilder extends java.lang.Object implements []")
+ inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.view.inline.InlinePresentationSpec> mPresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\npublic void setHostPackageName(java.lang.String)\npublic void setHostInputToken(android.os.IBinder)\nprivate void onConstructed()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.os.IBinder defaultHostInputToken()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setPresentationSpecs(java.util.List<android.view.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 469ab2e..0182975 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -8282,9 +8282,9 @@
if (getKeyListener() != null && !mSingleLine && mEditor != null
&& (mEditor.mInputType & EditorInfo.TYPE_MASK_CLASS)
== EditorInfo.TYPE_CLASS_TEXT) {
- int variation = mEditor.mInputType & EditorInfo.TYPE_MASK_VARIATION;
- if (variation == EditorInfo.TYPE_TEXT_FLAG_IME_MULTI_LINE
- || variation == EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE) {
+ int multilineFlags = EditorInfo.TYPE_TEXT_FLAG_IME_MULTI_LINE
+ | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
+ if ((mEditor.mInputType & multilineFlags) != 0) {
return false;
}
}
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index dabaf5a..1c1c254 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -18,6 +18,8 @@
import android.app.AppOpsManager;
import android.app.AsyncNotedAppOp;
+import android.app.SyncNotedAppOp;
+import android.app.RuntimeAppOpAccessMessage;
import android.content.pm.ParceledListSlice;
import android.os.Bundle;
import android.os.RemoteCallback;
@@ -25,6 +27,7 @@
import com.android.internal.app.IAppOpsActiveCallback;
import com.android.internal.app.IAppOpsAsyncNotedCallback;
import com.android.internal.app.IAppOpsNotedCallback;
+import com.android.internal.app.MessageSamplingConfig;
interface IAppOpsService {
// These methods are also called by native code, so must
@@ -54,6 +57,9 @@
// Remaining methods are only used in Java.
int checkPackage(int uid, String packageName);
+ RuntimeAppOpAccessMessage collectRuntimeAppOpAccessMessage();
+ MessageSamplingConfig reportRuntimeAppOpAccessMessageAndGetConfig(String packageName,
+ in SyncNotedAppOp appOp, String message);
@UnsupportedAppUsage
List<AppOpsManager.PackageOps> getPackagesForOps(in int[] ops);
@UnsupportedAppUsage
diff --git a/core/java/com/android/internal/app/MessageSamplingConfig.aidl b/core/java/com/android/internal/app/MessageSamplingConfig.aidl
new file mode 100644
index 0000000..ab89ca2
--- /dev/null
+++ b/core/java/com/android/internal/app/MessageSamplingConfig.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.internal.app;
+
+parcelable MessageSamplingConfig;
diff --git a/core/java/com/android/internal/app/MessageSamplingConfig.java b/core/java/com/android/internal/app/MessageSamplingConfig.java
new file mode 100644
index 0000000..5300c1c
--- /dev/null
+++ b/core/java/com/android/internal/app/MessageSamplingConfig.java
@@ -0,0 +1,187 @@
+/*
+ * 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.internal.app;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.app.AppOpsManager;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.annotations.Immutable;
+import com.android.internal.util.DataClass;
+
+/**
+ * Configuration for AppOps access messages sampling.
+ */
+@Immutable
+/*@DataClass*/
+public final class MessageSamplingConfig implements Parcelable {
+ /** Op code targeted during current sampling session */
+ private final @IntRange(from = -1L, to = AppOpsManager._NUM_OP - 1) int mSampledOpCode;
+ /** Range of ops which should be reported during current sampling session */
+ private final @IntRange(from = 0L, to = AppOpsManager._NUM_OP - 1) int mAcceptableLeftDistance;
+ /** Expiration time for this sampling config */
+ private final @IntRange(from = 0L) long mExpirationTimeSinceBootMillis;
+
+
+
+
+
+
+
+ // Code below generated by codegen v1.0.14.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/app/MessageSamplingConfig.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /**
+ * Creates a new MessageSamplingConfig.
+ *
+ * @param sampledOpCode
+ * Op code targeted during current sampling session
+ * @param acceptableLeftDistance
+ * Range of ops which should be reported during current sampling session
+ * @param expirationTimeSinceBootMillis
+ * Expiration time for this sampling config
+ */
+ @DataClass.Generated.Member
+ public MessageSamplingConfig(
+ @IntRange(from = -1L, to = AppOpsManager._NUM_OP - 1) int sampledOpCode,
+ @IntRange(from = 0L, to = AppOpsManager._NUM_OP - 1) int acceptableLeftDistance,
+ @IntRange(from = 0L) long expirationTimeSinceBootMillis) {
+ this.mSampledOpCode = sampledOpCode;
+ com.android.internal.util.AnnotationValidations.validate(
+ IntRange.class, null, mSampledOpCode,
+ "from", -1L,
+ "to", AppOpsManager._NUM_OP - 1);
+ this.mAcceptableLeftDistance = acceptableLeftDistance;
+ com.android.internal.util.AnnotationValidations.validate(
+ IntRange.class, null, mAcceptableLeftDistance,
+ "from", 0L,
+ "to", AppOpsManager._NUM_OP - 1);
+ this.mExpirationTimeSinceBootMillis = expirationTimeSinceBootMillis;
+ com.android.internal.util.AnnotationValidations.validate(
+ IntRange.class, null, mExpirationTimeSinceBootMillis,
+ "from", 0L);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ /**
+ * Op code targeted during current sampling session
+ */
+ @DataClass.Generated.Member
+ public @IntRange(from = -1L, to = AppOpsManager._NUM_OP - 1) int getSampledOpCode() {
+ return mSampledOpCode;
+ }
+
+ /**
+ * Range of ops which should be reported during current sampling session
+ */
+ @DataClass.Generated.Member
+ public @IntRange(from = 0L, to = AppOpsManager._NUM_OP - 1) int getAcceptableLeftDistance() {
+ return mAcceptableLeftDistance;
+ }
+
+ /**
+ * Expiration time for this sampling config
+ */
+ @DataClass.Generated.Member
+ public @IntRange(from = 0L) long getExpirationTimeSinceBootMillis() {
+ return mExpirationTimeSinceBootMillis;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeInt(mSampledOpCode);
+ dest.writeInt(mAcceptableLeftDistance);
+ dest.writeLong(mExpirationTimeSinceBootMillis);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ MessageSamplingConfig(@NonNull Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ int sampledOpCode = in.readInt();
+ int acceptableLeftDistance = in.readInt();
+ long expirationTimeSinceBootMillis = in.readLong();
+
+ this.mSampledOpCode = sampledOpCode;
+ com.android.internal.util.AnnotationValidations.validate(
+ IntRange.class, null, mSampledOpCode,
+ "from", -1L,
+ "to", AppOpsManager._NUM_OP - 1);
+ this.mAcceptableLeftDistance = acceptableLeftDistance;
+ com.android.internal.util.AnnotationValidations.validate(
+ IntRange.class, null, mAcceptableLeftDistance,
+ "from", 0L,
+ "to", AppOpsManager._NUM_OP - 1);
+ this.mExpirationTimeSinceBootMillis = expirationTimeSinceBootMillis;
+ com.android.internal.util.AnnotationValidations.validate(
+ IntRange.class, null, mExpirationTimeSinceBootMillis,
+ "from", 0L);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<MessageSamplingConfig> CREATOR
+ = new Parcelable.Creator<MessageSamplingConfig>() {
+ @Override
+ public MessageSamplingConfig[] newArray(int size) {
+ return new MessageSamplingConfig[size];
+ }
+
+ @Override
+ public MessageSamplingConfig createFromParcel(@NonNull Parcel in) {
+ return new MessageSamplingConfig(in);
+ }
+ };
+
+ /*@DataClass.Generated(
+ time = 1580691255495L,
+ codegenVersion = "1.0.14",
+ sourceFile = "frameworks/base/core/java/com/android/internal/app/MessageSamplingConfig.java",
+ inputSignatures = "private final @android.annotation.IntRange(from=-1L, to=AppOpsManager._NUM_OP - 1) int mSampledOpCode\nprivate final @android.annotation.IntRange(from=0L, to=AppOpsManager._NUM_OP - 1) int mAcceptableLeftDistance\nprivate final @android.annotation.IntRange(from=0L) long mExpirationTimeSinceBootMillis\nclass MessageSamplingConfig extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass")*/
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index adb4036..36025e3 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -1627,9 +1627,12 @@
int opacity = PixelFormat.OPAQUE;
final WindowConfiguration winConfig = getResources().getConfiguration().windowConfiguration;
+ // TODO(b/149585281) remove when root task has the correct bounds for freeform
+ final boolean renderShadowsInCompositor = mWindow.mRenderShadowsInCompositor
+ && winConfig.getWindowingMode() != WINDOWING_MODE_FREEFORM;
// If we draw shadows in the compositor we don't need to force the surface to be
// translucent.
- if (winConfig.hasWindowShadow() && !mWindow.mRenderShadowsInCompositor) {
+ if (winConfig.hasWindowShadow() && !renderShadowsInCompositor) {
// If the window has a shadow, it must be translucent.
opacity = PixelFormat.TRANSLUCENT;
} else{
@@ -2414,16 +2417,18 @@
}
private void updateElevation() {
+ final int windowingMode =
+ getResources().getConfiguration().windowConfiguration.getWindowingMode();
+ final boolean renderShadowsInCompositor = mWindow.mRenderShadowsInCompositor
+ && windowingMode != WINDOWING_MODE_FREEFORM;
// If rendering shadows in the compositor, don't set an elevation on the view
- if (mWindow.mRenderShadowsInCompositor) {
+ if (renderShadowsInCompositor) {
return;
}
float elevation = 0;
final boolean wasAdjustedForStack = mElevationAdjustedForStack;
// Do not use a shadow when we are in resizing mode (mBackdropFrameRenderer not null)
// since the shadow is bound to the content size and not the target size.
- final int windowingMode =
- getResources().getConfiguration().windowConfiguration.getWindowingMode();
if ((windowingMode == WINDOWING_MODE_FREEFORM) && !isResizing()) {
elevation = hasWindowFocus() ?
DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP : DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
diff --git a/core/java/com/android/internal/policy/DockedDividerUtils.java b/core/java/com/android/internal/policy/DockedDividerUtils.java
index c68e506..b61b9de 100644
--- a/core/java/com/android/internal/policy/DockedDividerUtils.java
+++ b/core/java/com/android/internal/policy/DockedDividerUtils.java
@@ -16,14 +16,15 @@
package com.android.internal.policy;
-import android.graphics.Rect;
-
import static android.view.WindowManager.DOCKED_BOTTOM;
import static android.view.WindowManager.DOCKED_INVALID;
import static android.view.WindowManager.DOCKED_LEFT;
import static android.view.WindowManager.DOCKED_RIGHT;
import static android.view.WindowManager.DOCKED_TOP;
+import android.content.res.Resources;
+import android.graphics.Rect;
+
/**
* Utility functions for docked stack divider used by both window manager and System UI.
*
@@ -105,23 +106,6 @@
return start + (end - start) / 2 - dividerSize / 2;
}
- public static int getDockSideFromCreatedMode(boolean dockOnTopOrLeft,
- boolean isHorizontalDivision) {
- if (dockOnTopOrLeft) {
- if (isHorizontalDivision) {
- return DOCKED_TOP;
- } else {
- return DOCKED_LEFT;
- }
- } else {
- if (isHorizontalDivision) {
- return DOCKED_BOTTOM;
- } else {
- return DOCKED_RIGHT;
- }
- }
- }
-
public static int invertDockSide(int dockSide) {
switch (dockSide) {
case DOCKED_LEFT:
@@ -136,4 +120,21 @@
return DOCKED_INVALID;
}
}
+
+ /** Returns the inset distance from the divider window edge to the dividerview. */
+ public static int getDividerInsets(Resources res) {
+ return res.getDimensionPixelSize(com.android.internal.R.dimen.docked_stack_divider_insets);
+ }
+
+ /** Returns the size of the divider */
+ public static int getDividerSize(Resources res, int dividerInsets) {
+ final int windowWidth = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.docked_stack_divider_thickness);
+ return windowWidth - 2 * dividerInsets;
+ }
+
+ /** Returns the docked-stack side */
+ public static int getDockSide(int displayWidth, int displayHeight) {
+ return displayWidth > displayHeight ? DOCKED_LEFT : DOCKED_TOP;
+ }
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index d1a7d24..653cbc9 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -234,6 +234,7 @@
"libui",
"libgraphicsenv",
"libgui",
+ "libmediandk",
"libsensor",
"libinput",
"libcamera_client",
@@ -449,9 +450,10 @@
],
shared_libs: [
"libandroidfw",
+ "libEGL",
+ "libmediandk",
"libnativedisplay",
"libnativewindow",
- "libgui",
"libpdfium",
],
static_libs: [
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index f564d75..ce9a048 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -65,6 +65,7 @@
static struct {
jclass clazz;
jmethodID ctor;
+ jfieldID isInternal;
jfieldID density;
jfieldID secure;
} gDisplayInfoClassInfo;
@@ -781,6 +782,8 @@
}
jobject object = env->NewObject(gDisplayInfoClassInfo.clazz, gDisplayInfoClassInfo.ctor);
+ env->SetBooleanField(object, gDisplayInfoClassInfo.isInternal,
+ info.connectionType == DisplayConnectionType::Internal);
env->SetFloatField(object, gDisplayInfoClassInfo.density, info.density);
env->SetBooleanField(object, gDisplayInfoClassInfo.secure, info.secure);
return object;
@@ -1528,6 +1531,7 @@
jclass infoClazz = FindClassOrDie(env, "android/view/SurfaceControl$DisplayInfo");
gDisplayInfoClassInfo.clazz = MakeGlobalRefOrDie(env, infoClazz);
gDisplayInfoClassInfo.ctor = GetMethodIDOrDie(env, infoClazz, "<init>", "()V");
+ gDisplayInfoClassInfo.isInternal = GetFieldIDOrDie(env, infoClazz, "isInternal", "Z");
gDisplayInfoClassInfo.density = GetFieldIDOrDie(env, infoClazz, "density", "F");
gDisplayInfoClassInfo.secure = GetFieldIDOrDie(env, infoClazz, "secure", "Z");
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 5a8225c..27c5a73 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -17,40 +17,34 @@
#define LOG_TAG "ThreadedRenderer"
#define ATRACE_TAG ATRACE_TAG_VIEW
-#include <algorithm>
-#include <atomic>
-#include <inttypes.h>
-
-#include "jni.h"
-#include <nativehelper/JNIHelp.h>
-#include "core_jni_helpers.h"
-#include <GraphicsJNI.h>
-
-#include <gui/BufferItemConsumer.h>
-#include <gui/BufferQueue.h>
-#include <gui/Surface.h>
-
-#include "android_graphics_HardwareRendererObserver.h"
-
-#include <private/EGL/cache.h>
-
-#include <utils/RefBase.h>
-#include <utils/StrongPointer.h>
-#include <utils/Timers.h>
-#include <utils/TraceUtils.h>
-#include <android_runtime/android_view_Surface.h>
-#include <system/window.h>
-
#include <FrameInfo.h>
+#include <GraphicsJNI.h>
#include <Picture.h>
#include <Properties.h>
#include <RootRenderNode.h>
+#include <dlfcn.h>
+#include <inttypes.h>
+#include <media/NdkImage.h>
+#include <media/NdkImageReader.h>
+#include <nativehelper/JNIHelp.h>
+#include <pipeline/skia/ShaderCache.h>
+#include <private/EGL/cache.h>
#include <renderthread/CanvasContext.h>
#include <renderthread/RenderProxy.h>
#include <renderthread/RenderTask.h>
#include <renderthread/RenderThread.h>
-#include <pipeline/skia/ShaderCache.h>
#include <utils/Color.h>
+#include <utils/RefBase.h>
+#include <utils/StrongPointer.h>
+#include <utils/Timers.h>
+#include <utils/TraceUtils.h>
+
+#include <algorithm>
+#include <atomic>
+
+#include "android_graphics_HardwareRendererObserver.h"
+#include "core_jni_helpers.h"
+#include "jni.h"
namespace android {
@@ -78,6 +72,9 @@
return env;
}
+typedef ANativeWindow* (*ANW_fromSurface)(JNIEnv* env, jobject surface);
+ANW_fromSurface fromSurface;
+
class JvmErrorReporter : public ErrorHandler {
public:
JvmErrorReporter(JNIEnv* env) {
@@ -178,9 +175,9 @@
static void android_view_ThreadedRenderer_setSurface(JNIEnv* env, jobject clazz,
jlong proxyPtr, jobject jsurface, jboolean discardBuffer) {
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
- sp<Surface> surface;
+ ANativeWindow* window = nullptr;
if (jsurface) {
- surface = android_view_Surface_getSurface(env, jsurface);
+ window = fromSurface(env, jsurface);
}
bool enableTimeout = true;
if (discardBuffer) {
@@ -188,7 +185,7 @@
enableTimeout = false;
proxy->setSwapBehavior(SwapBehavior::kSwap_discardBuffer);
}
- proxy->setSurface(surface, enableTimeout);
+ proxy->setSurface(window, enableTimeout);
}
static jboolean android_view_ThreadedRenderer_pause(JNIEnv* env, jobject clazz,
@@ -458,8 +455,10 @@
jint right, jint bottom, jlong bitmapPtr) {
SkBitmap bitmap;
bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap);
- sp<Surface> surface = android_view_Surface_getSurface(env, jsurface);
- return RenderProxy::copySurfaceInto(surface, left, top, right, bottom, &bitmap);
+ ANativeWindow* window = fromSurface(env, jsurface);
+ jint result = RenderProxy::copySurfaceInto(window, left, top, right, bottom, &bitmap);
+ ANativeWindow_release(window);
+ return result;
}
class ContextFactory : public IContextFactory {
@@ -480,23 +479,35 @@
uint32_t width = jwidth;
uint32_t height = jheight;
- // Create a Surface wired up to a BufferItemConsumer
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> rawConsumer;
- BufferQueue::createBufferQueue(&producer, &rawConsumer);
- // We only need 1 buffer but some drivers have bugs so workaround it by setting max count to 2
- rawConsumer->setMaxBufferCount(2);
- sp<BufferItemConsumer> consumer = new BufferItemConsumer(rawConsumer,
- GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_SW_READ_NEVER | GRALLOC_USAGE_SW_WRITE_NEVER);
- consumer->setDefaultBufferSize(width, height);
- sp<Surface> surface = new Surface(producer);
+ // Create an ImageReader wired up to a BufferItemConsumer
+ AImageReader* rawReader;
+ media_status_t result =
+ AImageReader_newWithUsage(width, height, AIMAGE_FORMAT_RGBA_8888,
+ AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE, 2, &rawReader);
+ std::unique_ptr<AImageReader, decltype(&AImageReader_delete)> reader(rawReader,
+ AImageReader_delete);
+
+ if (result != AMEDIA_OK) {
+ ALOGW("Error creating image reader!");
+ return nullptr;
+ }
+
+ // Note that ownership of this window is maintained by AImageReader, so we
+ // shouldn't need to wrap around a smart pointer.
+ ANativeWindow* window;
+ result = AImageReader_getWindow(rawReader, &window);
+
+ if (result != AMEDIA_OK) {
+ ALOGW("Error retrieving the native window!");
+ return nullptr;
+ }
// Render into the surface
{
ContextFactory factory;
RenderProxy proxy{true, renderNode, &factory};
proxy.setSwapBehavior(SwapBehavior::kSwap_discardBuffer);
- proxy.setSurface(surface);
+ proxy.setSurface(window);
// Shadows can't be used via this interface, so just set the light source
// to all 0s.
proxy.setLightAlpha(0, 0);
@@ -508,33 +519,34 @@
proxy.syncAndDrawFrame();
}
- // Yank out the GraphicBuffer
- BufferItem bufferItem;
- status_t err;
- if ((err = consumer->acquireBuffer(&bufferItem, 0, true)) != OK) {
- ALOGW("Failed to acquireBuffer, error %d (%s)", err, strerror(-err));
+ AImage* rawImage;
+ result = AImageReader_acquireNextImage(rawReader, &rawImage);
+ std::unique_ptr<AImage, decltype(&AImage_delete)> image(rawImage, AImage_delete);
+ if (result != AMEDIA_OK) {
+ ALOGW("Error reading image: %d!", result);
return nullptr;
}
- sp<GraphicBuffer> buffer = bufferItem.mGraphicBuffer;
- // We don't really care if this fails or not since we're just going to destroy this anyway
- consumer->releaseBuffer(bufferItem);
- if (!buffer.get()) {
- ALOGW("GraphicBuffer is null?");
- return nullptr;
- }
- if (buffer->getWidth() != width || buffer->getHeight() != height) {
- ALOGW("GraphicBuffer size mismatch, got %dx%d expected %dx%d",
- buffer->getWidth(), buffer->getHeight(), width, height);
+
+ AHardwareBuffer* buffer;
+ result = AImage_getHardwareBuffer(rawImage, &buffer);
+
+ AHardwareBuffer_Desc desc;
+ AHardwareBuffer_describe(buffer, &desc);
+
+ if (desc.width != width || desc.height != height) {
+ ALOGW("AHardwareBuffer size mismatch, got %dx%d expected %dx%d", desc.width, desc.height,
+ width, height);
// Continue I guess?
}
- sk_sp<SkColorSpace> cs = uirenderer::DataSpaceToColorSpace(bufferItem.mDataSpace);
+ sk_sp<SkColorSpace> cs = uirenderer::DataSpaceToColorSpace(
+ static_cast<android_dataspace>(ANativeWindow_getBuffersDataSpace(window)));
if (cs == nullptr) {
// nullptr is treated as SRGB in Skia, thus explicitly use SRGB in order to make sure
// the returned bitmap has a color space.
cs = SkColorSpace::MakeSRGB();
}
- sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer->toAHardwareBuffer(), cs);
+ sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer, cs);
return bitmap::createBitmap(env, bitmap.release(),
android::bitmap::kBitmapCreateFlag_Premultiplied);
}
@@ -722,6 +734,11 @@
gFrameCompleteCallback.onFrameComplete = GetMethodIDOrDie(env, frameCompleteClass,
"onFrameComplete", "(J)V");
+ void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE);
+ fromSurface = (ANW_fromSurface)dlsym(handle_, "ANativeWindow_fromSurface");
+ LOG_ALWAYS_FATAL_IF(fromSurface == nullptr,
+ "Failed to find required symbol ANativeWindow_fromSurface!");
+
return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
}
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 03676dd..829c252 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -34,6 +34,7 @@
import "frameworks/base/core/proto/android/providers/settings.proto";
import "frameworks/base/core/proto/android/server/activitymanagerservice.proto";
import "frameworks/base/core/proto/android/server/alarmmanagerservice.proto";
+import "frameworks/base/core/proto/android/server/bluetooth_manager_service.proto";
import "frameworks/base/core/proto/android/server/fingerprint.proto";
import "frameworks/base/core/proto/android/server/jobscheduler.proto";
import "frameworks/base/core/proto/android/server/location/context_hub.proto";
@@ -488,6 +489,11 @@
(section).args = "connmetrics --proto"
];
+ optional com.android.server.BluetoothManagerServiceDumpProto bluetooth_manager = 3050 [
+ (section).type = SECTION_DUMPSYS,
+ (section).args = "bluetooth_manager --proto"
+ ];
+
optional com.android.server.location.ContextHubServiceProto context_hub = 3051 [
(section).type = SECTION_DUMPSYS,
(section).args = "contexthub --proto"
diff --git a/core/proto/android/server/bluetooth_manager_service.proto b/core/proto/android/server/bluetooth_manager_service.proto
new file mode 100644
index 0000000..998413f
--- /dev/null
+++ b/core/proto/android/server/bluetooth_manager_service.proto
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 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 com.android.server;
+
+import "frameworks/base/core/proto/android/bluetooth/enums.proto";
+import "frameworks/base/core/proto/android/privacy.proto";
+
+option java_multiple_files = true;
+
+message BluetoothManagerServiceDumpProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ message ActiveLog {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+ optional int64 timestamp_ms = 1;
+ optional bool enable = 2;
+ optional string package_name = 3;
+ optional .android.bluetooth.EnableDisableReasonEnum reason = 4;
+ }
+
+ optional bool enabled = 1;
+ optional int32 state = 2;
+ optional string state_name = 3;
+ optional string address = 4 [(.android.privacy).dest = DEST_EXPLICIT];
+ optional string name = 5 [(.android.privacy).dest = DEST_EXPLICIT];
+ optional int64 last_enabled_time_ms = 6;
+ optional int64 curr_timestamp_ms = 7;
+ repeated ActiveLog active_logs = 8;
+ optional int32 num_crashes = 9;
+ optional bool crash_log_maxed = 10;
+ repeated int64 crash_timestamps_ms = 11;
+ optional int32 num_ble_apps = 12;
+ repeated string ble_app_package_names = 13;
+}
\ No newline at end of file
diff --git a/core/proto/android/server/notificationhistory.proto b/core/proto/android/server/notificationhistory.proto
index 6749719..15f4abb 100644
--- a/core/proto/android/server/notificationhistory.proto
+++ b/core/proto/android/server/notificationhistory.proto
@@ -55,6 +55,11 @@
// The small icon of the notification
optional Icon icon = 12;
+ // The conversation id, if any, that this notification belongs to
+ optional string conversation_id = 13;
+ // conversation_id_index contains the index + 1 of the conversation id in the string pool
+ optional int32 conversation_id_index = 14;
+
// Matches the constants of android.graphics.drawable.Icon
enum ImageTypeEnum {
TYPE_UNKNOWN = 0;
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index b0b9ce6f..08db454 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -275,6 +275,7 @@
optional float adjust_divider_amount = 25;
optional bool animating_bounds = 26;
optional float minimize_amount = 27;
+ optional bool created_by_organizer = 28;
}
/* represents ActivityRecordProto */
diff --git a/core/res/Android.bp b/core/res/Android.bp
index 3dc74f8..b365de4 100644
--- a/core/res/Android.bp
+++ b/core/res/Android.bp
@@ -54,3 +54,22 @@
"res/**/*",
],
}
+
+// Generate a text file containing a list of permissions that non-system apps
+// are allowed to obtain.
+genrule {
+ name: "permission-list-normal",
+ out: ["permission-list-normal.txt"],
+ srcs: ["AndroidManifest.xml"],
+ cmd: "cat $(in) " +
+ // xmllint has trouble accessing attributes under the android namespace.
+ // Strip these prefixes prior to processing with xmllint.
+ " | sed -r 's/android:(name|protectionLevel)/\\1/g' " +
+ " | $(location xmllint) /dev/stdin --xpath " +
+ " '//permission[not(contains(@protectionLevel, \"signature\"))]/@name'" +
+ // The result of xmllint is name="value" pairs. Format these to just the
+ // permission name, one per-line.
+ " | sed -r 's/\\s*name=\\s*//g' | tr -d '\"'" +
+ " > $(out)",
+ tools: ["xmllint"]
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 9dc40c8..b2047ad 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4941,7 +4941,13 @@
<p>Not for use by third-party applications.
@hide -->
<permission android:name="android.permission.ACCESS_TV_TUNER"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged|vendorPrivileged" />
+
+ <!-- @SystemApi Allows an application to access descrambler of TV tuner HAL
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.ACCESS_TV_DESCRAMBLER"
+ android:protectionLevel="signature|privileged|vendorPrivileged" />
<!-- @hide @SystemApi Allows an application to access locusId events in the usage stats. -->
<permission android:name="android.permission.ACCESS_LOCUS_ID_USAGE_STATS"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index c6f3962..bbe9a6d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -59,6 +59,7 @@
<item><xliff:g id="id">@string/status_bar_airplane</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_battery</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_sensors_off</xliff:g></item>
+ <item><xliff:g id="id">@string/status_bar_screen_record</xliff:g></item>
</string-array>
<string translatable="false" name="status_bar_rotate">rotate</string>
@@ -94,6 +95,7 @@
<string translatable="false" name="status_bar_camera">camera</string>
<string translatable="false" name="status_bar_airplane">airplane</string>
<string translatable="false" name="status_bar_sensors_off">sensors_off</string>
+ <string translatable="false" name="status_bar_screen_record">screen_record</string>
<!-- Flag indicating whether the surface flinger has limited
alpha compositing functionality in hardware. If set, the window
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index b3dd1fc..500bfe9 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2908,6 +2908,7 @@
<java-symbol type="string" name="status_bar_microphone" />
<java-symbol type="string" name="status_bar_camera" />
<java-symbol type="string" name="status_bar_sensors_off" />
+ <java-symbol type="string" name="status_bar_screen_record" />
<!-- Locale picker -->
<java-symbol type="id" name="locale_search_menu" />
diff --git a/core/tests/coretests/src/android/app/NotificationHistoryTest.java b/core/tests/coretests/src/android/app/NotificationHistoryTest.java
index d1608d0..8d8acb7 100644
--- a/core/tests/coretests/src/android/app/NotificationHistoryTest.java
+++ b/core/tests/coretests/src/android/app/NotificationHistoryTest.java
@@ -21,6 +21,7 @@
import android.app.NotificationHistory.HistoricalNotification;
import android.graphics.drawable.Icon;
import android.os.Parcel;
+import android.util.Slog;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
@@ -48,6 +49,10 @@
String expectedText = "text" + index;
Icon expectedIcon = Icon.createWithResource(InstrumentationRegistry.getContext(),
index);
+ String conversationId = null;
+ if (index % 2 == 0) {
+ conversationId = "convo" + index;
+ }
return new HistoricalNotification.Builder()
.setPackage(packageName)
@@ -59,6 +64,7 @@
.setTitle(expectedTitle)
.setText(expectedText)
.setIcon(expectedIcon)
+ .setConversationId(conversationId)
.build();
}
@@ -74,6 +80,7 @@
String expectedText = "text";
Icon expectedIcon = Icon.createWithResource(InstrumentationRegistry.getContext(),
android.R.drawable.btn_star);
+ String expectedConversationId = "convo";
HistoricalNotification n = new HistoricalNotification.Builder()
.setPackage(expectedPackage)
@@ -85,6 +92,7 @@
.setTitle(expectedTitle)
.setText(expectedText)
.setIcon(expectedIcon)
+ .setConversationId(expectedConversationId)
.build();
assertThat(n.getPackage()).isEqualTo(expectedPackage);
@@ -96,6 +104,7 @@
assertThat(n.getTitle()).isEqualTo(expectedTitle);
assertThat(n.getText()).isEqualTo(expectedText);
assertThat(expectedIcon.sameAs(n.getIcon())).isTrue();
+ assertThat(n.getConversationId()).isEqualTo(expectedConversationId);
}
@Test
@@ -153,6 +162,9 @@
expectedStrings.add(n.getPackage());
expectedStrings.add(n.getChannelName());
expectedStrings.add(n.getChannelId());
+ if (n.getConversationId() != null) {
+ expectedStrings.add(n.getConversationId());
+ }
history.addNotificationToWrite(n);
}
@@ -180,6 +192,9 @@
expectedStrings.add(n.getPackage());
expectedStrings.add(n.getChannelName());
expectedStrings.add(n.getChannelId());
+ if (n.getConversationId() != null) {
+ expectedStrings.add(n.getConversationId());
+ }
history.addNotificationToWrite(n);
}
@@ -212,6 +227,9 @@
postRemoveExpectedStrings.add(n.getPackage());
postRemoveExpectedStrings.add(n.getChannelName());
postRemoveExpectedStrings.add(n.getChannelId());
+ if (n.getConversationId() != null) {
+ postRemoveExpectedStrings.add(n.getConversationId());
+ }
postRemoveExpectedEntries.add(n);
}
@@ -221,14 +239,14 @@
history.poolStringsFromNotifications();
assertThat(history.getNotificationsToWrite().size()).isEqualTo(10);
- // 2 package names and 10 * 2 unique channel names and ids
- assertThat(history.getPooledStringsToWrite().length).isEqualTo(22);
+ // 2 package names and 10 * 2 unique channel names and ids and 5 conversation ids
+ assertThat(history.getPooledStringsToWrite().length).isEqualTo(27);
history.removeNotificationsFromWrite("pkgOdd");
- // 1 package names and 5 * 2 unique channel names and ids
- assertThat(history.getPooledStringsToWrite().length).isEqualTo(11);
+ // 1 package names and 5 * 2 unique channel names and ids and 5 conversation ids
+ assertThat(history.getPooledStringsToWrite().length).isEqualTo(16);
assertThat(history.getNotificationsToWrite())
.containsExactlyElementsIn(postRemoveExpectedEntries);
}
@@ -246,6 +264,9 @@
postRemoveExpectedStrings.add(n.getPackage());
postRemoveExpectedStrings.add(n.getChannelName());
postRemoveExpectedStrings.add(n.getChannelId());
+ if (n.getConversationId() != null) {
+ postRemoveExpectedStrings.add(n.getConversationId());
+ }
postRemoveExpectedEntries.add(n);
}
@@ -255,14 +276,14 @@
history.poolStringsFromNotifications();
assertThat(history.getNotificationsToWrite().size()).isEqualTo(10);
- // 1 package name and 10 unique channel names and ids
- assertThat(history.getPooledStringsToWrite().length).isEqualTo(21);
+ // 1 package name and 20 unique channel names and ids and 5 conversation ids
+ assertThat(history.getPooledStringsToWrite().length).isEqualTo(26);
history.removeNotificationFromWrite("pkg", 987654323);
- // 1 package names and 9 * 2 unique channel names and ids
- assertThat(history.getPooledStringsToWrite().length).isEqualTo(19);
+ // 1 package names and 9 * 2 unique channel names and ids and 4 conversation ids
+ assertThat(history.getPooledStringsToWrite().length).isEqualTo(23);
assertThat(history.getNotificationsToWrite())
.containsExactlyElementsIn(postRemoveExpectedEntries);
}
diff --git a/core/tests/coretests/src/android/os/BundleTest.java b/core/tests/coretests/src/android/os/BundleTest.java
index e4dc993..4cc70ba 100644
--- a/core/tests/coretests/src/android/os/BundleTest.java
+++ b/core/tests/coretests/src/android/os/BundleTest.java
@@ -30,12 +30,23 @@
* Unit tests for bundle that requires accessing hidden APS. Tests that can be written only with
* public APIs should go in the CTS counterpart.
*
- * Run with:
- * bit FrameworksCoreTests:android.os.BundleTest
+ * Run with: atest FrameworksCoreTests:android.os.BundleTest
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
public class BundleTest {
+
+ /**
+ * Take a bundle, write it to a parcel and return the parcel.
+ */
+ private Parcel getParcelledBundle(Bundle bundle) {
+ final Parcel p = Parcel.obtain();
+ // Don't use p.writeParcelabe(), which would write the creator, which we don't need.
+ bundle.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ return p;
+ }
+
/**
* Create a test bundle, parcel it and return the parcel.
*/
@@ -48,12 +59,7 @@
pipe[1].close();
source.putParcelable("fd", pipe[0]);
}
- final Parcel p = Parcel.obtain();
- // Don't use p.writeParcelabe(), which would write the creator, which we don't need.
- source.writeToParcel(p, 0);
- p.setDataPosition(0);
-
- return p;
+ return getParcelledBundle(source);
}
/**
@@ -137,4 +143,78 @@
checkBundle(b, withFd);
p.recycle();
}
+
+ @Test
+ public void kindofEquals_bothUnparcelled_same() {
+ Bundle bundle1 = new Bundle();
+ bundle1.putString("StringKey", "S");
+ bundle1.putInt("IntKey", 2);
+
+ Bundle bundle2 = new Bundle();
+ bundle2.putString("StringKey", "S");
+ bundle2.putInt("IntKey", 2);
+
+ assertTrue(BaseBundle.kindofEquals(bundle1, bundle2));
+ }
+
+ @Test
+ public void kindofEquals_bothUnparcelled_different() {
+ Bundle bundle1 = new Bundle();
+ bundle1.putString("StringKey", "S");
+ bundle1.putInt("IntKey", 2);
+
+ Bundle bundle2 = new Bundle();
+ bundle2.putString("StringKey", "T");
+ bundle2.putLong("LongKey", 30L);
+
+ assertFalse(BaseBundle.kindofEquals(bundle1, bundle2));
+ }
+
+ @Test
+ public void kindofEquals_bothParcelled_same() {
+ Bundle bundle1 = new Bundle();
+ bundle1.putString("StringKey", "S");
+ bundle1.putInt("IntKey", 2);
+ bundle1.readFromParcel(getParcelledBundle(bundle1));
+
+ Bundle bundle2 = new Bundle();
+ bundle2.putString("StringKey", "S");
+ bundle2.putInt("IntKey", 2);
+ bundle2.readFromParcel(getParcelledBundle(bundle2));
+
+ assertTrue(bundle1.isParcelled());
+ assertTrue(bundle2.isParcelled());
+ assertTrue(BaseBundle.kindofEquals(bundle1, bundle2));
+ }
+
+ @Test
+ public void kindofEquals_bothParcelled_different() {
+ Bundle bundle1 = new Bundle();
+ bundle1.putString("StringKey", "S");
+ bundle1.putInt("IntKey", 2);
+ bundle1.readFromParcel(getParcelledBundle(bundle1));
+
+ Bundle bundle2 = new Bundle();
+ bundle2.putString("StringKey", "T");
+ bundle2.putLong("LongKey", 5);
+ bundle2.readFromParcel(getParcelledBundle(bundle2));
+
+ assertTrue(bundle1.isParcelled());
+ assertTrue(bundle2.isParcelled());
+ assertFalse(BaseBundle.kindofEquals(bundle1, bundle2));
+ }
+
+ @Test
+ public void kindofEquals_ParcelledUnparcelled_empty() {
+ Bundle bundle1 = new Bundle();
+ bundle1.readFromParcel(getParcelledBundle(bundle1));
+
+ Bundle bundle2 = new Bundle();
+
+ assertTrue(bundle1.isParcelled());
+ assertFalse(bundle2.isParcelled());
+ // Even though one is parcelled and the other is not, both are empty, so it should
+ // return true
+ assertTrue(BaseBundle.kindofEquals(bundle1, bundle2));
+ }
}
diff --git a/identity/java/android/security/identity/CredstoreIdentityCredential.java b/identity/java/android/security/identity/CredstoreIdentityCredential.java
index c520331..7c0af6d 100644
--- a/identity/java/android/security/identity/CredstoreIdentityCredential.java
+++ b/identity/java/android/security/identity/CredstoreIdentityCredential.java
@@ -152,8 +152,8 @@
derivedKey = Util.computeHkdf("HmacSha256", sharedSecret, salt, info, 32);
mReaderSecretKey = new SecretKeySpec(derivedKey, "AES");
- mEphemeralCounter = 0;
- mReadersExpectedEphemeralCounter = 0;
+ mEphemeralCounter = 1;
+ mReadersExpectedEphemeralCounter = 1;
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Error performing key agreement", e);
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index debb38b..81dedda 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -84,7 +84,6 @@
"libGLESv3",
"libvulkan",
"libui",
- "libgui",
"libnativedisplay",
"libnativewindow",
"libprotobuf-cpp-lite",
@@ -289,6 +288,13 @@
name: "hwui_test_defaults",
defaults: ["hwui_defaults"],
test_suites: ["device-tests"],
+ target: {
+ android: {
+ shared_libs: [
+ "libgui",
+ ],
+ }
+ },
srcs: [
"tests/common/scenes/*.cpp",
"tests/common/LeakChecker.cpp",
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 58cc08b..914c046 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -32,7 +32,6 @@
#ifndef _WIN32
#include <binder/IServiceManager.h>
-#include <private/gui/ComposerService.h>
#endif
#include <ui/PixelFormat.h>
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
index e482cad..fc6e114 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
@@ -16,8 +16,10 @@
#pragma once
-#include "SkiaPipeline.h"
+#include <EGL/egl.h>
+#include <system/window.h>
+#include "SkiaPipeline.h"
#include "renderstate/RenderState.h"
namespace android {
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 29b4dd7..41aa1ff 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -24,18 +24,19 @@
#include <SkOverdrawColorFilter.h>
#include <SkPicture.h>
#include <SkPictureRecorder.h>
-#include <SkTypeface.h>
#include <SkSerialProcs.h>
+#include <SkTypeface.h>
+#include <android-base/properties.h>
+#include <unistd.h>
+
+#include <sstream>
+
#include "LightingInfo.h"
#include "VectorDrawable.h"
#include "thread/CommonPool.h"
#include "tools/SkSharingProc.h"
-#include "utils/TraceUtils.h"
#include "utils/String8.h"
-
-#include <unistd.h>
-
-#include <android-base/properties.h>
+#include "utils/TraceUtils.h"
using namespace android::uirenderer::renderthread;
@@ -600,27 +601,24 @@
// Overdraw debugging
// These colors should be kept in sync with Caches::getOverdrawColor() with a few differences.
-// This implementation:
-// (1) Requires transparent entries for "no overdraw" and "single draws".
-// (2) Requires premul colors (instead of unpremul).
-// (3) Requires RGBA colors (instead of BGRA).
-static const uint32_t kOverdrawColors[2][6] = {
- {
- 0x00000000,
- 0x00000000,
- 0x2f2f0000,
- 0x2f002f00,
- 0x3f00003f,
- 0x7f00007f,
- },
- {
- 0x00000000,
- 0x00000000,
- 0x2f2f0000,
- 0x4f004f4f,
- 0x5f50335f,
- 0x7f00007f,
- },
+// This implementation requires transparent entries for "no overdraw" and "single draws".
+static const SkColor kOverdrawColors[2][6] = {
+ {
+ 0x00000000,
+ 0x00000000,
+ 0x2f0000ff,
+ 0x2f00ff00,
+ 0x3fff0000,
+ 0x7fff0000,
+ },
+ {
+ 0x00000000,
+ 0x00000000,
+ 0x2f0000ff,
+ 0x4fffff00,
+ 0x5fff89d7,
+ 0x7fff0000,
+ },
};
void SkiaPipeline::renderOverdraw(const SkRect& clip,
@@ -642,8 +640,8 @@
// Draw overdraw colors to the canvas. The color filter will convert counts to colors.
SkPaint paint;
- const SkPMColor* colors = kOverdrawColors[static_cast<int>(Properties::overdrawColorSet)];
- paint.setColorFilter(SkOverdrawColorFilter::Make(colors));
+ const SkColor* colors = kOverdrawColors[static_cast<int>(Properties::overdrawColorSet)];
+ paint.setColorFilter(SkOverdrawColorFilter::MakeWithSkColors(colors));
surface->getCanvas()->drawImage(counts.get(), 0.0f, 0.0f, &paint);
}
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 1df3336..4299dd3 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -139,15 +139,15 @@
mAnimationContext->destroy();
}
-void CanvasContext::setSurface(sp<Surface>&& surface, bool enableTimeout) {
+void CanvasContext::setSurface(ANativeWindow* window, bool enableTimeout) {
ATRACE_CALL();
- if (surface) {
- mNativeSurface = std::make_unique<ReliableSurface>(std::move(surface));
+ if (window) {
+ mNativeSurface = std::make_unique<ReliableSurface>(window);
mNativeSurface->init();
if (enableTimeout) {
// TODO: Fix error handling & re-shorten timeout
- ANativeWindow_setDequeueTimeout(mNativeSurface->getNativeWindow(), 4000_ms);
+ ANativeWindow_setDequeueTimeout(window, 4000_ms);
}
} else {
mNativeSurface = nullptr;
@@ -167,7 +167,7 @@
mFrameNumber = -1;
- if (hasSurface) {
+ if (window != nullptr && hasSurface) {
mHaveNewSurface = true;
mSwapHistory.clear();
// Enable frame stats after the surface has been bound to the appropriate graphics API.
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 629c741..0f1b8ae 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -35,7 +35,6 @@
#include <SkRect.h>
#include <SkSize.h>
#include <cutils/compiler.h>
-#include <gui/Surface.h>
#include <utils/Functor.h>
#include <functional>
@@ -111,7 +110,7 @@
// Won't take effect until next EGLSurface creation
void setSwapBehavior(SwapBehavior swapBehavior);
- void setSurface(sp<Surface>&& surface, bool enableTimeout = true);
+ void setSurface(ANativeWindow* window, bool enableTimeout = true);
bool pauseSurface();
void setStopped(bool stopped);
bool hasSurface() const { return mNativeSurface.get(); }
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index c1fed26..5e0471c 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -20,7 +20,6 @@
#include <GLES/gl.h>
#include <cutils/properties.h>
#include <log/log.h>
-#include <private/gui/SyncFeatures.h>
#include <sync/sync.h>
#include <utils/Trace.h>
@@ -79,6 +78,9 @@
bool displayP3 = false;
bool contextPriority = false;
bool surfacelessContext = false;
+ bool nativeFenceSync = false;
+ bool fenceSync = false;
+ bool waitSync = false;
} EglExtensions;
EglManager::EglManager()
@@ -226,6 +228,9 @@
EglExtensions.displayP3 = extensions.has("EGL_EXT_gl_colorspace_display_p3_passthrough");
EglExtensions.contextPriority = extensions.has("EGL_IMG_context_priority");
EglExtensions.surfacelessContext = extensions.has("EGL_KHR_surfaceless_context");
+ EglExtensions.nativeFenceSync = extensions.has("EGL_ANDROID_native_fence_sync");
+ EglExtensions.fenceSync = extensions.has("EGL_KHR_fence_sync");
+ EglExtensions.waitSync = extensions.has("EGL_KHR_wait_sync");
}
bool EglManager::hasEglContext() {
@@ -527,8 +532,7 @@
return INVALID_OPERATION;
}
- if (SyncFeatures::getInstance().useWaitSync() &&
- SyncFeatures::getInstance().useNativeFenceSync()) {
+ if (EglExtensions.waitSync && EglExtensions.nativeFenceSync) {
// Block GPU on the fence.
// Create an EGLSyncKHR from the current fence.
int fenceFd = ::dup(fence);
@@ -572,7 +576,7 @@
return INVALID_OPERATION;
}
- if (SyncFeatures::getInstance().useNativeFenceSync()) {
+ if (EglExtensions.nativeFenceSync) {
EGLSyncKHR sync = eglCreateSyncKHR(mEglDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
if (sync == EGL_NO_SYNC_KHR) {
ALOGE("EglManager::createReleaseFence: error creating EGL fence: %#x", eglGetError());
@@ -589,7 +593,7 @@
}
*nativeFence = fenceFd;
*eglFence = EGL_NO_SYNC_KHR;
- } else if (useFenceSync && SyncFeatures::getInstance().useFenceSync()) {
+ } else if (useFenceSync && EglExtensions.fenceSync) {
if (*eglFence != EGL_NO_SYNC_KHR) {
// There is already a fence for the current slot. We need to
// wait on that before replacing it with another fence to
diff --git a/libs/hwui/renderthread/ReliableSurface.cpp b/libs/hwui/renderthread/ReliableSurface.cpp
index e92500f..8a0b4e8 100644
--- a/libs/hwui/renderthread/ReliableSurface.cpp
+++ b/libs/hwui/renderthread/ReliableSurface.cpp
@@ -16,7 +16,10 @@
#include "ReliableSurface.h"
+#include <log/log_main.h>
#include <private/android/AHardwareBufferHelpers.h>
+// TODO: this should be including apex instead.
+#include <vndk/window.h>
namespace android::uirenderer::renderthread {
@@ -26,8 +29,9 @@
// to propagate this error back to the caller
constexpr bool DISABLE_BUFFER_PREFETCH = true;
-ReliableSurface::ReliableSurface(sp<Surface>&& surface) : mSurface(std::move(surface)) {
- LOG_ALWAYS_FATAL_IF(!mSurface, "Error, unable to wrap a nullptr");
+ReliableSurface::ReliableSurface(ANativeWindow* window) : mWindow(window) {
+ LOG_ALWAYS_FATAL_IF(!mWindow, "Error, unable to wrap a nullptr");
+ ANativeWindow_acquire(mWindow);
}
ReliableSurface::~ReliableSurface() {
@@ -36,26 +40,27 @@
// As a concrete example, if the underlying ANativeWindow is associated with
// an EGLSurface that is still in use, then if we don't clear out the
// interceptors then we walk into undefined behavior.
- ANativeWindow_setCancelBufferInterceptor(mSurface.get(), nullptr, nullptr);
- ANativeWindow_setDequeueBufferInterceptor(mSurface.get(), nullptr, nullptr);
- ANativeWindow_setQueueBufferInterceptor(mSurface.get(), nullptr, nullptr);
- ANativeWindow_setPerformInterceptor(mSurface.get(), nullptr, nullptr);
+ ANativeWindow_setCancelBufferInterceptor(mWindow, nullptr, nullptr);
+ ANativeWindow_setDequeueBufferInterceptor(mWindow, nullptr, nullptr);
+ ANativeWindow_setQueueBufferInterceptor(mWindow, nullptr, nullptr);
+ ANativeWindow_setPerformInterceptor(mWindow, nullptr, nullptr);
+ ANativeWindow_release(mWindow);
}
void ReliableSurface::init() {
- int result = ANativeWindow_setCancelBufferInterceptor(mSurface.get(), hook_cancelBuffer, this);
+ int result = ANativeWindow_setCancelBufferInterceptor(mWindow, hook_cancelBuffer, this);
LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set cancelBuffer interceptor: error = %d",
result);
- result = ANativeWindow_setDequeueBufferInterceptor(mSurface.get(), hook_dequeueBuffer, this);
+ result = ANativeWindow_setDequeueBufferInterceptor(mWindow, hook_dequeueBuffer, this);
LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set dequeueBuffer interceptor: error = %d",
result);
- result = ANativeWindow_setQueueBufferInterceptor(mSurface.get(), hook_queueBuffer, this);
+ result = ANativeWindow_setQueueBufferInterceptor(mWindow, hook_queueBuffer, this);
LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set queueBuffer interceptor: error = %d",
result);
- result = ANativeWindow_setPerformInterceptor(mSurface.get(), hook_perform, this);
+ result = ANativeWindow_setPerformInterceptor(mWindow, hook_perform, this);
LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set perform interceptor: error = %d",
result);
}
@@ -87,7 +92,7 @@
ANativeWindowBuffer* buffer = nullptr;
// Note that this calls back into our own hooked method.
- int result = ANativeWindow_dequeueBuffer(mSurface.get(), &buffer, &fenceFd);
+ int result = ANativeWindow_dequeueBuffer(mWindow, &buffer, &fenceFd);
{
std::lock_guard _lock{mMutex};
@@ -117,7 +122,7 @@
// Note that clearReservedBuffer may be reentrant here, so
// mReservedBuffer must be cleared once we reach here to avoid recursing
// forever.
- ANativeWindow_cancelBuffer(mSurface.get(), buffer, releaseFd);
+ ANativeWindow_cancelBuffer(mWindow, buffer, releaseFd);
}
}
@@ -239,10 +244,10 @@
case ANATIVEWINDOW_PERFORM_SET_BUFFERS_GEOMETRY:
/* width */ va_arg(args, uint32_t);
/* height */ va_arg(args, uint32_t);
- rs->mFormat = va_arg(args, PixelFormat);
+ rs->mFormat = static_cast<AHardwareBuffer_Format>(va_arg(args, int32_t));
break;
case ANATIVEWINDOW_PERFORM_SET_BUFFERS_FORMAT:
- rs->mFormat = va_arg(args, PixelFormat);
+ rs->mFormat = static_cast<AHardwareBuffer_Format>(va_arg(args, int32_t));
break;
}
}
diff --git a/libs/hwui/renderthread/ReliableSurface.h b/libs/hwui/renderthread/ReliableSurface.h
index e3cd8c0..58cd067 100644
--- a/libs/hwui/renderthread/ReliableSurface.h
+++ b/libs/hwui/renderthread/ReliableSurface.h
@@ -16,12 +16,14 @@
#pragma once
+#include <android-base/unique_fd.h>
#include <apex/window.h>
-#include <gui/Surface.h>
+#include <utils/Errors.h>
#include <utils/Macros.h>
#include <utils/StrongPointer.h>
#include <memory>
+#include <mutex>
namespace android::uirenderer::renderthread {
@@ -29,7 +31,7 @@
PREVENT_COPY_AND_ASSIGN(ReliableSurface);
public:
- ReliableSurface(sp<Surface>&& surface);
+ ReliableSurface(ANativeWindow* window);
~ReliableSurface();
// Performs initialization that is not safe to do in the constructor.
@@ -37,12 +39,10 @@
// passed as the data pointer is not safe.
void init();
- ANativeWindow* getNativeWindow() { return mSurface.get(); }
+ ANativeWindow* getNativeWindow() { return mWindow; }
int reserveNext();
- int query(int what, int* value) const { return mSurface->query(what, value); }
-
int getAndClearError() {
int ret = mBufferQueueState;
mBufferQueueState = OK;
@@ -50,12 +50,12 @@
}
private:
- sp<Surface> mSurface;
+ ANativeWindow* mWindow;
mutable std::mutex mMutex;
uint64_t mUsage = AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER;
- PixelFormat mFormat = PIXEL_FORMAT_RGBA_8888;
+ AHardwareBuffer_Format mFormat = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
std::unique_ptr<AHardwareBuffer, void (*)(AHardwareBuffer*)> mScratchBuffer{
nullptr, AHardwareBuffer_release};
ANativeWindowBuffer* mReservedBuffer = nullptr;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 1e7fc71..b66a13d 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -16,8 +16,6 @@
#include "RenderProxy.h"
-#include <gui/Surface.h>
-
#include "DeferredLayerUpdater.h"
#include "DisplayList.h"
#include "Properties.h"
@@ -78,9 +76,11 @@
mRenderThread.queue().runSync([this, name]() { mContext->setName(std::string(name)); });
}
-void RenderProxy::setSurface(const sp<Surface>& surface, bool enableTimeout) {
- mRenderThread.queue().post([this, surf = surface, enableTimeout]() mutable {
- mContext->setSurface(std::move(surf), enableTimeout);
+void RenderProxy::setSurface(ANativeWindow* window, bool enableTimeout) {
+ ANativeWindow_acquire(window);
+ mRenderThread.queue().post([this, win = window, enableTimeout]() mutable {
+ mContext->setSurface(win, enableTimeout);
+ ANativeWindow_release(win);
});
}
@@ -314,10 +314,9 @@
[context = mContext, renderAhead] { context->setRenderAheadDepth(renderAhead); });
}
-int RenderProxy::copySurfaceInto(sp<Surface>& surface, int left, int top, int right, int bottom,
+int RenderProxy::copySurfaceInto(ANativeWindow* window, int left, int top, int right, int bottom,
SkBitmap* bitmap) {
auto& thread = RenderThread::getInstance();
- ANativeWindow* window = surface.get();
return static_cast<int>(thread.queue().runSync([&]() -> auto {
return thread.readback().copySurfaceInto(window, Rect(left, top, right, bottom), bitmap);
}));
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index ab0dd2b..3baeb2f 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -18,6 +18,7 @@
#define RENDERPROXY_H_
#include <SkBitmap.h>
+#include <android/native_window.h>
#include <cutils/compiler.h>
#include <utils/Functor.h>
@@ -69,7 +70,7 @@
ANDROID_API bool loadSystemProperties();
ANDROID_API void setName(const char* name);
- ANDROID_API void setSurface(const sp<Surface>& surface, bool enableTimeout = true);
+ ANDROID_API void setSurface(ANativeWindow* window, bool enableTimeout = true);
ANDROID_API void allocateBuffers();
ANDROID_API bool pause();
ANDROID_API void setStopped(bool stopped);
@@ -140,11 +141,7 @@
*/
ANDROID_API void setRenderAheadDepth(int renderAhead);
- // TODO: This api will need to take in an ANativeWindow instead, but the
- // caller, ThreadedRenderer, doesn't have access to libandroid due to a
- // circular dependency, so it can't use the JNI ANativeWindow methods. Once
- // that is resolved then replace the surface type here.
- ANDROID_API static int copySurfaceInto(sp<Surface>& surface, int left, int top, int right,
+ ANDROID_API static int copySurfaceInto(ANativeWindow* window, int left, int top, int right,
int bottom, SkBitmap* bitmap);
ANDROID_API static void prepareToDraw(Bitmap& bitmap);
diff --git a/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp b/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp
index f6cff1c..f4fce27 100644
--- a/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp
@@ -70,7 +70,7 @@
magnifier->getSkBitmap(&temp);
constexpr int x = 90;
constexpr int y = 325;
- RenderProxy::copySurfaceInto(renderTarget, x, y, x + magnifier->width(),
+ RenderProxy::copySurfaceInto(renderTarget.get(), x, y, x + magnifier->width(),
y + magnifier->height(), &temp);
}
}
diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
index 3b6baa7..801cb7d 100644
--- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp
+++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
@@ -131,7 +131,7 @@
ContextFactory factory;
std::unique_ptr<RenderProxy> proxy(new RenderProxy(false, rootNode.get(), &factory));
proxy->loadSystemProperties();
- proxy->setSurface(surface);
+ proxy->setSurface(surface.get());
float lightX = width / 2.0;
proxy->setLightAlpha(255 * 0.075, 255 * 0.15);
proxy->setLightGeometry((Vector3){lightX, dp(-200.0f), dp(800.0f)}, dp(800.0f));
diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
index 7d999c4..d08aea6 100644
--- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
+++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
@@ -210,7 +210,7 @@
auto surface = testContext.surface();
int width = ANativeWindow_getWidth(surface.get());
int height = ANativeWindow_getHeight(surface.get());
- canvasContext->setSurface(std::move(surface));
+ canvasContext->setSurface(surface.get());
TreeInfo info(TreeInfo::MODE_FULL, *canvasContext.get());
DamageAccumulator damageAccumulator;
diff --git a/libs/protoutil/Android.bp b/libs/protoutil/Android.bp
index b0af997..d2b7d5c 100644
--- a/libs/protoutil/Android.bp
+++ b/libs/protoutil/Android.bp
@@ -45,6 +45,12 @@
defaults: ["libprotoutil_defaults"],
export_include_dirs: ["include"],
+
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.os.statsd",
+ "test_com.android.os.statsd",
+ ],
}
cc_test {
diff --git a/location/java/android/location/GnssMeasurementCorrections.java b/location/java/android/location/GnssMeasurementCorrections.java
index 19c3992..ffbe11a 100644
--- a/location/java/android/location/GnssMeasurementCorrections.java
+++ b/location/java/android/location/GnssMeasurementCorrections.java
@@ -179,6 +179,10 @@
* Gets the environment bearing in degrees clockwise from true north, in the direction of user
* motion. Environment bearing is provided when it is known with high probability that
* velocity is aligned with an environment feature (such as edge of a building, or road).
+ *
+ * {@link #hasEnvironmentBearing} should be called to check the environment bearing is available
+ * before calling this method. The value is undefined if {@link #hasEnvironmentBearing} returns
+ * false.
*/
@FloatRange(from = 0.0f, to = 360.0f)
public float getEnvironmentBearingDegrees() {
@@ -186,7 +190,15 @@
}
/**
- * Gets the environment bearing uncertainty in degrees.
+ * Gets the environment bearing uncertainty in degrees. It represents the standard deviation of
+ * the physical structure in the circle of position uncertainty. The uncertainty can take values
+ * between 0 and 180 degrees. The {@link #hasEnvironmentBearing} becomes false as the
+ * uncertainty value passes a predefined threshold depending on the physical structure around
+ * the user.
+ *
+ * {@link #hasEnvironmentBearing} should be called to check the environment bearing is available
+ * before calling this method. The value is undefined if {@link #hasEnvironmentBearing} returns
+ * false.
*/
@FloatRange(from = 0.0f, to = 180.0f)
public float getEnvironmentBearingUncertaintyDegrees() {
@@ -358,6 +370,8 @@
* user motion. Environment bearing is provided when it is known with high probability
* that velocity is aligned with an environment feature (such as edge of a building, or
* road).
+ *
+ * Both the bearing and uncertainty must be set for the environment bearing to be valid.
*/
@NonNull public Builder setEnvironmentBearingDegrees(
@FloatRange(from = 0.0f, to = 360.0f)
@@ -369,6 +383,8 @@
/**
* Sets the environment bearing uncertainty in degrees.
+ *
+ * Both the bearing and uncertainty must be set for the environment bearing to be valid.
*/
@NonNull public Builder setEnvironmentBearingUncertaintyDegrees(
@FloatRange(from = 0.0f, to = 180.0f)
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 44142e3..4db1109 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -612,10 +612,10 @@
*
* @return a {@link Descrambler} object.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+ @RequiresPermission(android.Manifest.permission.ACCESS_TV_DESCRAMBLER)
@Nullable
public Descrambler openDescrambler() {
- TunerUtils.checkTunerPermission(mContext);
+ TunerUtils.checkDescramblerPermission(mContext);
return nativeOpenDescrambler();
}
diff --git a/media/java/android/media/tv/tuner/TunerUtils.java b/media/java/android/media/tv/tuner/TunerUtils.java
index 30aaa02..5ecb8f0 100644
--- a/media/java/android/media/tv/tuner/TunerUtils.java
+++ b/media/java/android/media/tv/tuner/TunerUtils.java
@@ -27,7 +27,9 @@
* @hide
*/
public final class TunerUtils {
- private static final String PERMISSION = android.Manifest.permission.ACCESS_TV_TUNER;
+ private static final String TUNER_PERMISSION = android.Manifest.permission.ACCESS_TV_TUNER;
+ private static final String DESCRAMBLER_PERMISSION =
+ android.Manifest.permission.ACCESS_TV_DESCRAMBLER;
/**
* Checks whether the caller has permission to access tuner.
@@ -36,9 +38,30 @@
* @throws SecurityException if the caller doesn't have the permission.
*/
public static void checkTunerPermission(Context context) {
- if (context.checkCallingOrSelfPermission(PERMISSION)
+ checkPermission(context, TUNER_PERMISSION);
+ }
+
+ /**
+ * Checks whether the caller has permission to access the descrambler.
+ *
+ * @param context context of the caller.
+ * @throws SecurityException if the caller doesn't have the permission.
+ */
+ public static void checkDescramblerPermission(Context context) {
+ checkPermission(context, DESCRAMBLER_PERMISSION);
+ }
+
+ /**
+ * Checks whether the caller has the given permission.
+ *
+ * @param context context of the caller.
+ * @param permission the given permission.
+ * @throws SecurityException if the caller doesn't have the permission.
+ */
+ public static void checkPermission(Context context, String permission) {
+ if (context.checkCallingOrSelfPermission(permission)
!= PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Caller must have " + PERMISSION + " permission.");
+ throw new SecurityException("Caller must have " + permission + " permission.");
}
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java
index 07b7b22..8eeaefd 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java
@@ -17,8 +17,10 @@
package com.android.systemui;
import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.bubbles.dagger.BubbleModule;
import com.android.systemui.globalactions.GlobalActionsComponent;
import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.keyguard.dagger.KeyguardModule;
import com.android.systemui.navigationbar.car.CarNavigationBar;
import com.android.systemui.pip.PipUI;
import com.android.systemui.power.PowerUI;
@@ -29,6 +31,7 @@
import com.android.systemui.statusbar.car.CarStatusBar;
import com.android.systemui.statusbar.car.CarStatusBarModule;
import com.android.systemui.statusbar.notification.InstantAppNotifier;
+import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.tv.TvStatusBar;
import com.android.systemui.theme.ThemeOverlayController;
@@ -42,7 +45,8 @@
import dagger.multibindings.IntoMap;
/** Binder for car specific {@link SystemUI} modules. */
-@Module(includes = {RecentsModule.class, CarStatusBarModule.class})
+@Module(includes = {RecentsModule.class, CarStatusBarModule.class, NotificationsModule.class,
+ BubbleModule.class, KeyguardModule.class})
public abstract class CarSystemUIBinder {
/** Inject into AuthController. */
@Binds
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
index 498bd87..7f64990 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
@@ -54,10 +54,10 @@
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationViewHierarchyManager;
import com.android.systemui.statusbar.PulseExpansionHandler;
-import com.android.systemui.statusbar.StatusBarDependenciesModule;
import com.android.systemui.statusbar.SuperStatusBarViewFactory;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.dagger.StatusBarDependenciesModule;
import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationAlertingManager;
@@ -67,6 +67,7 @@
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.row.NotificationRowModule;
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.DozeParameters;
@@ -87,6 +88,7 @@
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
+import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneDependenciesModule;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -112,7 +114,8 @@
/**
* Dagger Module providing {@link CarStatusBar}.
*/
-@Module(includes = {StatusBarDependenciesModule.class})
+@Module(includes = {StatusBarDependenciesModule.class, StatusBarPhoneDependenciesModule.class,
+ NotificationRowModule.class})
public class CarStatusBarModule {
/**
* Provides our instance of StatusBar which is considered optional.
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/ConversationIconFactory.java b/packages/SettingsLib/src/com/android/settingslib/notification/ConversationIconFactory.java
new file mode 100644
index 0000000..885b7d3
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/ConversationIconFactory.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settingslib.notification;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.LauncherApps;
+import android.content.pm.PackageManager;
+import android.content.pm.ShortcutInfo;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.util.IconDrawableFactory;
+
+import com.android.launcher3.icons.BaseIconFactory;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.ShadowGenerator;
+
+/**
+ * Factory for creating normalized conversation icons.
+ * We are not using Launcher's IconFactory because conversation rendering only runs on the UI
+ * thread, so there is no need to manage a pool across multiple threads.
+ */
+public class ConversationIconFactory extends BaseIconFactory {
+
+ final LauncherApps mLauncherApps;
+ final PackageManager mPackageManager;
+ final IconDrawableFactory mIconDrawableFactory;
+
+ public ConversationIconFactory(Context context, LauncherApps la, PackageManager pm,
+ IconDrawableFactory iconDrawableFactory, int iconSizePx) {
+ super(context, context.getResources().getConfiguration().densityDpi,
+ iconSizePx);
+ mLauncherApps = la;
+ mPackageManager = pm;
+ mIconDrawableFactory = iconDrawableFactory;
+ }
+
+ private int getBadgeSize() {
+ return mContext.getResources().getDimensionPixelSize(
+ com.android.launcher3.icons.R.dimen.profile_badge_size);
+ }
+ /**
+ * Returns the conversation info drawable
+ */
+ private Drawable getConversationDrawable(ShortcutInfo shortcutInfo) {
+ return mLauncherApps.getShortcutIconDrawable(shortcutInfo, mFillResIconDpi);
+ }
+
+ /**
+ * Get the {@link Drawable} that represents the app icon
+ */
+ private Drawable getBadgedIcon(String packageName, int userId) {
+ try {
+ final ApplicationInfo appInfo = mPackageManager.getApplicationInfoAsUser(
+ packageName, PackageManager.GET_META_DATA, userId);
+ return mIconDrawableFactory.getBadgedIcon(appInfo, userId);
+ } catch (PackageManager.NameNotFoundException e) {
+ return mPackageManager.getDefaultActivityIcon();
+ }
+ }
+
+ /**
+ * Turns a Drawable into a Bitmap
+ */
+ BitmapInfo toBitmap(Drawable userBadgedAppIcon) {
+ Bitmap bitmap = createIconBitmap(
+ userBadgedAppIcon, 1f, getBadgeSize());
+
+ Canvas c = new Canvas();
+ ShadowGenerator shadowGenerator = new ShadowGenerator(getBadgeSize());
+ c.setBitmap(bitmap);
+ shadowGenerator.recreateIcon(Bitmap.createBitmap(bitmap), c);
+ return createIconBitmap(bitmap);
+ }
+
+ /**
+ * Returns a {@link BitmapInfo} for the entire conversation icon including the badge.
+ */
+ public Bitmap getConversationBitmap(ShortcutInfo info, String packageName, int uid) {
+ return getConversationBitmap(getConversationDrawable(info), packageName, uid);
+ }
+
+ /**
+ * Returns a {@link BitmapInfo} for the entire conversation icon including the badge.
+ */
+ public Bitmap getConversationBitmap(Drawable baseIcon, String packageName, int uid) {
+ int userId = UserHandle.getUserId(uid);
+ Drawable badge = getBadgedIcon(packageName, userId);
+ BitmapInfo iconInfo = createBadgedIconBitmap(baseIcon,
+ UserHandle.of(userId),
+ true /* shrinkNonAdaptiveIcons */);
+
+ badgeWithDrawable(iconInfo.icon,
+ new BitmapDrawable(mContext.getResources(), toBitmap(badge).icon));
+ return iconInfo.icon;
+ }
+}
diff --git a/packages/SystemUI/res/drawable/ic_screen_record_background.xml b/packages/SystemUI/res/drawable/ic_screen_record_background.xml
new file mode 100644
index 0000000..9195305
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_screen_record_background.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2020 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="24dp"
+ android:height="24dp"
+ android:tint="?android:attr/colorError"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M10,0L14,0A10,10 0,0 1,24 10L24,10A10,10 0,0 1,14 20L10,20A10,10 0,0 1,0 10L0,10A10,10 0,0 1,10 0z"
+ android:fillColor="@android:color/white"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_screen_record.xml b/packages/SystemUI/res/drawable/stat_sys_screen_record.xml
new file mode 100644
index 0000000..486af9e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_screen_record.xml
@@ -0,0 +1,16 @@
+<!--
+Copyright (C) 2020 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.statusbar.ScreenRecordDrawable />
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/stat_sys_screen_record_1.xml b/packages/SystemUI/res/drawable/stat_sys_screen_record_1.xml
new file mode 100644
index 0000000..ab2314e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_screen_record_1.xml
@@ -0,0 +1,18 @@
+<!--
+Copyright (C) 2020 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.statusbar.ScreenRecordDrawable
+ level="1"
+/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/stat_sys_screen_record_2.xml b/packages/SystemUI/res/drawable/stat_sys_screen_record_2.xml
new file mode 100644
index 0000000..8764ff9
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_screen_record_2.xml
@@ -0,0 +1,18 @@
+<!--
+Copyright (C) 2020 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.statusbar.ScreenRecordDrawable
+ level="2"
+/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/stat_sys_screen_record_3.xml b/packages/SystemUI/res/drawable/stat_sys_screen_record_3.xml
new file mode 100644
index 0000000..0ff4d9a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_screen_record_3.xml
@@ -0,0 +1,18 @@
+<!--
+Copyright (C) 2020 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.statusbar.ScreenRecordDrawable
+ level="3"
+/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index ec56c1f..15575a4 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1228,6 +1228,8 @@
<!-- Screen Record -->
<dimen name="screenrecord_dialog_padding">18dp</dimen>
<dimen name="screenrecord_logo_size">24dp</dimen>
+ <dimen name="screenrecord_status_text_size">14sp</dimen>
+ <dimen name="screenrecord_status_icon_radius">5dp</dimen>
<dimen name="kg_user_switcher_text_size">16sp</dimen>
</resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index 2d288ff..b1d39f5 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -123,4 +123,9 @@
*/
void handleImageAsScreenshot(in Bitmap screenImage, in Rect locationInScreen,
in Insets visibleInsets, int taskId) = 21;
+
+ /**
+ * Sets the split-screen divider minimized state
+ */
+ void setSplitScreenMinimized(boolean minimized) = 22;
}
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 27fe37e..dc5cb1f 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -61,6 +61,7 @@
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.DevicePolicyManagerWrapper;
import com.android.systemui.shared.system.PackageManagerWrapper;
+import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NavigationBarController;
import com.android.systemui.statusbar.NotificationListener;
@@ -329,6 +330,7 @@
@Inject Lazy<DisplayImeController> mDisplayImeController;
@Inject Lazy<RecordingController> mRecordingController;
@Inject Lazy<ProtoTracer> mProtoTracer;
+ @Inject Lazy<Divider> mDivider;
@Inject
public Dependency() {
@@ -530,6 +532,7 @@
mProviders.put(AutoHideController.class, mAutoHideController::get);
mProviders.put(RecordingController.class, mRecordingController::get);
+ mProviders.put(Divider.class, mDivider::get);
sDependency = this;
}
diff --git a/packages/SystemUI/src/com/android/systemui/DockedStackExistsListener.java b/packages/SystemUI/src/com/android/systemui/DockedStackExistsListener.java
deleted file mode 100644
index 5c0df17..0000000
--- a/packages/SystemUI/src/com/android/systemui/DockedStackExistsListener.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui;
-
-import android.os.RemoteException;
-import android.util.Log;
-import android.view.IDockedStackListener;
-import android.view.WindowManagerGlobal;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.function.Consumer;
-
-/**
- * Utility wrapper to listen for whether or not a docked stack exists, to be
- * used for things like the different overview icon in that mode.
- */
-public class DockedStackExistsListener {
-
- private static final String TAG = "DockedStackExistsListener";
-
- private static ArrayList<WeakReference<Consumer<Boolean>>> sCallbacks = new ArrayList<>();
- private static boolean mLastExists;
-
- static {
- try {
- WindowManagerGlobal.getWindowManagerService().registerDockedStackListener(
- new IDockedStackListener.Stub() {
- @Override
- public void onDividerVisibilityChanged(boolean b) throws RemoteException {
-
- }
-
- @Override
- public void onDockedStackExistsChanged(boolean exists)
- throws RemoteException {
- DockedStackExistsListener.onDockedStackExistsChanged(exists);
- }
-
- @Override
- public void onDockedStackMinimizedChanged(boolean b, long l, boolean b1)
- throws RemoteException {
-
- }
-
- @Override
- public void onAdjustedForImeChanged(boolean b, long l)
- throws RemoteException {
-
- }
-
- @Override
- public void onDockSideChanged(int i) throws RemoteException {
-
- }
- });
- } catch (RemoteException e) {
- Log.e(TAG, "Failed registering docked stack exists listener", e);
- }
- }
-
-
- private static void onDockedStackExistsChanged(boolean exists) {
- mLastExists = exists;
- synchronized (sCallbacks) {
- sCallbacks.removeIf(wf -> {
- Consumer<Boolean> l = wf.get();
- if (l != null) l.accept(exists);
- return l == null;
- });
- }
- }
-
- public static void register(Consumer<Boolean> callback) {
- callback.accept(mLastExists);
- synchronized (sCallbacks) {
- sCallbacks.add(new WeakReference<>(callback));
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index e66b9f2..a2ae59e 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -22,12 +22,8 @@
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-import static com.android.systemui.tuner.TunablePadding.FLAG_END;
-import static com.android.systemui.tuner.TunablePadding.FLAG_START;
-
import android.annotation.Dimension;
import android.app.ActivityManager;
-import android.app.Fragment;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -69,13 +65,7 @@
import com.android.systemui.RegionInterceptingFrameLayout.RegionInterceptableView;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.fragments.FragmentHostManager;
-import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
-import com.android.systemui.plugins.qs.QS;
import com.android.systemui.qs.SecureSetting;
-import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
-import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.tuner.TunablePadding;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
import com.android.systemui.util.leak.RotationUtils;
@@ -86,8 +76,6 @@
import javax.inject.Inject;
import javax.inject.Singleton;
-import dagger.Lazy;
-
/**
* An overlay that draws screen decorations in software (e.g for rounded corners or display cutout)
* for antialiasing and emulation purposes.
@@ -102,7 +90,6 @@
private static final boolean DEBUG_SCREENSHOT_ROUNDED_CORNERS =
SystemProperties.getBoolean("debug.screenshot_rounded_corners", false);
private static final boolean VERBOSE = false;
- private final Lazy<StatusBar> mStatusBarLazy;
private DisplayManager mDisplayManager;
private final BroadcastDispatcher mBroadcastDispatcher;
@@ -146,12 +133,10 @@
@Inject
public ScreenDecorations(Context context,
- Lazy<StatusBar> statusBarLazy,
@Main Handler handler,
BroadcastDispatcher broadcastDispatcher,
TunerService tunerService) {
super(context);
- mStatusBarLazy = statusBarLazy;
mMainHandler = handler;
mBroadcastDispatcher = broadcastDispatcher;
mTunerService = tunerService;
@@ -161,7 +146,6 @@
public void start() {
mHandler = startHandlerThread();
mHandler.post(this::startOnScreenDecorationsThread);
- setupStatusBarPaddingIfNeeded();
}
@VisibleForTesting
@@ -440,42 +424,6 @@
com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout);
}
-
- private void setupStatusBarPaddingIfNeeded() {
- // TODO: This should be moved to a more appropriate place, as it is not related to the
- // screen decorations overlay.
- int padding = mContext.getResources().getDimensionPixelSize(
- R.dimen.rounded_corner_content_padding);
- if (padding != 0) {
- setupStatusBarPadding(padding);
- }
-
- }
-
- private void setupStatusBarPadding(int padding) {
- // Add some padding to all the content near the edge of the screen.
- StatusBar statusBar = mStatusBarLazy.get();
- final View notificationShadeWindowView = statusBar.getNotificationShadeWindowView();
- if (notificationShadeWindowView != null) {
- TunablePadding.addTunablePadding(
- notificationShadeWindowView.findViewById(R.id.keyguard_header),
- PADDING, padding, FLAG_END);
-
- final FragmentHostManager fragmentHostManager =
- FragmentHostManager.get(notificationShadeWindowView);
- fragmentHostManager.addTagListener(QS.TAG,
- new TunablePaddingTagListener(padding, R.id.header));
- }
-
- final View statusBarWindow = statusBar.getStatusBarWindow();
- if (statusBarWindow != null) {
- final FragmentHostManager fragmentHostManager =
- FragmentHostManager.get(statusBarWindow);
- fragmentHostManager.addTagListener(CollapsedStatusBarFragment.TAG,
- new TunablePaddingTagListener(padding, R.id.status_bar));
- }
- }
-
@VisibleForTesting
WindowManager.LayoutParams getWindowLayoutParams() {
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
@@ -566,32 +514,6 @@
view.setLayoutParams(params);
}
- @VisibleForTesting
- static class TunablePaddingTagListener implements FragmentListener {
-
- private final int mPadding;
- private final int mId;
- private TunablePadding mTunablePadding;
-
- public TunablePaddingTagListener(int padding, int id) {
- mPadding = padding;
- mId = id;
- }
-
- @Override
- public void onFragmentViewCreated(String tag, Fragment fragment) {
- if (mTunablePadding != null) {
- mTunablePadding.destroy();
- }
- View view = fragment.getView();
- if (mId != 0) {
- view = view.findViewById(mId);
- }
- mTunablePadding = TunablePadding.addTunablePadding(view, PADDING, mPadding,
- FLAG_START | FLAG_END);
- }
- }
-
public static class DisplayCutoutView extends View implements DisplayManager.DisplayListener,
RegionInterceptableView {
@@ -763,11 +685,11 @@
return false;
}
if (mStart) {
- return displayCutout.getSafeInsetLeft() > 0
- || displayCutout.getSafeInsetTop() > 0;
+ return !displayCutout.getBoundingRectLeft().isEmpty()
+ || !displayCutout.getBoundingRectTop().isEmpty();
} else {
- return displayCutout.getSafeInsetRight() > 0
- || displayCutout.getSafeInsetBottom() > 0;
+ return !displayCutout.getBoundingRectRight().isEmpty()
+ || !displayCutout.getBoundingRectBottom().isEmpty();
}
}
@@ -809,15 +731,15 @@
private int getGravity(DisplayCutout displayCutout) {
if (mStart) {
- if (displayCutout.getSafeInsetLeft() > 0) {
+ if (!displayCutout.getBoundingRectLeft().isEmpty()) {
return Gravity.LEFT;
- } else if (displayCutout.getSafeInsetTop() > 0) {
+ } else if (!displayCutout.getBoundingRectTop().isEmpty()) {
return Gravity.TOP;
}
} else {
- if (displayCutout.getSafeInsetRight() > 0) {
+ if (!displayCutout.getBoundingRectRight().isEmpty()) {
return Gravity.RIGHT;
- } else if (displayCutout.getSafeInsetBottom() > 0) {
+ } else if (!displayCutout.getBoundingRectBottom().isEmpty()) {
return Gravity.BOTTOM;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 762e5f2..1f94dbd 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -72,9 +72,7 @@
import com.android.systemui.DumpController;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
-import com.android.systemui.bubbles.BubbleController.BubbleExpandListener;
-import com.android.systemui.bubbles.BubbleController.BubbleStateChangeListener;
-import com.android.systemui.bubbles.BubbleController.NotifCallback;
+import com.android.systemui.bubbles.dagger.BubbleModule;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.PinnedStackListenerForwarder;
@@ -105,16 +103,12 @@
import java.util.HashSet;
import java.util.List;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
/**
* Bubbles are a special type of content that can "float" on top of other apps or System UI.
* Bubbles can be expanded to show more content.
*
* The controller manages addition, removal, and visible state of bubbles on screen.
*/
-@Singleton
public class BubbleController implements ConfigurationController.ConfigurationListener, Dumpable {
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleController" : TAG_BUBBLES;
@@ -277,7 +271,6 @@
}
}
- @Inject
public BubbleController(Context context,
NotificationShadeWindowController notificationShadeWindowController,
StatusBarStateController statusBarStateController,
@@ -298,6 +291,9 @@
notifPipeline, featureFlags, dumpController);
}
+ /**
+ * Injected constructor. See {@link BubbleModule}.
+ */
public BubbleController(Context context,
NotificationShadeWindowController notificationShadeWindowController,
StatusBarStateController statusBarStateController,
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
new file mode 100644
index 0000000..0337ee3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2020 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.bubbles.dagger;
+
+import android.content.Context;
+
+import com.android.systemui.DumpController;
+import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.bubbles.BubbleData;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.ZenModeController;
+
+import javax.inject.Singleton;
+
+import dagger.Module;
+import dagger.Provides;
+
+/** */
+@Module
+public interface BubbleModule {
+
+ /**
+ */
+ @Singleton
+ @Provides
+ static BubbleController newBubbleController(
+ Context context,
+ NotificationShadeWindowController notificationShadeWindowController,
+ StatusBarStateController statusBarStateController,
+ ShadeController shadeController,
+ BubbleData data,
+ ConfigurationController configurationController,
+ NotificationInterruptionStateProvider interruptionStateProvider,
+ ZenModeController zenModeController,
+ NotificationLockscreenUserManager notifUserManager,
+ NotificationGroupManager groupManager,
+ NotificationEntryManager entryManager,
+ NotifPipeline notifPipeline,
+ FeatureFlags featureFlags,
+ DumpController dumpController) {
+ return new BubbleController(
+ context,
+ notificationShadeWindowController,
+ statusBarStateController,
+ shadeController,
+ data,
+ /* synchronizer */null,
+ configurationController,
+ interruptionStateProvider,
+ zenModeController,
+ notifUserManager,
+ groupManager,
+ entryManager,
+ notifPipeline,
+ featureFlags,
+ dumpController);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index bf501ce..7c0033c 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -27,7 +27,6 @@
import android.os.ServiceManager;
import android.util.DisplayMetrics;
import android.view.Choreographer;
-import android.view.IWindowManager;
import android.view.LayoutInflater;
import android.view.WindowManager;
@@ -46,8 +45,6 @@
import com.android.systemui.shared.system.DevicePolicyManagerWrapper;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NavigationBarController;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DataSaverController;
@@ -162,16 +159,6 @@
@Singleton
@Provides
- public AutoHideController provideAutoHideController(Context context,
- @Main Handler mainHandler,
- NotificationRemoteInputManager notificationRemoteInputManager,
- IWindowManager iWindowManager) {
- return new AutoHideController(context, mainHandler, notificationRemoteInputManager,
- iWindowManager);
- }
-
- @Singleton
- @Provides
public ActivityManagerWrapper provideActivityManagerWrapper() {
return ActivityManagerWrapper.getInstance();
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
index 5de88e1..413a522 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
@@ -24,17 +24,19 @@
import com.android.systemui.accessibility.SystemActions;
import com.android.systemui.accessibility.WindowMagnification;
import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.bubbles.dagger.BubbleModule;
import com.android.systemui.globalactions.GlobalActionsComponent;
import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.keyguard.dagger.KeyguardModule;
import com.android.systemui.pip.PipUI;
import com.android.systemui.power.PowerUI;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsModule;
import com.android.systemui.shortcut.ShortcutKeyDispatcher;
import com.android.systemui.stackdivider.Divider;
+import com.android.systemui.statusbar.dagger.StatusBarModule;
import com.android.systemui.statusbar.notification.InstantAppNotifier;
import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.phone.StatusBarModule;
import com.android.systemui.statusbar.tv.TvStatusBar;
import com.android.systemui.theme.ThemeOverlayController;
import com.android.systemui.toast.ToastUI;
@@ -49,7 +51,8 @@
/**
* SystemUI objects that are injectable should go here.
*/
-@Module(includes = {RecentsModule.class, StatusBarModule.class})
+@Module(includes = {RecentsModule.class, StatusBarModule.class, BubbleModule.class,
+ KeyguardModule.class})
public abstract class SystemUIBinder {
/** Inject into AuthController. */
@Binds
@@ -142,7 +145,7 @@
@ClassKey(StatusBar.class)
public abstract SystemUI bindsStatusBar(StatusBar sysui);
- /** Inject into SystemActions. */
+ /** Inject into SystemActions. */
@Binds
@IntoMap
@ClassKey(SystemActions.class)
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 91aeb22..030b504 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -35,10 +35,8 @@
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationShadeWindowBlurController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
-import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
import com.android.systemui.statusbar.notification.people.PeopleHubModule;
import com.android.systemui.statusbar.notification.row.dagger.ExpandableNotificationRowComponent;
import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent;
@@ -69,7 +67,6 @@
AssistModule.class,
ConcurrencyModule.class,
LogModule.class,
- NotificationsModule.class,
PeopleHubModule.class,
},
subcomponents = {StatusBarComponent.class,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 83a6d75..b1db7df 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -86,6 +86,7 @@
import com.android.systemui.SystemUIFactory;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.UiBackground;
+import com.android.systemui.keyguard.dagger.KeyguardModule;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -100,9 +101,6 @@
import java.util.ArrayList;
import java.util.concurrent.Executor;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
import dagger.Lazy;
/**
@@ -146,7 +144,6 @@
* directly to the keyguard UI is posted to a {@link android.os.Handler} to ensure it is taken on the UI
* thread of the keyguard.
*/
-@Singleton
public class KeyguardViewMediator extends SystemUI {
private static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000;
private static final long KEYGUARD_DONE_PENDING_TIMEOUT_MS = 3000;
@@ -688,7 +685,9 @@
}
};
- @Inject
+ /**
+ * Injected constructor. See {@link KeyguardModule}.
+ */
public KeyguardViewMediator(
Context context,
FalsingManager falsingManager,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
new file mode 100644
index 0000000..2c023ca
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2020 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.keyguard.dagger;
+
+import android.content.Context;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.UiBackground;
+import com.android.systemui.keyguard.DismissCallbackRegistry;
+import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+
+import java.util.concurrent.Executor;
+
+import javax.inject.Singleton;
+
+import dagger.Lazy;
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Dagger Module providing {@link StatusBar}.
+ */
+@Module
+public class KeyguardModule {
+ /**
+ * Provides our instance of KeyguardViewMediator which is considered optional.
+ */
+ @Provides
+ @Singleton
+ public static KeyguardViewMediator newKeyguardViewMediator(
+ Context context,
+ FalsingManager falsingManager,
+ LockPatternUtils lockPatternUtils,
+ BroadcastDispatcher broadcastDispatcher,
+ NotificationShadeWindowController notificationShadeWindowController,
+ Lazy<StatusBarKeyguardViewManager> statusBarKeyguardViewManagerLazy,
+ DismissCallbackRegistry dismissCallbackRegistry,
+ @UiBackground Executor uiBgExecutor) {
+ return new KeyguardViewMediator(
+ context,
+ falsingManager,
+ lockPatternUtils,
+ broadcastDispatcher,
+ notificationShadeWindowController,
+ statusBarKeyguardViewManagerLazy,
+ dismissCallbackRegistry,
+ uiBgExecutor);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/model/SysUiState.java b/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
index 029ab43..a827f59 100644
--- a/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
+++ b/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
@@ -19,6 +19,7 @@
import static android.view.Display.DEFAULT_DISPLAY;
import android.annotation.NonNull;
+import android.util.Log;
import com.android.systemui.Dumpable;
import com.android.systemui.shared.system.QuickStepContract;
@@ -37,6 +38,9 @@
@Singleton
public class SysUiState implements Dumpable {
+ private static final String TAG = SysUiState.class.getSimpleName();
+ public static final boolean DEBUG = true;
+
private @QuickStepContract.SystemUiStateFlags int mFlags;
private final List<SysUiStateCallback> mCallbacks = new ArrayList<>();
private int mFlagsToSet = 0;
@@ -76,6 +80,7 @@
private void updateFlags(int displayId) {
if (displayId != DEFAULT_DISPLAY) {
// Ignore non-default displays for now
+ Log.w(TAG, "Ignoring flag update for display: " + displayId, new Throwable());
return;
}
@@ -87,6 +92,9 @@
/** Notify all those who are registered that the state has changed. */
private void notifyAndSetSystemUiStateChanged(int newFlags, int oldFlags) {
+ if (DEBUG) {
+ Log.d(TAG, "SysUiState changed: old=" + oldFlags + " new=" + newFlags);
+ }
if (newFlags != oldFlags) {
mCallbacks.forEach(callback -> callback.onSystemUiStateChanged(newFlags));
mFlags = newFlags;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index d4af154..8cd70cf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -61,9 +61,9 @@
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.qs.QSDetail.Callback;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.phone.PhoneStatusBarView;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
+import com.android.systemui.statusbar.phone.StatusBarWindowView;
import com.android.systemui.statusbar.phone.StatusIconContainer;
import com.android.systemui.statusbar.policy.Clock;
import com.android.systemui.statusbar.policy.DateView;
@@ -146,6 +146,7 @@
}
};
private boolean mHasTopCutout = false;
+ private int mRoundedCornerPadding = 0;
@Inject
public QuickStatusBarHeader(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
@@ -325,6 +326,9 @@
Resources resources = mContext.getResources();
updateMinimumHeight();
+ mRoundedCornerPadding = resources.getDimensionPixelSize(
+ R.dimen.rounded_corner_content_padding);
+
// Update height for a few views, especially due to landscape mode restricting space.
mHeaderTextContainerView.getLayoutParams().height =
resources.getDimensionPixelSize(R.dimen.qs_header_tooltip_height);
@@ -432,16 +436,23 @@
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
DisplayCutout cutout = insets.getDisplayCutout();
- Pair<Integer, Integer> padding = PhoneStatusBarView.cornerCutoutMargins(
- cutout, getDisplay());
- if (padding == null) {
- mSystemIconsView.setPaddingRelative(
- getResources().getDimensionPixelSize(R.dimen.status_bar_padding_start), 0,
- getResources().getDimensionPixelSize(R.dimen.status_bar_padding_end), 0);
- } else {
- mSystemIconsView.setPadding(padding.first, 0, padding.second, 0);
- }
+ // Handle padding of QuickStatusBarHeader
+ Pair<Integer, Integer> cornerCutoutPadding = StatusBarWindowView.cornerCutoutMargins(
+ cutout, getDisplay());
+ Pair<Integer, Integer> padding =
+ StatusBarWindowView.paddingNeededForCutoutAndRoundedCorner(
+ cutout, cornerCutoutPadding, mRoundedCornerPadding);
+ setPadding(padding.first, 0, padding.second, getPaddingBottom());
+
+ // Handle padding of SystemIconsView
+ final int waterfallTopInset = cutout == null ? 0 : cutout.getWaterfallInsets().top;
+ mSystemIconsView.setPaddingRelative(
+ getResources().getDimensionPixelSize(R.dimen.status_bar_padding_start),
+ waterfallTopInset,
+ getResources().getDimensionPixelSize(R.dimen.status_bar_padding_end),
+ 0);
+
return super.onApplyWindowInsets(insets);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
index 88a30a1..84891ec 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -31,20 +31,26 @@
/**
* Quick settings tile for screen recording
*/
-public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState> {
+public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState>
+ implements RecordingController.RecordingStateChangeCallback {
private static final String TAG = "ScreenRecordTile";
private RecordingController mController;
private long mMillisUntilFinished = 0;
+ private Callback mCallback = new Callback();
@Inject
public ScreenRecordTile(QSHost host, RecordingController controller) {
super(host);
mController = controller;
+ mController.observe(this, mCallback);
}
@Override
public BooleanState newTileState() {
- return new BooleanState();
+ BooleanState state = new BooleanState();
+ state.label = mContext.getString(R.string.quick_settings_screen_record_label);
+ state.handlesLongClick = false;
+ return state;
}
@Override
@@ -59,24 +65,13 @@
refreshState();
}
- /**
- * Refresh tile state
- * @param millisUntilFinished Time until countdown completes, or 0 if not counting down
- */
- public void refreshState(long millisUntilFinished) {
- mMillisUntilFinished = millisUntilFinished;
- refreshState();
- }
-
@Override
protected void handleUpdateState(BooleanState state, Object arg) {
boolean isStarting = mController.isStarting();
boolean isRecording = mController.isRecording();
- state.label = mContext.getString(R.string.quick_settings_screen_record_label);
state.value = isRecording || isStarting;
state.state = (isRecording || isStarting) ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
- state.handlesLongClick = false;
if (isRecording) {
state.icon = ResourceIcon.get(R.drawable.ic_qs_screenrecord);
@@ -125,4 +120,22 @@
Log.d(TAG, "Stopping recording from tile");
mController.stopRecording();
}
+
+ private final class Callback implements RecordingController.RecordingStateChangeCallback {
+ @Override
+ public void onCountdown(long millisUntilFinished) {
+ mMillisUntilFinished = millisUntilFinished;
+ refreshState();
+ }
+
+ @Override
+ public void onRecordingStart() {
+ refreshState();
+ }
+
+ @Override
+ public void onRecordingEnd() {
+ refreshState();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 9f64b39..34cad51 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -380,6 +380,14 @@
taskId, mHandler, null);
}
+ @Override
+ public void setSplitScreenMinimized(boolean minimized) {
+ Divider divider = mDividerOptional.get();
+ if (divider != null) {
+ divider.setMinimized(minimized);
+ }
+ }
+
private boolean verifyCaller(String reason) {
final int callerId = Binder.getCallingUserHandle().getIdentifier();
if (callerId != mCurrentBoundedUserId) {
@@ -410,6 +418,9 @@
private final ServiceConnection mOverviewServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
+ if (SysUiState.DEBUG) {
+ Log.d(TAG_OPS, "Overview proxy service connected");
+ }
mConnectionBackoffAttempts = 0;
mHandler.removeCallbacks(mDeferredConnectionCallback);
try {
@@ -562,6 +573,10 @@
mNavBarController.getDefaultNavigationBarFragment();
final NavigationBarView navBarView =
mNavBarController.getNavigationBarView(mContext.getDisplayId());
+ if (SysUiState.DEBUG) {
+ Log.d(TAG_OPS, "Updating sysui state flags: navBarFragment=" + navBarFragment
+ + " navBarView=" + navBarView);
+ }
if (navBarFragment != null) {
navBarFragment.updateSystemUiStateFlags(-1);
@@ -576,6 +591,10 @@
}
private void notifySystemUiStateFlags(int flags) {
+ if (SysUiState.DEBUG) {
+ Log.d(TAG_OPS, "Notifying sysui state change to overview service: proxy="
+ + mOverviewProxy + " flags=" + flags);
+ }
try {
if (mOverviewProxy != null) {
mOverviewProxy.onSystemUiStateChanged(flags);
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
index 188501e..6ad9c40 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
@@ -24,6 +24,9 @@
import android.util.Log;
import com.android.systemui.qs.tiles.ScreenRecordTile;
+import com.android.systemui.statusbar.policy.CallbackController;
+
+import java.util.ArrayList;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -32,7 +35,8 @@
* Helper class to initiate a screen recording
*/
@Singleton
-public class RecordingController {
+public class RecordingController
+ implements CallbackController<RecordingController.RecordingStateChangeCallback> {
private static final String TAG = "RecordingController";
private static final String SYSUI_PACKAGE = "com.android.systemui";
private static final String SYSUI_SCREENRECORD_LAUNCHER =
@@ -41,10 +45,11 @@
private final Context mContext;
private boolean mIsStarting;
private boolean mIsRecording;
- private ScreenRecordTile mTileToUpdate;
private PendingIntent mStopIntent;
private CountDownTimer mCountDownTimer = null;
+ private ArrayList<RecordingStateChangeCallback> mListeners = new ArrayList<>();
+
/**
* Create a new RecordingController
* @param context Context for the controller
@@ -63,10 +68,7 @@
final Intent intent = new Intent();
intent.setComponent(launcherComponent);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.putExtra("com.android.systemui.screenrecord.EXTRA_SETTINGS_ONLY", true);
mContext.startActivity(intent);
-
- mTileToUpdate = tileToUpdate;
}
/**
@@ -82,16 +84,21 @@
mCountDownTimer = new CountDownTimer(ms, 1000) {
@Override
public void onTick(long millisUntilFinished) {
- refreshTile(millisUntilFinished);
+ for (RecordingStateChangeCallback cb : mListeners) {
+ cb.onCountdown(millisUntilFinished);
+ }
}
@Override
public void onFinish() {
mIsStarting = false;
mIsRecording = true;
- refreshTile();
+ for (RecordingStateChangeCallback cb : mListeners) {
+ cb.onRecordingEnd();
+ }
try {
startIntent.send();
+ Log.d(TAG, "sent start intent");
} catch (PendingIntent.CanceledException e) {
Log.e(TAG, "Pending intent was cancelled: " + e.getMessage());
}
@@ -101,18 +108,6 @@
mCountDownTimer.start();
}
- private void refreshTile() {
- refreshTile(0);
- }
-
- private void refreshTile(long millisUntilFinished) {
- if (mTileToUpdate != null) {
- mTileToUpdate.refreshState(millisUntilFinished);
- } else {
- Log.e(TAG, "No tile to refresh");
- }
- }
-
/**
* Cancel a countdown in progress. This will not stop the recording if it already started.
*/
@@ -123,7 +118,10 @@
Log.e(TAG, "Timer was null");
}
mIsStarting = false;
- refreshTile();
+
+ for (RecordingStateChangeCallback cb : mListeners) {
+ cb.onRecordingEnd();
+ }
}
/**
@@ -152,7 +150,10 @@
} catch (PendingIntent.CanceledException e) {
Log.e(TAG, "Error stopping: " + e.getMessage());
}
- refreshTile();
+
+ for (RecordingStateChangeCallback cb : mListeners) {
+ cb.onRecordingEnd();
+ }
}
/**
@@ -161,6 +162,44 @@
*/
public void updateState(boolean isRecording) {
mIsRecording = isRecording;
- refreshTile();
+ for (RecordingStateChangeCallback cb : mListeners) {
+ if (isRecording) {
+ cb.onRecordingStart();
+ } else {
+ cb.onRecordingEnd();
+ }
+ }
+ }
+
+ @Override
+ public void addCallback(RecordingStateChangeCallback listener) {
+ mListeners.add(listener);
+ }
+
+ @Override
+ public void removeCallback(RecordingStateChangeCallback listener) {
+ mListeners.remove(listener);
+ }
+
+ /**
+ * A callback for changes in the screen recording state
+ */
+ public interface RecordingStateChangeCallback {
+ /**
+ * Called when a countdown to recording has updated
+ *
+ * @param millisUntilFinished Time in ms remaining in the countdown
+ */
+ default void onCountdown(long millisUntilFinished) {}
+
+ /**
+ * Called when a screen recording has started
+ */
+ default void onRecordingStart() {}
+
+ /**
+ * Called when a screen recording has ended
+ */
+ default void onRecordingEnd() {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
index 5ae0954..4f20492 100644
--- a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
@@ -22,10 +22,8 @@
import android.content.Context;
import android.content.res.Configuration;
import android.os.RemoteException;
-import android.util.Log;
import android.view.IWindowManager;
import android.view.KeyEvent;
-import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import com.android.internal.policy.DividerSnapAlgorithm;
@@ -94,29 +92,24 @@
}
private void handleDockKey(long shortcutCode) {
- try {
- int dockSide = mWindowManagerService.getDockedStackSide();
- if (dockSide == WindowManager.DOCKED_INVALID) {
- // Split the screen
- mRecents.splitPrimaryTask((shortcutCode == SC_DOCK_LEFT)
- ? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT
- : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT, null, -1);
- } else {
- // If there is already a docked window, we respond by resizing the docking pane.
- DividerView dividerView = mDivider.getView();
- DividerSnapAlgorithm snapAlgorithm = dividerView.getSnapAlgorithm();
- int dividerPosition = dividerView.getCurrentPosition();
- DividerSnapAlgorithm.SnapTarget currentTarget =
- snapAlgorithm.calculateNonDismissingSnapTarget(dividerPosition);
- DividerSnapAlgorithm.SnapTarget target = (shortcutCode == SC_DOCK_LEFT)
- ? snapAlgorithm.getPreviousTarget(currentTarget)
- : snapAlgorithm.getNextTarget(currentTarget);
- dividerView.startDragging(true /* animate */, false /* touching */);
- dividerView.stopDragging(target.position, 0f, false /* avoidDismissStart */,
- true /* logMetrics */);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "handleDockKey() failed.");
+ if (mDivider == null || !mDivider.inSplitMode()) {
+ // Split the screen
+ mRecents.splitPrimaryTask((shortcutCode == SC_DOCK_LEFT)
+ ? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT
+ : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT, null, -1);
+ } else {
+ // If there is already a docked window, we respond by resizing the docking pane.
+ DividerView dividerView = mDivider.getView();
+ DividerSnapAlgorithm snapAlgorithm = dividerView.getSnapAlgorithm();
+ int dividerPosition = dividerView.getCurrentPosition();
+ DividerSnapAlgorithm.SnapTarget currentTarget =
+ snapAlgorithm.calculateNonDismissingSnapTarget(dividerPosition);
+ DividerSnapAlgorithm.SnapTarget target = (shortcutCode == SC_DOCK_LEFT)
+ ? snapAlgorithm.getPreviousTarget(currentTarget)
+ : snapAlgorithm.getNextTarget(currentTarget);
+ dividerView.startDragging(true /* animate */, false /* touching */);
+ dividerView.stopDragging(target.position, 0f, false /* avoidDismissStart */,
+ true /* logMetrics */);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index 90cc0e57..ba9eb4a 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -17,69 +17,281 @@
package com.android.systemui.stackdivider;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.Display.DEFAULT_DISPLAY;
+import android.app.ActivityTaskManager;
import android.content.Context;
import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.Handler;
import android.os.RemoteException;
+import android.provider.Settings;
import android.util.Log;
-import android.view.IDockedStackListener;
+import android.util.Slog;
+import android.view.IWindowContainer;
import android.view.LayoutInflater;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
import android.view.View;
-import android.view.WindowManagerGlobal;
+import android.view.WindowContainerTransaction;
+import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
import com.android.systemui.recents.Recents;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.wm.DisplayChangeController;
+import com.android.systemui.wm.DisplayController;
+import com.android.systemui.wm.DisplayImeController;
+import com.android.systemui.wm.DisplayLayout;
+import com.android.systemui.wm.SystemWindows;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
import java.util.Optional;
+import java.util.function.Consumer;
+
+import javax.inject.Singleton;
import dagger.Lazy;
/**
* Controls the docked stack divider.
*/
-public class Divider extends SystemUI implements DividerView.DividerCallbacks {
+@Singleton
+public class Divider extends SystemUI implements DividerView.DividerCallbacks,
+ DisplayController.OnDisplaysChangedListener {
private static final String TAG = "Divider";
+
+ static final boolean DEBUG = true;
+
+ static final int DEFAULT_APP_TRANSITION_DURATION = 336;
+
private final Optional<Lazy<Recents>> mRecentsOptionalLazy;
private DividerWindowManager mWindowManager;
private DividerView mView;
private final DividerState mDividerState = new DividerState();
- private DockDividerVisibilityListener mDockDividerVisibilityListener;
private boolean mVisible = false;
private boolean mMinimized = false;
private boolean mAdjustedForIme = false;
private boolean mHomeStackResizable = false;
private ForcedResizableInfoActivityController mForcedResizableController;
+ private SystemWindows mSystemWindows;
+ final SurfaceSession mSurfaceSession = new SurfaceSession();
+ private DisplayController mDisplayController;
+ private DisplayImeController mImeController;
- public Divider(Context context, Optional<Lazy<Recents>> recentsOptionalLazy) {
+ // Keeps track of real-time split geometry including snap positions and ime adjustments
+ private SplitDisplayLayout mSplitLayout;
+
+ // Transient: this contains the layout calculated for a new rotation requested by WM. This is
+ // kept around so that we can wait for a matching configuration change and then use the exact
+ // layout that we sent back to WM.
+ private SplitDisplayLayout mRotateSplitLayout;
+
+ private Handler mHandler;
+ private KeyguardStateController mKeyguardStateController;
+
+ private final ArrayList<WeakReference<Consumer<Boolean>>> mDockedStackExistsListeners =
+ new ArrayList<>();
+
+ private SplitScreenTaskOrganizer mSplits = new SplitScreenTaskOrganizer(this);
+
+ private DisplayChangeController.OnDisplayChangingListener mRotationController =
+ (display, fromRotation, toRotation, t) -> {
+ DisplayLayout displayLayout =
+ new DisplayLayout(mDisplayController.getDisplayLayout(display));
+ SplitDisplayLayout sdl = new SplitDisplayLayout(mContext, displayLayout, mSplits);
+ sdl.rotateTo(toRotation);
+ mRotateSplitLayout = sdl;
+ int position = mMinimized ? mView.mSnapTargetBeforeMinimized.position
+ : mView.getCurrentPosition();
+ DividerSnapAlgorithm snap = sdl.getSnapAlgorithm();
+ final DividerSnapAlgorithm.SnapTarget target =
+ snap.calculateNonDismissingSnapTarget(position);
+ sdl.resizeSplits(target.position, t);
+
+ if (inSplitMode()) {
+ WindowManagerProxy.applyHomeTasksMinimized(sdl, mSplits.mSecondary.token, t);
+ }
+ };
+
+ private IWindowContainer mLastImeTarget = null;
+ private boolean mShouldAdjustForIme = false;
+
+ private DisplayImeController.ImePositionProcessor mImePositionProcessor =
+ new DisplayImeController.ImePositionProcessor() {
+ private int mStartTop = 0;
+ private int mFinalTop = 0;
+ @Override
+ public void onImeStartPositioning(int displayId, int imeTop, int finalImeTop,
+ boolean showing, SurfaceControl.Transaction t) {
+ mStartTop = imeTop;
+ mFinalTop = finalImeTop;
+ if (showing) {
+ try {
+ mLastImeTarget = ActivityTaskManager.getTaskOrganizerController()
+ .getImeTarget(displayId);
+ mShouldAdjustForIme = !mSplitLayout.mDisplayLayout.isLandscape()
+ && (mLastImeTarget.asBinder()
+ == mSplits.mSecondary.token.asBinder());
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to get IME target", e);
+ }
+ }
+ if (!mShouldAdjustForIme) {
+ setAdjustedForIme(false);
+ return;
+ }
+ mView.setAdjustedForIme(showing, showing
+ ? DisplayImeController.ANIMATION_DURATION_SHOW_MS
+ : DisplayImeController.ANIMATION_DURATION_HIDE_MS);
+ // Reposition the server's secondary split position so that it evaluates
+ // insets properly.
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ if (showing) {
+ mSplitLayout.updateAdjustedBounds(finalImeTop, imeTop, finalImeTop);
+ wct.setBounds(mSplits.mSecondary.token, mSplitLayout.mAdjustedSecondary);
+ } else {
+ wct.setBounds(mSplits.mSecondary.token, mSplitLayout.mSecondary);
+ }
+ try {
+ ActivityTaskManager.getTaskOrganizerController()
+ .applyContainerTransaction(wct, null /* organizer */);
+ } catch (RemoteException e) {
+ }
+ setAdjustedForIme(showing);
+ }
+
+ @Override
+ public void onImePositionChanged(int displayId, int imeTop,
+ SurfaceControl.Transaction t) {
+ if (!mShouldAdjustForIme) {
+ return;
+ }
+ mSplitLayout.updateAdjustedBounds(imeTop, mStartTop, mFinalTop);
+ mView.resizeSplitSurfaces(t, mSplitLayout.mAdjustedPrimary,
+ mSplitLayout.mAdjustedSecondary);
+ final boolean showing = mFinalTop < mStartTop;
+ final float progress = ((float) (imeTop - mStartTop)) / (mFinalTop - mStartTop);
+ final float fraction = showing ? progress : 1.f - progress;
+ mView.setResizeDimLayer(t, true /* primary */, fraction * 0.3f);
+ }
+
+ @Override
+ public void onImeEndPositioning(int displayId, int imeTop,
+ boolean showing, SurfaceControl.Transaction t) {
+ if (!mShouldAdjustForIme) {
+ return;
+ }
+ mSplitLayout.updateAdjustedBounds(imeTop, mStartTop, mFinalTop);
+ mView.resizeSplitSurfaces(t, mSplitLayout.mAdjustedPrimary,
+ mSplitLayout.mAdjustedSecondary);
+ mView.setResizeDimLayer(t, true /* primary */, showing ? 0.3f : 0.f);
+ }
+ };
+
+ public Divider(Context context, Optional<Lazy<Recents>> recentsOptionalLazy,
+ DisplayController displayController, SystemWindows systemWindows,
+ DisplayImeController imeController, Handler handler,
+ KeyguardStateController keyguardStateController) {
super(context);
+ mDisplayController = displayController;
+ mSystemWindows = systemWindows;
+ mImeController = imeController;
+ mHandler = handler;
+ mKeyguardStateController = keyguardStateController;
mRecentsOptionalLazy = recentsOptionalLazy;
+ mForcedResizableController = new ForcedResizableInfoActivityController(context, this);
}
@Override
public void start() {
- mWindowManager = new DividerWindowManager(mContext);
- update(mContext.getResources().getConfiguration());
- mDockDividerVisibilityListener = new DockDividerVisibilityListener();
- try {
- WindowManagerGlobal.getWindowManagerService().registerDockedStackListener(
- mDockDividerVisibilityListener);
- } catch (Exception e) {
- Log.e(TAG, "Failed to register docked stack listener", e);
- }
- mForcedResizableController = new ForcedResizableInfoActivityController(mContext);
+ mWindowManager = new DividerWindowManager(mSystemWindows);
+ mDisplayController.addDisplayWindowListener(this);
+ // Hide the divider when keyguard is showing. Even though keyguard/statusbar is above
+ // everything, it is actually transparent except for notifications, so we still need to
+ // hide any surfaces that are below it.
+ // TODO(b/148906453): Figure out keyguard dismiss animation for divider view.
+ mKeyguardStateController.addCallback(new KeyguardStateController.Callback() {
+ @Override
+ public void onUnlockedChanged() {
+
+ }
+
+ @Override
+ public void onKeyguardShowingChanged() {
+ if (!inSplitMode() || mView == null || mView.getViewRootImpl() == null
+ || mView.getViewRootImpl().getSurfaceControl() == null) {
+ return;
+ }
+ mView.setHidden(mKeyguardStateController.isShowing());
+ }
+
+ @Override
+ public void onKeyguardFadingAwayChanged() {
+
+ }
+ });
+ // Don't initialize the divider or anything until we get the default display.
}
@Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
+ public void onDisplayAdded(int displayId) {
+ if (displayId != DEFAULT_DISPLAY) {
+ return;
+ }
+ mSplitLayout = new SplitDisplayLayout(mDisplayController.getDisplayContext(displayId),
+ mDisplayController.getDisplayLayout(displayId), mSplits);
+ mImeController.addPositionProcessor(mImePositionProcessor);
+ mDisplayController.addDisplayChangingController(mRotationController);
+ try {
+ mSplits.init(ActivityTaskManager.getTaskOrganizerController(), mSurfaceSession);
+ // Set starting tile bounds based on middle target
+ final WindowContainerTransaction tct = new WindowContainerTransaction();
+ int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position;
+ mSplitLayout.resizeSplits(midPos, tct);
+ ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(tct,
+ null /* organizer */);
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to register docked stack listener", e);
+ }
+ update(mDisplayController.getDisplayContext(displayId).getResources().getConfiguration());
+ }
+
+ @Override
+ public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
+ if (displayId != DEFAULT_DISPLAY) {
+ return;
+ }
+ mSplitLayout = new SplitDisplayLayout(mDisplayController.getDisplayContext(displayId),
+ mDisplayController.getDisplayLayout(displayId), mSplits);
+ if (mRotateSplitLayout == null) {
+ int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position;
+ final WindowContainerTransaction tct = new WindowContainerTransaction();
+ mSplitLayout.resizeSplits(midPos, tct);
+ try {
+ ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(tct,
+ null /* organizer */);
+ } catch (RemoteException e) {
+ }
+ } else if (mRotateSplitLayout != null
+ && mSplitLayout.mDisplayLayout.rotation()
+ == mRotateSplitLayout.mDisplayLayout.rotation()) {
+ mSplitLayout.mPrimary = new Rect(mRotateSplitLayout.mPrimary);
+ mSplitLayout.mSecondary = new Rect(mRotateSplitLayout.mSecondary);
+ mRotateSplitLayout = null;
+ }
update(newConfig);
}
+ Handler getHandler() {
+ return mHandler;
+ }
+
public DividerView getView() {
return mView;
}
@@ -92,18 +304,25 @@
return mHomeStackResizable;
}
+ /** {@code true} if this is visible */
+ public boolean inSplitMode() {
+ return mView != null && mView.getVisibility() == View.VISIBLE;
+ }
+
private void addDivider(Configuration configuration) {
+ Context dctx = mDisplayController.getDisplayContext(mContext.getDisplayId());
mView = (DividerView)
- LayoutInflater.from(mContext).inflate(R.layout.docked_stack_divider, null);
- mView.injectDependencies(mWindowManager, mDividerState, this);
+ LayoutInflater.from(dctx).inflate(R.layout.docked_stack_divider, null);
+ DisplayLayout displayLayout = mDisplayController.getDisplayLayout(mContext.getDisplayId());
+ mView.injectDependencies(mWindowManager, mDividerState, this, mSplits, mSplitLayout);
mView.setVisibility(mVisible ? View.VISIBLE : View.INVISIBLE);
mView.setMinimizedDockStack(mMinimized, mHomeStackResizable);
- final int size = mContext.getResources().getDimensionPixelSize(
+ final int size = dctx.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.docked_stack_divider_thickness);
final boolean landscape = configuration.orientation == ORIENTATION_LANDSCAPE;
- final int width = landscape ? size : MATCH_PARENT;
- final int height = landscape ? MATCH_PARENT : size;
- mWindowManager.add(mView, width, height);
+ final int width = landscape ? size : displayLayout.width();
+ final int height = landscape ? displayLayout.height() : size;
+ mWindowManager.add(mView, width, height, mContext.getDisplayId());
}
private void removeDivider() {
@@ -116,65 +335,86 @@
private void update(Configuration configuration) {
removeDivider();
addDivider(configuration);
- if (mMinimized) {
+ if (mMinimized && mView != null) {
mView.setMinimizedDockStack(true, mHomeStackResizable);
updateTouchable();
}
}
- private void updateVisibility(final boolean visible) {
- mView.post(new Runnable() {
- @Override
- public void run() {
- if (mVisible != visible) {
- mVisible = visible;
- mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
+ void updateVisibility(final boolean visible) {
+ if (mVisible != visible) {
+ mVisible = visible;
+ mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
- // Update state because animations won't finish.
- mView.setMinimizedDockStack(mMinimized, mHomeStackResizable);
- }
+ if (visible) {
+ mView.enterSplitMode(mHomeStackResizable);
+ // Update state because animations won't finish.
+ mView.setMinimizedDockStack(mMinimized, mHomeStackResizable);
+ } else {
+ mView.exitSplitMode();
+ // un-minimize so that next entry triggers minimize anim.
+ mView.setMinimizedDockStack(false /* minimized */, mHomeStackResizable);
}
- });
+ // Notify existence listeners
+ synchronized (mDockedStackExistsListeners) {
+ mDockedStackExistsListeners.removeIf(wf -> {
+ Consumer<Boolean> l = wf.get();
+ if (l != null) l.accept(visible);
+ return l == null;
+ });
+ }
+ }
+ }
+
+ private void setHomeStackResizable(boolean resizable) {
+ if (mHomeStackResizable == resizable) {
+ return;
+ }
+ mHomeStackResizable = resizable;
+ if (!inSplitMode()) {
+ return;
+ }
+ WindowManagerProxy.applyHomeTasksMinimized(mSplitLayout, mSplits.mSecondary.token);
}
private void updateMinimizedDockedStack(final boolean minimized, final long animDuration,
final boolean isHomeStackResizable) {
- mView.post(new Runnable() {
- @Override
- public void run() {
- mHomeStackResizable = isHomeStackResizable;
- if (mMinimized != minimized) {
- mMinimized = minimized;
- updateTouchable();
- if (animDuration > 0) {
- mView.setMinimizedDockStack(minimized, animDuration, isHomeStackResizable);
- } else {
- mView.setMinimizedDockStack(minimized, isHomeStackResizable);
- }
- }
+ setHomeStackResizable(isHomeStackResizable);
+ if (animDuration > 0) {
+ mView.setMinimizedDockStack(minimized, animDuration, isHomeStackResizable);
+ } else {
+ mView.setMinimizedDockStack(minimized, isHomeStackResizable);
+ }
+ updateTouchable();
+ }
+
+ /** Switch to minimized state if appropriate */
+ public void setMinimized(final boolean minimized) {
+ mHandler.post(() -> {
+ if (!inSplitMode()) {
+ return;
}
+ if (mMinimized == minimized) {
+ return;
+ }
+ mMinimized = minimized;
+ mView.setMinimizedDockStack(minimized, getAnimDuration(), mHomeStackResizable);
+ updateTouchable();
});
}
- private void notifyDockedStackExistsChanged(final boolean exists) {
- mView.post(new Runnable() {
- @Override
- public void run() {
- mForcedResizableController.notifyDockedStackExistsChanged(exists);
- }
- });
+ void setAdjustedForIme(boolean adjustedForIme) {
+ if (mAdjustedForIme == adjustedForIme) {
+ return;
+ }
+ mAdjustedForIme = adjustedForIme;
+ updateTouchable();
}
private void updateTouchable() {
mWindowManager.setTouchable((mHomeStackResizable || !mMinimized) && !mAdjustedForIme);
}
- public void onRecentsActivityStarting() {
- if (mView != null) {
- mView.onRecentsActivityStarting();
- }
- }
-
/**
* Workaround for b/62528361, at the time recents has drawn, it may happen before a
* configuration change to the Divider, and internally, the event will be posted to the
@@ -206,6 +446,9 @@
}
public void onAppTransitionFinished() {
+ if (mView == null) {
+ return;
+ }
mForcedResizableController.onAppTransitionFinished();
}
@@ -231,46 +474,66 @@
pw.print(" mAdjustedForIme="); pw.println(mAdjustedForIme);
}
- class DockDividerVisibilityListener extends IDockedStackListener.Stub {
+ long getAnimDuration() {
+ float transitionScale = Settings.Global.getFloat(mContext.getContentResolver(),
+ Settings.Global.TRANSITION_ANIMATION_SCALE,
+ mContext.getResources().getFloat(
+ com.android.internal.R.dimen
+ .config_appTransitionAnimationDurationScaleDefault));
+ final long transitionDuration = DEFAULT_APP_TRANSITION_DURATION;
+ return (long) (transitionDuration * transitionScale);
+ }
- @Override
- public void onDividerVisibilityChanged(boolean visible) throws RemoteException {
- updateVisibility(visible);
+ /** Register a listener that gets called whenever the existence of the divider changes */
+ public void registerInSplitScreenListener(Consumer<Boolean> listener) {
+ listener.accept(inSplitMode());
+ synchronized (mDockedStackExistsListeners) {
+ mDockedStackExistsListeners.add(new WeakReference<>(listener));
}
+ }
- @Override
- public void onDockedStackExistsChanged(boolean exists) throws RemoteException {
- notifyDockedStackExistsChanged(exists);
+ void startEnterSplit() {
+ // Set resizable directly here because applyEnterSplit already resizes home stack.
+ mHomeStackResizable = WindowManagerProxy.applyEnterSplit(mSplits, mSplitLayout);
+ }
+
+ void ensureMinimizedSplit() {
+ final boolean wasMinimized = mMinimized;
+ mMinimized = true;
+ setHomeStackResizable(mSplits.mSecondary.isResizable());
+ if (!inSplitMode()) {
+ // Wasn't in split-mode yet, so enter now.
+ if (DEBUG) {
+ Log.d(TAG, " entering split mode with minimized=true");
+ }
+ updateVisibility(true /* visible */);
+ } else if (!wasMinimized) {
+ if (DEBUG) {
+ Log.d(TAG, " in split mode, but minimizing ");
+ }
+ // Was already in split-mode, update just minimized state.
+ updateMinimizedDockedStack(mMinimized, getAnimDuration(),
+ mHomeStackResizable);
}
+ }
- @Override
- public void onDockedStackMinimizedChanged(boolean minimized, long animDuration,
- boolean isHomeStackResizable) throws RemoteException {
- mHomeStackResizable = isHomeStackResizable;
- updateMinimizedDockedStack(minimized, animDuration, isHomeStackResizable);
+ void ensureNormalSplit() {
+ if (!inSplitMode()) {
+ // Wasn't in split-mode, so enter now.
+ if (DEBUG) {
+ Log.d(TAG, " enter split mode unminimized ");
+ }
+ mMinimized = false;
+ updateVisibility(true /* visible */);
}
-
- @Override
- public void onAdjustedForImeChanged(boolean adjustedForIme, long animDuration)
- throws RemoteException {
- mView.post(() -> {
- if (mAdjustedForIme != adjustedForIme) {
- mAdjustedForIme = adjustedForIme;
- updateTouchable();
- if (!mMinimized) {
- if (animDuration > 0) {
- mView.setAdjustedForIme(adjustedForIme, animDuration);
- } else {
- mView.setAdjustedForIme(adjustedForIme);
- }
- }
- }
- });
- }
-
- @Override
- public void onDockSideChanged(final int newDockSide) throws RemoteException {
- mView.post(() -> mView.notifyDockSideChanged(newDockSide));
+ if (mMinimized) {
+ // Was in minimized state, so leave that.
+ if (DEBUG) {
+ Log.d(TAG, " in split mode already, but unminimizing ");
+ }
+ mMinimized = false;
+ updateMinimizedDockedStack(mMinimized, getAnimDuration(),
+ mHomeStackResizable);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java
index 49f4d5e..f3b2553 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java
@@ -17,8 +17,14 @@
package com.android.systemui.stackdivider;
import android.content.Context;
+import android.os.Handler;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.recents.Recents;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.wm.DisplayController;
+import com.android.systemui.wm.DisplayImeController;
+import com.android.systemui.wm.SystemWindows;
import java.util.Optional;
@@ -35,7 +41,11 @@
public class DividerModule {
@Singleton
@Provides
- static Divider provideDivider(Context context, Optional<Lazy<Recents>> recentsOptionalLazy) {
- return new Divider(context, recentsOptionalLazy);
+ static Divider provideDivider(Context context, Optional<Lazy<Recents>> recentsOptionalLazy,
+ DisplayController displayController, SystemWindows systemWindows,
+ DisplayImeController imeController, @Main Handler handler,
+ KeyguardStateController keyguardStateController) {
+ return new Divider(context, recentsOptionalLazy, displayController, systemWindows,
+ imeController, handler, keyguardStateController);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 9fe6e84..fdd04b9 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -16,12 +16,8 @@
package com.android.systemui.stackdivider;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW;
import static android.view.PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW;
-import static android.view.WindowManager.DOCKED_LEFT;
import static android.view.WindowManager.DOCKED_RIGHT;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
@@ -40,10 +36,11 @@
import android.util.AttributeSet;
import android.view.Choreographer;
import android.view.Display;
-import android.view.DisplayInfo;
import android.view.InsetsState;
import android.view.MotionEvent;
import android.view.PointerIcon;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
import android.view.VelocityTracker;
import android.view.View;
import android.view.View.OnTouchListener;
@@ -75,6 +72,7 @@
*/
public class DividerView extends FrameLayout implements OnTouchListener,
OnComputeInternalInsetsListener {
+ private static final String TAG = "DividerView";
public interface DividerCallbacks {
void onDraggingStart();
@@ -123,14 +121,11 @@
private int mTouchSlop;
private boolean mBackgroundLifted;
private boolean mIsInMinimizeInteraction;
- private SnapTarget mSnapTargetBeforeMinimized;
+ SnapTarget mSnapTargetBeforeMinimized;
private int mDividerInsets;
private final Display mDefaultDisplay;
- private int mDisplayWidth;
- private int mDisplayHeight;
- private int mDisplayRotation;
- private int mDividerWindowWidth;
+
private int mDividerSize;
private int mTouchElevation;
private int mLongPressEntraceAnimDuration;
@@ -147,8 +142,7 @@
private DividerWindowManager mWindowManager;
private VelocityTracker mVelocityTracker;
private FlingAnimationUtils mFlingAnimationUtils;
- private DividerSnapAlgorithm mSnapAlgorithm;
- private DividerSnapAlgorithm mMinimizedSnapAlgorithm;
+ private SplitDisplayLayout mSplitLayout;
private DividerCallbacks mCallback;
private final Rect mStableInsets = new Rect();
@@ -163,6 +157,10 @@
private DividerState mState;
private final SurfaceFlingerVsyncChoreographer mSfChoreographer;
+ private SplitScreenTaskOrganizer mTiles;
+ boolean mFirstLayout = true;
+ int mDividerPositionX;
+ int mDividerPositionY;
// The view is removed or in the process of been removed from the system.
private boolean mRemoved;
@@ -172,7 +170,7 @@
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_RESIZE_STACK:
- resizeStack(msg.arg1, msg.arg2, (SnapTarget) msg.obj);
+ resizeStackSurfaces(msg.arg1, msg.arg2, (SnapTarget) msg.obj);
break;
default:
super.handleMessage(msg);
@@ -228,16 +226,17 @@
public boolean performAccessibilityAction(View host, int action, Bundle args) {
int currentPosition = getCurrentPosition();
SnapTarget nextTarget = null;
+ DividerSnapAlgorithm snapAlgorithm = mSplitLayout.getSnapAlgorithm();
if (action == R.id.action_move_tl_full) {
- nextTarget = mSnapAlgorithm.getDismissEndTarget();
+ nextTarget = snapAlgorithm.getDismissEndTarget();
} else if (action == R.id.action_move_tl_70) {
- nextTarget = mSnapAlgorithm.getLastSplitTarget();
+ nextTarget = snapAlgorithm.getLastSplitTarget();
} else if (action == R.id.action_move_tl_50) {
- nextTarget = mSnapAlgorithm.getMiddleTarget();
+ nextTarget = snapAlgorithm.getMiddleTarget();
} else if (action == R.id.action_move_tl_30) {
- nextTarget = mSnapAlgorithm.getFirstSplitTarget();
+ nextTarget = snapAlgorithm.getFirstSplitTarget();
} else if (action == R.id.action_move_rb_full) {
- nextTarget = mSnapAlgorithm.getDismissStartTarget();
+ nextTarget = snapAlgorithm.getDismissStartTarget();
}
if (nextTarget != null) {
startDragging(true /* animate */, false /* touching */);
@@ -284,11 +283,11 @@
mBackground = findViewById(R.id.docked_divider_background);
mMinimizedShadow = findViewById(R.id.minimized_dock_shadow);
mHandle.setOnTouchListener(this);
- mDividerWindowWidth = getResources().getDimensionPixelSize(
+ final int dividerWindowWidth = getResources().getDimensionPixelSize(
com.android.internal.R.dimen.docked_stack_divider_thickness);
mDividerInsets = getResources().getDimensionPixelSize(
com.android.internal.R.dimen.docked_stack_divider_insets);
- mDividerSize = mDividerWindowWidth - 2 * mDividerInsets;
+ mDividerSize = dividerWindowWidth - 2 * mDividerInsets;
mTouchElevation = getResources().getDimensionPixelSize(
R.dimen.docked_stack_divider_lift_elevation);
mLongPressEntraceAnimDuration = getResources().getInteger(
@@ -296,7 +295,6 @@
mGrowRecents = getResources().getBoolean(R.bool.recents_grow_in_multiwindow);
mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
mFlingAnimationUtils = new FlingAnimationUtils(getResources().getDisplayMetrics(), 0.3f);
- updateDisplayInfo();
boolean landscape = getResources().getConfiguration().orientation
== Configuration.ORIENTATION_LANDSCAPE;
mHandle.setPointerIcon(PointerIcon.getSystemIcon(getContext(),
@@ -314,6 +312,7 @@
&& !mIsInMinimizeInteraction) {
saveSnapTargetBeforeMinimized(mSnapTargetBeforeMinimized);
}
+ mFirstLayout = true;
}
void onDividerRemoved() {
@@ -341,17 +340,17 @@
|| mStableInsets.bottom != insets.getStableInsetBottom()) {
mStableInsets.set(insets.getStableInsetLeft(), insets.getStableInsetTop(),
insets.getStableInsetRight(), insets.getStableInsetBottom());
- if (mSnapAlgorithm != null || mMinimizedSnapAlgorithm != null) {
- mSnapAlgorithm = null;
- mMinimizedSnapAlgorithm = null;
- initializeSnapAlgorithm();
- }
}
return super.onApplyWindowInsets(insets);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ if (mFirstLayout) {
+ // Wait for first layout so that the ViewRootImpl surface has been created.
+ initializeSurfaceState();
+ mFirstLayout = false;
+ }
super.onLayout(changed, left, top, right, bottom);
int minimizeLeft = 0;
int minimizeTop = 0;
@@ -372,19 +371,16 @@
}
public void injectDependencies(DividerWindowManager windowManager, DividerState dividerState,
- DividerCallbacks callback) {
+ DividerCallbacks callback, SplitScreenTaskOrganizer tiles, SplitDisplayLayout sdl) {
mWindowManager = windowManager;
mState = dividerState;
mCallback = callback;
-
- // Set the previous position ratio before minimized state after attaching this divider
- if (mStableInsets.isEmpty()) {
- WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
- }
+ mTiles = tiles;
+ mSplitLayout = sdl;
if (mState.mRatioPositionBeforeMinimized == 0) {
// Set the middle target as the initial state
- mSnapTargetBeforeMinimized = mSnapAlgorithm.getMiddleTarget();
+ mSnapTargetBeforeMinimized = mSplitLayout.getSnapAlgorithm().getMiddleTarget();
} else {
repositionSnapTargetBeforeMinimized();
}
@@ -411,18 +407,34 @@
return mOtherTaskRect;
}
+ private boolean inSplitMode() {
+ return getVisibility() == VISIBLE;
+ }
+
+ /** Unlike setVisible, this directly hides the surface without changing view visibility. */
+ void setHidden(boolean hidden) {
+ post(() -> {
+ if (!isViewSurfaceValid()) {
+ return;
+ }
+ Transaction t = mTiles.getTransaction();
+ if (hidden) {
+ t.hide(getViewRootImpl().getSurfaceControl());
+ } else {
+ t.show(getViewRootImpl().getSurfaceControl());
+ }
+ t.apply();
+ mTiles.releaseTransaction(t);
+ });
+ }
+
public boolean startDragging(boolean animate, boolean touching) {
cancelFlingAnimation();
if (touching) {
mHandle.setTouching(true, animate);
}
- mDockSide = mWindowManagerProxy.getDockSide();
+ mDockSide = mSplitLayout.getPrimarySplitSide();
- // Update snap algorithm if rotation has occurred
- if (mDisplayRotation != mDefaultDisplay.getRotation()) {
- updateDisplayInfo();
- }
- initializeSnapAlgorithm();
mWindowManagerProxy.setResizing(true);
if (touching) {
mWindowManager.setSlippery(false);
@@ -431,7 +443,7 @@
if (mCallback != null) {
mCallback.onDraggingStart();
}
- return mDockSide != WindowManager.DOCKED_INVALID;
+ return inSplitMode();
}
public void stopDragging(int position, float velocity, boolean avoidDismissStart,
@@ -467,38 +479,22 @@
}
private void updateDockSide() {
- mDockSide = mWindowManagerProxy.getDockSide();
+ mDockSide = mSplitLayout.getPrimarySplitSide();
mMinimizedShadow.setDockSide(mDockSide);
}
- private void initializeSnapAlgorithm() {
- if (mSnapAlgorithm == null) {
- mSnapAlgorithm = new DividerSnapAlgorithm(getContext().getResources(), mDisplayWidth,
- mDisplayHeight, mDividerSize, isHorizontalDivision(), mStableInsets, mDockSide);
- if (mSnapTargetBeforeMinimized != null && mSnapTargetBeforeMinimized.isMiddleTarget) {
- mSnapTargetBeforeMinimized = mSnapAlgorithm.getMiddleTarget();
- }
- }
- if (mMinimizedSnapAlgorithm == null) {
- mMinimizedSnapAlgorithm = new DividerSnapAlgorithm(getContext().getResources(),
- mDisplayWidth, mDisplayHeight, mDividerSize, isHorizontalDivision(),
- mStableInsets, mDockSide, mDockedStackMinimized && mHomeStackResizable);
- }
- }
-
public DividerSnapAlgorithm getSnapAlgorithm() {
- initializeSnapAlgorithm();
- return mDockedStackMinimized && mHomeStackResizable ? mMinimizedSnapAlgorithm :
- mSnapAlgorithm;
+ return mDockedStackMinimized
+ && mHomeStackResizable ? mSplitLayout.getMinimizedSnapAlgorithm()
+ : mSplitLayout.getSnapAlgorithm();
}
public int getCurrentPosition() {
- getLocationOnScreen(mTempInt2);
- if (isHorizontalDivision()) {
- return mTempInt2[1] + mDividerInsets;
- } else {
- return mTempInt2[0] + mDividerInsets;
- }
+ return isHorizontalDivision() ? mDividerPositionY : mDividerPositionX;
+ }
+
+ public boolean isMinimized() {
+ return mDockedStackMinimized;
}
@Override
@@ -557,25 +553,25 @@
}
private void logResizeEvent(SnapTarget snapTarget) {
- if (snapTarget == mSnapAlgorithm.getDismissStartTarget()) {
+ if (snapTarget == mSplitLayout.getSnapAlgorithm().getDismissStartTarget()) {
MetricsLogger.action(
mContext, MetricsEvent.ACTION_WINDOW_UNDOCK_MAX, dockSideTopLeft(mDockSide)
? LOG_VALUE_UNDOCK_MAX_OTHER
: LOG_VALUE_UNDOCK_MAX_DOCKED);
- } else if (snapTarget == mSnapAlgorithm.getDismissEndTarget()) {
+ } else if (snapTarget == mSplitLayout.getSnapAlgorithm().getDismissEndTarget()) {
MetricsLogger.action(
mContext, MetricsEvent.ACTION_WINDOW_UNDOCK_MAX, dockSideBottomRight(mDockSide)
? LOG_VALUE_UNDOCK_MAX_OTHER
: LOG_VALUE_UNDOCK_MAX_DOCKED);
- } else if (snapTarget == mSnapAlgorithm.getMiddleTarget()) {
+ } else if (snapTarget == mSplitLayout.getSnapAlgorithm().getMiddleTarget()) {
MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_RESIZE,
LOG_VALUE_RESIZE_50_50);
- } else if (snapTarget == mSnapAlgorithm.getFirstSplitTarget()) {
+ } else if (snapTarget == mSplitLayout.getSnapAlgorithm().getFirstSplitTarget()) {
MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_RESIZE,
dockSideTopLeft(mDockSide)
? LOG_VALUE_RESIZE_DOCKED_SMALLER
: LOG_VALUE_RESIZE_DOCKED_LARGER);
- } else if (snapTarget == mSnapAlgorithm.getLastSplitTarget()) {
+ } else if (snapTarget == mSplitLayout.getSnapAlgorithm().getLastSplitTarget()) {
MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_RESIZE,
dockSideTopLeft(mDockSide)
? LOG_VALUE_RESIZE_DOCKED_LARGER
@@ -625,12 +621,16 @@
: snapTarget.taskPosition,
snapTarget));
Runnable endAction = () -> {
- commitSnapFlags(snapTarget);
+ boolean dismissed = commitSnapFlags(snapTarget);
mWindowManagerProxy.setResizing(false);
updateDockSide();
mCurrentAnimator = null;
mEntranceAnimationRunning = false;
mExitAnimationRunning = false;
+ if (!dismissed) {
+ WindowManagerProxy.applyResizeSplits((mIsInMinimizeInteraction
+ ? mSnapTargetBeforeMinimized : snapTarget).position, mSplitLayout);
+ }
if (mCallback != null) {
mCallback.onDraggingEnd();
}
@@ -642,12 +642,13 @@
// position isn't negative.
final SnapTarget saveTarget;
if (snapTarget.position < 0) {
- saveTarget = mSnapAlgorithm.getMiddleTarget();
+ saveTarget = mSplitLayout.getSnapAlgorithm().getMiddleTarget();
} else {
saveTarget = snapTarget;
}
- if (saveTarget.position != mSnapAlgorithm.getDismissEndTarget().position
- && saveTarget.position != mSnapAlgorithm.getDismissStartTarget().position) {
+ final DividerSnapAlgorithm snapAlgo = mSplitLayout.getSnapAlgorithm();
+ if (saveTarget.position != snapAlgo.getDismissEndTarget().position
+ && saveTarget.position != snapAlgo.getDismissStartTarget().position) {
saveSnapTargetBeforeMinimized(saveTarget);
}
}
@@ -701,11 +702,11 @@
}
}
- private void commitSnapFlags(SnapTarget target) {
+ private boolean commitSnapFlags(SnapTarget target) {
if (target.flag == SnapTarget.FLAG_NONE) {
- return;
+ return false;
}
- boolean dismissOrMaximize;
+ final boolean dismissOrMaximize;
if (target.flag == SnapTarget.FLAG_DISMISS_START) {
dismissOrMaximize = mDockSide == WindowManager.DOCKED_LEFT
|| mDockSide == WindowManager.DOCKED_TOP;
@@ -713,12 +714,13 @@
dismissOrMaximize = mDockSide == WindowManager.DOCKED_RIGHT
|| mDockSide == WindowManager.DOCKED_BOTTOM;
}
- if (dismissOrMaximize) {
- mWindowManagerProxy.dismissDockedStack();
- } else {
- mWindowManagerProxy.maximizeDockedStack();
- }
- mWindowManagerProxy.setResizeDimLayer(false, WINDOWING_MODE_UNDEFINED, 0f);
+ mWindowManagerProxy.dismissOrMaximizeDocked(mTiles, dismissOrMaximize);
+ Transaction t = mTiles.getTransaction();
+ setResizeDimLayer(t, true /* primary */, 0f);
+ setResizeDimLayer(t, false /* primary */, 0f);
+ t.apply();
+ mTiles.releaseTransaction(t);
+ return true;
}
private void liftBackground() {
@@ -765,6 +767,28 @@
mBackgroundLifted = false;
}
+ private void initializeSurfaceState() {
+ int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position;
+ // Recalculate the split-layout's internal tile bounds
+ mSplitLayout.resizeSplits(midPos);
+ Transaction t = mTiles.getTransaction();
+ if (mDockedStackMinimized) {
+ int position = mSplitLayout.getMinimizedSnapAlgorithm().getMiddleTarget().position;
+ calculateBoundsForPosition(position, mDockSide, mDockedRect);
+ calculateBoundsForPosition(position, DockedDividerUtils.invertDockSide(mDockSide),
+ mOtherRect);
+ mDividerPositionX = mDividerPositionY = position;
+ resizeSplitSurfaces(t, mDockedRect, mSplitLayout.mPrimary,
+ mOtherRect, mSplitLayout.mSecondary);
+ } else {
+ resizeSplitSurfaces(t, mSplitLayout.mPrimary, null,
+ mSplitLayout.mSecondary, null);
+ }
+ setResizeDimLayer(t, true /* primary */, 0.f /* alpha */);
+ setResizeDimLayer(t, false /* secondary */, 0.f /* alpha */);
+ t.apply();
+ mTiles.releaseTransaction(t);
+ }
public void setMinimizedDockStack(boolean minimized, boolean isHomeStackResizable) {
mHomeStackResizable = isHomeStackResizable;
@@ -789,15 +813,11 @@
mDockedStackMinimized = minimized;
} else if (mDockedStackMinimized != minimized) {
mDockedStackMinimized = minimized;
- if (mDisplayRotation != mDefaultDisplay.getRotation()) {
+ if (mSplitLayout.mDisplayLayout.rotation() != mDefaultDisplay.getRotation()) {
// Splitscreen to minimize is about to starts after rotating landscape to seascape,
// update insets, display info and snap algorithm targets
WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
repositionSnapTargetBeforeMinimized();
- updateDisplayInfo();
- } else {
- mMinimizedSnapAlgorithm = null;
- initializeSnapAlgorithm();
}
if (mIsInMinimizeInteraction != minimized || mCurrentAnimator != null) {
cancelFlingAnimation();
@@ -805,15 +825,51 @@
// Relayout to recalculate the divider shadow when minimizing
requestLayout();
mIsInMinimizeInteraction = true;
- resizeStack(mMinimizedSnapAlgorithm.getMiddleTarget());
+ resizeStackSurfaces(mSplitLayout.getMinimizedSnapAlgorithm().getMiddleTarget());
} else {
- resizeStack(mSnapTargetBeforeMinimized);
+ resizeStackSurfaces(mSnapTargetBeforeMinimized);
mIsInMinimizeInteraction = false;
}
}
}
}
+ void enterSplitMode(boolean isHomeStackResizable) {
+ post(() -> {
+ if (!isViewSurfaceValid()) {
+ return;
+ }
+ Transaction t = mTiles.getTransaction();
+ t.show(getViewRootImpl().getSurfaceControl()).apply();
+ mTiles.releaseTransaction(t);
+ });
+ if (isHomeStackResizable) {
+ SnapTarget miniMid = mSplitLayout.getMinimizedSnapAlgorithm().getMiddleTarget();
+ if (mDockedStackMinimized) {
+ mDividerPositionY = mDividerPositionX = miniMid.position;
+ }
+ }
+ }
+
+ private boolean isViewSurfaceValid() {
+ return getViewRootImpl() != null && getViewRootImpl().getSurfaceControl() != null
+ && getViewRootImpl().getSurfaceControl().isValid();
+ }
+
+ void exitSplitMode() {
+ // Reset tile bounds
+ post(() -> {
+ if (!isViewSurfaceValid()) {
+ return;
+ }
+ Transaction t = mTiles.getTransaction();
+ t.hide(getViewRootImpl().getSurfaceControl()).apply();
+ mTiles.releaseTransaction(t);
+ });
+ int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position;
+ WindowManagerProxy.applyResizeSplits(midPos, mSplitLayout);
+ }
+
public void setMinimizedDockStack(boolean minimized, long animDuration,
boolean isHomeStackResizable) {
mHomeStackResizable = isHomeStackResizable;
@@ -844,14 +900,12 @@
mDockedStackMinimized = minimized;
} else if (mDockedStackMinimized != minimized) {
mIsInMinimizeInteraction = true;
- mMinimizedSnapAlgorithm = null;
mDockedStackMinimized = minimized;
- initializeSnapAlgorithm();
stopDragging(minimized
? mSnapTargetBeforeMinimized.position
: getCurrentPosition(),
minimized
- ? mMinimizedSnapAlgorithm.getMiddleTarget()
+ ? mSplitLayout.getMinimizedSnapAlgorithm().getMiddleTarget()
: mSnapTargetBeforeMinimized,
animDuration, Interpolators.FAST_OUT_SLOW_IN, 0);
setAdjustedForIme(false, animDuration);
@@ -865,18 +919,6 @@
.start();
}
- public void setAdjustedForIme(boolean adjustedForIme) {
- updateDockSide();
- mHandle.setAlpha(adjustedForIme ? 0f : 1f);
- if (!adjustedForIme) {
- resetBackground();
- } else if (mDockSide == WindowManager.DOCKED_TOP) {
- mBackground.setPivotY(0);
- mBackground.setScaleY(ADJUSTED_FOR_IME_SCALE);
- }
- mAdjustedForIme = adjustedForIme;
- }
-
public void setAdjustedForIme(boolean adjustedForIme, long animDuration) {
updateDockSide();
mHandle.animate()
@@ -902,7 +944,8 @@
private void saveSnapTargetBeforeMinimized(SnapTarget target) {
mSnapTargetBeforeMinimized = target;
mState.mRatioPositionBeforeMinimized = (float) target.position /
- (isHorizontalDivision() ? mDisplayHeight : mDisplayWidth);
+ (isHorizontalDivision() ? mSplitLayout.mDisplayLayout.height()
+ : mSplitLayout.mDisplayLayout.width());
}
private void resetBackground() {
@@ -916,51 +959,17 @@
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- updateDisplayInfo();
- }
-
- public void notifyDockSideChanged(int newDockSide) {
- int oldDockSide = mDockSide;
- mDockSide = newDockSide;
- mMinimizedShadow.setDockSide(mDockSide);
- requestLayout();
-
- // Update the snap position to the new docked side with correct insets
- WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
- mMinimizedSnapAlgorithm = null;
- initializeSnapAlgorithm();
-
- if (oldDockSide == DOCKED_LEFT && mDockSide == DOCKED_RIGHT
- || oldDockSide == DOCKED_RIGHT && mDockSide == DOCKED_LEFT) {
- repositionSnapTargetBeforeMinimized();
- }
-
- // Landscape to seascape rotation requires minimized to resize docked app correctly
- if (mHomeStackResizable && mDockedStackMinimized) {
- resizeStack(mMinimizedSnapAlgorithm.getMiddleTarget());
- }
}
private void repositionSnapTargetBeforeMinimized() {
int position = (int) (mState.mRatioPositionBeforeMinimized *
- (isHorizontalDivision() ? mDisplayHeight : mDisplayWidth));
- mSnapAlgorithm = null;
- initializeSnapAlgorithm();
+ (isHorizontalDivision() ? mSplitLayout.mDisplayLayout.height()
+ : mSplitLayout.mDisplayLayout.width()));
// Set the snap target before minimized but do not save until divider is attached and not
// minimized because it does not know its minimized state yet.
- mSnapTargetBeforeMinimized = mSnapAlgorithm.calculateNonDismissingSnapTarget(position);
- }
-
- private void updateDisplayInfo() {
- mDisplayRotation = mDefaultDisplay.getRotation();
- final DisplayInfo info = new DisplayInfo();
- mDefaultDisplay.getDisplayInfo(info);
- mDisplayWidth = info.logicalWidth;
- mDisplayHeight = info.logicalHeight;
- mSnapAlgorithm = null;
- mMinimizedSnapAlgorithm = null;
- initializeSnapAlgorithm();
+ mSnapTargetBeforeMinimized =
+ mSplitLayout.getSnapAlgorithm().calculateNonDismissingSnapTarget(position);
}
private int calculatePosition(int touchX, int touchY) {
@@ -994,8 +1003,9 @@
}
public void calculateBoundsForPosition(int position, int dockSide, Rect outRect) {
- DockedDividerUtils.calculateBoundsForPosition(position, dockSide, outRect, mDisplayWidth,
- mDisplayHeight, mDividerSize);
+ DockedDividerUtils.calculateBoundsForPosition(position, dockSide, outRect,
+ mSplitLayout.mDisplayLayout.width(), mSplitLayout.mDisplayLayout.height(),
+ mDividerSize);
}
public void resizeStackDelayed(int position, int taskPosition, SnapTarget taskSnapTarget) {
@@ -1005,16 +1015,61 @@
mSfChoreographer.scheduleAtSfVsync(mHandler, message);
}
- private void resizeStack(SnapTarget taskSnapTarget) {
- resizeStack(taskSnapTarget.position, taskSnapTarget.position, taskSnapTarget);
+ private void resizeStackSurfaces(SnapTarget taskSnapTarget) {
+ resizeStackSurfaces(taskSnapTarget.position, taskSnapTarget.position, taskSnapTarget);
}
- public void resizeStack(int position, int taskPosition, SnapTarget taskSnapTarget) {
+ void resizeSplitSurfaces(Transaction t, Rect dockedRect, Rect otherRect) {
+ post(() -> resizeSplitSurfaces(t, dockedRect, null, otherRect, null));
+ }
+
+ private void resizeSplitSurfaces(Transaction t, Rect dockedRect, Rect dockedTaskRect,
+ Rect otherRect, Rect otherTaskRect) {
+ dockedTaskRect = dockedTaskRect == null ? dockedRect : dockedTaskRect;
+ otherTaskRect = otherTaskRect == null ? otherRect : otherTaskRect;
+
+ mDividerPositionX = dockedRect.right;
+ mDividerPositionY = dockedRect.bottom;
+
+ t.setPosition(mTiles.mPrimarySurface, dockedTaskRect.left, dockedTaskRect.top);
+ Rect crop = new Rect(dockedRect);
+ crop.offsetTo(-Math.min(dockedTaskRect.left - dockedRect.left, 0),
+ -Math.min(dockedTaskRect.top - dockedRect.top, 0));
+ t.setWindowCrop(mTiles.mPrimarySurface, crop);
+ t.setPosition(mTiles.mSecondarySurface, otherTaskRect.left, otherTaskRect.top);
+ crop.set(otherRect);
+ crop.offsetTo(-(otherTaskRect.left - otherRect.left),
+ -(otherTaskRect.top - otherRect.top));
+ t.setWindowCrop(mTiles.mSecondarySurface, crop);
+ SurfaceControl dividerCtrl = getViewRootImpl() != null
+ ? getViewRootImpl().getSurfaceControl() : null;
+ if (dividerCtrl != null && dividerCtrl.isValid()) {
+ if (isHorizontalDivision()) {
+ t.setPosition(dividerCtrl, 0, mDividerPositionY - mDividerInsets);
+ } else {
+ t.setPosition(dividerCtrl, mDividerPositionX - mDividerInsets, 0);
+ }
+ }
+ }
+
+ void setResizeDimLayer(Transaction t, boolean primary, float alpha) {
+ SurfaceControl dim = primary ? mTiles.mPrimaryDim : mTiles.mSecondaryDim;
+ if (alpha <= 0.f) {
+ t.hide(dim);
+ } else {
+ t.setAlpha(dim, alpha);
+ t.show(dim);
+ }
+ }
+
+ void resizeStackSurfaces(int position, int taskPosition, SnapTarget taskSnapTarget) {
if (mRemoved) {
// This divider view has been removed so shouldn't have any additional influence.
return;
}
calculateBoundsForPosition(position, mDockSide, mDockedRect);
+ calculateBoundsForPosition(position, DockedDividerUtils.invertDockSide(mDockSide),
+ mOtherRect);
if (mDockedRect.equals(mLastResizeRect) && !mEntranceAnimationRunning) {
return;
@@ -1025,6 +1080,7 @@
mBackground.invalidate();
}
+ Transaction t = mTiles.getTransaction();
mLastResizeRect.set(mDockedRect);
if (mHomeStackResizable && mIsInMinimizeInteraction) {
calculateBoundsForPosition(mSnapTargetBeforeMinimized.position, mDockSide,
@@ -1037,8 +1093,10 @@
mDockedTaskRect.offset(Math.max(position, mStableInsets.left - mDividerSize)
- mDockedTaskRect.left + mDividerSize, 0);
}
- mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, mDockedTaskRect,
- mOtherTaskRect, null);
+ resizeSplitSurfaces(t, mDockedRect, mDockedTaskRect, mOtherRect,
+ mOtherTaskRect);
+ t.apply();
+ mTiles.releaseTransaction(t);
return;
}
@@ -1052,8 +1110,7 @@
}
calculateBoundsForPosition(taskPosition, DockedDividerUtils.invertDockSide(mDockSide),
mOtherTaskRect);
- mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, null,
- mOtherTaskRect, null);
+ resizeSplitSurfaces(t, mDockedRect, mDockedTaskRect, mOtherRect, mOtherTaskRect);
} else if (mExitAnimationRunning && taskPosition != TASK_POSITION_SAME) {
calculateBoundsForPosition(taskPosition, mDockSide, mDockedTaskRect);
mDockedInsetRect.set(mDockedTaskRect);
@@ -1066,8 +1123,7 @@
if (mDockSide == DOCKED_RIGHT) {
mDockedTaskRect.offset(position - mStableInsets.left + mDividerSize, 0);
}
- mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, mDockedInsetRect,
- mOtherTaskRect, mOtherInsetRect);
+ resizeSplitSurfaces(t, mDockedRect, mDockedTaskRect, mOtherRect, mOtherTaskRect);
} else if (taskPosition != TASK_POSITION_SAME) {
calculateBoundsForPosition(position, DockedDividerUtils.invertDockSide(mDockSide),
mOtherRect);
@@ -1078,7 +1134,8 @@
restrictDismissingTaskPosition(taskPosition, dockSideInverted, taskSnapTarget);
calculateBoundsForPosition(taskPositionDocked, mDockSide, mDockedTaskRect);
calculateBoundsForPosition(taskPositionOther, dockSideInverted, mOtherTaskRect);
- mTmpRect.set(0, 0, mDisplayWidth, mDisplayHeight);
+ mTmpRect.set(0, 0, mSplitLayout.mDisplayLayout.width(),
+ mSplitLayout.mDisplayLayout.height());
alignTopLeft(mDockedRect, mDockedTaskRect);
alignTopLeft(mOtherRect, mOtherTaskRect);
mDockedInsetRect.set(mDockedTaskRect);
@@ -1094,15 +1151,15 @@
taskPositionDocked);
applyDismissingParallax(mOtherTaskRect, dockSideInverted, taskSnapTarget, position,
taskPositionOther);
- mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, mDockedInsetRect,
- mOtherTaskRect, mOtherInsetRect);
+ resizeSplitSurfaces(t, mDockedRect, mDockedTaskRect, mOtherRect, mOtherTaskRect);
} else {
- mWindowManagerProxy.resizeDockedStack(mDockedRect, null, null, null, null);
+ resizeSplitSurfaces(t, mDockedRect, null, mOtherRect, null);
}
SnapTarget closestDismissTarget = getSnapAlgorithm().getClosestDismissTarget(position);
float dimFraction = getDimFraction(position, closestDismissTarget);
- mWindowManagerProxy.setResizeDimLayer(dimFraction != 0f,
- getWindowingModeForDismissTarget(closestDismissTarget), dimFraction);
+ setResizeDimLayer(t, isDismissTargetPrimary(closestDismissTarget), dimFraction);
+ t.apply();
+ mTiles.releaseTransaction(t);
}
private void applyExitAnimationParallax(Rect taskRect, int position) {
@@ -1156,10 +1213,12 @@
private int restrictDismissingTaskPosition(int taskPosition, int dockSide,
SnapTarget snapTarget) {
if (snapTarget.flag == SnapTarget.FLAG_DISMISS_START && dockSideTopLeft(dockSide)) {
- return Math.max(mSnapAlgorithm.getFirstSplitTarget().position, mStartPosition);
+ return Math.max(mSplitLayout.getSnapAlgorithm().getFirstSplitTarget().position,
+ mStartPosition);
} else if (snapTarget.flag == SnapTarget.FLAG_DISMISS_END
&& dockSideBottomRight(dockSide)) {
- return Math.min(mSnapAlgorithm.getLastSplitTarget().position, mStartPosition);
+ return Math.min(mSplitLayout.getSnapAlgorithm().getLastSplitTarget().position,
+ mStartPosition);
} else {
return taskPosition;
}
@@ -1171,19 +1230,19 @@
private void applyDismissingParallax(Rect taskRect, int dockSide, SnapTarget snapTarget,
int position, int taskPosition) {
float fraction = Math.min(1, Math.max(0,
- mSnapAlgorithm.calculateDismissingFraction(position)));
+ mSplitLayout.getSnapAlgorithm().calculateDismissingFraction(position)));
SnapTarget dismissTarget = null;
SnapTarget splitTarget = null;
int start = 0;
- if (position <= mSnapAlgorithm.getLastSplitTarget().position
+ if (position <= mSplitLayout.getSnapAlgorithm().getLastSplitTarget().position
&& dockSideTopLeft(dockSide)) {
- dismissTarget = mSnapAlgorithm.getDismissStartTarget();
- splitTarget = mSnapAlgorithm.getFirstSplitTarget();
+ dismissTarget = mSplitLayout.getSnapAlgorithm().getDismissStartTarget();
+ splitTarget = mSplitLayout.getSnapAlgorithm().getFirstSplitTarget();
start = taskPosition;
- } else if (position >= mSnapAlgorithm.getLastSplitTarget().position
+ } else if (position >= mSplitLayout.getSnapAlgorithm().getLastSplitTarget().position
&& dockSideBottomRight(dockSide)) {
- dismissTarget = mSnapAlgorithm.getDismissEndTarget();
- splitTarget = mSnapAlgorithm.getLastSplitTarget();
+ dismissTarget = mSplitLayout.getSnapAlgorithm().getDismissEndTarget();
+ splitTarget = mSplitLayout.getSnapAlgorithm().getLastSplitTarget();
start = splitTarget.position;
}
if (dismissTarget != null && fraction > 0f
@@ -1236,14 +1295,10 @@
}
}
- private int getWindowingModeForDismissTarget(SnapTarget dismissTarget) {
- if ((dismissTarget.flag == SnapTarget.FLAG_DISMISS_START && dockSideTopLeft(mDockSide))
+ private boolean isDismissTargetPrimary(SnapTarget dismissTarget) {
+ return (dismissTarget.flag == SnapTarget.FLAG_DISMISS_START && dockSideTopLeft(mDockSide))
|| (dismissTarget.flag == SnapTarget.FLAG_DISMISS_END
- && dockSideBottomRight(mDockSide))) {
- return WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
- } else {
- return WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
- }
+ && dockSideBottomRight(mDockSide));
}
/**
@@ -1297,7 +1352,7 @@
}
void onDockedFirstAnimationFrame() {
- saveSnapTargetBeforeMinimized(mSnapAlgorithm.getMiddleTarget());
+ saveSnapTargetBeforeMinimized(mSplitLayout.getSnapAlgorithm().getMiddleTarget());
}
void onDockedTopTask() {
@@ -1307,8 +1362,9 @@
updateDockSide();
mEntranceAnimationRunning = true;
- resizeStack(calculatePositionForInsetBounds(), mSnapAlgorithm.getMiddleTarget().position,
- mSnapAlgorithm.getMiddleTarget());
+ resizeStackSurfaces(calculatePositionForInsetBounds(),
+ mSplitLayout.getSnapAlgorithm().getMiddleTarget().position,
+ mSplitLayout.getSnapAlgorithm().getMiddleTarget());
}
void onRecentsDrawn() {
@@ -1337,13 +1393,12 @@
}
void onUndockingTask() {
- int dockSide = mWindowManagerProxy.getDockSide();
- if (dockSide != WindowManager.DOCKED_INVALID && (mHomeStackResizable
- || !mDockedStackMinimized)) {
+ int dockSide = mSplitLayout.getPrimarySplitSide();
+ if (inSplitMode() && (mHomeStackResizable || !mDockedStackMinimized)) {
startDragging(false /* animate */, false /* touching */);
SnapTarget target = dockSideTopLeft(dockSide)
- ? mSnapAlgorithm.getDismissEndTarget()
- : mSnapAlgorithm.getDismissStartTarget();
+ ? mSplitLayout.getSnapAlgorithm().getDismissEndTarget()
+ : mSplitLayout.getSnapAlgorithm().getDismissStartTarget();
// Don't start immediately - give a little bit time to settle the drag resize change.
mExitAnimationRunning = true;
@@ -1354,8 +1409,7 @@
}
private int calculatePositionForInsetBounds() {
- mTmpRect.set(0, 0, mDisplayWidth, mDisplayHeight);
- mTmpRect.inset(mStableInsets);
+ mSplitLayout.mDisplayLayout.getStableBounds(mTmpRect);
return DockedDividerUtils.calculatePositionForBounds(mTmpRect, mDockSide, mDividerSize);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
index 2486d653..bd843b0 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
@@ -26,12 +26,13 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
-import android.content.Context;
import android.graphics.PixelFormat;
import android.os.Binder;
import android.view.View;
import android.view.WindowManager;
+import com.android.systemui.wm.SystemWindows;
+
/**
* Manages the window parameters of the docked stack divider.
*/
@@ -39,15 +40,16 @@
private static final String WINDOW_TITLE = "DockedStackDivider";
- private final WindowManager mWindowManager;
+ private final SystemWindows mSystemWindows;
private WindowManager.LayoutParams mLp;
private View mView;
- public DividerWindowManager(Context ctx) {
- mWindowManager = ctx.getSystemService(WindowManager.class);
+ public DividerWindowManager(SystemWindows systemWindows) {
+ mSystemWindows = systemWindows;
}
- public void add(View view, int width, int height) {
+ /** Add a divider view */
+ public void add(View view, int width, int height, int displayId) {
mLp = new WindowManager.LayoutParams(
width, height, TYPE_DOCK_DIVIDER,
FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL
@@ -60,13 +62,13 @@
view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
- mWindowManager.addView(view, mLp);
+ mSystemWindows.addView(view, mLp, displayId, TYPE_DOCK_DIVIDER);
mView = view;
}
public void remove() {
if (mView != null) {
- mWindowManager.removeView(mView);
+ mSystemWindows.removeView(mView);
}
mView = null;
}
@@ -81,7 +83,7 @@
changed = true;
}
if (changed) {
- mWindowManager.updateViewLayout(mView, mLp);
+ mSystemWindows.updateViewLayout(mView, mLp);
}
}
@@ -95,7 +97,7 @@
changed = true;
}
if (changed) {
- mWindowManager.updateViewLayout(mView, mLp);
+ mSystemWindows.updateViewLayout(mView, mLp);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
index c6ac309..db7996e 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
@@ -31,6 +31,8 @@
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
+import java.util.function.Consumer;
+
/**
* Controller that decides when to show the {@link ForcedResizableInfoActivity}.
*/
@@ -52,6 +54,12 @@
}
};
+ private final Consumer<Boolean> mDockedStackExistsListener = exists -> {
+ if (!exists) {
+ mPackagesShownInSession.clear();
+ }
+ };
+
/** Record of force resized task that's pending to be handled. */
private class PendingTaskRecord {
int taskId;
@@ -67,7 +75,7 @@
}
}
- public ForcedResizableInfoActivityController(Context context) {
+ public ForcedResizableInfoActivityController(Context context, Divider divider) {
mContext = context;
ActivityManagerWrapper.getInstance().registerTaskStackListener(
new TaskStackChangeListener() {
@@ -87,12 +95,7 @@
activityLaunchOnSecondaryDisplayFailed();
}
});
- }
-
- public void notifyDockedStackExistsChanged(boolean exists) {
- if (!exists) {
- mPackagesShownInSession.clear();
- }
+ divider.registerInSplitScreenListener(mDockedStackExistsListener);
}
public void onAppTransitionFinished() {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java
new file mode 100644
index 0000000..b19f560
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2020 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.stackdivider;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.view.WindowManager.DOCKED_BOTTOM;
+import static android.view.WindowManager.DOCKED_INVALID;
+import static android.view.WindowManager.DOCKED_LEFT;
+import static android.view.WindowManager.DOCKED_RIGHT;
+import static android.view.WindowManager.DOCKED_TOP;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.util.TypedValue;
+import android.view.WindowContainerTransaction;
+
+import com.android.internal.policy.DividerSnapAlgorithm;
+import com.android.internal.policy.DockedDividerUtils;
+import com.android.systemui.wm.DisplayLayout;
+
+/**
+ * Handles split-screen related internal display layout. In general, this represents the
+ * WM-facing understanding of the splits.
+ */
+public class SplitDisplayLayout {
+ /** Minimum size of an adjusted stack bounds relative to original stack bounds. Used to
+ * restrict IME adjustment so that a min portion of top stack remains visible.*/
+ private static final float ADJUSTED_STACK_FRACTION_MIN = 0.3f;
+
+ private static final int DIVIDER_WIDTH_INACTIVE_DP = 4;
+
+ SplitScreenTaskOrganizer mTiles;
+ DisplayLayout mDisplayLayout;
+ Context mContext;
+
+ // Lazy stuff
+ boolean mResourcesValid = false;
+ int mDividerSize;
+ int mDividerSizeInactive;
+ private DividerSnapAlgorithm mSnapAlgorithm = null;
+ private DividerSnapAlgorithm mMinimizedSnapAlgorithm = null;
+ Rect mPrimary = null;
+ Rect mSecondary = null;
+ Rect mAdjustedPrimary = null;
+ Rect mAdjustedSecondary = null;
+
+ public SplitDisplayLayout(Context ctx, DisplayLayout dl, SplitScreenTaskOrganizer taskTiles) {
+ mTiles = taskTiles;
+ mDisplayLayout = dl;
+ mContext = ctx;
+ }
+
+ void rotateTo(int newRotation) {
+ mDisplayLayout.rotateTo(mContext.getResources(), newRotation);
+ final Configuration config = new Configuration();
+ config.unset();
+ config.orientation = mDisplayLayout.getOrientation();
+ Rect tmpRect = new Rect(0, 0, mDisplayLayout.width(), mDisplayLayout.height());
+ tmpRect.inset(mDisplayLayout.nonDecorInsets());
+ config.windowConfiguration.setAppBounds(tmpRect);
+ tmpRect.set(0, 0, mDisplayLayout.width(), mDisplayLayout.height());
+ tmpRect.inset(mDisplayLayout.stableInsets());
+ config.screenWidthDp = (int) (tmpRect.width() / mDisplayLayout.density());
+ config.screenHeightDp = (int) (tmpRect.height() / mDisplayLayout.density());
+ mContext = mContext.createConfigurationContext(config);
+ mSnapAlgorithm = null;
+ mMinimizedSnapAlgorithm = null;
+ mResourcesValid = false;
+ }
+
+ private void updateResources() {
+ if (mResourcesValid) {
+ return;
+ }
+ mResourcesValid = true;
+ Resources res = mContext.getResources();
+ mDividerSize = DockedDividerUtils.getDividerSize(res,
+ DockedDividerUtils.getDividerInsets(res));
+ mDividerSizeInactive = (int) TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP, DIVIDER_WIDTH_INACTIVE_DP, res.getDisplayMetrics());
+ }
+
+ int getPrimarySplitSide() {
+ return mDisplayLayout.isLandscape() ? DOCKED_LEFT : DOCKED_TOP;
+ }
+
+ boolean isMinimized() {
+ return mTiles.mSecondary.topActivityType == ACTIVITY_TYPE_HOME
+ || mTiles.mSecondary.topActivityType == ACTIVITY_TYPE_RECENTS;
+ }
+
+ DividerSnapAlgorithm getSnapAlgorithm() {
+ if (mSnapAlgorithm == null) {
+ updateResources();
+ boolean isHorizontalDivision = !mDisplayLayout.isLandscape();
+ mSnapAlgorithm = new DividerSnapAlgorithm(mContext.getResources(),
+ mDisplayLayout.width(), mDisplayLayout.height(), mDividerSize,
+ isHorizontalDivision, mDisplayLayout.stableInsets(), getPrimarySplitSide());
+ }
+ return mSnapAlgorithm;
+ }
+
+ DividerSnapAlgorithm getMinimizedSnapAlgorithm() {
+ if (mMinimizedSnapAlgorithm == null) {
+ updateResources();
+ boolean isHorizontalDivision = !mDisplayLayout.isLandscape();
+ mMinimizedSnapAlgorithm = new DividerSnapAlgorithm(mContext.getResources(),
+ mDisplayLayout.width(), mDisplayLayout.height(), mDividerSize,
+ isHorizontalDivision, mDisplayLayout.stableInsets(), getPrimarySplitSide(),
+ true /* isMinimized */);
+ }
+ return mMinimizedSnapAlgorithm;
+ }
+
+ void resizeSplits(int position) {
+ mPrimary = mPrimary == null ? new Rect() : mPrimary;
+ mSecondary = mSecondary == null ? new Rect() : mSecondary;
+ calcSplitBounds(position, mPrimary, mSecondary);
+ }
+
+ void resizeSplits(int position, WindowContainerTransaction t) {
+ resizeSplits(position);
+ t.setBounds(mTiles.mPrimary.token, mPrimary);
+ t.setBounds(mTiles.mSecondary.token, mSecondary);
+
+ t.setSmallestScreenWidthDp(mTiles.mPrimary.token,
+ getSmallestWidthDpForBounds(mContext, mDisplayLayout, mPrimary));
+ t.setSmallestScreenWidthDp(mTiles.mSecondary.token,
+ getSmallestWidthDpForBounds(mContext, mDisplayLayout, mSecondary));
+ }
+
+ void calcSplitBounds(int position, @NonNull Rect outPrimary, @NonNull Rect outSecondary) {
+ int dockSide = getPrimarySplitSide();
+ DockedDividerUtils.calculateBoundsForPosition(position, dockSide, outPrimary,
+ mDisplayLayout.width(), mDisplayLayout.height(), mDividerSize);
+
+ DockedDividerUtils.calculateBoundsForPosition(position,
+ DockedDividerUtils.invertDockSide(dockSide), outSecondary, mDisplayLayout.width(),
+ mDisplayLayout.height(), mDividerSize);
+ }
+
+ Rect calcMinimizedHomeStackBounds() {
+ DividerSnapAlgorithm.SnapTarget miniMid = getMinimizedSnapAlgorithm().getMiddleTarget();
+ Rect homeBounds = new Rect();
+ DockedDividerUtils.calculateBoundsForPosition(miniMid.position,
+ DockedDividerUtils.invertDockSide(getPrimarySplitSide()), homeBounds,
+ mDisplayLayout.width(), mDisplayLayout.height(), mDividerSize);
+ return homeBounds;
+ }
+
+ /**
+ * Updates the adjustment depending on it's current state.
+ */
+ void updateAdjustedBounds(int currImeTop, int startTop, int finalTop) {
+ updateAdjustedBounds(mDisplayLayout, currImeTop, startTop, finalTop, mDividerSize,
+ mDividerSizeInactive, mPrimary, mSecondary);
+ }
+
+ /**
+ * Updates the adjustment depending on it's current state.
+ */
+ private void updateAdjustedBounds(DisplayLayout dl, int currImeTop, int startTop, int finalTop,
+ int dividerWidth, int dividerWidthInactive, Rect primaryBounds, Rect secondaryBounds) {
+ adjustForIME(dl, currImeTop, startTop, finalTop, dividerWidth, dividerWidthInactive,
+ primaryBounds, secondaryBounds);
+ }
+
+ /** Assumes top/bottom split. Splits are not adjusted for left/right splits. */
+ private void adjustForIME(DisplayLayout dl, int currImeTop, int startTop, int finalTop,
+ int dividerWidth, int dividerWidthInactive, Rect primaryBounds, Rect secondaryBounds) {
+ if (mAdjustedPrimary == null) {
+ mAdjustedPrimary = new Rect();
+ mAdjustedSecondary = new Rect();
+ }
+
+ final Rect displayStableRect = new Rect();
+ dl.getStableBounds(displayStableRect);
+
+ final boolean showing = finalTop < startTop;
+ final float progress = ((float) (currImeTop - startTop)) / (finalTop - startTop);
+ final float dividerSquish = showing ? progress : 1.f - progress;
+ final int currDividerWidth =
+ (int) (dividerWidthInactive * dividerSquish + dividerWidth * (1.f - dividerSquish));
+
+ final int minTopStackBottom = displayStableRect.top
+ + (int) ((mPrimary.bottom - displayStableRect.top) * ADJUSTED_STACK_FRACTION_MIN);
+ final int minImeTop = minTopStackBottom + currDividerWidth;
+
+ // Calculate an offset which shifts the stacks up by the height of the IME, but still
+ // leaves at least 30% of the top stack visible.
+ final int yOffset = Math.max(0, dl.height() - Math.max(currImeTop, minImeTop));
+
+ // TOP
+ // Reduce the offset by an additional small amount to squish the divider bar.
+ mAdjustedPrimary.set(primaryBounds);
+ mAdjustedPrimary.offset(0, -yOffset + (dividerWidth - currDividerWidth));
+
+ // BOTTOM
+ mAdjustedSecondary.set(secondaryBounds);
+ mAdjustedSecondary.offset(0, -yOffset);
+ }
+
+ static int getSmallestWidthDpForBounds(@NonNull Context context, DisplayLayout dl,
+ Rect bounds) {
+ int dividerSize = DockedDividerUtils.getDividerSize(context.getResources(),
+ DockedDividerUtils.getDividerInsets(context.getResources()));
+
+ int minWidth = Integer.MAX_VALUE;
+
+ // Go through all screen orientations and find the orientation in which the task has the
+ // smallest width.
+ Rect tmpRect = new Rect();
+ Rect rotatedDisplayRect = new Rect();
+ Rect displayRect = new Rect(0, 0, dl.width(), dl.height());
+
+ DisplayLayout tmpDL = new DisplayLayout();
+ for (int rotation = 0; rotation < 4; rotation++) {
+ tmpDL.set(dl);
+ tmpDL.rotateTo(context.getResources(), rotation);
+ DividerSnapAlgorithm snap = initSnapAlgorithmForRotation(context, tmpDL, dividerSize);
+
+ tmpRect.set(bounds);
+ DisplayLayout.rotateBounds(tmpRect, displayRect, rotation - dl.rotation());
+ rotatedDisplayRect.set(0, 0, tmpDL.width(), tmpDL.height());
+ final int dockSide = getPrimarySplitSide(tmpRect, rotatedDisplayRect,
+ tmpDL.getOrientation());
+ final int position = DockedDividerUtils.calculatePositionForBounds(tmpRect, dockSide,
+ dividerSize);
+
+ final int snappedPosition =
+ snap.calculateNonDismissingSnapTarget(position).position;
+ DockedDividerUtils.calculateBoundsForPosition(snappedPosition, dockSide, tmpRect,
+ tmpDL.width(), tmpDL.height(), dividerSize);
+ Rect insettedDisplay = new Rect(rotatedDisplayRect);
+ insettedDisplay.inset(tmpDL.stableInsets());
+ tmpRect.intersect(insettedDisplay);
+ minWidth = Math.min(tmpRect.width(), minWidth);
+ }
+ return (int) (minWidth / dl.density());
+ }
+
+ static DividerSnapAlgorithm initSnapAlgorithmForRotation(Context context, DisplayLayout dl,
+ int dividerSize) {
+ final Configuration config = new Configuration();
+ config.unset();
+ config.orientation = dl.getOrientation();
+ Rect tmpRect = new Rect(0, 0, dl.width(), dl.height());
+ tmpRect.inset(dl.nonDecorInsets());
+ config.windowConfiguration.setAppBounds(tmpRect);
+ tmpRect.set(0, 0, dl.width(), dl.height());
+ tmpRect.inset(dl.stableInsets());
+ config.screenWidthDp = (int) (tmpRect.width() / dl.density());
+ config.screenHeightDp = (int) (tmpRect.height() / dl.density());
+ final Context rotationContext = context.createConfigurationContext(config);
+ return new DividerSnapAlgorithm(
+ rotationContext.getResources(), dl.width(), dl.height(), dividerSize,
+ config.orientation == ORIENTATION_PORTRAIT, dl.stableInsets());
+ }
+
+ /**
+ * Get the current primary-split side. Determined by its location of {@param bounds} within
+ * {@param displayRect} but if both are the same, it will try to dock to each side and determine
+ * if allowed in its respected {@param orientation}.
+ *
+ * @param bounds bounds of the primary split task to get which side is docked
+ * @param displayRect bounds of the display that contains the primary split task
+ * @param orientation the origination of device
+ * @return current primary-split side
+ */
+ static int getPrimarySplitSide(Rect bounds, Rect displayRect, int orientation) {
+ if (orientation == ORIENTATION_PORTRAIT) {
+ // Portrait mode, docked either at the top or the bottom.
+ final int diff = (displayRect.bottom - bounds.bottom) - (bounds.top - displayRect.top);
+ if (diff < 0) {
+ return DOCKED_BOTTOM;
+ } else {
+ // Top is default
+ return DOCKED_TOP;
+ }
+ } else if (orientation == ORIENTATION_LANDSCAPE) {
+ // Landscape mode, docked either on the left or on the right.
+ final int diff = (displayRect.right - bounds.right) - (bounds.left - displayRect.left);
+ if (diff < 0) {
+ return DOCKED_RIGHT;
+ }
+ return DOCKED_LEFT;
+ }
+ return DOCKED_INVALID;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
new file mode 100644
index 0000000..0a54d2f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2020 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.stackdivider;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ITaskOrganizerController;
+import android.app.WindowConfiguration;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Pools;
+import android.view.Display;
+import android.view.ITaskOrganizer;
+import android.view.IWindowContainer;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
+
+class SplitScreenTaskOrganizer extends ITaskOrganizer.Stub {
+ private static final String TAG = "SplitScreenTaskOrganizer";
+ private static final boolean DEBUG = Divider.DEBUG;
+
+ RunningTaskInfo mPrimary;
+ RunningTaskInfo mSecondary;
+ SurfaceControl mPrimarySurface;
+ SurfaceControl mSecondarySurface;
+ SurfaceControl mPrimaryDim;
+ SurfaceControl mSecondaryDim;
+ final Divider mDivider;
+
+ private final Pools.SynchronizedPool<SurfaceControl.Transaction> mTransactionPool =
+ new Pools.SynchronizedPool<>(4);
+
+ SplitScreenTaskOrganizer(Divider divider) {
+ mDivider = divider;
+ }
+
+ void init(ITaskOrganizerController organizerController, SurfaceSession session)
+ throws RemoteException {
+ organizerController.registerTaskOrganizer(this, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ organizerController.registerTaskOrganizer(this, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ mPrimary = organizerController.createRootTask(Display.DEFAULT_DISPLAY,
+ WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ mSecondary = organizerController.createRootTask(Display.DEFAULT_DISPLAY,
+ WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ mPrimarySurface = mPrimary.token.getLeash();
+ mSecondarySurface = mSecondary.token.getLeash();
+
+ // Initialize dim surfaces:
+ mPrimaryDim = new SurfaceControl.Builder(session).setParent(mPrimarySurface)
+ .setColorLayer().setName("Primary Divider Dim").build();
+ mSecondaryDim = new SurfaceControl.Builder(session).setParent(mSecondarySurface)
+ .setColorLayer().setName("Secondary Divider Dim").build();
+ SurfaceControl.Transaction t = getTransaction();
+ t.setLayer(mPrimaryDim, Integer.MAX_VALUE);
+ t.setColor(mPrimaryDim, new float[]{0f, 0f, 0f});
+ t.setLayer(mSecondaryDim, Integer.MAX_VALUE);
+ t.setColor(mSecondaryDim, new float[]{0f, 0f, 0f});
+ t.apply();
+ releaseTransaction(t);
+ }
+
+ SurfaceControl.Transaction getTransaction() {
+ SurfaceControl.Transaction t = mTransactionPool.acquire();
+ if (t == null) {
+ return new SurfaceControl.Transaction();
+ }
+ return t;
+ }
+
+ void releaseTransaction(SurfaceControl.Transaction t) {
+ mTransactionPool.release(t);
+ }
+
+ @Override
+ public void taskAppeared(RunningTaskInfo taskInfo) {
+ }
+
+ @Override
+ public void taskVanished(IWindowContainer container) {
+ }
+
+ @Override
+ public void transactionReady(int id, SurfaceControl.Transaction t) {
+ }
+
+ @Override
+ public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
+ if (taskInfo.displayId != DEFAULT_DISPLAY) {
+ return;
+ }
+ mDivider.getHandler().post(() -> handleTaskInfoChanged(taskInfo));
+ }
+
+ /**
+ * This is effectively a finite state machine which moves between the various split-screen
+ * presentations based on the contents of the split regions.
+ */
+ private void handleTaskInfoChanged(RunningTaskInfo info) {
+ final boolean primaryWasEmpty = mPrimary.topActivityType == ACTIVITY_TYPE_UNDEFINED;
+ final boolean secondaryWasEmpty = mSecondary.topActivityType == ACTIVITY_TYPE_UNDEFINED;
+ if (info.token.asBinder() == mPrimary.token.asBinder()) {
+ mPrimary = info;
+ } else if (info.token.asBinder() == mSecondary.token.asBinder()) {
+ mSecondary = info;
+ }
+ final boolean primaryIsEmpty = mPrimary.topActivityType == ACTIVITY_TYPE_UNDEFINED;
+ final boolean secondaryIsEmpty = mSecondary.topActivityType == ACTIVITY_TYPE_UNDEFINED;
+ if (DEBUG) {
+ Log.d(TAG, "onTaskInfoChanged " + mPrimary + " " + mSecondary);
+ }
+ if (primaryIsEmpty || secondaryIsEmpty) {
+ // At-least one of the splits is empty which means we are currently transitioning
+ // into or out-of split-screen mode.
+ if (DEBUG) {
+ Log.d(TAG, " at-least one split empty " + mPrimary.topActivityType
+ + " " + mSecondary.topActivityType);
+ }
+ if (mDivider.inSplitMode()) {
+ // Was in split-mode, which means we are leaving split, so continue that.
+ // This happens when the stack in the primary-split is dismissed.
+ if (DEBUG) {
+ Log.d(TAG, " was in split, so this means leave it "
+ + mPrimary.topActivityType + " " + mSecondary.topActivityType);
+ }
+ WindowManagerProxy.applyDismissSplit(this, true /* dismissOrMaximize */);
+ mDivider.updateVisibility(false /* visible */);
+ } else if (!primaryIsEmpty && primaryWasEmpty && secondaryWasEmpty) {
+ // Wasn't in split-mode (both were empty), but now that the primary split is
+ // populated, we should fully enter split by moving everything else into secondary.
+ // This just tells window-manager to reparent things, the UI will respond
+ // when it gets new task info for the secondary split.
+ if (DEBUG) {
+ Log.d(TAG, " was not in split, but primary is populated, so enter it");
+ }
+ mDivider.startEnterSplit();
+ }
+ } else if (mSecondary.topActivityType == ACTIVITY_TYPE_HOME
+ || mSecondary.topActivityType == ACTIVITY_TYPE_RECENTS) {
+ // Both splits are populated but the secondary split has a home/recents stack on top,
+ // so enter minimized mode.
+ mDivider.ensureMinimizedSplit();
+ } else {
+ // Both splits are populated by normal activities, so make sure we aren't minimized.
+ mDivider.ensureNormalSplit();
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
index 228aab5..7685733 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
@@ -16,16 +16,25 @@
package com.android.systemui.stackdivider;
-import static android.view.WindowManager.DOCKED_INVALID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.Display.DEFAULT_DISPLAY;
+import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.graphics.Rect;
import android.os.RemoteException;
import android.util.Log;
+import android.view.Display;
+import android.view.IWindowContainer;
+import android.view.WindowContainerTransaction;
import android.view.WindowManagerGlobal;
import com.android.internal.annotations.GuardedBy;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -35,88 +44,20 @@
public class WindowManagerProxy {
private static final String TAG = "WindowManagerProxy";
+ private static final int[] HOME_AND_RECENTS = {ACTIVITY_TYPE_HOME, ACTIVITY_TYPE_RECENTS};
private static final WindowManagerProxy sInstance = new WindowManagerProxy();
@GuardedBy("mDockedRect")
private final Rect mDockedRect = new Rect();
- private final Rect mTempDockedTaskRect = new Rect();
- private final Rect mTempDockedInsetRect = new Rect();
- private final Rect mTempOtherTaskRect = new Rect();
- private final Rect mTempOtherInsetRect = new Rect();
private final Rect mTmpRect1 = new Rect();
- private final Rect mTmpRect2 = new Rect();
- private final Rect mTmpRect3 = new Rect();
- private final Rect mTmpRect4 = new Rect();
- private final Rect mTmpRect5 = new Rect();
@GuardedBy("mDockedRect")
private final Rect mTouchableRegion = new Rect();
- private boolean mDimLayerVisible;
- private int mDimLayerTargetWindowingMode;
- private float mDimLayerAlpha;
-
private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
- private final Runnable mResizeRunnable = new Runnable() {
- @Override
- public void run() {
- synchronized (mDockedRect) {
- mTmpRect1.set(mDockedRect);
- mTmpRect2.set(mTempDockedTaskRect);
- mTmpRect3.set(mTempDockedInsetRect);
- mTmpRect4.set(mTempOtherTaskRect);
- mTmpRect5.set(mTempOtherInsetRect);
- }
- try {
- ActivityTaskManager.getService()
- .resizeDockedStack(mTmpRect1,
- mTmpRect2.isEmpty() ? null : mTmpRect2,
- mTmpRect3.isEmpty() ? null : mTmpRect3,
- mTmpRect4.isEmpty() ? null : mTmpRect4,
- mTmpRect5.isEmpty() ? null : mTmpRect5);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to resize stack: " + e);
- }
- }
- };
-
- private final Runnable mDismissRunnable = new Runnable() {
- @Override
- public void run() {
- try {
- ActivityTaskManager.getService().dismissSplitScreenMode(false /* onTop */);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to remove stack: " + e);
- }
- }
- };
-
- private final Runnable mMaximizeRunnable = new Runnable() {
- @Override
- public void run() {
- try {
- ActivityTaskManager.getService().dismissSplitScreenMode(true /* onTop */);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to resize stack: " + e);
- }
- }
- };
-
- private final Runnable mDimLayerRunnable = new Runnable() {
- @Override
- public void run() {
- try {
- WindowManagerGlobal.getWindowManagerService().setResizeDimLayer(mDimLayerVisible,
- mDimLayerTargetWindowingMode, mDimLayerAlpha);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to resize stack: " + e);
- }
- }
- };
-
private final Runnable mSetTouchableRegionRunnable = new Runnable() {
@Override
public void run() {
@@ -139,40 +80,9 @@
return sInstance;
}
- public void resizeDockedStack(Rect docked, Rect tempDockedTaskRect, Rect tempDockedInsetRect,
- Rect tempOtherTaskRect, Rect tempOtherInsetRect) {
- synchronized (mDockedRect) {
- mDockedRect.set(docked);
- if (tempDockedTaskRect != null) {
- mTempDockedTaskRect.set(tempDockedTaskRect);
- } else {
- mTempDockedTaskRect.setEmpty();
- }
- if (tempDockedInsetRect != null) {
- mTempDockedInsetRect.set(tempDockedInsetRect);
- } else {
- mTempDockedInsetRect.setEmpty();
- }
- if (tempOtherTaskRect != null) {
- mTempOtherTaskRect.set(tempOtherTaskRect);
- } else {
- mTempOtherTaskRect.setEmpty();
- }
- if (tempOtherInsetRect != null) {
- mTempOtherInsetRect.set(tempOtherInsetRect);
- } else {
- mTempOtherInsetRect.setEmpty();
- }
- }
- mExecutor.execute(mResizeRunnable);
- }
-
- public void dismissDockedStack() {
- mExecutor.execute(mDismissRunnable);
- }
-
- public void maximizeDockedStack() {
- mExecutor.execute(mMaximizeRunnable);
+ void dismissOrMaximizeDocked(
+ final SplitScreenTaskOrganizer tiles, final boolean dismissOrMaximize) {
+ mExecutor.execute(() -> applyDismissSplit(tiles, dismissOrMaximize));
}
public void setResizing(final boolean resizing) {
@@ -188,26 +98,204 @@
});
}
- public int getDockSide() {
- try {
- return WindowManagerGlobal.getWindowManagerService().getDockedStackSide();
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to get dock side: " + e);
- }
- return DOCKED_INVALID;
- }
-
- public void setResizeDimLayer(boolean visible, int targetWindowingMode, float alpha) {
- mDimLayerVisible = visible;
- mDimLayerTargetWindowingMode = targetWindowingMode;
- mDimLayerAlpha = alpha;
- mExecutor.execute(mDimLayerRunnable);
- }
-
+ /** Sets a touch region */
public void setTouchRegion(Rect region) {
synchronized (mDockedRect) {
mTouchableRegion.set(region);
}
mExecutor.execute(mSetTouchableRegionRunnable);
}
+
+ static void applyResizeSplits(int position, SplitDisplayLayout splitLayout) {
+ WindowContainerTransaction t = new WindowContainerTransaction();
+ splitLayout.resizeSplits(position, t);
+ try {
+ ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(t,
+ null /* organizer */);
+ } catch (RemoteException e) {
+ }
+ }
+
+ private static boolean getHomeAndRecentsTasks(List<IWindowContainer> out,
+ IWindowContainer parent) {
+ boolean resizable = false;
+ try {
+ List<ActivityManager.RunningTaskInfo> rootTasks = parent == null
+ ? ActivityTaskManager.getTaskOrganizerController().getRootTasks(
+ Display.DEFAULT_DISPLAY, HOME_AND_RECENTS)
+ : ActivityTaskManager.getTaskOrganizerController().getChildTasks(parent,
+ HOME_AND_RECENTS);
+ for (int i = 0, n = rootTasks.size(); i < n; ++i) {
+ final ActivityManager.RunningTaskInfo ti = rootTasks.get(i);
+ out.add(ti.token);
+ if (ti.topActivityType == ACTIVITY_TYPE_HOME) {
+ resizable = ti.isResizable();
+ }
+ }
+ } catch (RemoteException e) {
+ }
+ return resizable;
+ }
+
+ static void applyHomeTasksMinimized(SplitDisplayLayout layout, IWindowContainer parent) {
+ applyHomeTasksMinimized(layout, parent, null /* transaction */);
+ }
+
+ /**
+ * Assign a fixed override-bounds to home tasks that reflect their geometry while the primary
+ * split is minimized. This actually "sticks out" of the secondary split area, but when in
+ * minimized mode, the secondary split gets a 'negative' crop to expose it.
+ */
+ static boolean applyHomeTasksMinimized(SplitDisplayLayout layout, IWindowContainer parent,
+ WindowContainerTransaction t) {
+ // Resize the home/recents stacks to the larger minimized-state size
+ final Rect homeBounds;
+ final ArrayList<IWindowContainer> homeStacks = new ArrayList<>();
+ boolean isHomeResizable = getHomeAndRecentsTasks(homeStacks, parent);
+ if (isHomeResizable) {
+ homeBounds = layout.calcMinimizedHomeStackBounds();
+ } else {
+ homeBounds = new Rect(0, 0, layout.mDisplayLayout.width(),
+ layout.mDisplayLayout.height());
+ }
+ WindowContainerTransaction wct = t != null ? t : new WindowContainerTransaction();
+ for (int i = homeStacks.size() - 1; i >= 0; --i) {
+ wct.setBounds(homeStacks.get(i), homeBounds);
+ }
+ if (t != null) {
+ return isHomeResizable;
+ }
+ try {
+ ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(wct,
+ null /* organizer */);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to resize home stacks ", e);
+ }
+ return isHomeResizable;
+ }
+
+ /**
+ * Finishes entering split-screen by reparenting all FULLSCREEN tasks into the secondary split.
+ * This assumes there is already something in the primary split since that is usually what
+ * triggers a call to this. In the same transaction, this overrides the home task bounds via
+ * {@link #applyHomeTasksMinimized}.
+ *
+ * @return whether the home stack is resizable
+ */
+ static boolean applyEnterSplit(SplitScreenTaskOrganizer tiles, SplitDisplayLayout layout) {
+ try {
+ // Set launchtile first so that any stack created after
+ // getAllStackInfos and before reparent (even if unlikely) are placed
+ // correctly.
+ ActivityTaskManager.getTaskOrganizerController().setLaunchRoot(
+ DEFAULT_DISPLAY, tiles.mSecondary.token);
+ List<ActivityManager.RunningTaskInfo> rootTasks =
+ ActivityTaskManager.getTaskOrganizerController().getRootTasks(DEFAULT_DISPLAY,
+ null /* activityTypes */);
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ if (rootTasks.isEmpty()) {
+ return false;
+ }
+ for (int i = rootTasks.size() - 1; i >= 0; --i) {
+ if (rootTasks.get(i).configuration.windowConfiguration.getWindowingMode()
+ != WINDOWING_MODE_FULLSCREEN) {
+ continue;
+ }
+ wct.reparent(rootTasks.get(i).token, tiles.mSecondary.token,
+ true /* onTop */);
+ }
+ boolean isHomeResizable = applyHomeTasksMinimized(layout, null /* parent */, wct);
+ ActivityTaskManager.getTaskOrganizerController()
+ .applyContainerTransaction(wct, null /* organizer */);
+ return isHomeResizable;
+ } catch (RemoteException e) {
+ Log.w(TAG, "Error moving fullscreen tasks to secondary split: " + e);
+ }
+ return false;
+ }
+
+ private static boolean isHomeOrRecentTask(ActivityManager.RunningTaskInfo ti) {
+ final int atype = ti.configuration.windowConfiguration.getActivityType();
+ return atype == ACTIVITY_TYPE_HOME || atype == ACTIVITY_TYPE_RECENTS;
+ }
+
+ /**
+ * Reparents all tile members back to their display and resets home task override bounds.
+ * @param dismissOrMaximize When {@code true} this resolves the split by closing the primary
+ * split (thus resulting in the top of the secondary split becoming
+ * fullscreen. {@code false} resolves the other way.
+ */
+ static void applyDismissSplit(SplitScreenTaskOrganizer tiles, boolean dismissOrMaximize) {
+ try {
+ // Set launch root first so that any task created after getChildContainers and
+ // before reparent (pretty unlikely) are put into fullscreen.
+ ActivityTaskManager.getTaskOrganizerController().setLaunchRoot(Display.DEFAULT_DISPLAY,
+ null);
+ // TODO(task-org): Once task-org is more complete, consider using Appeared/Vanished
+ // plus specific APIs to clean this up.
+ List<ActivityManager.RunningTaskInfo> primaryChildren =
+ ActivityTaskManager.getTaskOrganizerController().getChildTasks(
+ tiles.mPrimary.token, null /* activityTypes */);
+ List<ActivityManager.RunningTaskInfo> secondaryChildren =
+ ActivityTaskManager.getTaskOrganizerController().getChildTasks(
+ tiles.mSecondary.token, null /* activityTypes */);
+ // In some cases (eg. non-resizable is launched), system-server will leave split-screen.
+ // as a result, the above will not capture any tasks; yet, we need to clean-up the
+ // home task bounds.
+ List<ActivityManager.RunningTaskInfo> freeHomeAndRecents =
+ ActivityTaskManager.getTaskOrganizerController().getRootTasks(
+ Display.DEFAULT_DISPLAY, HOME_AND_RECENTS);
+ if (primaryChildren.isEmpty() && secondaryChildren.isEmpty()
+ && freeHomeAndRecents.isEmpty()) {
+ return;
+ }
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ if (dismissOrMaximize) {
+ // Dismissing, so move all primary split tasks first
+ for (int i = primaryChildren.size() - 1; i >= 0; --i) {
+ wct.reparent(primaryChildren.get(i).token, null /* parent */,
+ true /* onTop */);
+ }
+ // Don't need to worry about home tasks because they are already in the "proper"
+ // order within the secondary split.
+ for (int i = secondaryChildren.size() - 1; i >= 0; --i) {
+ final ActivityManager.RunningTaskInfo ti = secondaryChildren.get(i);
+ wct.reparent(ti.token, null /* parent */, true /* onTop */);
+ if (isHomeOrRecentTask(ti)) {
+ wct.setBounds(ti.token, null);
+ }
+ }
+ } else {
+ // Maximize, so move non-home secondary split first
+ for (int i = secondaryChildren.size() - 1; i >= 0; --i) {
+ if (isHomeOrRecentTask(secondaryChildren.get(i))) {
+ continue;
+ }
+ wct.reparent(secondaryChildren.get(i).token, null /* parent */,
+ true /* onTop */);
+ }
+ // Find and place home tasks in-between. This simulates the fact that there was
+ // nothing behind the primary split's tasks.
+ for (int i = secondaryChildren.size() - 1; i >= 0; --i) {
+ final ActivityManager.RunningTaskInfo ti = secondaryChildren.get(i);
+ if (isHomeOrRecentTask(ti)) {
+ wct.reparent(ti.token, null /* parent */, true /* onTop */);
+ // reset bounds too
+ wct.setBounds(ti.token, null);
+ }
+ }
+ for (int i = primaryChildren.size() - 1; i >= 0; --i) {
+ wct.reparent(primaryChildren.get(i).token, null /* parent */,
+ true /* onTop */);
+ }
+ }
+ for (int i = freeHomeAndRecents.size() - 1; i >= 0; --i) {
+ wct.setBounds(freeHomeAndRecents.get(i).token, null);
+ }
+ ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(wct,
+ null /* organizer */);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to remove stack: " + e);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
index 4d6764e..b43fe73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -31,20 +31,17 @@
import android.util.Log;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.statusbar.dagger.StatusBarModule;
import com.android.systemui.statusbar.phone.NotificationListenerWithPlugins;
import java.util.ArrayList;
import java.util.List;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
/**
* This class handles listening to notification updates and passing them along to
* NotificationPresenter to be displayed to the user.
*/
@SuppressLint("OverrideAbstract")
-@Singleton
public class NotificationListener extends NotificationListenerWithPlugins {
private static final String TAG = "NotificationListener";
@@ -54,7 +51,9 @@
private final List<NotificationHandler> mNotificationHandlers = new ArrayList<>();
private final ArrayList<NotificationSettingsListener> mSettingsListeners = new ArrayList<>();
- @Inject
+ /**
+ * Injected constructor. See {@link StatusBarModule}.
+ */
public NotificationListener(
Context context,
NotificationManager notificationManager,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index a457266..d0af106 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -53,6 +53,7 @@
import com.android.systemui.Interpolators;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.dagger.StatusBarModule;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -73,16 +74,12 @@
import java.util.List;
import java.util.Set;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
import dagger.Lazy;
/**
* Handles tasks and state related to media notifications. For example, there is a 'current' media
* notification, which this class keeps track of.
*/
-@Singleton
public class NotificationMediaManager implements Dumpable {
private static final String TAG = "NotificationMediaManager";
public static final boolean DEBUG_MEDIA = false;
@@ -101,11 +98,10 @@
PAUSED_MEDIA_STATES.add(PlaybackState.STATE_ERROR);
}
-
private final NotificationEntryManager mEntryManager;
@Nullable
- private Lazy<NotificationShadeWindowController> mStatusBarWindowController;
+ private Lazy<NotificationShadeWindowController> mNotificationShadeWindowController;
@Nullable
private BiometricUnlockController mBiometricUnlockController;
@@ -176,11 +172,13 @@
}
};
- @Inject
+ /**
+ * Injected constructor. See {@link StatusBarModule}.
+ */
public NotificationMediaManager(
Context context,
Lazy<StatusBar> statusBarLazy,
- Lazy<NotificationShadeWindowController> statusBarWindowController,
+ Lazy<NotificationShadeWindowController> notificationShadeWindowController,
NotificationEntryManager notificationEntryManager,
MediaArtworkProcessor mediaArtworkProcessor,
KeyguardBypassController keyguardBypassController) {
@@ -194,7 +192,7 @@
Context.MEDIA_SESSION_SERVICE);
// TODO: use KeyguardStateController#isOccluded to remove this dependency
mStatusBarLazy = statusBarLazy;
- mStatusBarWindowController = statusBarWindowController;
+ mNotificationShadeWindowController = notificationShadeWindowController;
mEntryManager = notificationEntryManager;
notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
@Override
@@ -525,7 +523,8 @@
}
}
- NotificationShadeWindowController windowController = mStatusBarWindowController.get();
+ NotificationShadeWindowController windowController =
+ mNotificationShadeWindowController.get();
boolean hideBecauseOccluded = mStatusBarLazy.get().isOccluded();
final boolean hasArtwork = artworkDrawable != null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index f3783c8..ebc2fa6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -54,6 +54,7 @@
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.dagger.StatusBarModule;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -70,9 +71,6 @@
import java.util.Objects;
import java.util.Set;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
import dagger.Lazy;
/**
@@ -81,7 +79,6 @@
* interaction, keeping track of notifications to remove when NotificationPresenter is collapsed,
* and handling clicks on remote views.
*/
-@Singleton
public class NotificationRemoteInputManager implements Dumpable {
public static final boolean ENABLE_REMOTE_INPUT =
SystemProperties.getBoolean("debug.enable_remote_input", true);
@@ -257,7 +254,9 @@
}
};
- @Inject
+ /**
+ * Injected constructor. See {@link StatusBarModule}.
+ */
public NotificationRemoteInputManager(
Context context,
NotificationLockscreenUserManager lockscreenUserManager,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 37f9f88..7e70c20 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -29,6 +29,7 @@
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.dagger.StatusBarModule;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
@@ -46,9 +47,6 @@
import java.util.List;
import java.util.Stack;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
/**
* NotificationViewHierarchyManager manages updating the view hierarchy of notification views based
* on their group structure. For example, if a notification becomes bundled with another,
@@ -56,7 +54,6 @@
* tell NotificationListContainer which notifications to display, and inform it of changes to those
* notifications that might affect their display.
*/
-@Singleton
public class NotificationViewHierarchyManager implements DynamicPrivacyController.Listener {
private static final String TAG = "NotificationViewHierarchyManager";
@@ -94,8 +91,12 @@
// the problem.
private boolean mIsHandleDynamicPrivacyChangeScheduled;
- @Inject
- public NotificationViewHierarchyManager(Context context, @Main Handler mainHandler,
+ /**
+ * Injected constructor. See {@link StatusBarModule}.
+ */
+ public NotificationViewHierarchyManager(
+ Context context,
+ @Main Handler mainHandler,
NotificationLockscreenUserManager notificationLockscreenUserManager,
NotificationGroupManager groupManager,
VisualStabilityManager visualStabilityManager,
@@ -104,8 +105,7 @@
KeyguardBypassController bypassController,
BubbleController bubbleController,
DynamicPrivacyController privacyController,
- ForegroundServiceSectionController fgsSectionController
- ) {
+ ForegroundServiceSectionController fgsSectionController) {
mContext = context;
mHandler = mainHandler;
mLockscreenUserManager = notificationLockscreenUserManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ScreenRecordDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/ScreenRecordDrawable.java
new file mode 100644
index 0000000..44ef6b4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ScreenRecordDrawable.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.DrawableWrapper;
+import android.util.AttributeSet;
+
+import com.android.systemui.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/**
+ * The screen record drawable draws a colored background and either a countdown or circle to
+ * indicate that the screen is being recorded.
+ */
+public class ScreenRecordDrawable extends DrawableWrapper {
+ private Drawable mFillDrawable;
+ private int mHorizontalPadding;
+ private int mLevel;
+ private float mTextSize;
+ private float mIconRadius;
+ private Paint mPaint;
+
+ /** No-arg constructor used by drawable inflation. */
+ public ScreenRecordDrawable() {
+ super(null);
+ }
+
+ @Override
+ public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull AttributeSet attrs, @Nullable Resources.Theme theme)
+ throws XmlPullParserException, IOException {
+ super.inflate(r, parser, attrs, theme);
+ setDrawable(r.getDrawable(R.drawable.ic_screen_record_background, theme).mutate());
+ mFillDrawable = r.getDrawable(R.drawable.ic_screen_record_background, theme).mutate();
+ mHorizontalPadding = r.getDimensionPixelSize(R.dimen.status_bar_horizontal_padding);
+
+ mTextSize = r.getDimensionPixelSize(R.dimen.screenrecord_status_text_size);
+ mIconRadius = r.getDimensionPixelSize(R.dimen.screenrecord_status_icon_radius);
+ mLevel = attrs.getAttributeIntValue(null, "level", 0);
+
+ mPaint = new Paint();
+ mPaint.setTextAlign(Paint.Align.CENTER);
+ mPaint.setColor(Color.WHITE);
+ mPaint.setTextSize(mTextSize);
+ mPaint.setFakeBoldText(true);
+ }
+
+ @Override
+ public boolean canApplyTheme() {
+ return mFillDrawable.canApplyTheme() || super.canApplyTheme();
+ }
+
+ @Override
+ public void applyTheme(Resources.Theme t) {
+ super.applyTheme(t);
+ mFillDrawable.applyTheme(t);
+ }
+
+ @Override
+ protected void onBoundsChange(Rect bounds) {
+ super.onBoundsChange(bounds);
+ mFillDrawable.setBounds(bounds);
+ }
+
+ @Override
+ public boolean onLayoutDirectionChanged(int layoutDirection) {
+ mFillDrawable.setLayoutDirection(layoutDirection);
+ return super.onLayoutDirectionChanged(layoutDirection);
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ super.draw(canvas);
+ mFillDrawable.draw(canvas);
+
+ Rect b = mFillDrawable.getBounds();
+ if (mLevel > 0) {
+ String val = String.valueOf(mLevel);
+ Rect textBounds = new Rect();
+ mPaint.getTextBounds(val, 0, val.length(), textBounds);
+ float yOffset = textBounds.height() / 4; // half, and half again since it's centered
+ canvas.drawText(val, b.centerX(), b.centerY() + yOffset, mPaint);
+ } else {
+ canvas.drawCircle(b.centerX(), b.centerY() - mIconRadius / 2, mIconRadius, mPaint);
+ }
+ }
+
+ @Override
+ public boolean getPadding(Rect padding) {
+ padding.left += mHorizontalPadding;
+ padding.right += mHorizontalPadding;
+ padding.top = 0;
+ padding.bottom = 0;
+ android.util.Log.d("ScreenRecordDrawable", "set zero top/bottom pad");
+ return true;
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ super.setAlpha(alpha);
+ mFillDrawable.setAlpha(alpha);
+ }
+
+ @Override
+ public boolean setVisible(boolean visible, boolean restart) {
+ mFillDrawable.setVisible(visible, restart);
+ return super.setVisible(visible, restart);
+ }
+
+ @Override
+ public Drawable mutate() {
+ mFillDrawable.mutate();
+ return super.mutate();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
index 40f8e39..bb96f42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
@@ -21,27 +21,26 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.statusbar.dagger.StatusBarModule;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import java.util.Set;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
/**
* Handles when smart replies are added to a notification
* and clicked upon.
*/
-@Singleton
public class SmartReplyController {
private final IStatusBarService mBarService;
private final NotificationEntryManager mEntryManager;
private Set<String> mSendingKeys = new ArraySet<>();
private Callback mCallback;
- @Inject
+ /**
+ * Injected constructor. See {@link StatusBarModule}.
+ */
public SmartReplyController(NotificationEntryManager entryManager,
IStatusBarService statusBarService) {
mBarService = statusBarService;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarDependenciesModule.java
deleted file mode 100644
index 493482a..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarDependenciesModule.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar;
-
-import android.content.Context;
-
-import com.android.systemui.statusbar.notification.row.NotificationRowModule;
-import com.android.systemui.recents.OverviewProxyService;
-import com.android.systemui.tracing.ProtoTracer;
-
-import javax.inject.Singleton;
-
-import dagger.Module;
-import dagger.Provides;
-
-/**
- * Dagger Module providing common dependencies of StatusBar.
- */
-@Module(includes = {NotificationRowModule.class})
-public class StatusBarDependenciesModule {
- /**
- * Provides our instance of CommandQueue which is considered optional.
- */
- @Provides
- @Singleton
- public CommandQueue provideCommandQueue(Context context, ProtoTracer protoTracer) {
- return new CommandQueue(context, protoTracer);
- }
-
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
new file mode 100644
index 0000000..0b37c22
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.dagger;
+
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.Handler;
+
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.MediaArtworkProcessor;
+import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationViewHierarchyManager;
+import com.android.systemui.statusbar.SmartReplyController;
+import com.android.systemui.statusbar.notification.DynamicPrivacyController;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.RemoteInputUriController;
+import com.android.systemui.tracing.ProtoTracer;
+
+import javax.inject.Singleton;
+
+import dagger.Lazy;
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * This module provides instances needed to construct {@link StatusBar}. These are moved to this
+ * separate from {@link StatusBarModule} module so that components that wish to build their own
+ * version of StatusBar can include just dependencies, without injecting StatusBar itself.
+ */
+@Module
+public interface StatusBarDependenciesModule {
+ /** */
+ @Singleton
+ @Provides
+ static NotificationRemoteInputManager provideNotificationRemoteInputManager(
+ Context context,
+ NotificationLockscreenUserManager lockscreenUserManager,
+ SmartReplyController smartReplyController,
+ NotificationEntryManager notificationEntryManager,
+ Lazy<StatusBar> statusBarLazy,
+ StatusBarStateController statusBarStateController,
+ Handler mainHandler,
+ RemoteInputUriController remoteInputUriController) {
+ return new NotificationRemoteInputManager(
+ context,
+ lockscreenUserManager,
+ smartReplyController,
+ notificationEntryManager,
+ statusBarLazy,
+ statusBarStateController,
+ mainHandler,
+ remoteInputUriController);
+ }
+
+ /** */
+ @Singleton
+ @Provides
+ static NotificationMediaManager provideNotificationMediaManager(
+ Context context,
+ Lazy<StatusBar> statusBarLazy,
+ Lazy<NotificationShadeWindowController> notificationShadeWindowController,
+ NotificationEntryManager notificationEntryManager,
+ MediaArtworkProcessor mediaArtworkProcessor,
+ KeyguardBypassController keyguardBypassController) {
+ return new NotificationMediaManager(
+ context,
+ statusBarLazy,
+ notificationShadeWindowController,
+ notificationEntryManager,
+ mediaArtworkProcessor,
+ keyguardBypassController);
+ }
+
+ /** */
+ @Singleton
+ @Provides
+ static NotificationListener provideNotificationListener(
+ Context context,
+ NotificationManager notificationManager,
+ @Main Handler mainHandler) {
+ return new NotificationListener(
+ context, notificationManager, mainHandler);
+ }
+
+ /** */
+ @Singleton
+ @Provides
+ static SmartReplyController provideSmartReplyController(
+ NotificationEntryManager entryManager, IStatusBarService statusBarService) {
+ return new SmartReplyController(entryManager, statusBarService);
+ }
+
+ /** */
+ @Singleton
+ @Provides
+ static NotificationViewHierarchyManager provideNotificationViewHierarchyManager(
+ Context context,
+ @Main Handler mainHandler,
+ NotificationLockscreenUserManager notificationLockscreenUserManager,
+ NotificationGroupManager groupManager,
+ VisualStabilityManager visualStabilityManager,
+ StatusBarStateController statusBarStateController,
+ NotificationEntryManager notificationEntryManager,
+ KeyguardBypassController bypassController,
+ BubbleController bubbleController,
+ DynamicPrivacyController privacyController,
+ ForegroundServiceSectionController fgsSectionController) {
+ return new NotificationViewHierarchyManager(
+ context,
+ mainHandler,
+ notificationLockscreenUserManager,
+ groupManager,
+ visualStabilityManager,
+ statusBarStateController,
+ notificationEntryManager,
+ bypassController,
+ bubbleController,
+ privacyController,
+ fgsSectionController);
+ }
+
+ /**
+ * Provides our instance of CommandQueue which is considered optional.
+ */
+ @Provides
+ @Singleton
+ static CommandQueue provideCommandQueue(Context context, ProtoTracer protoTracer) {
+ return new CommandQueue(context, protoTracer);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.java
new file mode 100644
index 0000000..ad5ef20
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.dagger;
+
+import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
+import com.android.systemui.statusbar.notification.row.NotificationRowModule;
+import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneModule;
+
+import dagger.Module;
+
+/** */
+@Module(includes = {StatusBarPhoneModule.class, StatusBarDependenciesModule.class,
+ NotificationsModule.class, NotificationRowModule.class})
+public interface StatusBarModule {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
index 93f5805..55a20fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
@@ -51,10 +51,10 @@
import com.android.internal.messages.nano.SystemMessageProto;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.systemui.Dependency;
-import com.android.systemui.DockedStackExistsListener;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
import com.android.systemui.dagger.qualifiers.UiBackground;
+import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.NotificationChannels;
@@ -80,11 +80,13 @@
private final CommandQueue mCommandQueue;
private boolean mDockedStackExists;
private KeyguardStateController mKeyguardStateController;
+ private final Divider mDivider;
@Inject
public InstantAppNotifier(Context context, CommandQueue commandQueue,
- @UiBackground Executor uiBgExecutor) {
+ @UiBackground Executor uiBgExecutor, Divider divider) {
super(context);
+ mDivider = divider;
mCommandQueue = commandQueue;
mUiBgExecutor = uiBgExecutor;
}
@@ -103,7 +105,7 @@
mCommandQueue.addCallback(this);
mKeyguardStateController.addCallback(this);
- DockedStackExistsListener.register(
+ mDivider.registerInSplitScreenListener(
exists -> {
mDockedStackExists = exists;
updateForegroundInstantApps();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
index d0e238a..72a7e11 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
@@ -28,13 +28,10 @@
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
import com.android.systemui.statusbar.policy.HeadsUpManager;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
/** Handles heads-up and pulsing behavior driven by notification changes. */
-@Singleton
public class NotificationAlertingManager {
private static final String TAG = "NotifAlertManager";
@@ -47,7 +44,9 @@
private HeadsUpManager mHeadsUpManager;
- @Inject
+ /**
+ * Injected constructor. See {@link NotificationsModule}.
+ */
public NotificationAlertingManager(
NotificationEntryManager notificationEntryManager,
NotificationRemoteInputManager remoteInputManager,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index b0bf813..4f55e02 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -47,6 +47,7 @@
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -64,9 +65,6 @@
import java.util.Map;
import java.util.Set;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
import dagger.Lazy;
/**
@@ -94,7 +92,6 @@
* aware of
* @see #getActiveNotificationsForCurrentUser() to see every notification that the current user owns
*/
-@Singleton
public class NotificationEntryManager implements
CommonNotifCollection,
Dumpable,
@@ -181,7 +178,9 @@
}
}
- @Inject
+ /**
+ * Injected constructor. See {@link NotificationsModule}.
+ */
public NotificationEntryManager(
NotificationEntryManagerLogger logger,
NotificationGroupManager groupManager,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
index 99718ab..616c110 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
@@ -26,6 +26,7 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
@@ -33,14 +34,10 @@
import java.io.PrintWriter;
import java.util.ArrayList;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
/**
* A manager that ensures that notifications are visually stable. It will suppress reorderings
* and reorder at the right time when they are out of view.
*/
-@Singleton
public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpable {
private static final long TEMPORARY_REORDERING_ALLOWED_DURATION = 1000;
@@ -60,7 +57,9 @@
private ArraySet<View> mAddedChildren = new ArraySet<>();
private boolean mPulsing;
- @Inject
+ /**
+ * Injected constructor. See {@link NotificationsModule}.
+ */
public VisualStabilityManager(
NotificationEntryManager notificationEntryManager, @Main Handler handler) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 39f4dfa..8f8f742 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -17,15 +17,39 @@
package com.android.systemui.statusbar.notification.dagger;
import android.content.Context;
+import android.os.Handler;
+import android.view.accessibility.AccessibilityManager;
+import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dagger.qualifiers.UiBackground;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController;
+import com.android.systemui.statusbar.notification.NotificationAlertingManager;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
+import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
+import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.init.NotificationsControllerImpl;
import com.android.systemui.statusbar.notification.init.NotificationsControllerStub;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.util.leak.LeakDetector;
+
+import java.util.concurrent.Executor;
import javax.inject.Singleton;
@@ -33,9 +57,111 @@
import dagger.Module;
import dagger.Provides;
-/** Module for classes related to the notifications data pipeline */
+/**
+ * Dagger Module for classes found within the com.android.systemui.statusbar.notification package.
+ */
@Module
-public class NotificationsModule {
+public interface NotificationsModule {
+ /** Provides an instance of {@link NotificationEntryManager} */
+ @Singleton
+ @Provides
+ static NotificationEntryManager provideNotificationEntryManager(
+ NotificationEntryManagerLogger logger,
+ NotificationGroupManager groupManager,
+ NotificationRankingManager rankingManager,
+ NotificationEntryManager.KeyguardEnvironment keyguardEnvironment,
+ FeatureFlags featureFlags,
+ Lazy<NotificationRowBinder> notificationRowBinderLazy,
+ Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy,
+ LeakDetector leakDetector,
+ ForegroundServiceDismissalFeatureController fgsFeatureController) {
+ return new NotificationEntryManager(
+ logger,
+ groupManager,
+ rankingManager,
+ keyguardEnvironment,
+ featureFlags,
+ notificationRowBinderLazy,
+ notificationRemoteInputManagerLazy,
+ leakDetector,
+ fgsFeatureController);
+ }
+
+ /** Provides an instance of {@link NotificationGutsManager} */
+ @Singleton
+ @Provides
+ static NotificationGutsManager provideNotificationGutsManager(
+ Context context,
+ VisualStabilityManager visualStabilityManager,
+ Lazy<StatusBar> statusBarLazy,
+ @Main Handler mainHandler,
+ AccessibilityManager accessibilityManager,
+ HighPriorityProvider highPriorityProvider) {
+ return new NotificationGutsManager(
+ context,
+ visualStabilityManager,
+ statusBarLazy,
+ mainHandler,
+ accessibilityManager,
+ highPriorityProvider);
+ }
+
+ /** Provides an instance of {@link VisualStabilityManager} */
+ @Singleton
+ @Provides
+ static VisualStabilityManager provideVisualStabilityManager(
+ NotificationEntryManager notificationEntryManager, Handler handler) {
+ return new VisualStabilityManager(notificationEntryManager, handler);
+ }
+
+ /** Provides an instance of {@link NotificationAlertingManager} */
+ @Singleton
+ @Provides
+ static NotificationAlertingManager provideNotificationAlertingManager(
+ NotificationEntryManager notificationEntryManager,
+ NotificationRemoteInputManager remoteInputManager,
+ VisualStabilityManager visualStabilityManager,
+ StatusBarStateController statusBarStateController,
+ NotificationInterruptionStateProvider notificationInterruptionStateProvider,
+ NotificationListener notificationListener) {
+ return new NotificationAlertingManager(
+ notificationEntryManager,
+ remoteInputManager,
+ visualStabilityManager,
+ statusBarStateController,
+ notificationInterruptionStateProvider,
+ notificationListener);
+ }
+
+ /** Provides an instance of {@link NotificationLogger} */
+ @Singleton
+ @Provides
+ static NotificationLogger provideNotificationLogger(
+ NotificationListener notificationListener,
+ @UiBackground Executor uiBgExecutor,
+ NotificationEntryManager entryManager,
+ StatusBarStateController statusBarStateController,
+ NotificationLogger.ExpansionStateLogger expansionStateLogger) {
+ return new NotificationLogger(
+ notificationListener,
+ uiBgExecutor,
+ entryManager,
+ statusBarStateController,
+ expansionStateLogger);
+ }
+
+ /** Provides an instance of {@link NotificationBlockingHelperManager} */
+ @Singleton
+ @Provides
+ static NotificationBlockingHelperManager provideNotificationBlockingHelperManager(
+ Context context,
+ NotificationGutsManager notificationGutsManager,
+ NotificationEntryManager notificationEntryManager,
+ MetricsLogger metricsLogger) {
+ return new NotificationBlockingHelperManager(
+ context, notificationGutsManager, notificationEntryManager, metricsLogger);
+ }
+
/** Initializes the notification data pipeline (can be disabled via config). */
@Singleton
@Provides
@@ -55,7 +181,7 @@
*/
@Provides
@Singleton
- public CommonNotifCollection provideCommonNotifCollection(
+ static CommonNotifCollection provideCommonNotifCollection(
FeatureFlags featureFlags,
Lazy<NotifPipeline> pipeline,
NotificationEntryManager entryManager) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index 89e5f55..becb758 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -39,6 +39,7 @@
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -50,13 +51,11 @@
import java.util.concurrent.Executor;
import javax.inject.Inject;
-import javax.inject.Singleton;
/**
* Handles notification logging, in particular, logging which notifications are visible and which
* are not.
*/
-@Singleton
public class NotificationLogger implements StateListener {
private static final String TAG = "NotificationLogger";
@@ -192,7 +191,9 @@
}
}
- @Inject
+ /**
+ * Injected constructor. See {@link NotificationsModule}.
+ */
public NotificationLogger(NotificationListener notificationListener,
@UiBackground Executor uiBgExecutor,
NotificationEntryManager entryManager,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
index 7b758aa..9212325 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
@@ -16,8 +16,7 @@
package com.android.systemui.statusbar.notification.row;
-import static android.service.notification.NotificationListenerService.Ranking
- .USER_SENTIMENT_NEGATIVE;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
import android.content.Context;
import android.metrics.LogMaker;
@@ -27,29 +26,28 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.Dependency;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
import com.android.systemui.statusbar.notification.logging.NotificationCounters;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
/**
* Manager for the notification blocking helper - tracks and helps create the blocking helper
* affordance.
*/
-@Singleton
public class NotificationBlockingHelperManager {
/** Enables debug logging and always makes the blocking helper show up after a dismiss. */
private static final boolean DEBUG = false;
private static final String TAG = "BlockingHelper";
private final Context mContext;
+ private final NotificationGutsManager mNotificationGutsManager;
+ private final NotificationEntryManager mNotificationEntryManager;
+ private final MetricsLogger mMetricsLogger;
/** Row that the blocking helper will be shown in (via {@link NotificationGuts}. */
private ExpandableNotificationRow mBlockingHelperRow;
private Set<String> mNonBlockablePkgs;
@@ -60,11 +58,18 @@
*/
private boolean mIsShadeExpanded;
- private MetricsLogger mMetricsLogger = new MetricsLogger();
-
- @Inject
- public NotificationBlockingHelperManager(Context context) {
+ /**
+ * Injected constructor. See {@link NotificationsModule}.
+ */
+ public NotificationBlockingHelperManager(
+ Context context,
+ NotificationGutsManager notificationGutsManager,
+ NotificationEntryManager notificationEntryManager,
+ MetricsLogger metricsLogger) {
mContext = context;
+ mNotificationGutsManager = notificationGutsManager;
+ mNotificationEntryManager = notificationEntryManager;
+ mMetricsLogger = metricsLogger;
mNonBlockablePkgs = new HashSet<>();
Collections.addAll(mNonBlockablePkgs, mContext.getResources().getStringArray(
com.android.internal.R.array.config_nonBlockableNotificationPackages));
@@ -99,7 +104,6 @@
if (DEBUG) {
Log.d(TAG, "Manager.perhapsShowBlockingHelper: Showing new blocking helper");
}
- NotificationGutsManager manager = Dependency.get(NotificationGutsManager.class);
// Enable blocking helper on the row before moving forward so everything in the guts is
// correctly prepped.
@@ -113,10 +117,10 @@
// We don't care about the touch origin (x, y) since we're opening guts without any
// explicit user interaction.
- manager.openGuts(mBlockingHelperRow, 0, 0, menuRow.getLongpressMenuItem(mContext));
+ mNotificationGutsManager.openGuts(
+ mBlockingHelperRow, 0, 0, menuRow.getLongpressMenuItem(mContext));
- Dependency.get(MetricsLogger.class)
- .count(NotificationCounters.BLOCKING_HELPER_SHOWN, 1);
+ mMetricsLogger.count(NotificationCounters.BLOCKING_HELPER_SHOWN, 1);
return true;
}
return false;
@@ -139,8 +143,7 @@
mBlockingHelperRow.setBlockingHelperShowing(false);
if (mBlockingHelperRow.isAttachedToWindow()) {
- Dependency.get(NotificationEntryManager.class).updateNotifications(
- "dismissCurrentBlockingHelper");
+ mNotificationEntryManager.updateNotifications("dismissCurrentBlockingHelper");
}
mBlockingHelperRow = null;
return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 566da65..e3ca283 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -33,7 +33,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.ImageMessageConsumer;
-import com.android.systemui.Dependency;
import com.android.systemui.statusbar.InflationTask;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.SmartReplyController;
@@ -53,6 +52,8 @@
import javax.inject.Inject;
import javax.inject.Singleton;
+import dagger.Lazy;
+
/**
* {@link NotificationContentInflater} binds content to a {@link ExpandableNotificationRow} by
* asynchronously building the content's {@link RemoteViews} and applying it to the row.
@@ -66,13 +67,19 @@
private boolean mInflateSynchronously = false;
private final NotificationRemoteInputManager mRemoteInputManager;
private final NotifRemoteViewCache mRemoteViewCache;
+ private final Lazy<SmartReplyConstants> mSmartReplyConstants;
+ private final Lazy<SmartReplyController> mSmartReplyController;
@Inject
NotificationContentInflater(
NotifRemoteViewCache remoteViewCache,
- NotificationRemoteInputManager remoteInputManager) {
+ NotificationRemoteInputManager remoteInputManager,
+ Lazy<SmartReplyConstants> smartReplyConstants,
+ Lazy<SmartReplyController> smartReplyController) {
mRemoteViewCache = remoteViewCache;
mRemoteInputManager = remoteInputManager;
+ mSmartReplyConstants = smartReplyConstants;
+ mSmartReplyController = smartReplyController;
}
@Override
@@ -104,6 +111,8 @@
contentToBind,
mRemoteViewCache,
entry,
+ mSmartReplyConstants.get(),
+ mSmartReplyController.get(),
row,
bindParams.isLowPriority,
bindParams.isChildInGroup,
@@ -136,6 +145,7 @@
packageContext);
result = inflateSmartReplyViews(result, reInflateFlags, entry,
row.getContext(), packageContext, row.getHeadsUpManager(),
+ mSmartReplyConstants.get(), mSmartReplyController.get(),
row.getExistingSmartRepliesAndActions());
apply(
@@ -204,9 +214,8 @@
private static InflationProgress inflateSmartReplyViews(InflationProgress result,
@InflationFlag int reInflateFlags, NotificationEntry entry, Context context,
Context packageContext, HeadsUpManager headsUpManager,
+ SmartReplyConstants smartReplyConstants, SmartReplyController smartReplyController,
SmartRepliesAndActions previousSmartRepliesAndActions) {
- SmartReplyConstants smartReplyConstants = Dependency.get(SmartReplyConstants.class);
- SmartReplyController smartReplyController = Dependency.get(SmartReplyController.class);
if ((reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0 && result.newExpandedView != null) {
result.expandedInflatedSmartReplies =
InflatedSmartReplies.inflate(
@@ -643,6 +652,8 @@
private final boolean mUsesIncreasedHeadsUpHeight;
private final @InflationFlag int mReInflateFlags;
private final NotifRemoteViewCache mRemoteViewCache;
+ private final SmartReplyConstants mSmartReplyConstants;
+ private final SmartReplyController mSmartReplyController;
private ExpandableNotificationRow mRow;
private Exception mError;
private RemoteViews.OnClickHandler mRemoteViewClickHandler;
@@ -653,6 +664,8 @@
@InflationFlag int reInflateFlags,
NotifRemoteViewCache cache,
NotificationEntry entry,
+ SmartReplyConstants smartReplyConstants,
+ SmartReplyController smartReplyController,
ExpandableNotificationRow row,
boolean isLowPriority,
boolean isChildInGroup,
@@ -662,6 +675,8 @@
RemoteViews.OnClickHandler remoteViewClickHandler) {
mEntry = entry;
mRow = row;
+ mSmartReplyConstants = smartReplyConstants;
+ mSmartReplyController = smartReplyController;
mInflateSynchronously = inflateSynchronously;
mReInflateFlags = reInflateFlags;
mRemoteViewCache = cache;
@@ -701,6 +716,7 @@
mUsesIncreasedHeadsUpHeight, packageContext);
return inflateSmartReplyViews(inflationProgress, mReInflateFlags, mEntry,
mRow.getContext(), packageContext, mRow.getHeadsUpManager(),
+ mSmartReplyConstants, mSmartReplyController,
mRow.getExistingSmartRepliesAndActions());
} catch (Exception e) {
mError = e;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index a0af4ac..923c348 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -33,11 +33,9 @@
import android.app.NotificationChannelGroup;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
import android.graphics.drawable.Icon;
@@ -61,6 +59,7 @@
import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.settingslib.notification.ConversationIconFactory;
import com.android.settingslib.utils.ThreadUtils;
import com.android.systemui.Dependency;
import com.android.systemui.R;
@@ -87,6 +86,7 @@
private PackageManager mPm;
private VisualStabilityManager mVisualStabilityManager;
private ShadeController mShadeController;
+ private ConversationIconFactory mIconFactory;
private String mPackageName;
private String mAppName;
@@ -186,6 +186,7 @@
OnSettingsClickListener onSettingsClick,
OnAppSettingsClickListener onAppSettingsClick,
OnSnoozeClickListener onSnoozeClickListener,
+ ConversationIconFactory conversationIconFactory,
boolean isDeviceProvisioned) {
mSelectedAction = -1;
mINotificationManager = iNotificationManager;
@@ -203,6 +204,7 @@
mIsDeviceProvisioned = isDeviceProvisioned;
mOnSnoozeClickListener = onSnoozeClickListener;
mShadeController = Dependency.get(ShadeController.class);
+ mIconFactory = conversationIconFactory;
mShortcutManager = shortcutManager;
mLauncherApps = launcherApps;
@@ -320,8 +322,8 @@
private void bindIcon() {
ImageView image = findViewById(R.id.conversation_icon);
if (mShortcutInfo != null) {
- image.setImageDrawable(mLauncherApps.getShortcutBadgedIconDrawable(mShortcutInfo,
- mContext.getResources().getDisplayMetrics().densityDpi));
+ image.setImageBitmap(mIconFactory.getConversationBitmap(
+ mShortcutInfo, mPackageName, mAppUid));
} else {
if (mSbn.getNotification().extras.getBoolean(EXTRA_IS_GROUP_CONVERSATION, false)) {
// TODO: maybe use a generic group icon, or a composite of recent senders
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 352abcf..c01f6c4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -34,6 +34,7 @@
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.util.ArraySet;
+import android.util.IconDrawableFactory;
import android.util.Log;
import android.view.HapticFeedbackConstants;
import android.view.View;
@@ -42,8 +43,10 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
+import com.android.settingslib.notification.ConversationIconFactory;
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
+import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -56,25 +59,21 @@
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
+import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
import dagger.Lazy;
/**
* Handles various NotificationGuts related tasks, such as binding guts to a row, opening and
* closing guts, and keeping track of the currently exposed notification guts.
*/
-@Singleton
public class NotificationGutsManager implements Dumpable, NotificationLifetimeExtender {
private static final String TAG = "NotificationGutsManager";
@@ -111,7 +110,9 @@
private final Handler mMainHandler;
private Runnable mOpenRunnable;
- @Inject
+ /**
+ * Injected constructor. See {@link NotificationsModule}.
+ */
public NotificationGutsManager(Context context, VisualStabilityManager visualStabilityManager,
Lazy<StatusBar> statusBarLazy, @Main Handler mainHandler,
AccessibilityManager accessibilityManager,
@@ -388,6 +389,10 @@
notificationInfoView.closeControls(v, false);
};
}
+ ConversationIconFactory iconFactoryLoader = new ConversationIconFactory(mContext,
+ launcherApps, pmUser, IconDrawableFactory.newInstance(mContext),
+ mContext.getResources().getDimensionPixelSize(
+ R.dimen.notification_guts_conversation_icon_size));
notificationInfoView.bindNotification(
shortcutManager,
@@ -401,8 +406,8 @@
onSettingsClick,
onAppSettingsClick,
onSnoozeClickListener,
+ iconFactoryLoader,
mDeviceProvisionedController.isDeviceProvisioned());
-
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index b2b46d5..2eeda1f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -60,6 +60,7 @@
import android.util.MathUtils;
import android.util.Pair;
import android.view.ContextThemeWrapper;
+import android.view.DisplayCutout;
import android.view.InputDevice;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -524,6 +525,8 @@
private float mLastSentExpandedHeight;
private boolean mWillExpand;
+ private int mWaterfallTopInset;
+
@Inject
public NotificationStackScrollLayout(
@Named(VIEW_CONTEXT) Context context,
@@ -1739,6 +1742,12 @@
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
mBottomInset = insets.getSystemWindowInsetBottom();
+ mWaterfallTopInset = 0;
+ final DisplayCutout cutout = insets.getDisplayCutout();
+ if (cutout != null) {
+ mWaterfallTopInset = cutout.getWaterfallInsets().top;
+ }
+
if (ANCHOR_SCROLLING) {
// TODO
} else {
@@ -5335,7 +5344,9 @@
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
public int getMinExpansionHeight() {
- return mShelf.getIntrinsicHeight() - (mShelf.getIntrinsicHeight() - mStatusBarHeight) / 2;
+ return mShelf.getIntrinsicHeight()
+ - (mShelf.getIntrinsicHeight() - mStatusBarHeight + mWaterfallTopInset) / 2
+ + mWaterfallTopInset;
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java
index 3165597..971f045 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java
@@ -25,8 +25,7 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
-
-import javax.inject.Inject;
+import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneModule;
/** A controller to control all auto-hide things. */
public class AutoHideController {
@@ -51,7 +50,9 @@
}
};
- @Inject
+ /**
+ * Injected constructor. See {@link StatusBarPhoneModule}.
+ */
public AutoHideController(Context context, @Main Handler handler,
NotificationRemoteInputManager notificationRemoteInputManager,
IWindowManager iWindowManager) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index b24942a..b46ca40 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -102,6 +102,9 @@
*/
private int mCutoutSideNudge = 0;
+ private DisplayCutout mDisplayCutout;
+ private int mRoundedCornerPadding = 0;
+
public KeyguardStatusBarView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@@ -159,9 +162,15 @@
getResources().getDimensionPixelSize(R.dimen.keyguard_carrier_text_margin));
mCarrierLabel.setLayoutParams(lp);
- lp = (MarginLayoutParams) getLayoutParams();
+ updateKeyguardStatusBarHeight();
+ }
+
+ private void updateKeyguardStatusBarHeight() {
+ final int waterfallTop =
+ mDisplayCutout == null ? 0 : mDisplayCutout.getWaterfallInsets().top;
+ MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
lp.height = getResources().getDimensionPixelSize(
- R.dimen.status_bar_header_height_keyguard);
+ R.dimen.status_bar_header_height_keyguard) + waterfallTop;
setLayoutParams(lp);
}
@@ -175,6 +184,8 @@
R.dimen.display_cutout_margin_consumption);
mShowPercentAvailable = getContext().getResources().getBoolean(
com.android.internal.R.bool.config_battery_percentage_setting_available);
+ mRoundedCornerPadding = res.getDimensionPixelSize(
+ R.dimen.rounded_corner_content_padding);
}
private void updateVisibilities() {
@@ -225,23 +236,26 @@
}
private boolean updateLayoutConsideringCutout() {
- DisplayCutout dc = getRootWindowInsets().getDisplayCutout();
+ mDisplayCutout = getRootWindowInsets().getDisplayCutout();
+ updateKeyguardStatusBarHeight();
+
Pair<Integer, Integer> cornerCutoutMargins =
- PhoneStatusBarView.cornerCutoutMargins(dc, getDisplay());
- updateCornerCutoutPadding(cornerCutoutMargins);
- if (dc == null || cornerCutoutMargins != null) {
+ StatusBarWindowView.cornerCutoutMargins(mDisplayCutout, getDisplay());
+ updatePadding(cornerCutoutMargins);
+ if (mDisplayCutout == null || cornerCutoutMargins != null) {
return updateLayoutParamsNoCutout();
} else {
- return updateLayoutParamsForCutout(dc);
+ return updateLayoutParamsForCutout();
}
}
- private void updateCornerCutoutPadding(Pair<Integer, Integer> cornerCutoutMargins) {
- if (cornerCutoutMargins != null) {
- setPadding(cornerCutoutMargins.first, 0, cornerCutoutMargins.second, 0);
- } else {
- setPadding(0, 0, 0, 0);
- }
+ private void updatePadding(Pair<Integer, Integer> cornerCutoutMargins) {
+ final int waterfallTop =
+ mDisplayCutout == null ? 0 : mDisplayCutout.getWaterfallInsets().top;
+ Pair<Integer, Integer> padding =
+ StatusBarWindowView.paddingNeededForCutoutAndRoundedCorner(
+ mDisplayCutout, cornerCutoutMargins, mRoundedCornerPadding);
+ setPadding(padding.first, waterfallTop, padding.second, 0);
}
private boolean updateLayoutParamsNoCutout() {
@@ -268,7 +282,7 @@
return true;
}
- private boolean updateLayoutParamsForCutout(DisplayCutout dc) {
+ private boolean updateLayoutParamsForCutout() {
if (mLayoutState == LAYOUT_CUTOUT) {
return false;
}
@@ -279,7 +293,7 @@
}
Rect bounds = new Rect();
- boundsFromDirection(dc, Gravity.TOP, bounds);
+ boundsFromDirection(mDisplayCutout, Gravity.TOP, bounds);
mCutoutSpace.setVisibility(View.VISIBLE);
RelativeLayout.LayoutParams lp = (LayoutParams) mCutoutSpace.getLayoutParams();
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 ba9ba6c..84aecd4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -63,7 +63,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dependency;
-import com.android.systemui.DockedStackExistsListener;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.assist.AssistHandleViewController;
@@ -75,6 +74,7 @@
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.WindowManagerWrapper;
+import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NavigationBarController;
import com.android.systemui.statusbar.policy.DeadZone;
@@ -770,7 +770,14 @@
public void updatePanelSystemUiStateFlags() {
int displayId = mContext.getDisplayId();
+ if (SysUiState.DEBUG) {
+ Log.d(TAG, "Updating panel sysui state flags: panelView=" + mPanelView);
+ }
if (mPanelView != null) {
+ if (SysUiState.DEBUG) {
+ Log.d(TAG, "Updating panel sysui state flags: fullyExpanded="
+ + mPanelView.isFullyExpanded() + " inQs=" + mPanelView.isInSettings());
+ }
mSysUiFlagContainer.setFlag(SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED,
mPanelView.isFullyExpanded() && !mPanelView.isInSettings())
.setFlag(SYSUI_STATE_QUICK_SETTINGS_EXPANDED,
@@ -862,7 +869,8 @@
getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener);
- DockedStackExistsListener.register(mDockedListener);
+ Divider divider = Dependency.get(Divider.class);
+ divider.registerInSplitScreenListener(mDockedListener);
updateOrientationViews();
reloadNavIcons();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
index 826af66..67e7f23 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
@@ -73,7 +73,7 @@
public class NavigationModeController implements Dumpable {
private static final String TAG = NavigationModeController.class.getSimpleName();
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = true;
public interface ModeChangedListener {
void onNavigationModeChanged(int mode);
@@ -248,8 +248,7 @@
Secure.NAVIGATION_MODE, String.valueOf(mode));
});
if (DEBUG) {
- Log.e(TAG, "updateCurrentInteractionMode: mode=" + mMode
- + " contextUser=" + mCurrentUserContext.getUserId());
+ Log.e(TAG, "updateCurrentInteractionMode: mode=" + mMode);
dumpAssetPaths(mCurrentUserContext);
}
@@ -293,6 +292,7 @@
0 /* flags */, UserHandle.of(userId));
} catch (PackageManager.NameNotFoundException e) {
// Never happens for the sysui package
+ Log.e(TAG, "Failed to create package context", e);
return null;
}
}
@@ -404,11 +404,17 @@
defaultOverlays = "failed_to_fetch";
}
pw.println(" defaultOverlays=" + defaultOverlays);
+ pw.println(" restoreGesturalNavMode:");
+ for (int i = 0; i < mRestoreGesturalNavBarMode.size(); i++) {
+ pw.println(" userId=" + mRestoreGesturalNavBarMode.keyAt(i)
+ + " shouldRestore=" + mRestoreGesturalNavBarMode.valueAt(i));
+ }
dumpAssetPaths(mCurrentUserContext);
}
private void dumpAssetPaths(Context context) {
- Log.d(TAG, "assetPaths=");
+ Log.d(TAG, " contextUser=" + mCurrentUserContext.getUserId());
+ Log.d(TAG, " assetPaths=");
ApkAssets[] assets = context.getResources().getAssets().getApkAssets();
for (ApkAssets a : assets) {
Log.d(TAG, " " + a.getAssetPath());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
index bdca9a4..d709e02 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -36,21 +36,18 @@
import com.android.systemui.statusbar.notification.row.RowContentBindStage;
import com.android.systemui.statusbar.phone.NotificationGroupManager.NotificationGroup;
import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener;
+import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneModule;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import java.util.ArrayList;
import java.util.Objects;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
/**
* A helper class dealing with the alert interactions between {@link NotificationGroupManager} and
* {@link HeadsUpManager}. In particular, this class deals with keeping
* the correct notification in a group alerting based off the group suppression.
*/
-@Singleton
public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedListener,
StateListener {
@@ -76,7 +73,9 @@
private boolean mIsDozing;
- @Inject
+ /**
+ * Injected constructor. See {@link StatusBarPhoneModule}.
+ */
public NotificationGroupAlertTransferHelper(RowContentBindStage bindStage) {
Dependency.get(StatusBarStateController.class).addCallback(this);
mRowContentBindStage = bindStage;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java
index 7650a3a..bc80a1a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java
@@ -88,33 +88,11 @@
boolean paddingChanged = insets.top != getPaddingTop()
|| insets.bottom != getPaddingBottom();
- int rightCutout = 0;
- int leftCutout = 0;
- DisplayCutout displayCutout = getRootWindowInsets().getDisplayCutout();
- if (displayCutout != null) {
- leftCutout = displayCutout.getSafeInsetLeft();
- rightCutout = displayCutout.getSafeInsetRight();
- }
-
- int targetLeft = Math.max(insets.left, leftCutout);
- int targetRight = Math.max(insets.right, rightCutout);
-
- // Super-special right inset handling, because scrims and backdrop need to ignore it.
- if (targetRight != mRightInset || targetLeft != mLeftInset) {
- mRightInset = targetRight;
- mLeftInset = targetLeft;
- applyMargins();
- }
// Drop top inset, and pass through bottom inset.
if (paddingChanged) {
setPadding(0, 0, 0, 0);
}
} else {
- if (mRightInset != 0 || mLeftInset != 0) {
- mRightInset = 0;
- mLeftInset = 0;
- applyMargins();
- }
boolean changed = getPaddingLeft() != 0
|| getPaddingRight() != 0
|| getPaddingTop() != 0
@@ -123,6 +101,17 @@
setPadding(0, 0, 0, 0);
}
}
+
+ mLeftInset = 0;
+ mRightInset = 0;
+ DisplayCutout displayCutout = getRootWindowInsets().getDisplayCutout();
+ if (displayCutout != null) {
+ mLeftInset = displayCutout.getSafeInsetLeft();
+ mRightInset = displayCutout.getSafeInsetRight();
+ }
+ mLeftInset = Math.max(insets.left, mLeftInset);
+ mRightInset = Math.max(insets.right, mRightInset);
+ applyMargins();
return windowInsets;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 41d8968..260f94c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -43,6 +43,7 @@
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.qs.tiles.DndTile;
import com.android.systemui.qs.tiles.RotationLockTile;
+import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.BluetoothController;
import com.android.systemui.statusbar.policy.CastController;
@@ -76,7 +77,8 @@
ZenModeController.Callback,
DeviceProvisionedListener,
KeyguardStateController.Callback,
- LocationController.LocationChangeCallback {
+ LocationController.LocationChangeCallback,
+ RecordingController.RecordingStateChangeCallback {
private static final String TAG = "PhoneStatusBarPolicy";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -98,6 +100,7 @@
private final String mSlotMicrophone;
private final String mSlotCamera;
private final String mSlotSensorsOff;
+ private final String mSlotScreenRecord;
private final Context mContext;
private final Handler mHandler = new Handler();
@@ -116,6 +119,7 @@
private final LocationController mLocationController;
private final Executor mUiBgExecutor;
private final SensorPrivacyController mSensorPrivacyController;
+ private final RecordingController mRecordingController;
// Assume it's all good unless we hear otherwise. We don't always seem
// to get broadcasts that it *is* there.
@@ -149,6 +153,7 @@
mKeyguardStateController = Dependency.get(KeyguardStateController.class);
mLocationController = Dependency.get(LocationController.class);
mSensorPrivacyController = Dependency.get(SensorPrivacyController.class);
+ mRecordingController = Dependency.get(RecordingController.class);
mUiBgExecutor = uiBgExecutor;
mSlotCast = context.getString(com.android.internal.R.string.status_bar_cast);
@@ -167,6 +172,8 @@
mSlotMicrophone = context.getString(com.android.internal.R.string.status_bar_microphone);
mSlotCamera = context.getString(com.android.internal.R.string.status_bar_camera);
mSlotSensorsOff = context.getString(com.android.internal.R.string.status_bar_sensors_off);
+ mSlotScreenRecord = context.getString(
+ com.android.internal.R.string.status_bar_screen_record);
// listen for broadcasts
IntentFilter filter = new IntentFilter();
@@ -235,6 +242,10 @@
mIconController.setIconVisibility(mSlotSensorsOff,
mSensorPrivacyController.isSensorPrivacyEnabled());
+ // screen record
+ mIconController.setIcon(mSlotScreenRecord, R.drawable.stat_sys_screen_record, null);
+ mIconController.setIconVisibility(mSlotScreenRecord, false);
+
mRotationLockController.addCallback(this);
mBluetooth.addCallback(this);
mProvisionedController.addCallback(this);
@@ -246,6 +257,7 @@
mKeyguardStateController.addCallback(this);
mSensorPrivacyController.addCallback(mSensorPrivacyListener);
mLocationController.addCallback(this);
+ mRecordingController.addCallback(this);
commandQueue.addCallback(this);
}
@@ -438,7 +450,7 @@
}
if (DEBUG) Log.v(TAG, "updateCast: isCasting: " + isCasting);
mHandler.removeCallbacks(mRemoveCastIconRunnable);
- if (isCasting) {
+ if (isCasting && !mRecordingController.isRecording()) { // screen record has its own icon
mIconController.setIcon(mSlotCast, R.drawable.stat_sys_cast,
mContext.getString(R.string.accessibility_casting));
mIconController.setIconVisibility(mSlotCast, true);
@@ -643,4 +655,40 @@
mIconController.setIconVisibility(mSlotCast, false);
}
};
+
+ // Screen Recording
+ @Override
+ public void onCountdown(long millisUntilFinished) {
+ if (DEBUG) Log.d(TAG, "screenrecord: countdown " + millisUntilFinished);
+ int countdown = (int) Math.floorDiv(millisUntilFinished + 500, 1000);
+ int resourceId = R.drawable.stat_sys_screen_record;
+ switch (countdown) {
+ case 1:
+ resourceId = R.drawable.stat_sys_screen_record_1;
+ break;
+ case 2:
+ resourceId = R.drawable.stat_sys_screen_record_2;
+ break;
+ case 3:
+ resourceId = R.drawable.stat_sys_screen_record_3;
+ break;
+ }
+ mIconController.setIcon(mSlotScreenRecord, resourceId, null);
+ mIconController.setIconVisibility(mSlotScreenRecord, true);
+ }
+
+ @Override
+ public void onRecordingStart() {
+ if (DEBUG) Log.d(TAG, "screenrecord: showing icon");
+ mIconController.setIcon(mSlotScreenRecord,
+ R.drawable.stat_sys_screen_record, null);
+ mIconController.setIconVisibility(mSlotScreenRecord, true);
+ }
+
+ @Override
+ public void onRecordingEnd() {
+ // Ensure this is on the main thread, since it could be called during countdown
+ if (DEBUG) Log.d(TAG, "screenrecord: hiding icon");
+ mHandler.post(() -> mIconController.setIconVisibility(mSlotScreenRecord, false));
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index f3b0a79..156a7e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -23,12 +23,10 @@
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Configuration;
-import android.graphics.Point;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.EventLog;
import android.util.Pair;
-import android.view.Display;
import android.view.DisplayCutout;
import android.view.Gravity;
import android.view.MotionEvent;
@@ -36,7 +34,6 @@
import android.view.ViewGroup;
import android.view.WindowInsets;
import android.view.accessibility.AccessibilityEvent;
-import android.widget.FrameLayout;
import android.widget.LinearLayout;
import com.android.systemui.Dependency;
@@ -84,6 +81,8 @@
private int mCutoutSideNudge = 0;
private boolean mHeadsUpVisible;
+ private int mRoundedCornerPadding = 0;
+
public PhoneStatusBarView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -294,15 +293,25 @@
public void updateResources() {
mCutoutSideNudge = getResources().getDimensionPixelSize(
R.dimen.display_cutout_margin_consumption);
+ mRoundedCornerPadding = getResources().getDimensionPixelSize(
+ R.dimen.rounded_corner_content_padding);
+ updateStatusBarHeight();
+ }
+
+ private void updateStatusBarHeight() {
+ final int waterfallTopInset =
+ mDisplayCutout == null ? 0 : mDisplayCutout.getWaterfallInsets().top;
ViewGroup.LayoutParams layoutParams = getLayoutParams();
- layoutParams.height = getResources().getDimensionPixelSize(R.dimen.status_bar_height);
+ layoutParams.height =
+ getResources().getDimensionPixelSize(R.dimen.status_bar_height) - waterfallTopInset;
setLayoutParams(layoutParams);
}
private void updateLayoutForCutout() {
- Pair<Integer, Integer> cornerCutoutMargins = cornerCutoutMargins(mDisplayCutout,
- getDisplay());
+ updateStatusBarHeight();
+ Pair<Integer, Integer> cornerCutoutMargins =
+ StatusBarWindowView.cornerCutoutMargins(mDisplayCutout, getDisplay());
updateCutoutLocation(cornerCutoutMargins);
updateSafeInsets(cornerCutoutMargins);
}
@@ -337,47 +346,11 @@
// Depending on our rotation, we may have to work around a cutout in the middle of the view,
// or letterboxing from the right or left sides.
- FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
- if (mDisplayCutout == null || mDisplayCutout.isEmpty()
- || mLastOrientation != ORIENTATION_PORTRAIT || cornerCutoutMargins == null) {
- lp.leftMargin = 0;
- lp.rightMargin = 0;
- return;
- }
+ Pair<Integer, Integer> padding =
+ StatusBarWindowView.paddingNeededForCutoutAndRoundedCorner(
+ mDisplayCutout, cornerCutoutMargins, mRoundedCornerPadding);
- lp.leftMargin = Math.max(lp.leftMargin, cornerCutoutMargins.first);
- lp.rightMargin = Math.max(lp.rightMargin, cornerCutoutMargins.second);
-
- // If we're already inset enough (e.g. on the status bar side), we can have 0 margin
- WindowInsets insets = getRootWindowInsets();
- int leftInset = insets.getSystemWindowInsetLeft();
- int rightInset = insets.getSystemWindowInsetRight();
- if (lp.leftMargin <= leftInset) {
- lp.leftMargin = 0;
- }
- if (lp.rightMargin <= rightInset) {
- lp.rightMargin = 0;
- }
- }
-
- public static Pair<Integer, Integer> cornerCutoutMargins(DisplayCutout cutout,
- Display display) {
- if (cutout == null) {
- return null;
- }
- Point size = new Point();
- display.getRealSize(size);
-
- Rect bounds = new Rect();
- boundsFromDirection(cutout, Gravity.TOP, bounds);
-
- if (bounds.left <= 0) {
- return new Pair<>(bounds.right, 0);
- }
- if (bounds.right >= size.x) {
- return new Pair<>(0, size.x - bounds.left);
- }
- return null;
+ setPadding(padding.first, getPaddingTop(), padding.second, getPaddingBottom());
}
public void setHeadsUpVisible(boolean headsUpVisible) {
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 823adff..6298afe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -163,7 +163,6 @@
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.systemui.stackdivider.Divider;
-import com.android.systemui.stackdivider.WindowManagerProxy;
import com.android.systemui.statusbar.BackDropView;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CrossFadeHelper;
@@ -199,6 +198,7 @@
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
+import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneModule;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
@@ -597,7 +597,7 @@
* Public constructor for StatusBar.
*
* StatusBar is considered optional, and therefore can not be marked as @Inject directly.
- * Instead, an @Provide method is included.
+ * Instead, an @Provide method is included. See {@link StatusBarPhoneModule}.
*/
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
public StatusBar(
@@ -1409,8 +1409,11 @@
if (!mRecentsOptional.isPresent()) {
return false;
}
- int dockSide = WindowManagerProxy.getInstance().getDockSide();
- if (dockSide == WindowManager.DOCKED_INVALID) {
+ Divider divider = null;
+ if (mDividerOptional.isPresent()) {
+ divider = mDividerOptional.get();
+ }
+ if (divider == null || !divider.inSplitMode()) {
final int navbarPos = WindowManagerWrapper.getInstance().getNavBarPosition(mDisplayId);
if (navbarPos == NAV_BAR_POS_INVALID) {
return false;
@@ -1420,16 +1423,13 @@
: SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
return mRecentsOptional.get().splitPrimaryTask(createMode, null, metricsDockAction);
} else {
- if (mDividerOptional.isPresent()) {
- Divider divider = mDividerOptional.get();
- if (divider.isMinimized() && !divider.isHomeStackResizable()) {
- // Undocking from the minimized state is not supported
- return false;
- } else {
- divider.onUndockingTask();
- if (metricsUndockAction != -1) {
- mMetricsLogger.action(metricsUndockAction);
- }
+ if (divider.isMinimized() && !divider.isHomeStackResizable()) {
+ // Undocking from the minimized state is not supported
+ return false;
+ } else {
+ divider.onUndockingTask();
+ if (metricsUndockAction != -1) {
+ mMetricsLogger.action(metricsUndockAction);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index da5df6a..22bf513 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -16,8 +16,21 @@
package com.android.systemui.statusbar.phone;
+import static android.view.WindowInsets.Type.systemBars;
+
+import static com.android.systemui.ScreenDecorations.DisplayCutoutView.boundsFromDirection;
+
import android.content.Context;
+import android.graphics.Insets;
+import android.graphics.Point;
+import android.graphics.Rect;
import android.util.AttributeSet;
+import android.util.Pair;
+import android.view.Display;
+import android.view.DisplayCutout;
+import android.view.Gravity;
+import android.view.View;
+import android.view.WindowInsets;
import android.widget.FrameLayout;
/**
@@ -28,7 +41,118 @@
public static final String TAG = "PhoneStatusBarWindowView";
public static final boolean DEBUG = StatusBar.DEBUG;
+ private int mLeftInset = 0;
+ private int mRightInset = 0;
+ private int mTopInset = 0;
+
public StatusBarWindowView(Context context, AttributeSet attrs) {
super(context, attrs);
}
+
+ @Override
+ public WindowInsets onApplyWindowInsets(WindowInsets windowInsets) {
+ final Insets insets = windowInsets.getInsetsIgnoringVisibility(systemBars());
+ mLeftInset = 0;
+ mRightInset = 0;
+ mTopInset = 0;
+ DisplayCutout displayCutout = getRootWindowInsets().getDisplayCutout();
+ if (displayCutout != null) {
+ mTopInset = displayCutout.getWaterfallInsets().top;
+ mLeftInset = displayCutout.getSafeInsetLeft();
+ mRightInset = displayCutout.getSafeInsetRight();
+ }
+ mLeftInset = Math.max(insets.left, mLeftInset);
+ mRightInset = Math.max(insets.right, mRightInset);
+ applyMargins();
+ return windowInsets;
+ }
+
+ private void applyMargins() {
+ final int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ View child = getChildAt(i);
+ if (child.getLayoutParams() instanceof LayoutParams) {
+ LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ if (lp.rightMargin != mRightInset || lp.leftMargin != mLeftInset
+ || lp.topMargin != mTopInset) {
+ lp.rightMargin = mRightInset;
+ lp.leftMargin = mLeftInset;
+ lp.topMargin = mTopInset;
+ child.requestLayout();
+ }
+ }
+ }
+ }
+
+ /**
+ * Compute the padding needed for status bar related views, e.g., PhoneStatusBar,
+ * QuickStatusBarHeader and KeyguardStatusBarView).
+ *
+ * @param cutout
+ * @param cornerCutoutPadding
+ * @param roundedCornerContentPadding
+ * @return
+ */
+ public static Pair<Integer, Integer> paddingNeededForCutoutAndRoundedCorner(
+ DisplayCutout cutout, Pair<Integer, Integer> cornerCutoutPadding,
+ int roundedCornerContentPadding) {
+ if (cutout == null) {
+ return new Pair<>(roundedCornerContentPadding, roundedCornerContentPadding);
+ }
+
+ // compute the padding needed for corner cutout.
+ final int leftMargin = cutout.getSafeInsetLeft();
+ final int rightMargin = cutout.getSafeInsetRight();
+ int leftCornerCutoutPadding = 0;
+ int rightCornerCutoutPadding = 0;
+ if (cornerCutoutPadding != null) {
+ if (cornerCutoutPadding.first > leftMargin) {
+ leftCornerCutoutPadding = cornerCutoutPadding.first - leftMargin;
+ }
+ if (cornerCutoutPadding.second > rightMargin) {
+ rightCornerCutoutPadding = cornerCutoutPadding.second - rightMargin;
+ }
+ }
+
+ // compute the padding needed for rounded corner
+ int leftRoundedCornerPadding = 0;
+ int rightRoundedCornerPadding = 0;
+ if (roundedCornerContentPadding > leftMargin) {
+ leftRoundedCornerPadding = roundedCornerContentPadding - leftMargin;
+ }
+ if (roundedCornerContentPadding > rightMargin) {
+ rightRoundedCornerPadding = roundedCornerContentPadding - rightMargin;
+ }
+
+ return new Pair<>(
+ Math.max(leftCornerCutoutPadding, leftRoundedCornerPadding),
+ Math.max(rightCornerCutoutPadding, rightRoundedCornerPadding));
+ }
+
+ /**
+ * Compute the corner cutout margins
+ *
+ * @param cutout
+ * @param display
+ * @return
+ */
+ public static Pair<Integer, Integer> cornerCutoutMargins(DisplayCutout cutout,
+ Display display) {
+ if (cutout == null) {
+ return null;
+ }
+ Point size = new Point();
+ display.getRealSize(size);
+
+ Rect bounds = new Rect();
+ boundsFromDirection(cutout, Gravity.TOP, bounds);
+
+ if (bounds.left <= 0) {
+ return new Pair<>(bounds.right, 0);
+ }
+ if (bounds.right >= size.x) {
+ return new Pair<>(0, size.x - bounds.left);
+ }
+ return null;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneDependenciesModule.java
new file mode 100644
index 0000000..fcf698c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneDependenciesModule.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone.dagger;
+
+import android.content.Context;
+import android.os.Handler;
+import android.view.IWindowManager;
+
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.notification.row.RowContentBindStage;
+import com.android.systemui.statusbar.phone.AutoHideController;
+import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+import javax.inject.Singleton;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * This module provides instances needed to construct {@link StatusBar}. These are moved to this
+ * separate from {@link StatusBarPhoneModule} module so that components that wish to build their own
+ * version of StatusBar can include just dependencies, without injecting StatusBar itself.
+ */
+@Module
+public interface StatusBarPhoneDependenciesModule {
+ /** */
+ @Singleton
+ @Provides
+ static AutoHideController newAutoHideController(Context context,
+ @Main Handler handler,
+ NotificationRemoteInputManager notificationRemoteInputManager,
+ IWindowManager iWindowManager) {
+ return new AutoHideController(context, handler, notificationRemoteInputManager,
+ iWindowManager);
+ }
+
+ /** */
+ @Singleton
+ @Provides
+ static NotificationGroupAlertTransferHelper provideNotificationGroupAlertTransferHelper(
+ RowContentBindStage bindStage) {
+ return new NotificationGroupAlertTransferHelper(bindStage);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
similarity index 87%
rename from packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index 15a0e08..26459a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.phone;
+package com.android.systemui.statusbar.phone.dagger;
import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
@@ -52,7 +52,6 @@
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationViewHierarchyManager;
import com.android.systemui.statusbar.PulseExpansionHandler;
-import com.android.systemui.statusbar.StatusBarDependenciesModule;
import com.android.systemui.statusbar.SuperStatusBarViewFactory;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
@@ -65,7 +64,27 @@
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
+import com.android.systemui.statusbar.phone.AutoHideController;
+import com.android.systemui.statusbar.phone.BiometricUnlockController;
+import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.phone.DozeScrimController;
+import com.android.systemui.statusbar.phone.DozeServiceHost;
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
+import com.android.systemui.statusbar.phone.KeyguardLiftController;
+import com.android.systemui.statusbar.phone.LightBarController;
+import com.android.systemui.statusbar.phone.LightsOutNotifController;
+import com.android.systemui.statusbar.phone.LockscreenLockIconController;
+import com.android.systemui.statusbar.phone.LockscreenWallpaper;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.StatusBarIconController;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -91,8 +110,8 @@
/**
* Dagger Module providing {@link StatusBar}.
*/
-@Module(includes = {StatusBarDependenciesModule.class})
-public class StatusBarModule {
+@Module(includes = {StatusBarPhoneDependenciesModule.class})
+public interface StatusBarPhoneModule {
/**
* Provides our instance of StatusBar which is considered optional.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt b/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt
index 70bcc21..0487ce6 100644
--- a/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt
@@ -98,6 +98,14 @@
private val allContentBounds: MutableMap<FloatingContent, Rect> = HashMap()
/**
+ * Whether we are currently resolving conflicts by asking content to move. If we are, we'll
+ * temporarily ignore calls to [onContentMoved] - those calls are from the content that is
+ * moving to new, conflict-free bounds, so we don't need to perform conflict detection
+ * calculations in response.
+ */
+ private var currentlyResolvingConflicts = false
+
+ /**
* Makes the coordinator aware of a new piece of floating content, and moves any existing
* content out of the way, if necessary.
*
@@ -126,6 +134,13 @@
*/
@JvmOverloads
fun onContentMoved(content: FloatingContent) {
+
+ // Ignore calls when we are currently resolving conflicts, since those calls are from
+ // content that is moving to new, conflict-free bounds.
+ if (currentlyResolvingConflicts) {
+ return
+ }
+
if (!allContentBounds.containsKey(content)) {
Log.wtf(TAG, "Received onContentMoved call before onContentAdded! " +
"This should never happen.")
@@ -162,6 +177,8 @@
* them to move out of the way.
*/
private fun maybeMoveConflictingContent(fromContent: FloatingContent) {
+ currentlyResolvingConflicts = true
+
val conflictingNewBounds = allContentBounds[fromContent]!!
allContentBounds
// Filter to content that intersects with the new bounds. That's content that needs
@@ -182,6 +199,8 @@
.minus(conflictingNewBounds)))
allContentBounds[content] = content.getFloatingBoundsOnScreen()
}
+
+ currentlyResolvingConflicts = false
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
index 7dad05d..9227cf0 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
@@ -48,8 +48,8 @@
public class DisplayImeController implements DisplayController.OnDisplaysChangedListener {
private static final String TAG = "DisplayImeController";
- static final int ANIMATION_DURATION_SHOW_MS = 275;
- static final int ANIMATION_DURATION_HIDE_MS = 340;
+ public static final int ANIMATION_DURATION_SHOW_MS = 275;
+ public static final int ANIMATION_DURATION_HIDE_MS = 340;
static final Interpolator INTERPOLATOR = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
private static final int DIRECTION_NONE = 0;
private static final int DIRECTION_SHOW = 1;
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java
index 64b0b66..4652abf 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java
@@ -35,6 +35,7 @@
import android.graphics.Rect;
import android.os.SystemProperties;
import android.provider.Settings;
+import android.util.DisplayMetrics;
import android.util.RotationUtils;
import android.util.Size;
import android.view.Display;
@@ -191,6 +192,11 @@
return mDensityDpi;
}
+ /** Get the density scale for the display. */
+ public float density() {
+ return mDensityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
+ }
+
/** Get whether this layout is landscape. */
public boolean isLandscape() {
return mWidth > mHeight;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index 5706bee..79188ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -17,25 +17,18 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
import static com.android.systemui.ScreenDecorations.rectsToRegion;
-import static com.android.systemui.tuner.TunablePadding.FLAG_END;
-import static com.android.systemui.tuner.TunablePadding.FLAG_START;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.app.Fragment;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Handler;
@@ -43,21 +36,12 @@
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.Display;
-import android.view.View;
import android.view.WindowManager;
import androidx.test.filters.SmallTest;
import com.android.systemui.R.dimen;
-import com.android.systemui.ScreenDecorations.TunablePaddingTagListener;
import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.fragments.FragmentHostManager;
-import com.android.systemui.fragments.FragmentService;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
-import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.phone.StatusBarWindowView;
-import com.android.systemui.tuner.TunablePadding;
-import com.android.systemui.tuner.TunablePadding.TunablePaddingService;
import com.android.systemui.tuner.TunerService;
import org.junit.Before;
@@ -68,8 +52,6 @@
import java.util.Collections;
-import dagger.Lazy;
-
@RunWithLooper
@RunWith(AndroidTestingRunner.class)
@SmallTest
@@ -77,19 +59,12 @@
private TestableLooper mTestableLooper;
private ScreenDecorations mScreenDecorations;
- @Mock private StatusBar mStatusBar;
private WindowManager mWindowManager;
- private FragmentService mFragmentService;
- private FragmentHostManager mFragmentHostManager;
- private NotificationShadeWindowView mView;
- private StatusBarWindowView mStatusBarWindowView;
- private TunablePaddingService mTunablePaddingService;
private Handler mMainHandler;
@Mock
private TunerService mTunerService;
@Mock
private BroadcastDispatcher mBroadcastDispatcher;
- @Mock private Lazy<StatusBar> mStatusBarLazy;
@Before
public void setup() {
@@ -97,25 +72,14 @@
mTestableLooper = TestableLooper.get(this);
mMainHandler = new Handler(mTestableLooper.getLooper());
- mTunablePaddingService = mDependency.injectMockDependency(TunablePaddingService.class);
- mFragmentService = mDependency.injectMockDependency(FragmentService.class);
mWindowManager = mock(WindowManager.class);
- mView = spy(new NotificationShadeWindowView(mContext, null));
- mStatusBarWindowView = spy(new StatusBarWindowView(mContext, null));
- when(mStatusBarLazy.get()).thenReturn(mStatusBar);
- when(mStatusBar.getNotificationShadeWindowView()).thenReturn(mView);
- when(mStatusBar.getStatusBarWindow()).thenReturn(mStatusBarWindowView);
Display display = mContext.getSystemService(WindowManager.class).getDefaultDisplay();
when(mWindowManager.getDefaultDisplay()).thenReturn(display);
mContext.addMockSystemService(WindowManager.class, mWindowManager);
- mFragmentHostManager = mock(FragmentHostManager.class);
- when(mFragmentService.getFragmentHostManager(any())).thenReturn(mFragmentHostManager);
-
-
- mScreenDecorations = new ScreenDecorations(mContext, mStatusBarLazy, mMainHandler,
+ mScreenDecorations = new ScreenDecorations(mContext, mMainHandler,
mBroadcastDispatcher, mTunerService) {
@Override
public void start() {
@@ -159,8 +123,6 @@
mScreenDecorations.start();
// No views added.
verify(mWindowManager, never()).addView(any(), any());
- // No Fragments watched.
- verify(mFragmentHostManager, never()).addTagListener(any(), any());
// No Tuners tuned.
verify(mTunerService, never()).addTunable(any(), any());
}
@@ -178,12 +140,8 @@
// Add 2 windows for rounded corners (top and bottom).
verify(mWindowManager, times(2)).addView(any(), any());
- // Add 2 tag listeners for each of the fragments that are needed.
- verify(mFragmentHostManager, times(2)).addTagListener(any(), any());
// One tunable.
verify(mTunerService, times(1)).addTunable(any(), any());
- // One TunablePadding.
- verify(mTunablePaddingService, times(1)).add(any(), anyString(), anyInt(), anyInt());
}
@Test
@@ -227,29 +185,6 @@
}
@Test
- public void testPaddingTagListener() {
- TunablePaddingTagListener tagListener = new TunablePaddingTagListener(14, 5);
- View v = mock(View.class);
- View child = mock(View.class);
- Fragment f = mock(Fragment.class);
- TunablePadding padding = mock(TunablePadding.class);
-
- when(mTunablePaddingService.add(any(), anyString(), anyInt(), anyInt()))
- .thenReturn(padding);
- when(f.getView()).thenReturn(v);
- when(v.findViewById(5)).thenReturn(child);
-
- // Trigger callback and verify we get a TunablePadding created.
- tagListener.onFragmentViewCreated(null, f);
- verify(mTunablePaddingService).add(eq(child), eq(ScreenDecorations.PADDING), eq(14),
- eq(FLAG_START | FLAG_END));
-
- // Call again and verify destroy is called.
- tagListener.onFragmentViewCreated(null, f);
- verify(padding).destroy();
- }
-
- @Test
public void testUpdateRoundedCorners() {
mContext.getOrCreateTestableResources().addOverride(
com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
index 1dfe7bc..a8c438a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
@@ -28,6 +28,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -43,6 +44,7 @@
import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
+import com.android.internal.logging.MetricsLogger;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
@@ -83,13 +85,12 @@
any(NotificationMenuRowPlugin.MenuItem.class)))
.thenReturn(true);
when(mMenuRow.getLongpressMenuItem(any(Context.class))).thenReturn(mMenuItem);
- mDependency.injectTestDependency(NotificationGutsManager.class, mGutsManager);
- mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
mDependency.injectMockDependency(BubbleController.class);
mHelper = new NotificationTestHelper(mContext, mDependency);
- mBlockingHelperManager = new NotificationBlockingHelperManager(mContext);
+ mBlockingHelperManager = new NotificationBlockingHelperManager(
+ mContext, mGutsManager, mEntryManager, mock(MetricsLogger.class));
// By default, have the shade visible/expanded.
mBlockingHelperManager.setNotificationShadeExpanded(1f);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index 8a42e5f..149a95a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
@@ -50,10 +50,12 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
+import com.android.systemui.statusbar.policy.SmartReplyConstants;
import com.android.systemui.tests.R;
import org.junit.Assert;
@@ -92,9 +94,14 @@
ExpandableNotificationRow row = new NotificationTestHelper(mContext, mDependency).createRow(
mBuilder.build());
mRow = spy(row);
+
+ final SmartReplyConstants smartReplyConstants = mock(SmartReplyConstants.class);
+ final SmartReplyController smartReplyController = mock(SmartReplyController.class);
mNotificationInflater = new NotificationContentInflater(
mCache,
- mock(NotificationRemoteInputManager.class));
+ mock(NotificationRemoteInputManager.class),
+ () -> smartReplyConstants,
+ () -> smartReplyController);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
index e8de10f7..27b263f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
@@ -51,7 +51,8 @@
import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
-import android.graphics.drawable.Drawable;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Icon;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
@@ -60,12 +61,12 @@
import android.testing.TestableLooper;
import android.view.LayoutInflater;
import android.view.View;
-import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import com.android.internal.logging.MetricsLogger;
+import com.android.settingslib.notification.ConversationIconFactory;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
@@ -112,7 +113,8 @@
private NotificationEntry mBubbleEntry;
@Mock
private ShortcutInfo mShortcutInfo;
- private Drawable mImage;
+ @Mock
+ private Bitmap mImage;
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
@@ -134,6 +136,8 @@
private NotificationGuts mNotificationGuts;
@Mock
private ShadeController mShadeController;
+ @Mock
+ private ConversationIconFactory mIconFactory;
@Before
public void setUp() throws Exception {
@@ -176,9 +180,8 @@
when(mShortcutInfo.getShortLabel()).thenReturn("Convo name");
List<ShortcutInfo> shortcuts = Arrays.asList(mShortcutInfo);
when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcuts);
- mImage = mContext.getDrawable(R.drawable.ic_remove);
- when(mLauncherApps.getShortcutBadgedIconDrawable(eq(mShortcutInfo),
- anyInt())).thenReturn(mImage);
+ when(mIconFactory.getConversationBitmap(any(ShortcutInfo.class), anyString(), anyInt()))
+ .thenReturn(mImage);
mNotificationChannel = new NotificationChannel(
TEST_CHANNEL, TEST_CHANNEL_NAME, IMPORTANCE_LOW);
@@ -224,9 +227,10 @@
null,
null,
null,
+ mIconFactory,
true);
final ImageView view = mNotificationInfo.findViewById(R.id.conversation_icon);
- assertEquals(mImage, view.getDrawable());
+ assertEquals(mImage, ((BitmapDrawable) view.getDrawable()).getBitmap());
}
@Test
@@ -244,6 +248,7 @@
null,
null,
null,
+ mIconFactory,
true);
final TextView textView = mNotificationInfo.findViewById(R.id.pkg_name);
assertTrue(textView.getText().toString().contains("App Name"));
@@ -290,6 +295,7 @@
null,
null,
null,
+ mIconFactory,
true);
final TextView textView = mNotificationInfo.findViewById(R.id.group_name);
assertTrue(textView.getText().toString().contains(group.getName()));
@@ -312,6 +318,7 @@
null,
null,
null,
+ mIconFactory,
true);
final TextView textView = mNotificationInfo.findViewById(R.id.group_name);
assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
@@ -333,6 +340,7 @@
null,
null,
null,
+ mIconFactory,
true);
final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
assertEquals(GONE, nameView.getVisibility());
@@ -361,6 +369,7 @@
null,
null,
null,
+ mIconFactory,
true);
final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
assertEquals(VISIBLE, nameView.getVisibility());
@@ -385,6 +394,7 @@
},
null,
null,
+ mIconFactory,
true);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
@@ -407,6 +417,7 @@
null,
null,
null,
+ mIconFactory,
true);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertTrue(settingsButton.getVisibility() != View.VISIBLE);
@@ -430,6 +441,7 @@
},
null,
null,
+ mIconFactory,
false);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertTrue(settingsButton.getVisibility() != View.VISIBLE);
@@ -449,6 +461,7 @@
null,
null,
null,
+ mIconFactory,
true);
View bubbleView = mNotificationInfo.findViewById(R.id.bubble);
@@ -469,6 +482,7 @@
null,
null,
null,
+ mIconFactory,
true);
View bubbleView = mNotificationInfo.findViewById(R.id.bubble);
@@ -491,9 +505,9 @@
null,
null,
null,
+ mIconFactory,
true);
-
// Promote it
mNotificationInfo.findViewById(R.id.home).performClick();
mTestableLooper.processAllMessages();
@@ -522,9 +536,9 @@
(View v, int hours) -> {
latch.countDown();
},
+ mIconFactory,
true);
-
// Promote it
mNotificationInfo.findViewById(R.id.snooze).performClick();
mTestableLooper.processAllMessages();
@@ -551,6 +565,7 @@
null,
null,
null,
+ mIconFactory,
true);
assertFalse(mBubbleEntry.isBubble());
@@ -583,6 +598,7 @@
null,
null,
null,
+ mIconFactory,
true);
assertTrue(mBubbleEntry.isBubble());
@@ -613,6 +629,7 @@
null,
null,
null,
+ mIconFactory,
true);
assertFalse(mBubbleEntry.isBubble());
@@ -641,9 +658,9 @@
null,
null,
null,
+ mIconFactory,
true);
-
ImageButton fave = mNotificationInfo.findViewById(R.id.fave);
assertEquals(mContext.getString(R.string.notification_conversation_unfavorite),
fave.getContentDescription().toString());
@@ -675,6 +692,7 @@
null,
null,
null,
+ mIconFactory,
true);
ImageButton fave = mNotificationInfo.findViewById(R.id.fave);
@@ -708,6 +726,7 @@
null,
null,
null,
+ mIconFactory,
true);
ImageButton mute = mNotificationInfo.findViewById(R.id.mute);
@@ -743,9 +762,9 @@
null,
null,
null,
+ mIconFactory,
true);
-
ImageButton mute = mNotificationInfo.findViewById(R.id.mute);
assertEquals(mContext.getString(R.string.notification_conversation_mute),
mute.getContentDescription().toString());
@@ -774,7 +793,9 @@
null,
null,
null,
+ mIconFactory,
true);
+
verify(mMockINotificationManager, times(1)).createConversationNotificationChannelForPackage(
anyString(), anyInt(), anyString(), any(), eq(CONVERSATION_ID));
}
@@ -794,7 +815,9 @@
null,
null,
null,
+ mIconFactory,
true);
+
verify(mMockINotificationManager, never()).createConversationNotificationChannelForPackage(
anyString(), anyInt(), anyString(), any(), eq(CONVERSATION_ID));
}
@@ -815,6 +838,7 @@
null,
null,
null,
+ mIconFactory,
true);
mNotificationInfo.findViewById(R.id.mute).performClick();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 7a1bd05..9a52ee8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -60,6 +60,7 @@
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
+import com.android.systemui.statusbar.policy.SmartReplyConstants;
import com.android.systemui.tests.R;
import org.mockito.ArgumentCaptor;
@@ -98,7 +99,6 @@
dependency.injectMockDependency(NotificationMediaManager.class);
dependency.injectMockDependency(BubbleController.class);
dependency.injectMockDependency(NotificationShadeWindowController.class);
- dependency.injectMockDependency(SmartReplyController.class);
mStatusBarStateController = mock(StatusBarStateController.class);
mGroupManager = new NotificationGroupManager(mStatusBarStateController);
mHeadsUpManager = new HeadsUpManagerPhone(mContext, mStatusBarStateController,
@@ -106,10 +106,11 @@
mHeadsUpManager.setUp(null, mGroupManager, null, null);
mGroupManager.setHeadsUpManager(mHeadsUpManager);
-
NotificationContentInflater contentBinder = new NotificationContentInflater(
mock(NotifRemoteViewCache.class),
- mock(NotificationRemoteInputManager.class));
+ mock(NotificationRemoteInputManager.class),
+ () -> mock(SmartReplyConstants.class),
+ () -> mock(SmartReplyController.class));
contentBinder.setInflateSynchronously(true);
mBindStage = new RowContentBindStage(contentBinder,
mock(NotifInflationErrorManager.class),
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index 63dd99e..eda3fb9 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -248,6 +248,10 @@
// Package: android
NOTE_LOCATION_CHANGED = 59;
+ // Notify user that a SIM is required to connect to Wifi network
+ // Package: android
+ NOTE_ID_WIFI_SIM_REQUIRED = 60;
+
// ADD_NEW_IDS_ABOVE_THIS_LINE
// Legacy IDs with arbitrary values appear below
// Legacy IDs existed as stable non-conflicting constants prior to the O release
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 4474f60..872f0eb 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -119,6 +119,9 @@
private final LocalLog mUiLatencyHistory;
private final LocalLog mWtfHistory;
private final FieldClassificationStrategy mFieldClassificationStrategy;
+
+ @GuardedBy("mLock")
+ @Nullable
private RemoteInlineSuggestionRenderService mRemoteInlineSuggestionRenderService;
/**
@@ -236,17 +239,8 @@
sendStateToClients(/* resetClient= */ false);
}
updateRemoteAugmentedAutofillService();
+ updateRemoteInlineSuggestionRenderServiceLocked();
- final ComponentName componentName = RemoteInlineSuggestionRenderService
- .getServiceComponentName(getContext(), mUserId);
- if (componentName != null) {
- mRemoteInlineSuggestionRenderService = new RemoteInlineSuggestionRenderService(
- getContext(), componentName, InlineSuggestionRenderService.SERVICE_INTERFACE,
- mUserId, new InlineSuggestionRenderCallbacksImpl(),
- mMaster.isBindInstantServiceAllowed(), mMaster.verbose);
- } else {
- mRemoteInlineSuggestionRenderService = null;
- }
return enabledChanged;
}
@@ -1644,7 +1638,29 @@
return mFieldClassificationStrategy.getDefaultAlgorithm();
}
- RemoteInlineSuggestionRenderService getRemoteInlineSuggestionRenderService() {
+ private void updateRemoteInlineSuggestionRenderServiceLocked() {
+ if (mRemoteInlineSuggestionRenderService != null) {
+ if (sVerbose) {
+ Slog.v(TAG, "updateRemoteInlineSuggestionRenderService(): "
+ + "destroying old remote service");
+ }
+ mRemoteInlineSuggestionRenderService = null;
+ }
+
+ mRemoteInlineSuggestionRenderService = getRemoteInlineSuggestionRenderServiceLocked();
+ }
+
+ RemoteInlineSuggestionRenderService getRemoteInlineSuggestionRenderServiceLocked() {
+ final ComponentName componentName = RemoteInlineSuggestionRenderService
+ .getServiceComponentName(getContext(), mUserId);
+
+ if (mRemoteInlineSuggestionRenderService == null) {
+ mRemoteInlineSuggestionRenderService = new RemoteInlineSuggestionRenderService(
+ getContext(), componentName, InlineSuggestionRenderService.SERVICE_INTERFACE,
+ mUserId, new InlineSuggestionRenderCallbacksImpl(),
+ mMaster.isBindInstantServiceAllowed(), mMaster.verbose);
+ }
+
return mRemoteInlineSuggestionRenderService;
}
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index d93ac68..1eb7692 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -146,7 +146,8 @@
@Nullable AutofillValue focusedValue,
@Nullable InlineSuggestionsRequest inlineSuggestionsRequest,
@Nullable IInlineSuggestionsResponseCallback inlineSuggestionsCallback,
- @NonNull Runnable onErrorCallback) {
+ @NonNull Runnable onErrorCallback,
+ @NonNull RemoteInlineSuggestionRenderService remoteRenderService) {
long requestTime = SystemClock.elapsedRealtime();
AtomicReference<ICancellationSignal> cancellationRef = new AtomicReference<>();
@@ -168,7 +169,7 @@
maybeRequestShowInlineSuggestions(sessionId,
inlineSuggestionsRequest, inlineSuggestionsData,
focusedId, inlineSuggestionsCallback, client,
- onErrorCallback);
+ onErrorCallback, remoteRenderService);
requestAutofill.complete(null);
}
@@ -234,7 +235,8 @@
@Nullable InlineSuggestionsRequest request, @Nullable Dataset[] inlineSuggestionsData,
@NonNull AutofillId focusedId,
@Nullable IInlineSuggestionsResponseCallback inlineSuggestionsCallback,
- @NonNull IAutoFillManagerClient client, @NonNull Runnable onErrorCallback) {
+ @NonNull IAutoFillManagerClient client, @NonNull Runnable onErrorCallback,
+ @NonNull RemoteInlineSuggestionRenderService remoteRenderService) {
if (ArrayUtils.isEmpty(inlineSuggestionsData) || inlineSuggestionsCallback == null
|| request == null) {
return;
@@ -254,7 +256,7 @@
} catch (RemoteException e) {
Slog.w(TAG, "Encounter exception autofilling the values");
}
- }, onErrorCallback));
+ }, onErrorCallback, remoteRenderService));
} catch (RemoteException e) {
Slog.w(TAG, "Exception sending inline suggestions response back to IME.");
}
diff --git a/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java b/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java
index 31dc23f..5d5af53 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java
@@ -36,7 +36,10 @@
import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
-final class RemoteInlineSuggestionRenderService extends
+/**
+ * Remote service to help connect to InlineSuggestionRenderService in ExtServices.
+ */
+public final class RemoteInlineSuggestionRenderService extends
AbstractMultiplePendingRequestsRemoteService<RemoteInlineSuggestionRenderService,
IInlineSuggestionRenderService> {
@@ -81,9 +84,11 @@
* Called by {@link Session} to generate a call to the
* {@link RemoteInlineSuggestionRenderService} to request rendering a slice .
*/
- void renderSuggestion(@NonNull IInlineSuggestionUiCallback callback,
- @NonNull InlinePresentation presentation, int width, int height) {
- scheduleAsyncRequest((s) -> s.renderSuggestion(callback, presentation, width, height));
+ public void renderSuggestion(@NonNull IInlineSuggestionUiCallback callback,
+ @NonNull InlinePresentation presentation, int width, int height,
+ @Nullable IBinder hostInputToken) {
+ scheduleAsyncRequest(
+ (s) -> s.renderSuggestion(callback, presentation, width, height, hostInputToken));
}
@Nullable
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 72029d1..697e5d7 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -1018,7 +1018,8 @@
// FillServiceCallbacks
@Override
- public void authenticate(int requestId, int datasetIndex, IntentSender intent, Bundle extras) {
+ public void authenticate(int requestId, int datasetIndex, IntentSender intent, Bundle extras,
+ boolean authenticateInline) {
if (sDebug) {
Slog.d(TAG, "authenticate(): requestId=" + requestId + "; datasetIdx=" + datasetIndex
+ "; intentSender=" + intent);
@@ -1042,7 +1043,7 @@
final int authenticationId = AutofillManager.makeAuthenticationId(requestId, datasetIndex);
mHandler.sendMessage(obtainMessage(
Session::startAuthentication,
- this, authenticationId, intent, fillInIntent));
+ this, authenticationId, intent, fillInIntent, authenticateInline));
}
// VultureCallback
@@ -2623,8 +2624,6 @@
mService.getServicePackageName(), mComponentName,
serviceLabel, serviceIcon, this, id, mCompatMode);
- mService.logDatasetShown(id, mClientState);
-
synchronized (mLock) {
if (mUiShownTime == 0) {
// Log first time UI is shown.
@@ -2656,10 +2655,6 @@
private boolean requestShowInlineSuggestionsLocked(@NonNull FillResponse response,
@Nullable String filterText) {
final List<Dataset> datasets = response.getDatasets();
- if (datasets == null) {
- Log.w(TAG, "response returned null datasets");
- return false;
- }
final InlineSuggestionSession.ImeResponse imeResponse =
mInlineSuggestionSession.waitAndGetImeResponse();
@@ -2671,13 +2666,12 @@
final InlineSuggestionsRequest request = imeResponse.getRequest();
InlineSuggestionsResponse inlineSuggestionsResponse =
InlineSuggestionFactory.createInlineSuggestionsResponse(request,
- response.getRequestId(),
- datasets.toArray(new Dataset[]{}), filterText, response.getInlineActions(),
- mCurrentViewId, mContext, this, () -> {
+ response, filterText, response.getInlineActions(), mCurrentViewId, mContext,
+ this, () -> {
synchronized (mLock) {
requestHideFillUi(mCurrentViewId);
}
- });
+ }, mService.getRemoteInlineSuggestionRenderServiceLocked());
try {
imeResponse.getCallback().onInlineSuggestionsResponse(inlineSuggestionsResponse);
} catch (RemoteException e) {
@@ -2981,7 +2975,7 @@
synchronized (mLock) {
cancelAugmentedAutofillLocked();
}
- });
+ }, mService.getRemoteInlineSuggestionRenderServiceLocked());
if (mAugmentedAutofillDestroyer == null) {
mAugmentedAutofillDestroyer = () -> remoteService.onDestroyAutofillWindowsRequest();
@@ -3160,7 +3154,8 @@
}
final int authenticationId = AutofillManager.makeAuthenticationId(requestId,
datasetIndex);
- startAuthentication(authenticationId, dataset.getAuthentication(), fillInIntent);
+ startAuthentication(authenticationId, dataset.getAuthentication(), fillInIntent,
+ /* authenticateInline= */false);
}
}
@@ -3184,10 +3179,11 @@
}
private void startAuthentication(int authenticationId, IntentSender intent,
- Intent fillInIntent) {
+ Intent fillInIntent, boolean authenticateInline) {
try {
synchronized (mLock) {
- mClient.authenticate(id, authenticationId, intent, fillInIntent);
+ mClient.authenticate(id, authenticationId, intent, fillInIntent,
+ authenticateInline);
}
} catch (RemoteException e) {
Slog.e(TAG, "Error launching auth intent", e);
diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
index 26bb7c3..71c3c16 100644
--- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
@@ -79,7 +79,7 @@
public interface AutoFillUiCallback {
void authenticate(int requestId, int datasetIndex, @NonNull IntentSender intent,
- @Nullable Bundle extras);
+ @Nullable Bundle extras, boolean authenticateInline);
void fill(int requestId, int datasetIndex, @NonNull Dataset dataset);
void save();
void cancelSave();
@@ -217,7 +217,8 @@
if (mCallback != null) {
mCallback.authenticate(response.getRequestId(),
AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED,
- response.getAuthentication(), response.getClientState());
+ response.getAuthentication(), response.getClientState(),
+ /* authenticateInline= */ false);
}
}
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
index 5f6e47b..a8886fc 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
@@ -22,14 +22,17 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.os.IBinder;
import android.os.RemoteException;
import android.service.autofill.Dataset;
+import android.service.autofill.FillResponse;
+import android.service.autofill.IInlineSuggestionUiCallback;
import android.service.autofill.InlinePresentation;
import android.text.TextUtils;
import android.util.Slog;
import android.view.SurfaceControl;
-import android.view.View;
import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillValue;
import android.view.inline.InlinePresentationSpec;
import android.view.inputmethod.InlineSuggestion;
@@ -40,11 +43,14 @@
import com.android.internal.view.inline.IInlineContentCallback;
import com.android.internal.view.inline.IInlineContentProvider;
+import com.android.server.LocalServices;
import com.android.server.UiThread;
+import com.android.server.autofill.RemoteInlineSuggestionRenderService;
+import com.android.server.wm.WindowManagerInternal;
import java.util.ArrayList;
import java.util.List;
-import java.util.function.BiFunction;
+import java.util.function.BiConsumer;
import java.util.regex.Pattern;
public final class InlineSuggestionFactory {
@@ -61,8 +67,42 @@
}
/**
- * Creates an {@link InlineSuggestionsResponse} with the {@code datasets} provided by
- * augmented autofill service.
+ * Creates an {@link InlineSuggestionsResponse} with the {@code datasets} provided by the
+ * autofill service, potentially filtering the datasets.
+ */
+ public static InlineSuggestionsResponse createInlineSuggestionsResponse(
+ @NonNull InlineSuggestionsRequest request, @NonNull FillResponse response,
+ @Nullable String filterText,
+ @Nullable List<InlinePresentation> inlineActions,
+ @NonNull AutofillId autofillId,
+ @NonNull Context context,
+ @NonNull AutoFillUI.AutoFillUiCallback client,
+ @NonNull Runnable onErrorCallback,
+ @Nullable RemoteInlineSuggestionRenderService remoteRenderService) {
+ if (sDebug) Slog.d(TAG, "createInlineSuggestionsResponse called");
+ final BiConsumer<Dataset, Integer> onClickFactory;
+ if (response.getAuthentication() != null) {
+ onClickFactory = (dataset, datasetIndex) -> client.authenticate(response.getRequestId(),
+ datasetIndex, response.getAuthentication(), response.getClientState(),
+ /* authenticateInline= */ true);
+ } else {
+ onClickFactory = (dataset, datasetIndex) ->
+ client.fill(response.getRequestId(), datasetIndex, dataset);
+ }
+
+ final List<Dataset> datasetList = response.getDatasets();
+ final Dataset[] datasets = datasetList == null
+ ? null
+ : datasetList.toArray(new Dataset[]{});
+
+ return createInlineSuggestionsResponseInternal(/* isAugmented= */ false, request, response,
+ datasets, filterText, inlineActions, autofillId, context, onErrorCallback,
+ onClickFactory, remoteRenderService);
+ }
+
+ /**
+ * Creates an {@link InlineSuggestionsResponse} with the {@code datasets} provided by augmented
+ * autofill service.
*/
public static InlineSuggestionsResponse createAugmentedInlineSuggestionsResponse(
@NonNull InlineSuggestionsRequest request,
@@ -70,44 +110,42 @@
@NonNull AutofillId autofillId,
@NonNull Context context,
@NonNull InlineSuggestionUiCallback inlineSuggestionUiCallback,
- @NonNull Runnable onErrorCallback) {
+ @NonNull Runnable onErrorCallback,
+ @Nullable RemoteInlineSuggestionRenderService remoteRenderService) {
if (sDebug) Slog.d(TAG, "createAugmentedInlineSuggestionsResponse called");
return createInlineSuggestionsResponseInternal(/* isAugmented= */ true, request,
- datasets, /* filterText= */ null, /* inlineActions= */ null, autofillId, context,
- onErrorCallback,
- (dataset, filedIndex) -> (v -> inlineSuggestionUiCallback.autofill(dataset)));
- }
-
- /**
- * Creates an {@link InlineSuggestionsResponse} with the {@code datasets} provided by the
- * autofill service, potentially filtering the datasets.
- */
- public static InlineSuggestionsResponse createInlineSuggestionsResponse(
- @NonNull InlineSuggestionsRequest request, int requestId,
- @NonNull Dataset[] datasets,
- @Nullable String filterText,
- @Nullable List<InlinePresentation> inlineActions,
- @NonNull AutofillId autofillId,
- @NonNull Context context,
- @NonNull AutoFillUI.AutoFillUiCallback client,
- @NonNull Runnable onErrorCallback) {
- if (sDebug) Slog.d(TAG, "createInlineSuggestionsResponse called");
- return createInlineSuggestionsResponseInternal(/* isAugmented= */ false, request, datasets,
- filterText, inlineActions, autofillId, context, onErrorCallback,
- (dataset, filedIndex) -> (v -> client.fill(requestId, filedIndex, dataset)));
+ /* fillResponse= */ null, datasets, /* filterText= */ null,
+ /* inlineActions= */ null, autofillId, context, onErrorCallback,
+ (dataset, fieldIndex) ->
+ inlineSuggestionUiCallback.autofill(dataset), remoteRenderService);
}
private static InlineSuggestionsResponse createInlineSuggestionsResponseInternal(
boolean isAugmented, @NonNull InlineSuggestionsRequest request,
- @NonNull Dataset[] datasets, @Nullable String filterText,
+ @Nullable FillResponse response, @Nullable Dataset[] datasets,
+ @Nullable String filterText,
@Nullable List<InlinePresentation> inlineActions, @NonNull AutofillId autofillId,
@NonNull Context context, @NonNull Runnable onErrorCallback,
- @NonNull BiFunction<Dataset, Integer, View.OnClickListener> onClickListenerFactory) {
+ @NonNull BiConsumer<Dataset, Integer> onClickFactory,
+ @Nullable RemoteInlineSuggestionRenderService remoteRenderService) {
+
final ArrayList<InlineSuggestion> inlineSuggestions = new ArrayList<>();
- final InlineSuggestionUi inlineSuggestionUi = new InlineSuggestionUi(context,
- onErrorCallback);
- for (int i = 0; i < datasets.length; i++) {
- final Dataset dataset = datasets[i];
+ if (response.getAuthentication() != null) {
+ InlineSuggestion inlineAuthSuggestion = createInlineAuthSuggestion(response,
+ remoteRenderService, onClickFactory, onErrorCallback,
+ request.getHostInputToken());
+ inlineSuggestions.add(inlineAuthSuggestion);
+
+ return new InlineSuggestionsResponse(inlineSuggestions);
+ }
+
+ if (datasets == null) {
+ Slog.w(TAG, "Datasets should not be null here");
+ return null;
+ }
+
+ for (int datasetIndex = 0; datasetIndex < datasets.length; datasetIndex++) {
+ final Dataset dataset = datasets[datasetIndex];
final int fieldIndex = dataset.getFieldIds().indexOf(autofillId);
if (fieldIndex < 0) {
Slog.w(TAG, "AutofillId=" + autofillId + " not found in dataset");
@@ -123,15 +161,17 @@
continue;
}
InlineSuggestion inlineSuggestion = createInlineSuggestion(isAugmented, dataset,
- fieldIndex, mergedInlinePresentation(request, i, inlinePresentation),
- inlineSuggestionUi, onClickListenerFactory);
+ fieldIndex, mergedInlinePresentation(request, datasetIndex, inlinePresentation),
+ onClickFactory, remoteRenderService, onErrorCallback,
+ request.getHostInputToken());
+
inlineSuggestions.add(inlineSuggestion);
}
if (inlineActions != null) {
for (InlinePresentation inlinePresentation : inlineActions) {
final InlineSuggestion inlineAction = createInlineAction(isAugmented, context,
mergedInlinePresentation(request, 0, inlinePresentation),
- inlineSuggestionUi);
+ remoteRenderService, onErrorCallback, request.getHostInputToken());
inlineSuggestions.add(inlineAction);
}
}
@@ -173,43 +213,59 @@
private static InlineSuggestion createInlineAction(boolean isAugmented,
@NonNull Context context,
@NonNull InlinePresentation inlinePresentation,
- @NonNull InlineSuggestionUi inlineSuggestionUi) {
+ @Nullable RemoteInlineSuggestionRenderService remoteRenderService,
+ @NonNull Runnable onErrorCallback, @Nullable IBinder hostInputToken) {
// TODO(b/146453195): fill in the autofill hint properly.
final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
inlinePresentation.getInlinePresentationSpec(),
isAugmented ? InlineSuggestionInfo.SOURCE_PLATFORM
: InlineSuggestionInfo.SOURCE_AUTOFILL, new String[]{""},
InlineSuggestionInfo.TYPE_ACTION);
- final View.OnClickListener onClickListener = v -> {
- // TODO(b/148567875): Launch the intent provided through the slice. This
- // should be part of the UI renderer therefore will be moved to the support
- // library.
+ final Runnable onClickAction = () -> {
Toast.makeText(context, "icon clicked", Toast.LENGTH_SHORT).show();
};
return new InlineSuggestion(inlineSuggestionInfo,
- createInlineContentProvider(inlinePresentation, inlineSuggestionUi,
- onClickListener));
+ createInlineContentProvider(inlinePresentation, onClickAction, onErrorCallback,
+ remoteRenderService, hostInputToken));
}
private static InlineSuggestion createInlineSuggestion(boolean isAugmented,
- @NonNull Dataset dataset,
- int fieldIndex, @NonNull InlinePresentation inlinePresentation,
- @NonNull InlineSuggestionUi inlineSuggestionUi,
- @NonNull BiFunction<Dataset, Integer, View.OnClickListener> onClickListenerFactory) {
+ @NonNull Dataset dataset, int datasetIndex,
+ @NonNull InlinePresentation inlinePresentation,
+ @NonNull BiConsumer<Dataset, Integer> onClickFactory,
+ @NonNull RemoteInlineSuggestionRenderService remoteRenderService,
+ @NonNull Runnable onErrorCallback, @Nullable IBinder hostInputToken) {
// TODO(b/146453195): fill in the autofill hint properly.
final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
inlinePresentation.getInlinePresentationSpec(),
isAugmented ? InlineSuggestionInfo.SOURCE_PLATFORM
: InlineSuggestionInfo.SOURCE_AUTOFILL, new String[]{""},
InlineSuggestionInfo.TYPE_SUGGESTION);
- final View.OnClickListener onClickListener = onClickListenerFactory.apply(dataset,
- fieldIndex);
+
final InlineSuggestion inlineSuggestion = new InlineSuggestion(inlineSuggestionInfo,
- createInlineContentProvider(inlinePresentation, inlineSuggestionUi,
- onClickListener));
+ createInlineContentProvider(inlinePresentation,
+ () -> onClickFactory.accept(dataset, datasetIndex), onErrorCallback,
+ remoteRenderService, hostInputToken));
+
return inlineSuggestion;
}
+ private static InlineSuggestion createInlineAuthSuggestion(@NonNull FillResponse response,
+ @NonNull RemoteInlineSuggestionRenderService remoteRenderService,
+ @NonNull BiConsumer<Dataset, Integer> onClickFactory, @NonNull Runnable onErrorCallback,
+ @Nullable IBinder hostInputToken) {
+ final InlinePresentation inlinePresentation = response.getInlinePresentation();
+ final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
+ inlinePresentation.getInlinePresentationSpec(),
+ InlineSuggestionInfo.SOURCE_AUTOFILL, null, InlineSuggestionInfo.TYPE_SUGGESTION);
+
+ return new InlineSuggestion(inlineSuggestionInfo,
+ createInlineContentProvider(inlinePresentation,
+ () -> onClickFactory.accept(null,
+ AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED),
+ onErrorCallback, remoteRenderService, hostInputToken));
+ }
+
/**
* Returns an {@link InlinePresentation} with the style spec from the request/host, and
* everything else from the provided {@code inlinePresentation}.
@@ -231,27 +287,64 @@
}
private static IInlineContentProvider.Stub createInlineContentProvider(
- @NonNull InlinePresentation inlinePresentation,
- @NonNull InlineSuggestionUi inlineSuggestionUi,
- @Nullable View.OnClickListener onClickListener) {
+ @NonNull InlinePresentation inlinePresentation, @Nullable Runnable onClickAction,
+ @NonNull Runnable onErrorCallback,
+ @Nullable RemoteInlineSuggestionRenderService remoteRenderService,
+ @Nullable IBinder hostInputToken) {
return new IInlineContentProvider.Stub() {
@Override
- public void provideContent(int width, int height,
- IInlineContentCallback callback) {
+ public void provideContent(int width, int height, IInlineContentCallback callback) {
UiThread.getHandler().post(() -> {
- SurfaceControl sc = inlineSuggestionUi.inflate(inlinePresentation, width,
- height,
- onClickListener);
- try {
- callback.onContent(sc);
- } catch (RemoteException e) {
- Slog.w(TAG, "Encounter exception calling back with inline content.");
+ final IInlineSuggestionUiCallback uiCallback = createInlineSuggestionUiCallback(
+ callback, onClickAction, onErrorCallback);
+
+ if (remoteRenderService == null) {
+ Slog.e(TAG, "RemoteInlineSuggestionRenderService is null");
+ return;
}
+
+ remoteRenderService.renderSuggestion(uiCallback, inlinePresentation,
+ width, height, hostInputToken);
});
}
};
}
+ private static IInlineSuggestionUiCallback.Stub createInlineSuggestionUiCallback(
+ @NonNull IInlineContentCallback callback, @NonNull Runnable onAutofillCallback,
+ @NonNull Runnable onErrorCallback) {
+ return new IInlineSuggestionUiCallback.Stub() {
+ @Override
+ public void onAutofill() throws RemoteException {
+ onAutofillCallback.run();
+ }
+
+ @Override
+ public void onContent(SurfaceControl surface)
+ throws RemoteException {
+ callback.onContent(surface);
+ }
+
+ @Override
+ public void onError() throws RemoteException {
+ onErrorCallback.run();
+ }
+
+ @Override
+ public void onTransferTouchFocusToImeWindow(IBinder sourceInputToken, int displayId)
+ throws RemoteException {
+ //TODO(b/149574510): Move logic to IMMS
+ final WindowManagerInternal windowManagerInternal = LocalServices.getService(
+ WindowManagerInternal.class);
+ if (!windowManagerInternal.transferTouchFocusToImeWindow(sourceInputToken,
+ displayId)) {
+ Slog.e(TAG, "Cannot transfer touch focus from suggestion to IME");
+ onErrorCallback.run();
+ }
+ }
+ };
+ }
+
private InlineSuggestionFactory() {
}
-}
+}
\ No newline at end of file
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionUi.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionUi.java
deleted file mode 100644
index bf148a6..0000000
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionUi.java
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.autofill.ui;
-
-import static android.app.slice.SliceItem.FORMAT_IMAGE;
-import static android.app.slice.SliceItem.FORMAT_TEXT;
-
-import android.annotation.MainThread;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.PixelFormat;
-import android.graphics.drawable.Icon;
-import android.graphics.fonts.SystemFonts;
-import android.os.IBinder;
-import android.service.autofill.InlinePresentation;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.ContextThemeWrapper;
-import android.view.LayoutInflater;
-import android.view.SurfaceControl;
-import android.view.SurfaceControlViewHost;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.internal.R;
-
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * This is a temporary inline suggestion UI inflater which will be replaced by the ExtServices
- * implementation.
- *
- * TODO(b/146453086): remove this class once autofill ext service is implemented.
- *
- * @hide
- */
-public class InlineSuggestionUi {
-
- private static final String TAG = "InlineSuggestionUi";
-
- // The pattern to match the value can be obtained by calling {@code Resources#getResourceName
- // (int)}. This name is a single string of the form "package:type/entry".
- private static final Pattern RESOURCE_NAME_PATTERN = Pattern.compile("([^:]+):([^/]+)/(\\S+)");
-
- private final @NonNull Context mContext;
- private final @NonNull Runnable mOnErrorCallback;
-
- InlineSuggestionUi(@NonNull Context context, @NonNull Runnable onErrorCallback) {
- this.mContext = context;
- mOnErrorCallback = onErrorCallback;
- }
-
- /**
- * Returns a {@link SurfaceControl} with the inflated content embedded in it.
- */
- @MainThread
- @Nullable
- public SurfaceControl inflate(@NonNull InlinePresentation inlinePresentation, int width,
- int height, @Nullable View.OnClickListener onClickListener) {
- Log.d(TAG, "Inflating the inline suggestion UI");
-
- //TODO(b/137800469): Pass in inputToken from IME.
- final SurfaceControlViewHost wvr = new SurfaceControlViewHost(mContext,
- mContext.getDisplay(), (IBinder) null);
- final SurfaceControl sc = wvr.getSurfacePackage().getSurfaceControl();
-
- Context contextThemeWrapper = getContextThemeWrapper(mContext,
- inlinePresentation.getInlinePresentationSpec().getStyle());
- if (contextThemeWrapper == null) {
- contextThemeWrapper = getDefaultContextThemeWrapper(mContext);
- }
- final View suggestionView = renderSlice(inlinePresentation.getSlice(),
- contextThemeWrapper);
-
- final InlineSuggestionRoot suggestionRoot = new InlineSuggestionRoot(
- mContext, mOnErrorCallback);
- suggestionRoot.addView(suggestionView);
- suggestionRoot.setOnClickListener(onClickListener);
-
- WindowManager.LayoutParams lp =
- new WindowManager.LayoutParams(width, height,
- WindowManager.LayoutParams.TYPE_APPLICATION, 0,
- PixelFormat.TRANSPARENT);
- wvr.addView(suggestionRoot, lp);
- return sc;
- }
-
- private static View renderSlice(Slice slice, Context context) {
- final LayoutInflater inflater = LayoutInflater.from(context);
- final ViewGroup suggestionView =
- (ViewGroup) inflater.inflate(R.layout.autofill_inline_suggestion, null);
-
- final ImageView startIconView =
- suggestionView.findViewById(R.id.autofill_inline_suggestion_start_icon);
- final TextView titleView =
- suggestionView.findViewById(R.id.autofill_inline_suggestion_title);
- final TextView subtitleView =
- suggestionView.findViewById(R.id.autofill_inline_suggestion_subtitle);
- final ImageView endIconView =
- suggestionView.findViewById(R.id.autofill_inline_suggestion_end_icon);
-
- boolean hasStartIcon = false;
- boolean hasEndIcon = false;
- boolean hasSubtitle = false;
- final List<SliceItem> sliceItems = slice.getItems();
- for (int i = 0; i < sliceItems.size(); i++) {
- final SliceItem sliceItem = sliceItems.get(i);
- if (sliceItem.getFormat().equals(FORMAT_IMAGE)) {
- final Icon sliceIcon = sliceItem.getIcon();
- if (i == 0) { // start icon
- startIconView.setImageIcon(sliceIcon);
- hasStartIcon = true;
- } else { // end icon
- endIconView.setImageIcon(sliceIcon);
- hasEndIcon = true;
- }
- } else if (sliceItem.getFormat().equals(FORMAT_TEXT)) {
- final List<String> sliceHints = sliceItem.getHints();
- final String sliceText = sliceItem.getText().toString();
- if (sliceHints.contains("inline_title")) { // title
- titleView.setText(sliceText);
- } else { // subtitle
- subtitleView.setText(sliceText);
- hasSubtitle = true;
- }
- }
- }
- if (!hasStartIcon) {
- startIconView.setVisibility(View.GONE);
- }
- if (!hasEndIcon) {
- endIconView.setVisibility(View.GONE);
- }
- if (!hasSubtitle) {
- subtitleView.setVisibility(View.GONE);
- }
-
- return suggestionView;
- }
-
- private Context getDefaultContextThemeWrapper(@NonNull Context context) {
- Resources.Theme theme = context.getResources().newTheme();
- theme.applyStyle(android.R.style.Theme_AutofillInlineSuggestion, true);
- return new ContextThemeWrapper(context, theme);
- }
-
- /**
- * Returns a context wrapping the theme in the provided {@code style}, or null if {@code
- * style} doesn't pass validation.
- */
- @Nullable
- private static Context getContextThemeWrapper(@NonNull Context context,
- @Nullable String style) {
- if (style == null) {
- return null;
- }
- Matcher matcher = RESOURCE_NAME_PATTERN.matcher(style);
- if (!matcher.matches()) {
- Log.d(TAG, "Can not parse the style=" + style);
- return null;
- }
- String packageName = matcher.group(1);
- String type = matcher.group(2);
- String entry = matcher.group(3);
- if (TextUtils.isEmpty(packageName) || TextUtils.isEmpty(type) || TextUtils.isEmpty(entry)) {
- Log.d(TAG, "Can not proceed with empty field values in the style=" + style);
- return null;
- }
- Resources resources = null;
- try {
- resources = context.getPackageManager().getResourcesForApplication(
- packageName);
- } catch (PackageManager.NameNotFoundException e) {
- return null;
- }
- int resId = resources.getIdentifier(entry, type, packageName);
- if (resId == Resources.ID_NULL) {
- return null;
- }
- Resources.Theme theme = resources.newTheme();
- theme.applyStyle(resId, true);
- if (!validateBaseTheme(theme, resId)) {
- Log.d(TAG, "Provided theme is not a child of Theme.InlineSuggestion, ignoring it.");
- return null;
- }
- if (!validateFontFamilyForTextViewStyles(theme)) {
- Log.d(TAG,
- "Provided theme specifies a font family that is not system font, ignoring it.");
- return null;
- }
- return new ContextThemeWrapper(context, theme);
- }
-
- private static boolean validateFontFamilyForTextViewStyles(Resources.Theme theme) {
- return validateFontFamily(theme, android.R.attr.autofillInlineSuggestionTitle)
- && validateFontFamily(theme, android.R.attr.autofillInlineSuggestionSubtitle);
- }
-
- private static boolean validateFontFamily(Resources.Theme theme, int styleAttr) {
- TypedArray ta = null;
- try {
- ta = theme.obtainStyledAttributes(null, new int[]{android.R.attr.fontFamily},
- styleAttr,
- 0);
- if (ta.getIndexCount() == 0) {
- return true;
- }
- String fontFamily = ta.getString(ta.getIndex(0));
- return SystemFonts.getRawSystemFallbackMap().containsKey(fontFamily);
- } finally {
- if (ta != null) {
- ta.recycle();
- }
- }
- }
-
- private static boolean validateBaseTheme(Resources.Theme theme, int styleAttr) {
- TypedArray ta = null;
- try {
- ta = theme.obtainStyledAttributes(null,
- new int[]{android.R.attr.isAutofillInlineSuggestionTheme}, styleAttr, 0);
- if (ta.getIndexCount() == 0) {
- return false;
- }
- return ta.getBoolean(ta.getIndex(0), false);
- } finally {
- if (ta != null) {
- ta.recycle();
- }
- }
- }
-}
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 311a494..a4a42bc 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -69,6 +69,7 @@
import android.util.FeatureFlagUtils;
import android.util.Log;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
@@ -196,6 +197,12 @@
+ " due to " + getEnableDisableReasonString(mReason) + " by " + mPackageName;
}
+ void dump(ProtoOutputStream proto) {
+ proto.write(BluetoothManagerServiceDumpProto.ActiveLog.TIMESTAMP_MS, mTimestamp);
+ proto.write(BluetoothManagerServiceDumpProto.ActiveLog.ENABLE, mEnable);
+ proto.write(BluetoothManagerServiceDumpProto.ActiveLog.PACKAGE_NAME, mPackageName);
+ proto.write(BluetoothManagerServiceDumpProto.ActiveLog.REASON, mReason);
+ }
}
private final LinkedList<ActiveLog> mActiveLogs = new LinkedList<>();
@@ -2408,56 +2415,56 @@
if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) {
return;
}
+ if ((args.length > 0) && args[0].startsWith("--proto")) {
+ dumpProto(fd);
+ return;
+ }
String errorMsg = null;
- boolean protoOut = (args.length > 0) && args[0].startsWith("--proto");
+ writer.println("Bluetooth Status");
+ writer.println(" enabled: " + isEnabled());
+ writer.println(" state: " + BluetoothAdapter.nameForState(mState));
+ writer.println(" address: " + mAddress);
+ writer.println(" name: " + mName);
+ if (mEnable) {
+ long onDuration = SystemClock.elapsedRealtime() - mLastEnabledTime;
+ String onDurationString = String.format(Locale.US, "%02d:%02d:%02d.%03d",
+ (int) (onDuration / (1000 * 60 * 60)),
+ (int) ((onDuration / (1000 * 60)) % 60), (int) ((onDuration / 1000) % 60),
+ (int) (onDuration % 1000));
+ writer.println(" time since enabled: " + onDurationString);
+ }
- if (!protoOut) {
- writer.println("Bluetooth Status");
- writer.println(" enabled: " + isEnabled());
- writer.println(" state: " + BluetoothAdapter.nameForState(mState));
- writer.println(" address: " + mAddress);
- writer.println(" name: " + mName);
- if (mEnable) {
- long onDuration = SystemClock.elapsedRealtime() - mLastEnabledTime;
- String onDurationString = String.format(Locale.US, "%02d:%02d:%02d.%03d",
- (int) (onDuration / (1000 * 60 * 60)),
- (int) ((onDuration / (1000 * 60)) % 60), (int) ((onDuration / 1000) % 60),
- (int) (onDuration % 1000));
- writer.println(" time since enabled: " + onDurationString);
+ if (mActiveLogs.size() == 0) {
+ writer.println("\nBluetooth never enabled!");
+ } else {
+ writer.println("\nEnable log:");
+ for (ActiveLog log : mActiveLogs) {
+ writer.println(" " + log);
}
+ }
- if (mActiveLogs.size() == 0) {
- writer.println("\nBluetooth never enabled!");
- } else {
- writer.println("\nEnable log:");
- for (ActiveLog log : mActiveLogs) {
- writer.println(" " + log);
- }
- }
+ writer.println(
+ "\nBluetooth crashed " + mCrashes + " time" + (mCrashes == 1 ? "" : "s"));
+ if (mCrashes == CRASH_LOG_MAX_SIZE) {
+ writer.println("(last " + CRASH_LOG_MAX_SIZE + ")");
+ }
+ for (Long time : mCrashTimestamps) {
+ writer.println(" " + timeToLog(time));
+ }
- writer.println(
- "\nBluetooth crashed " + mCrashes + " time" + (mCrashes == 1 ? "" : "s"));
- if (mCrashes == CRASH_LOG_MAX_SIZE) {
- writer.println("(last " + CRASH_LOG_MAX_SIZE + ")");
- }
- for (Long time : mCrashTimestamps) {
- writer.println(" " + timeToLog(time));
- }
+ writer.println("\n" + mBleApps.size() + " BLE app" + (mBleApps.size() == 1 ? "" : "s")
+ + " registered");
+ for (ClientDeathRecipient app : mBleApps.values()) {
+ writer.println(" " + app.getPackageName());
+ }
- writer.println("\n" + mBleApps.size() + " BLE app" + (mBleApps.size() == 1 ? "" : "s")
- + "registered");
- for (ClientDeathRecipient app : mBleApps.values()) {
- writer.println(" " + app.getPackageName());
- }
-
- writer.println("");
- writer.flush();
- if (args.length == 0) {
- // Add arg to produce output
- args = new String[1];
- args[0] = "--print";
- }
+ writer.println("");
+ writer.flush();
+ if (args.length == 0) {
+ // Add arg to produce output
+ args = new String[1];
+ args[0] = "--print";
}
if (mBluetoothBinder == null) {
@@ -2470,14 +2477,42 @@
}
}
if (errorMsg != null) {
- // Silently return if we are extracting metrics in Protobuf format
- if (protoOut) {
- return;
- }
writer.println(errorMsg);
}
}
+ private void dumpProto(FileDescriptor fd) {
+ final ProtoOutputStream proto = new ProtoOutputStream(fd);
+ proto.write(BluetoothManagerServiceDumpProto.ENABLED, isEnabled());
+ proto.write(BluetoothManagerServiceDumpProto.STATE, mState);
+ proto.write(BluetoothManagerServiceDumpProto.STATE_NAME,
+ BluetoothAdapter.nameForState(mState));
+ proto.write(BluetoothManagerServiceDumpProto.ADDRESS, mAddress);
+ proto.write(BluetoothManagerServiceDumpProto.NAME, mName);
+ if (mEnable) {
+ proto.write(BluetoothManagerServiceDumpProto.LAST_ENABLED_TIME_MS, mLastEnabledTime);
+ }
+ proto.write(BluetoothManagerServiceDumpProto.CURR_TIMESTAMP_MS,
+ SystemClock.elapsedRealtime());
+ for (ActiveLog log : mActiveLogs) {
+ long token = proto.start(BluetoothManagerServiceDumpProto.ACTIVE_LOGS);
+ log.dump(proto);
+ proto.end(token);
+ }
+ proto.write(BluetoothManagerServiceDumpProto.NUM_CRASHES, mCrashes);
+ proto.write(BluetoothManagerServiceDumpProto.CRASH_LOG_MAXED,
+ mCrashes == CRASH_LOG_MAX_SIZE);
+ for (Long time : mCrashTimestamps) {
+ proto.write(BluetoothManagerServiceDumpProto.CRASH_TIMESTAMPS_MS, time);
+ }
+ proto.write(BluetoothManagerServiceDumpProto.NUM_BLE_APPS, mBleApps.size());
+ for (ClientDeathRecipient app : mBleApps.values()) {
+ proto.write(BluetoothManagerServiceDumpProto.BLE_APP_PACKAGE_NAMES,
+ app.getPackageName());
+ }
+ proto.flush();
+ }
+
private static String getEnableDisableReasonString(int reason) {
switch (reason) {
case BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST:
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index b5b22f1..8a095af 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -3975,7 +3975,7 @@
if (mIsFuseEnabled && hasMtp) {
ApplicationInfo ai = mIPackageManager.getApplicationInfo(packageName,
0, UserHandle.getUserId(uid));
- if (ai.isSignedWithPlatformKey()) {
+ if (ai != null && ai.isSignedWithPlatformKey()) {
// Platform processes hosting the MTP server should be able to write in Android/
return Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE;
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 68de2c6..1da07ef 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8387,6 +8387,9 @@
@Override
public void setActivityController(IActivityController controller, boolean imAMonkey) {
+ if (controller != null) {
+ Binder.allowBlocking(controller.asBinder());
+ }
mActivityTaskManager.setActivityController(controller, imAMonkey);
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 88eb885..fb48db4 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -30,6 +30,7 @@
import static android.os.Process.SHELL_UID;
import static android.os.Process.SYSTEM_UID;
+import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__FRAMEWORK_LOCKED_BOOT_COMPLETED;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -92,8 +93,8 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.FgThread;
import com.android.server.LocalServices;
@@ -398,13 +399,14 @@
// Do not report secondary users, runtime restarts or first boot/upgrade
if (userId == UserHandle.USER_SYSTEM
&& !mInjector.isRuntimeRestarted() && !mInjector.isFirstBootOrUpgrade()) {
- int uptimeSeconds = (int)(SystemClock.elapsedRealtime() / 1000);
- MetricsLogger.histogram(mInjector.getContext(),
- "framework_locked_boot_completed", uptimeSeconds);
- final int MAX_UPTIME_SECONDS = 120;
- if (uptimeSeconds > MAX_UPTIME_SECONDS) {
+ final long elapsedTimeMs = SystemClock.elapsedRealtime();
+ FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
+ BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__FRAMEWORK_LOCKED_BOOT_COMPLETED,
+ elapsedTimeMs);
+ final long maxElapsedTimeMs = 120_000;
+ if (elapsedTimeMs > maxElapsedTimeMs) {
Slog.wtf("SystemServerTiming",
- "finishUserBoot took too long. uptimeSeconds=" + uptimeSeconds);
+ "finishUserBoot took too long. elapsedTimeMs=" + elapsedTimeMs);
}
}
@@ -618,9 +620,10 @@
// Do not report secondary users, runtime restarts or first boot/upgrade
if (userId == UserHandle.USER_SYSTEM
&& !mInjector.isRuntimeRestarted() && !mInjector.isFirstBootOrUpgrade()) {
- int uptimeSeconds = (int) (SystemClock.elapsedRealtime() / 1000);
- MetricsLogger.histogram(mInjector.getContext(), "framework_boot_completed",
- uptimeSeconds);
+ final long elapsedTimeMs = SystemClock.elapsedRealtime();
+ FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
+ FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__FRAMEWORK_BOOT_COMPLETED,
+ elapsedTimeMs);
}
final Intent bootIntent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
bootIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 0edc160..5e1582c 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -34,6 +34,7 @@
import static android.app.AppOpsManager.OP_COARSE_LOCATION;
import static android.app.AppOpsManager.OP_FLAGS_ALL;
import static android.app.AppOpsManager.OP_FLAG_SELF;
+import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXIED;
import static android.app.AppOpsManager.OP_NONE;
import static android.app.AppOpsManager.OP_PLAY_AUDIO;
import static android.app.AppOpsManager.OP_RECORD_AUDIO;
@@ -52,11 +53,14 @@
import static android.app.AppOpsManager.makeKey;
import static android.app.AppOpsManager.modeToName;
import static android.app.AppOpsManager.opToName;
+import static android.app.AppOpsManager.opToPublicName;
import static android.app.AppOpsManager.resolveFirstUnrestrictedUidState;
import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static android.content.Intent.EXTRA_REPLACING;
import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
import static android.content.pm.PermissionInfo.PROTECTION_FLAG_APPOP;
+import static android.util.StatsLogInternal.RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__RARELY_USED;
+import static android.util.StatsLogInternal.RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__UNIFORM;
import static com.android.server.appop.AppOpsService.ModeCallback.ALL_OPS;
@@ -79,6 +83,8 @@
import android.app.AppOpsManagerInternal;
import android.app.AppOpsManagerInternal.CheckOpsDelegate;
import android.app.AsyncNotedAppOp;
+import android.app.RuntimeAppOpAccessMessage;
+import android.app.SyncNotedAppOp;
import android.compat.Compatibility;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -87,6 +93,7 @@
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PermissionInfo;
@@ -137,6 +144,7 @@
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsNotedCallback;
import com.android.internal.app.IAppOpsService;
+import com.android.internal.app.MessageSamplingConfig;
import com.android.internal.os.Zygote;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
@@ -147,6 +155,7 @@
import com.android.server.LocalServices;
import com.android.server.LockGuard;
import com.android.server.SystemServerInitThreadPool;
+import com.android.server.pm.PackageList;
import libcore.util.EmptyArray;
@@ -163,6 +172,8 @@
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -172,6 +183,8 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.function.Consumer;
public class AppOpsService extends IAppOpsService.Stub {
static final String TAG = "AppOps";
@@ -308,6 +321,34 @@
private ActivityManagerInternal mActivityManagerInternal;
+ /** Package sampled for message collection in the current session */
+ @GuardedBy("this")
+ private String mSampledPackage = null;
+
+ /** Appop sampled for message collection in the current session */
+ @GuardedBy("this")
+ private int mSampledAppOpCode = OP_NONE;
+
+ /** Maximum distance for appop to be considered for message collection in the current session */
+ @GuardedBy("this")
+ private int mAcceptableLeftDistance = 0;
+
+ /** Number of messages collected for sampled package and appop in the current session */
+ @GuardedBy("this")
+ private float mMessagesCollectedCount;
+
+ /** List of rarely used packages priorities for message collection */
+ @GuardedBy("this")
+ private ArraySet<String> mRarelyUsedPackages = new ArraySet<>();
+
+ /** Sampling strategy used for current session */
+ @GuardedBy("this")
+ @AppOpsManager.SamplingStrategy
+ private int mSamplingStrategy;
+
+ /** Last runtime permission access message collected and ready for reporting */
+ @GuardedBy("this")
+ private RuntimeAppOpAccessMessage mCollectedRuntimePermissionMessage;
/**
* An unsynchronized pool of {@link OpEventProxyInfo} objects.
*/
@@ -1236,9 +1277,16 @@
UserHandle.formatUid(sb, mWatchingUid);
sb.append(" flags=0x");
sb.append(Integer.toHexString(mFlags));
- if (mWatchedOpCode != OP_NONE) {
- sb.append(" op=");
- sb.append(opToName(mWatchedOpCode));
+ switch (mWatchedOpCode) {
+ case OP_NONE:
+ break;
+ case ALL_OPS:
+ sb.append(" op=(all)");
+ break;
+ default:
+ sb.append(" op=");
+ sb.append(opToName(mWatchedOpCode));
+ break;
}
sb.append(" from uid=");
UserHandle.formatUid(sb, mCallingUid);
@@ -1535,6 +1583,38 @@
}
}, packageSuspendFilter);
+ final IntentFilter packageAddedFilter = new IntentFilter();
+ packageAddedFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ packageAddedFilter.addDataScheme("package");
+ mContext.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final Uri data = intent.getData();
+
+ final String packageName = data.getSchemeSpecificPart();
+ PackageInfo pi = LocalServices.getService(
+ PackageManagerInternal.class).getPackageInfo(packageName,
+ PackageManager.GET_PERMISSIONS, Process.myUid(), mContext.getUserId());
+ if (isSamplingTarget(pi)) {
+ synchronized (this) {
+ mRarelyUsedPackages.add(packageName);
+ }
+ }
+ }
+ }, packageAddedFilter);
+
+ List<String> packageNames = getPackageNamesForSampling();
+ synchronized (this) {
+ resamplePackageAndAppOpLocked(packageNames);
+ }
+
+ AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
+ @Override
+ public void run() {
+ initializeRarelyUsedPackagesList(new ArraySet<>(packageNames));
+ }
+ });
+
PackageManagerInternal packageManagerInternal = LocalServices.getService(
PackageManagerInternal.class);
packageManagerInternal.setExternalSourcesPolicy(
@@ -3030,6 +3110,9 @@
featureId, message, System.currentTimeMillis());
final boolean[] wasNoteForwarded = {false};
+ reportRuntimeAppOpAccessMessageAsyncLocked(uid, packageName, opCode, featureId,
+ message);
+
if (callbacks != null) {
callbacks.broadcast((cb) -> {
try {
@@ -5494,6 +5577,227 @@
mHistoricalRegistry.clearHistory();
}
+ /**
+ * Report runtime access to AppOp together with message (including stack trace)
+ *
+ * @param packageName The package which reported the op
+ * @param notedAppOp contains code of op and featureId provided by developer
+ * @param message Message describing AppOp access (can be stack trace)
+ *
+ * @return Config for future sampling to reduce amount of reporting
+ */
+ @Override
+ public MessageSamplingConfig reportRuntimeAppOpAccessMessageAndGetConfig(
+ String packageName, SyncNotedAppOp notedAppOp, String message) {
+ int uid = Binder.getCallingUid();
+ Objects.requireNonNull(packageName);
+ synchronized (this) {
+ switchPackageIfRarelyUsedLocked(packageName);
+ if (!packageName.equals(mSampledPackage)) {
+ return new MessageSamplingConfig(OP_NONE, 0,
+ Instant.now().plus(1, ChronoUnit.HOURS).toEpochMilli());
+ }
+
+ Objects.requireNonNull(notedAppOp);
+ Objects.requireNonNull(message);
+
+ reportRuntimeAppOpAccessMessageInternalLocked(uid, packageName,
+ AppOpsManager.strOpToOp(notedAppOp.getOp()),
+ notedAppOp.getFeatureId(), message);
+
+ return new MessageSamplingConfig(mSampledAppOpCode, mAcceptableLeftDistance,
+ Instant.now().plus(1, ChronoUnit.HOURS).toEpochMilli());
+ }
+ }
+
+ /**
+ * Report runtime access to AppOp together with message (entry point for reporting
+ * asynchronous access)
+ * @param uid Uid of the package which reported the op
+ * @param packageName The package which reported the op
+ * @param opCode Code of AppOp
+ * @param featureId FeautreId of AppOp reported
+ * @param message Message describing AppOp access (can be stack trace)
+ */
+ private void reportRuntimeAppOpAccessMessageAsyncLocked(int uid,
+ @NonNull String packageName, int opCode, @Nullable String featureId,
+ @NonNull String message) {
+ switchPackageIfRarelyUsedLocked(packageName);
+ if (!Objects.equals(mSampledPackage, packageName)) {
+ return;
+ }
+ reportRuntimeAppOpAccessMessageInternalLocked(uid, packageName, opCode, featureId, message);
+ }
+
+ /**
+ * Decides whether reported message is within the range of watched AppOps and picks it for
+ * reporting uniformly at random across all received messages.
+ */
+ private void reportRuntimeAppOpAccessMessageInternalLocked(int uid,
+ @NonNull String packageName, int opCode, @Nullable String featureId,
+ @NonNull String message) {
+ int newLeftDistance = AppOpsManager.leftCircularDistance(opCode,
+ mSampledAppOpCode, _NUM_OP);
+
+ if (mAcceptableLeftDistance < newLeftDistance) {
+ return;
+ }
+
+ if (mAcceptableLeftDistance > newLeftDistance) {
+ mAcceptableLeftDistance = newLeftDistance;
+ mMessagesCollectedCount = 0.0f;
+ }
+
+ mMessagesCollectedCount += 1.0f;
+ if (ThreadLocalRandom.current().nextFloat() <= 1.0f / mMessagesCollectedCount) {
+ mCollectedRuntimePermissionMessage = new RuntimeAppOpAccessMessage(uid, opCode,
+ packageName, featureId, message, mSamplingStrategy);
+ }
+ return;
+ }
+
+ /** Pulls current AppOps access report and resamples package and app op to watch */
+ @Override
+ public @Nullable RuntimeAppOpAccessMessage collectRuntimeAppOpAccessMessage() {
+ mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
+ Binder.getCallingPid(), Binder.getCallingUid(), null);
+ RuntimeAppOpAccessMessage result;
+ List<String> packageNames = getPackageNamesForSampling();
+ synchronized (this) {
+ result = mCollectedRuntimePermissionMessage;
+ resamplePackageAndAppOpLocked(packageNames);
+ }
+ return result;
+ }
+
+ /**
+ * Checks if package is in the list of rarely used package and starts watching the new package
+ * to collect incoming message.
+ * @param packageName
+ */
+ private void switchPackageIfRarelyUsedLocked(@NonNull String packageName) {
+ if (mRarelyUsedPackages.contains(packageName)) {
+ mRarelyUsedPackages.remove(packageName);
+ if (ThreadLocalRandom.current().nextFloat() < 0.5f) {
+ mSamplingStrategy = RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__RARELY_USED;
+ resampleAppOpForPackageLocked(packageName);
+ }
+ }
+ }
+
+ /** Resamples package and appop to watch from the list provided. */
+ private void resamplePackageAndAppOpLocked(@NonNull List<String> packageNames) {
+ if (!packageNames.isEmpty()) {
+ mSamplingStrategy = RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__UNIFORM;
+ resampleAppOpForPackageLocked(packageNames.get(
+ ThreadLocalRandom.current().nextInt(packageNames.size())));
+ }
+ }
+
+ /** Resamples appop for the chosen package and initializes sampling state */
+ private void resampleAppOpForPackageLocked(@NonNull String packageName) {
+ mMessagesCollectedCount = 0.0f;
+ mSampledAppOpCode = ThreadLocalRandom.current().nextInt(_NUM_OP);
+ mAcceptableLeftDistance = _NUM_OP;
+ mSampledPackage = packageName;
+ mCollectedRuntimePermissionMessage = null;
+ }
+
+ /**
+ * Creates list of rarely used packages - packages which were not used over last week or
+ * which declared but did not use permissions over last week.
+ * */
+ private void initializeRarelyUsedPackagesList(@NonNull ArraySet<String> candidates) {
+ AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
+ List<String> runtimeAppOpsList = getRuntimeAppOpsList();
+ AppOpsManager.HistoricalOpsRequest histOpsRequest =
+ new AppOpsManager.HistoricalOpsRequest.Builder(
+ Instant.now().minus(7, ChronoUnit.DAYS).toEpochMilli(),
+ Long.MAX_VALUE).setOpNames(runtimeAppOpsList).setFlags(
+ OP_FLAG_SELF | OP_FLAG_TRUSTED_PROXIED).build();
+ appOps.getHistoricalOps(histOpsRequest, AsyncTask.THREAD_POOL_EXECUTOR,
+ new Consumer<HistoricalOps>() {
+ @Override
+ public void accept(HistoricalOps histOps) {
+ int uidCount = histOps.getUidCount();
+ for (int uidIdx = 0; uidIdx < uidCount; uidIdx++) {
+ final AppOpsManager.HistoricalUidOps uidOps = histOps.getUidOpsAt(
+ uidIdx);
+ int pkgCount = uidOps.getPackageCount();
+ for (int pkgIdx = 0; pkgIdx < pkgCount; pkgIdx++) {
+ String packageName = uidOps.getPackageOpsAt(
+ pkgIdx).getPackageName();
+ if (!candidates.contains(packageName)) {
+ continue;
+ }
+ AppOpsManager.HistoricalPackageOps packageOps =
+ uidOps.getPackageOpsAt(pkgIdx);
+ if (packageOps.getOpCount() != 0) {
+ candidates.remove(packageName);
+ }
+ }
+ }
+ synchronized (this) {
+ mRarelyUsedPackages = candidates;
+ }
+ }
+ });
+ }
+
+ /** List of app ops related to runtime permissions */
+ private List<String> getRuntimeAppOpsList() {
+ ArrayList<String> result = new ArrayList();
+ for (int i = 0; i < _NUM_OP; i++) {
+ if (shouldCollectNotes(i)) {
+ result.add(opToPublicName(i));
+ }
+ }
+ return result;
+ }
+
+ /** Returns list of packages to be used for package sampling */
+ private @NonNull List<String> getPackageNamesForSampling() {
+ List<String> packageNames = new ArrayList<>();
+ PackageManagerInternal packageManagerInternal = LocalServices.getService(
+ PackageManagerInternal.class);
+ PackageList packages = packageManagerInternal.getPackageList();
+ for (String packageName : packages.getPackageNames()) {
+ PackageInfo pkg = packageManagerInternal.getPackageInfo(packageName,
+ PackageManager.GET_PERMISSIONS, Process.myUid(), mContext.getUserId());
+ if (isSamplingTarget(pkg)) {
+ packageNames.add(pkg.packageName);
+ }
+ }
+ return packageNames;
+ }
+
+ /** Checks whether package should be included in sampling pool */
+ private boolean isSamplingTarget(@Nullable PackageInfo pkg) {
+ if (pkg == null) {
+ return false;
+ }
+ if (pkg.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.Q) {
+ return false;
+ }
+
+ String[] requestedPermissions = pkg.requestedPermissions;
+ if (requestedPermissions == null) {
+ return false;
+ }
+ for (String permission : requestedPermissions) {
+ PermissionInfo permissionInfo;
+ try {
+ permissionInfo = mContext.getPackageManager().getPermissionInfo(permission, 0);
+ } catch (PackageManager.NameNotFoundException ignored) {
+ continue;
+ }
+ if (permissionInfo.getProtection() == PROTECTION_DANGEROUS) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private void removeUidsForUserLocked(int userHandle) {
for (int i = mUidStates.size() - 1; i >= 0; --i) {
final int uid = mUidStates.keyAt(i);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index f41eeeb..4be74b5 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -30,6 +30,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
@@ -158,6 +159,7 @@
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
@@ -1075,6 +1077,15 @@
+ AudioSystem.audioSystemErrorToString(status)
+ " when connecting mixes for policy " + policy.toLogFriendlyString());
policy.release();
+ } else {
+ final int deviceAffinitiesStatus = policy.setupDeviceAffinities();
+ if (deviceAffinitiesStatus != AudioSystem.SUCCESS) {
+ Log.e(TAG, "onAudioServerDied: error "
+ + AudioSystem.audioSystemErrorToString(deviceAffinitiesStatus)
+ + " when connecting device affinities for policy "
+ + policy.toLogFriendlyString());
+ policy.release();
+ }
}
}
}
@@ -7898,22 +7909,16 @@
int setUidDeviceAffinities(int uid, @NonNull int[] types, @NonNull String[] addresses) {
final Integer Uid = new Integer(uid);
- int res;
if (mUidDeviceAffinities.remove(Uid) != null) {
- final long identity = Binder.clearCallingIdentity();
- res = AudioSystem.removeUidDeviceAffinities(uid);
- Binder.restoreCallingIdentity(identity);
- if (res != AudioSystem.SUCCESS) {
+ if (removeUidDeviceAffinitiesFromSystem(uid) != AudioSystem.SUCCESS) {
Log.e(TAG, "AudioSystem. removeUidDeviceAffinities(" + uid + ") failed, "
+ " cannot call AudioSystem.setUidDeviceAffinities");
return AudioManager.ERROR;
}
}
- final long identity = Binder.clearCallingIdentity();
- res = AudioSystem.setUidDeviceAffinities(uid, types, addresses);
- Binder.restoreCallingIdentity(identity);
- if (res == AudioSystem.SUCCESS) {
- mUidDeviceAffinities.put(Uid, new AudioDeviceArray(types, addresses));
+ AudioDeviceArray deviceArray = new AudioDeviceArray(types, addresses);
+ if (setUidDeviceAffinitiesOnSystem(uid, deviceArray) == AudioSystem.SUCCESS) {
+ mUidDeviceAffinities.put(Uid, deviceArray);
return AudioManager.SUCCESS;
}
Log.e(TAG, "AudioSystem. setUidDeviceAffinities(" + uid + ") failed");
@@ -7922,10 +7927,7 @@
int removeUidDeviceAffinities(int uid) {
if (mUidDeviceAffinities.remove(new Integer(uid)) != null) {
- final long identity = Binder.clearCallingIdentity();
- final int res = AudioSystem.removeUidDeviceAffinities(uid);
- Binder.restoreCallingIdentity(identity);
- if (res == AudioSystem.SUCCESS) {
+ if (removeUidDeviceAffinitiesFromSystem(uid) == AudioSystem.SUCCESS) {
return AudioManager.SUCCESS;
}
}
@@ -7933,26 +7935,41 @@
return AudioManager.ERROR;
}
+ @AudioSystem.AudioSystemError private int removeUidDeviceAffinitiesFromSystem(int uid) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return AudioSystem.removeUidDeviceAffinities(uid);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @AudioSystem.AudioSystemError private int setUidDeviceAffinitiesOnSystem(int uid,
+ AudioDeviceArray deviceArray) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return AudioSystem.setUidDeviceAffinities(uid, deviceArray.mDeviceTypes,
+ deviceArray.mDeviceAddresses);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
int setUserIdDeviceAffinities(int userId,
@NonNull int[] types, @NonNull String[] addresses) {
final Integer UserId = new Integer(userId);
- int res;
if (mUserIdDeviceAffinities.remove(UserId) != null) {
- final long identity = Binder.clearCallingIdentity();
- res = AudioSystem.removeUserIdDeviceAffinities(UserId);
- Binder.restoreCallingIdentity(identity);
- if (res != AudioSystem.SUCCESS) {
+ if (removeUserIdDeviceAffinitiesFromSystem(userId) != AudioSystem.SUCCESS) {
Log.e(TAG, "AudioSystem. removeUserIdDeviceAffinities("
+ UserId + ") failed, "
+ " cannot call AudioSystem.setUserIdDeviceAffinities");
return AudioManager.ERROR;
}
}
- final long identity = Binder.clearCallingIdentity();
- res = AudioSystem.setUserIdDeviceAffinities(userId, types, addresses);
- Binder.restoreCallingIdentity(identity);
- if (res == AudioSystem.SUCCESS) {
- mUserIdDeviceAffinities.put(UserId, new AudioDeviceArray(types, addresses));
+ AudioDeviceArray audioDeviceArray = new AudioDeviceArray(types, addresses);
+ if (setUserIdDeviceAffinitiesOnSystem(userId, audioDeviceArray)
+ == AudioSystem.SUCCESS) {
+ mUserIdDeviceAffinities.put(UserId, audioDeviceArray);
return AudioManager.SUCCESS;
}
Log.e(TAG, "AudioSystem.setUserIdDeviceAffinities(" + userId + ") failed");
@@ -7961,10 +7978,7 @@
int removeUserIdDeviceAffinities(int userId) {
if (mUserIdDeviceAffinities.remove(new Integer(userId)) != null) {
- final long identity = Binder.clearCallingIdentity();
- final int res = AudioSystem.removeUserIdDeviceAffinities(userId);
- Binder.restoreCallingIdentity(identity);
- if (res == AudioSystem.SUCCESS) {
+ if (removeUserIdDeviceAffinitiesFromSystem(userId) == AudioSystem.SUCCESS) {
return AudioManager.SUCCESS;
}
}
@@ -7972,9 +7986,76 @@
return AudioManager.ERROR;
}
+ @AudioSystem.AudioSystemError private int removeUserIdDeviceAffinitiesFromSystem(
+ @UserIdInt int userId) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return AudioSystem.removeUserIdDeviceAffinities(userId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @AudioSystem.AudioSystemError private int setUserIdDeviceAffinitiesOnSystem(
+ @UserIdInt int userId, AudioDeviceArray deviceArray) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return AudioSystem.setUserIdDeviceAffinities(userId, deviceArray.mDeviceTypes,
+ deviceArray.mDeviceAddresses);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @AudioSystem.AudioSystemError int setupDeviceAffinities() {
+ for (Map.Entry<Integer, AudioDeviceArray> uidEntry : mUidDeviceAffinities.entrySet()) {
+ int uidStatus = removeUidDeviceAffinitiesFromSystem(uidEntry.getKey());
+ if (uidStatus != AudioSystem.SUCCESS) {
+ Log.e(TAG,
+ "setupDeviceAffinities failed to remove device affinity for uid "
+ + uidEntry.getKey());
+ return uidStatus;
+ }
+ uidStatus = setUidDeviceAffinitiesOnSystem(uidEntry.getKey(), uidEntry.getValue());
+ if (uidStatus != AudioSystem.SUCCESS) {
+ Log.e(TAG,
+ "setupDeviceAffinities failed to set device affinity for uid "
+ + uidEntry.getKey());
+ return uidStatus;
+ }
+ }
+
+ for (Map.Entry<Integer, AudioDeviceArray> userIdEntry :
+ mUserIdDeviceAffinities.entrySet()) {
+ int userIdStatus = removeUserIdDeviceAffinitiesFromSystem(userIdEntry.getKey());
+ if (userIdStatus != AudioSystem.SUCCESS) {
+ Log.e(TAG,
+ "setupDeviceAffinities failed to remove device affinity for userId "
+ + userIdEntry.getKey());
+ return userIdStatus;
+ }
+ userIdStatus = setUserIdDeviceAffinitiesOnSystem(userIdEntry.getKey(),
+ userIdEntry.getValue());
+ if (userIdStatus != AudioSystem.SUCCESS) {
+ Log.e(TAG,
+ "setupDeviceAffinities failed to set device affinity for userId "
+ + userIdEntry.getKey());
+ return userIdStatus;
+ }
+ }
+ return AudioSystem.SUCCESS;
+ }
+
/** @return human readable debug informations summarizing the state of the object. */
public String toLogFriendlyString() {
String textDump = super.toLogFriendlyString();
+ textDump += " Uid Device Affinities:\n";
+ String spacer = " ";
+ textDump += logFriendlyAttributeDeviceArrayMap("Uid",
+ mUidDeviceAffinities, spacer);
+ textDump += " UserId Device Affinities:\n";
+ textDump += logFriendlyAttributeDeviceArrayMap("UserId",
+ mUserIdDeviceAffinities, spacer);
textDump += " Proxy:\n";
textDump += " is focus policy= " + mIsFocusPolicy + "\n";
if (mIsFocusPolicy) {
@@ -7985,6 +8066,24 @@
textDump += " media projection= " + mProjection + "\n";
return textDump;
}
+
+ private String logFriendlyAttributeDeviceArrayMap(String attribute,
+ Map<Integer, AudioDeviceArray> map, String spacer) {
+ final StringBuilder stringBuilder = new StringBuilder();
+ for (Map.Entry<Integer, AudioDeviceArray> mapEntry : map.entrySet()) {
+ stringBuilder.append(spacer).append(attribute).append(": ")
+ .append(mapEntry.getKey()).append("\n");
+ AudioDeviceArray deviceArray = mapEntry.getValue();
+ String deviceSpacer = spacer + " ";
+ for (int i = 0; i < deviceArray.mDeviceTypes.length; i++) {
+ stringBuilder.append(deviceSpacer).append("Type: 0x")
+ .append(Integer.toHexString(deviceArray.mDeviceTypes[i]))
+ .append(" Address: ").append(deviceArray.mDeviceAddresses[i])
+ .append("\n");
+ }
+ }
+ return stringBuilder.toString();
+ }
};
//======================
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 9140589..1ff8a94 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -121,7 +121,7 @@
* The display manager service relies on a collection of {@link DisplayAdapter} components,
* for discovering and configuring physical display devices attached to the system.
* There are separate display adapters for each manner that devices are attached:
- * one display adapter for built-in local displays, one for simulated non-functional
+ * one display adapter for physical displays, one for simulated non-functional
* displays when the system is headless, one for simulated overlay displays used for
* development, one for wifi displays, etc.
* </p><p>
@@ -231,7 +231,7 @@
private int mGlobalDisplayState = Display.STATE_ON;
// The overall display brightness.
- // For now, this only applies to the built-in display but we may split it up eventually.
+ // For now, this only applies to the default display but we may split it up eventually.
private float mGlobalDisplayBrightness;
// Set to true when there are pending display changes that have yet to be applied
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 2b225e5..4ebbdda 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -35,7 +35,6 @@
import android.view.DisplayAddress;
import android.view.DisplayCutout;
import android.view.DisplayEventReceiver;
-import android.view.Surface;
import android.view.SurfaceControl;
import com.android.internal.BrightnessSynchronizer;
@@ -53,7 +52,7 @@
import java.util.Objects;
/**
- * A display adapter for the local displays managed by Surface Flinger.
+ * A display adapter for the local displays managed by SurfaceFlinger.
* <p>
* Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
* </p>
@@ -129,10 +128,10 @@
LocalDisplayDevice device = mDevices.get(physicalDisplayId);
if (device == null) {
// Display was added.
- final boolean isInternal = mDevices.size() == 0;
+ final boolean isDefaultDisplay = mDevices.size() == 0;
device = new LocalDisplayDevice(displayToken, physicalDisplayId, info,
configs, activeConfig, configSpecs, colorModes, activeColorMode,
- hdrCapabilities, isInternal);
+ hdrCapabilities, isDefaultDisplay);
mDevices.put(physicalDisplayId, device);
sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
} else if (device.updateDisplayProperties(configs, activeConfig,
@@ -175,7 +174,7 @@
private final LogicalLight mBacklight;
private final SparseArray<DisplayModeRecord> mSupportedModes = new SparseArray<>();
private final ArrayList<Integer> mSupportedColorModes = new ArrayList<>();
- private final boolean mIsInternal;
+ private final boolean mIsDefaultDisplay;
private DisplayDeviceInfo mInfo;
private boolean mHavePendingChanges;
@@ -207,15 +206,15 @@
SurfaceControl.DisplayInfo info, SurfaceControl.DisplayConfig[] configs,
int activeConfigId, SurfaceControl.DesiredDisplayConfigSpecs configSpecs,
int[] colorModes, int activeColorMode, Display.HdrCapabilities hdrCapabilities,
- boolean isInternal) {
+ boolean isDefaultDisplay) {
super(LocalDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + physicalDisplayId);
mPhysicalDisplayId = physicalDisplayId;
- mIsInternal = isInternal;
+ mIsDefaultDisplay = isDefaultDisplay;
mDisplayInfo = info;
updateDisplayProperties(configs, activeConfigId, configSpecs, colorModes,
activeColorMode, hdrCapabilities);
mSidekickInternal = LocalServices.getService(SidekickInternal.class);
- if (mIsInternal) {
+ if (mIsDefaultDisplay) {
LightsManager lights = LocalServices.getService(LightsManager.class);
mBacklight = lights.getLight(LightsManager.LIGHT_ID_BACKLIGHT);
} else {
@@ -523,11 +522,10 @@
}
final Resources res = getOverlayContext().getResources();
- if (mIsInternal) {
- mInfo.name = res.getString(
- com.android.internal.R.string.display_manager_built_in_display_name);
- mInfo.flags |= DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
- | DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
+
+ if (mIsDefaultDisplay) {
+ mInfo.flags |= DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY;
+
if (res.getBoolean(com.android.internal.R.bool.config_mainBuiltInDisplayIsRound)
|| (Build.IS_EMULATOR
&& SystemProperties.getBoolean(PROPERTY_EMULATOR_CIRCULAR, false))) {
@@ -539,28 +537,7 @@
}
mInfo.displayCutout = DisplayCutout.fromResourcesRectApproximation(res,
mInfo.width, mInfo.height);
- mInfo.type = Display.TYPE_BUILT_IN;
- mInfo.touch = DisplayDeviceInfo.TOUCH_INTERNAL;
} else {
- mInfo.displayCutout = null;
- mInfo.type = Display.TYPE_HDMI;
- mInfo.flags |= DisplayDeviceInfo.FLAG_PRESENTATION;
- mInfo.name = getContext().getResources().getString(
- com.android.internal.R.string.display_manager_hdmi_display_name);
- mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
-
- // For demonstration purposes, allow rotation of the external display.
- // In the future we might allow the user to configure this directly.
- if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) {
- mInfo.rotation = Surface.ROTATION_270;
- }
-
- // For demonstration purposes, allow rotation of the external display
- // to follow the built-in display.
- if (SystemProperties.getBoolean("persist.demo.hdmirotates", false)) {
- mInfo.flags |= DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
- }
-
if (!res.getBoolean(
com.android.internal.R.bool.config_localDisplaysMirrorContent)) {
mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY;
@@ -570,6 +547,20 @@
mInfo.flags |= DisplayDeviceInfo.FLAG_PRIVATE;
}
}
+
+ if (mDisplayInfo.isInternal) {
+ mInfo.type = Display.TYPE_INTERNAL;
+ mInfo.touch = DisplayDeviceInfo.TOUCH_INTERNAL;
+ mInfo.flags |= DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
+ mInfo.name = res.getString(
+ com.android.internal.R.string.display_manager_built_in_display_name);
+ } else {
+ mInfo.type = Display.TYPE_EXTERNAL;
+ mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
+ mInfo.flags |= DisplayDeviceInfo.FLAG_PRESENTATION;
+ mInfo.name = getContext().getResources().getString(
+ com.android.internal.R.string.display_manager_hdmi_display_name);
+ }
}
return mInfo;
}
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index 648e07a..ac3a653 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -395,7 +395,7 @@
static final String PROPERTY_PREFERRED_ADDRESS_TV = "persist.sys.hdmi.addr.tv";
// TODO(OEM): Set this to false to keep the playback device in sleep upon hotplug event.
- // True by default.
+ // False by default.
static final String PROPERTY_WAKE_ON_HOTPLUG = "ro.hdmi.wake_on_hotplug";
/**
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 0944324..5541b11 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -45,7 +45,7 @@
private static final String TAG = "HdmiCecLocalDevicePlayback";
private static final boolean WAKE_ON_HOTPLUG =
- SystemProperties.getBoolean(Constants.PROPERTY_WAKE_ON_HOTPLUG, true);
+ SystemProperties.getBoolean(Constants.PROPERTY_WAKE_ON_HOTPLUG, false);
private static final boolean SET_MENU_LANGUAGE =
HdmiProperties.set_menu_language().orElse(false);
diff --git a/services/core/java/com/android/server/notification/NotificationHistoryProtoHelper.java b/services/core/java/com/android/server/notification/NotificationHistoryProtoHelper.java
index 2831d37..7ba993b 100644
--- a/services/core/java/com/android/server/notification/NotificationHistoryProtoHelper.java
+++ b/services/core/java/com/android/server/notification/NotificationHistoryProtoHelper.java
@@ -19,6 +19,7 @@
import android.app.NotificationHistory.HistoricalNotification;
import android.content.res.Resources;
import android.graphics.drawable.Icon;
+import android.text.TextUtils;
import android.util.Slog;
import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoOutputStream;
@@ -141,6 +142,16 @@
loadIcon(parser, notification, pkg);
parser.end(iconToken);
break;
+ case (int) Notification.CONVERSATION_ID_INDEX:
+ String conversationId =
+ stringPool.get(parser.readInt(Notification.CONVERSATION_ID_INDEX) - 1);
+ notification.setConversationId(conversationId);
+ break;
+ case (int) Notification.CONVERSATION_ID:
+ conversationId = parser.readString(Notification.CONVERSATION_ID);
+ notification.setConversationId(conversationId);
+ stringPool.add(conversationId);
+ break;
case ProtoInputStream.NO_MORE_FIELDS:
return notification.build();
}
@@ -271,6 +282,17 @@
+ ") not found in string cache");
proto.write(Notification.CHANNEL_ID, notification.getChannelId());
}
+ if (!TextUtils.isEmpty(notification.getConversationId())) {
+ final int conversationIdIndex = Arrays.binarySearch(
+ stringPool, notification.getConversationId());
+ if (conversationIdIndex >= 0) {
+ proto.write(Notification.CONVERSATION_ID_INDEX, conversationIdIndex + 1);
+ } else {
+ Slog.w(TAG, "notification conversation id (" + notification.getConversationId()
+ + ") not found in string cache");
+ proto.write(Notification.CONVERSATION_ID, notification.getConversationId());
+ }
+ }
proto.write(Notification.UID, notification.getUid());
proto.write(Notification.USER_ID, notification.getUserId());
proto.write(Notification.POSTED_TIME_MS, notification.getPostedTimeMs());
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index d0d0f5a..2e4a977 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -6243,6 +6243,7 @@
private final int mRank;
private final int mCount;
private final ManagedServiceInfo mListener;
+ private final long mWhen;
CancelNotificationRunnable(final int callingUid, final int callingPid,
final String pkg, final String tag, final int id,
@@ -6262,6 +6263,7 @@
this.mRank = rank;
this.mCount = count;
this.mListener = listener;
+ this.mWhen = System.currentTimeMillis();
}
@Override
@@ -6273,13 +6275,28 @@
}
synchronized (mNotificationLock) {
- // If the notification is currently enqueued, repost this runnable so it has a
- // chance to notify listeners
- if ((findNotificationByListLocked(mEnqueuedNotifications, mPkg, mTag, mId, mUserId))
- != null) {
+ // Check to see if there is a notification in the enqueued list that hasn't had a
+ // chance to post yet.
+ List<NotificationRecord> enqueued = findEnqueuedNotificationsForCriteria(
+ mPkg, mTag, mId, mUserId);
+ boolean repost = false;
+ if (enqueued.size() > 0) {
+ // Found something, let's see what it was
+ repost = true;
+ // If all enqueues happened before this cancel then wait for them to happen,
+ // otherwise we should let this cancel through so the next enqueue happens
+ for (NotificationRecord r : enqueued) {
+ if (r.mUpdateTimeMs > mWhen) {
+ // At least one enqueue was posted after the cancel, so we're invalid
+ return;
+ }
+ }
+ }
+ if (repost) {
mHandler.post(this);
return;
}
+
// Look for the notification in the posted list, since we already checked enqueued.
NotificationRecord r =
findNotificationByListLocked(mNotificationList, mPkg, mTag, mId, mUserId);
@@ -6298,6 +6315,10 @@
if ((r.getNotification().flags & mMustNotHaveFlags) != 0) {
return;
}
+ if (r.getUpdateTimeMs() > mWhen) {
+ // In this case, a post must have slipped by when this runnable reposted
+ return;
+ }
// Bubbled children get to stick around if the summary was manually cancelled
// (user removed) from systemui.
@@ -8220,6 +8241,29 @@
return null;
}
+ /**
+ * There may be multiple records that match your criteria. For instance if there have been
+ * multiple notifications posted which are enqueued for the same pkg, tag, id, userId. This
+ * method will find all of them in the given list
+ * @return
+ */
+ @GuardedBy("mNotificationLock")
+ private List<NotificationRecord> findEnqueuedNotificationsForCriteria(
+ String pkg, String tag, int id, int userId) {
+ final ArrayList<NotificationRecord> records = new ArrayList<>();
+ final int n = mEnqueuedNotifications.size();
+ for (int i = 0; i < n; i++) {
+ NotificationRecord r = mEnqueuedNotifications.get(i);
+ if (notificationMatchesUserId(r, userId)
+ && r.getSbn().getId() == id
+ && TextUtils.equals(r.getSbn().getTag(), tag)
+ && r.getSbn().getPackageName().equals(pkg)) {
+ records.add(r);
+ }
+ }
+ return records;
+ }
+
@GuardedBy("mNotificationLock")
int indexOfNotificationLocked(String key) {
final int N = mNotificationList.size();
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 0ada58e..f92e1fc 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -906,6 +906,10 @@
return (int) (now - mInterruptionTimeMs);
}
+ public long getUpdateTimeMs() {
+ return mUpdateTimeMs;
+ }
+
/**
* Set the visibility of the notification.
*/
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index 96d3cc1..3939f26 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -68,7 +68,6 @@
private Context mContext;
private Injector mInjector;
- private AppOpsManager mAppOpsManager;
public CrossProfileAppsServiceImpl(Context context) {
this(context, new InjectorImpl(context));
@@ -217,19 +216,27 @@
mInjector.getActivityTaskManagerInternal().startActivityAsUser(caller, callingPackage,
callingFeatureId, launchIntent, /* options= */ null, userId);
+ logStartActivityByIntent(callingPackage);
+ }
+
+ private void logStartActivityByIntent(String packageName) {
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.START_ACTIVITY_BY_INTENT)
+ .setStrings(packageName)
+ .setBoolean(isCallingUserAManagedProfile())
+ .write();
}
@Override
public boolean canRequestInteractAcrossProfiles(String callingPackage) {
Objects.requireNonNull(callingPackage);
verifyCallingPackage(callingPackage);
- return canRequestInteractAcrossProfilesUnchecked(
- callingPackage, mInjector.getCallingUserId());
+ return canRequestInteractAcrossProfilesUnchecked(callingPackage);
}
- private boolean canRequestInteractAcrossProfilesUnchecked(
- String packageName, @UserIdInt int userId) {
- List<UserHandle> targetUserProfiles = getTargetUserProfilesUnchecked(packageName, userId);
+ private boolean canRequestInteractAcrossProfilesUnchecked(String packageName) {
+ List<UserHandle> targetUserProfiles =
+ getTargetUserProfilesUnchecked(packageName, mInjector.getCallingUserId());
if (targetUserProfiles.isEmpty()) {
return false;
}
@@ -256,12 +263,11 @@
Objects.requireNonNull(callingPackage);
verifyCallingPackage(callingPackage);
- final List<UserHandle> targetUserProfiles = getTargetUserProfilesUnchecked(callingPackage,
- mInjector.getCallingUserId());
+ final List<UserHandle> targetUserProfiles = getTargetUserProfilesUnchecked(
+ callingPackage, mInjector.getCallingUserId());
if (targetUserProfiles.isEmpty()) {
return false;
}
-
return hasInteractAcrossProfilesPermission(callingPackage);
}
@@ -363,6 +369,12 @@
});
}
+ /**
+ * See {@link android.content.pm.CrossProfileApps#setInteractAcrossProfilesAppOp(String, int)}.
+ *
+ * <p>Logs metrics. Use {@link #setInteractAcrossProfilesAppOpUnchecked(String, int, boolean)}
+ * to avoid permission checks or to specify not to log metrics.
+ */
@Override
public void setInteractAcrossProfilesAppOp(String packageName, @Mode int newMode) {
final int callingUid = mInjector.getCallingUid();
@@ -379,6 +391,11 @@
"MANAGE_APP_OPS_MODES or CONFIGURE_INTERACT_ACROSS_PROFILES is required to set"
+ " the app-op for interacting across profiles.");
}
+ setInteractAcrossProfilesAppOpUnchecked(packageName, newMode, /* logMetrics= */ true);
+ }
+
+ private void setInteractAcrossProfilesAppOpUnchecked(
+ String packageName, @Mode int newMode, boolean logMetrics) {
if (newMode == AppOpsManager.MODE_ALLOWED
&& !canConfigureInteractAcrossProfiles(packageName)) {
// The user should not be prompted for apps that cannot request to interact across
@@ -394,7 +411,7 @@
if (!isPackageInstalled(packageName, profileId)) {
continue;
}
- setInteractAcrossProfilesAppOpForUser(packageName, newMode, profileId);
+ setInteractAcrossProfilesAppOpForUser(packageName, newMode, profileId, logMetrics);
}
}
@@ -413,16 +430,16 @@
}
private void setInteractAcrossProfilesAppOpForUser(
- String packageName, @Mode int newMode, @UserIdInt int userId) {
+ String packageName, @Mode int newMode, @UserIdInt int userId, boolean logMetrics) {
try {
- setInteractAcrossProfilesAppOpForUserOrThrow(packageName, newMode, userId);
+ setInteractAcrossProfilesAppOpForUserOrThrow(packageName, newMode, userId, logMetrics);
} catch (PackageManager.NameNotFoundException e) {
Slog.e(TAG, "Missing package " + packageName + " on user ID " + userId, e);
}
}
private void setInteractAcrossProfilesAppOpForUserOrThrow(
- String packageName, @Mode int newMode, @UserIdInt int userId)
+ String packageName, @Mode int newMode, @UserIdInt int userId, boolean logMetrics)
throws PackageManager.NameNotFoundException {
final int uid = mInjector.getPackageManager()
.getPackageUidAsUser(packageName, /* flags= */ 0, userId);
@@ -444,6 +461,28 @@
.setMode(OP_INTERACT_ACROSS_PROFILES, uid, packageName, newMode);
}
sendCanInteractAcrossProfilesChangedBroadcast(packageName, uid, UserHandle.of(userId));
+ maybeLogSetInteractAcrossProfilesAppOp(packageName, newMode, userId, logMetrics, uid);
+ }
+
+ private void maybeLogSetInteractAcrossProfilesAppOp(
+ String packageName,
+ @Mode int newMode,
+ @UserIdInt int userId,
+ boolean logMetrics,
+ int uid) {
+ if (!logMetrics) {
+ return;
+ }
+ if (userId != mInjector.getCallingUserId()) {
+ // Only log once per profile group by checking for the calling user ID.
+ return;
+ }
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_INTERACT_ACROSS_PROFILES_APP_OP)
+ .setStrings(packageName)
+ .setInt(newMode)
+ .setBoolean(appDeclaresCrossProfileAttribute(uid))
+ .write();
}
/**
@@ -541,11 +580,13 @@
permission, uid, /* owningUid= */-1, /* exported= */ true);
}
- private AppOpsManager getAppOpsManager() {
- if (mAppOpsManager == null) {
- mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
- }
- return mAppOpsManager;
+ private boolean isCallingUserAManagedProfile() {
+ return isManagedProfile(mInjector.getCallingUserId());
+ }
+
+ private boolean isManagedProfile(@UserIdInt int userId) {
+ return mInjector.withCleanCallingIdentity(()
+ -> mContext.getSystemService(UserManager.class).isManagedProfile(userId));
}
private static class InjectorImpl implements Injector {
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 9395c97..a9035b2 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -578,7 +578,9 @@
/** Returns true if standard APK Verity is enabled. */
static boolean isApkVerityEnabled() {
- return SystemProperties.getInt("ro.apk_verity.mode", FSVERITY_DISABLED) == FSVERITY_ENABLED;
+ return Build.VERSION.FIRST_SDK_INT >= Build.VERSION_CODES.R
+ || SystemProperties.getInt("ro.apk_verity.mode", FSVERITY_DISABLED)
+ == FSVERITY_ENABLED;
}
static boolean isLegacyApkVerityEnabled() {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 832c9b7..d451152 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -5233,9 +5233,7 @@
List<RuntimePermissionsState.PermissionState> permissions =
getPermissionsFromPermissionsState(
packageSetting.getPermissionsState(), userId);
- if (permissions != null) {
- packagePermissions.put(packageName, permissions);
- }
+ packagePermissions.put(packageName, permissions);
}
}
@@ -5248,9 +5246,7 @@
List<RuntimePermissionsState.PermissionState> permissions =
getPermissionsFromPermissionsState(
sharedUserSetting.getPermissionsState(), userId);
- if (permissions != null) {
- sharedUserPermissions.put(sharedUserName, permissions);
- }
+ sharedUserPermissions.put(sharedUserName, permissions);
}
runtimePermissions = new RuntimePermissionsState(version, fingerprint,
@@ -5260,15 +5256,11 @@
mPersistence.write(runtimePermissions, UserHandle.of(userId));
}
- @Nullable
+ @NonNull
private List<RuntimePermissionsState.PermissionState> getPermissionsFromPermissionsState(
@NonNull PermissionsState permissionsState, @UserIdInt int userId) {
List<PermissionState> permissionStates = permissionsState.getRuntimePermissionStates(
userId);
- if (permissionStates.isEmpty()) {
- return null;
- }
-
List<RuntimePermissionsState.PermissionState> permissions =
new ArrayList<>();
int permissionStatesSize = permissionStates.size();
@@ -5340,31 +5332,60 @@
boolean defaultPermissionsGranted = Build.FINGERPRINT.equals(fingerprint);
mDefaultPermissionsGranted.put(userId, defaultPermissionsGranted);
- for (Map.Entry<String, List<RuntimePermissionsState.PermissionState>> entry
- : runtimePermissions.getPackagePermissions().entrySet()) {
- String packageName = entry.getKey();
- List<RuntimePermissionsState.PermissionState> permissions = entry.getValue();
+ boolean isUpgradeToR = getInternalVersion().sdkVersion < Build.VERSION_CODES.R;
- PackageSetting packageSetting = mPackages.get(packageName);
- if (packageSetting == null) {
- Slog.w(PackageManagerService.TAG, "Unknown package:" + packageName);
- continue;
+ Map<String, List<RuntimePermissionsState.PermissionState>> packagePermissions =
+ runtimePermissions.getPackagePermissions();
+ int packagesSize = mPackages.size();
+ for (int i = 0; i < packagesSize; i++) {
+ String packageName = mPackages.keyAt(i);
+ PackageSetting packageSetting = mPackages.valueAt(i);
+
+ List<RuntimePermissionsState.PermissionState> permissions =
+ packagePermissions.get(packageName);
+ if (permissions != null) {
+ readPermissionsStateLpr(permissions, packageSetting.getPermissionsState(),
+ userId);
+ } else if (packageSetting.sharedUser == null && !isUpgradeToR) {
+ Slog.w(TAG, "Missing permission state for package: " + packageName);
+ generateFallbackPermissionsStateLpr(
+ packageSetting.pkg.getRequestedPermissions(),
+ packageSetting.pkg.getTargetSdkVersion(),
+ packageSetting.getPermissionsState(), userId);
}
- readPermissionsStateLpr(permissions, packageSetting.getPermissionsState(), userId);
}
- for (Map.Entry<String, List<RuntimePermissionsState.PermissionState>> entry
- : runtimePermissions.getSharedUserPermissions().entrySet()) {
- String sharedUserName = entry.getKey();
- List<RuntimePermissionsState.PermissionState> permissions = entry.getValue();
+ Map<String, List<RuntimePermissionsState.PermissionState>> sharedUserPermissions =
+ runtimePermissions.getSharedUserPermissions();
+ int sharedUsersSize = mSharedUsers.size();
+ for (int i = 0; i < sharedUsersSize; i++) {
+ String sharedUserName = mSharedUsers.keyAt(i);
+ SharedUserSetting sharedUserSetting = mSharedUsers.valueAt(i);
- SharedUserSetting sharedUserSetting = mSharedUsers.get(sharedUserName);
- if (sharedUserSetting == null) {
- Slog.w(PackageManagerService.TAG, "Unknown shared user:" + sharedUserName);
- continue;
+ List<RuntimePermissionsState.PermissionState> permissions =
+ sharedUserPermissions.get(sharedUserName);
+ if (permissions != null) {
+ readPermissionsStateLpr(permissions, sharedUserSetting.getPermissionsState(),
+ userId);
+ } else if (!isUpgradeToR) {
+ Slog.w(TAG, "Missing permission state for shared user: " + sharedUserName);
+ ArraySet<String> requestedPermissions = new ArraySet<>();
+ int targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
+ int sharedUserPackagesSize = sharedUserSetting.packages.size();
+ for (int packagesI = 0; packagesI < sharedUserPackagesSize; packagesI++) {
+ PackageSetting packageSetting = sharedUserSetting.packages.valueAt(
+ packagesI);
+ if (packageSetting == null || packageSetting.pkg == null
+ || !packageSetting.getInstalled(userId)) {
+ continue;
+ }
+ AndroidPackage pkg = packageSetting.pkg;
+ requestedPermissions.addAll(pkg.getRequestedPermissions());
+ targetSdkVersion = Math.min(targetSdkVersion, pkg.getTargetSdkVersion());
+ }
+ generateFallbackPermissionsStateLpr(requestedPermissions, targetSdkVersion,
+ sharedUserSetting.getPermissionsState(), userId);
}
- readPermissionsStateLpr(permissions, sharedUserSetting.getPermissionsState(),
- userId);
}
}
@@ -5395,6 +5416,30 @@
}
}
+ private void generateFallbackPermissionsStateLpr(
+ @NonNull Collection<String> requestedPermissions, int targetSdkVersion,
+ @NonNull PermissionsState permissionsState, @UserIdInt int userId) {
+ for (String permissionName : requestedPermissions) {
+ BasePermission permission = mPermissions.getPermission(permissionName);
+ if (Objects.equals(permission.getSourcePackageName(), PLATFORM_PACKAGE_NAME)
+ && permission.isRuntime() && !permission.isRemoved()) {
+ if (permission.isHardOrSoftRestricted() || permission.isImmutablyRestricted()) {
+ permissionsState.updatePermissionFlags(permission, userId,
+ PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT,
+ PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT);
+ }
+ if (targetSdkVersion < Build.VERSION_CODES.M) {
+ permissionsState.updatePermissionFlags(permission, userId,
+ PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED
+ | PackageManager.FLAG_PERMISSION_REVOKED_COMPAT,
+ PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED
+ | PackageManager.FLAG_PERMISSION_REVOKED_COMPAT);
+ permissionsState.grantRuntimePermission(permission, userId);
+ }
+ }
+ }
+ }
+
@GuardedBy("Settings.this.mLock")
private void readLegacyStateForUserSyncLPr(int userId) {
File permissionsFile = getUserRuntimePermissionsFile(userId);
diff --git a/services/core/java/com/android/server/security/FileIntegrityService.java b/services/core/java/com/android/server/security/FileIntegrityService.java
index e90c02a..8cf2d03 100644
--- a/services/core/java/com/android/server/security/FileIntegrityService.java
+++ b/services/core/java/com/android/server/security/FileIntegrityService.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.os.Build;
import android.os.IBinder;
import android.os.Process;
import android.os.SystemProperties;
@@ -55,7 +56,8 @@
private final IBinder mService = new IFileIntegrityService.Stub() {
@Override
public boolean isApkVeritySupported() {
- return SystemProperties.getInt("ro.apk_verity.mode", 0) == 2;
+ return Build.VERSION.FIRST_SDK_INT >= Build.VERSION_CODES.R
+ || SystemProperties.getInt("ro.apk_verity.mode", 0) == 2;
}
@Override
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
index 372c1f5..cd5e360 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
@@ -169,7 +169,7 @@
}
@Override
- public synchronized void serviceDied(long cookie) {
+ public synchronized void serviceDied(long cookie) {
Log.w(TAG, String.format("Underlying HAL driver died."));
for (Session session : mActiveSessions) {
session.moduleDied();
@@ -248,44 +248,77 @@
@Override
public int loadModel(@NonNull SoundModel model) {
Log.d(TAG, String.format("loadModel(model=%s)", model));
- synchronized (SoundTriggerModule.this) {
- checkValid();
- if (mNumLoadedModels == mProperties.maxSoundModels) {
- throw new RecoverableException(Status.RESOURCE_CONTENTION,
- "Maximum number of models loaded.");
+
+ // We must do this outside the lock, to avoid possible deadlocks with the remote process
+ // that provides the audio sessions, which may also be calling into us.
+ SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession =
+ mAudioSessionProvider.acquireSession();
+
+ try {
+ synchronized (SoundTriggerModule.this) {
+ checkValid();
+ if (mNumLoadedModels == mProperties.maxSoundModels) {
+ throw new RecoverableException(Status.RESOURCE_CONTENTION,
+ "Maximum number of models loaded.");
+ }
+ Model loadedModel = new Model();
+ int result = loadedModel.load(model, audioSession);
+ ++mNumLoadedModels;
+ return result;
}
- Model loadedModel = new Model();
- int result = loadedModel.load(model);
- ++mNumLoadedModels;
- return result;
+ } catch (Exception e) {
+ // We must do this outside the lock, to avoid possible deadlocks with the remote
+ // process that provides the audio sessions, which may also be calling into us.
+ mAudioSessionProvider.releaseSession(audioSession.mSessionHandle);
+ throw e;
}
}
@Override
public int loadPhraseModel(@NonNull PhraseSoundModel model) {
Log.d(TAG, String.format("loadPhraseModel(model=%s)", model));
- synchronized (SoundTriggerModule.this) {
- checkValid();
- if (mNumLoadedModels == mProperties.maxSoundModels) {
- throw new RecoverableException(Status.RESOURCE_CONTENTION,
- "Maximum number of models loaded.");
+
+ // We must do this outside the lock, to avoid possible deadlocks with the remote process
+ // that provides the audio sessions, which may also be calling into us.
+ SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession =
+ mAudioSessionProvider.acquireSession();
+
+ try {
+ synchronized (SoundTriggerModule.this) {
+ checkValid();
+ if (mNumLoadedModels == mProperties.maxSoundModels) {
+ throw new RecoverableException(Status.RESOURCE_CONTENTION,
+ "Maximum number of models loaded.");
+ }
+ Model loadedModel = new Model();
+ int result = loadedModel.load(model, audioSession);
+ ++mNumLoadedModels;
+ Log.d(TAG, String.format("loadPhraseModel()->%d", result));
+ return result;
}
- Model loadedModel = new Model();
- int result = loadedModel.load(model);
- ++mNumLoadedModels;
- Log.d(TAG, String.format("loadPhraseModel()->%d", result));
- return result;
+ } catch (Exception e) {
+ // We must do this outside the lock, to avoid possible deadlocks with the remote
+ // process that provides the audio sessions, which may also be calling into us.
+ mAudioSessionProvider.releaseSession(audioSession.mSessionHandle);
+ throw e;
}
}
@Override
public void unloadModel(int modelHandle) {
Log.d(TAG, String.format("unloadModel(handle=%d)", modelHandle));
+
+ int sessionId;
+
synchronized (SoundTriggerModule.this) {
checkValid();
- mLoadedModels.get(modelHandle).unload();
+ sessionId = mLoadedModels.get(modelHandle).unload();
--mNumLoadedModels;
}
+
+ // We must do this outside the lock, to avoid possible deadlocks with the remote process
+ // that provides the audio sessions, which may also be calling into us.
+ mAudioSessionProvider.releaseSession(sessionId);
}
@Override
@@ -413,45 +446,40 @@
SoundTriggerModule.this.notifyAll();
}
- private int load(@NonNull SoundModel model) {
+ private int load(@NonNull SoundModel model,
+ SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession) {
mModelType = model.type;
+ mSession = audioSession;
ISoundTriggerHw.SoundModel hidlModel = ConversionUtil.aidl2hidlSoundModel(model);
- mSession = mAudioSessionProvider.acquireSession();
- try {
- mHandle = mHalService.loadSoundModel(hidlModel, this, 0);
- } catch (Exception e) {
- mAudioSessionProvider.releaseSession(mSession.mSessionHandle);
- throw e;
- }
-
+ mHandle = mHalService.loadSoundModel(hidlModel, this, 0);
setState(ModelState.LOADED);
mLoadedModels.put(mHandle, this);
return mHandle;
}
- private int load(@NonNull PhraseSoundModel model) {
+ private int load(@NonNull PhraseSoundModel model,
+ SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession) {
mModelType = model.common.type;
+ mSession = audioSession;
ISoundTriggerHw.PhraseSoundModel hidlModel =
ConversionUtil.aidl2hidlPhraseSoundModel(model);
- mSession = mAudioSessionProvider.acquireSession();
- try {
- mHandle = mHalService.loadPhraseSoundModel(hidlModel, this, 0);
- } catch (Exception e) {
- mAudioSessionProvider.releaseSession(mSession.mSessionHandle);
- throw e;
- }
+ mHandle = mHalService.loadPhraseSoundModel(hidlModel, this, 0);
setState(ModelState.LOADED);
mLoadedModels.put(mHandle, this);
return mHandle;
}
- private void unload() {
- mAudioSessionProvider.releaseSession(mSession.mSessionHandle);
+ /**
+ * Unloads the model.
+ * @return The audio session handle.
+ */
+ private int unload() {
mHalService.unloadSoundModel(mHandle);
mLoadedModels.remove(mHandle);
+ return mSession.mSessionHandle;
}
private void startRecognition(@NonNull RecognitionConfig config) {
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 0502d0b..68a7188 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -581,6 +581,9 @@
/**
* Notifies the tracker that all windows of the app have been drawn.
+ *
+ * @return Non-null info if the activity was pending to draw, otherwise it might have been set
+ * to invisible (removed from active transition) or it was already drawn.
*/
@Nullable
TransitionInfoSnapshot notifyWindowsDrawn(@NonNull ActivityRecord r, long timestampNs) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 084800c..b3b8159 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -5202,11 +5202,17 @@
}
final TransitionInfoSnapshot info = mStackSupervisor
.getActivityMetricsLogger().notifyWindowsDrawn(this, timestampNs);
- final int windowsDrawnDelayMs = info != null ? info.windowsDrawnDelayMs : INVALID_DELAY;
- final @LaunchState int launchState = info != null ? info.getLaunchState() : -1;
- mStackSupervisor.reportActivityLaunchedLocked(false /* timeout */, this,
- windowsDrawnDelayMs, launchState);
- mStackSupervisor.stopWaitingForActivityVisible(this, windowsDrawnDelayMs);
+ final boolean validInfo = info != null;
+ final int windowsDrawnDelayMs = validInfo ? info.windowsDrawnDelayMs : INVALID_DELAY;
+ final @LaunchState int launchState = validInfo ? info.getLaunchState() : -1;
+ // The activity may have been requested to be invisible (another activity has been launched)
+ // so there is no valid info. But if it is the current top activity (e.g. sleeping), the
+ // invalid state is still reported to make sure the waiting result is notified.
+ if (validInfo || this == mDisplayContent.topRunningActivity()) {
+ mStackSupervisor.reportActivityLaunchedLocked(false /* timeout */, this,
+ windowsDrawnDelayMs, launchState);
+ mStackSupervisor.stopWaitingForActivityVisible(this, windowsDrawnDelayMs);
+ }
finishLaunchTickingLocked();
if (task != null) {
task.hasBeenVisible = true;
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index ab8e975..3c39b39 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -111,6 +111,7 @@
import static com.android.server.wm.TaskProto.ADJUST_IME_AMOUNT;
import static com.android.server.wm.TaskProto.ANIMATING_BOUNDS;
import static com.android.server.wm.TaskProto.BOUNDS;
+import static com.android.server.wm.TaskProto.CREATED_BY_ORGANIZER;
import static com.android.server.wm.TaskProto.DEFER_REMOVAL;
import static com.android.server.wm.TaskProto.DISPLAYED_BOUNDS;
import static com.android.server.wm.TaskProto.DISPLAY_ID;
@@ -731,53 +732,14 @@
newBounds);
hasNewOverrideBounds = true;
}
-
- // Use override windowing mode to prevent extra bounds changes if inheriting the mode.
- if (overrideWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
- || overrideWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
- // If entering split screen or if something about the available split area changes,
- // recalculate the split windows to match the new configuration.
- if (rotationChanged || windowingModeChanged
- || prevDensity != getConfiguration().densityDpi
- || prevScreenW != getConfiguration().screenWidthDp
- || prevScreenH != getConfiguration().screenHeightDp) {
- calculateDockedBoundsForConfigChange(newParentConfig, newBounds);
- hasNewOverrideBounds = true;
- }
- }
}
if (windowingModeChanged) {
- // Use override windowing mode to prevent extra bounds changes if inheriting the mode.
- if (overrideWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- getStackDockedModeBounds(null /* dockedBounds */, null /* currentTempTaskBounds */,
- newBounds /* outStackBounds */, mTmpRect2 /* outTempTaskBounds */);
- // immediately resize so docked bounds are available in onSplitScreenModeActivated
- setTaskDisplayedBounds(null);
- setTaskBounds(newBounds);
- setBounds(newBounds);
- newBounds.set(newBounds);
- } else if (overrideWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
- Rect dockedBounds = display.getRootSplitScreenPrimaryTask().getBounds();
- final boolean isMinimizedDock =
- display.mDisplayContent.getDockedDividerController().isMinimizedDock();
- if (isMinimizedDock) {
- Task topTask = display.getRootSplitScreenPrimaryTask().getTopMostTask();
- if (topTask != null) {
- dockedBounds = topTask.getBounds();
- }
- }
- getStackDockedModeBounds(dockedBounds, null /* currentTempTaskBounds */,
- newBounds /* outStackBounds */, mTmpRect2 /* outTempTaskBounds */);
- hasNewOverrideBounds = true;
- }
+ display.onStackWindowingModeChanged(this);
}
if (hasNewOverrideBounds) {
- if (inSplitScreenPrimaryWindowingMode()) {
- mStackSupervisor.resizeDockedStackLocked(new Rect(newBounds),
- null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
- null /* tempOtherTaskBounds */, null /* tempOtherTaskInsetBounds */,
- PRESERVE_WINDOWS, true /* deferResume */);
+ if (inSplitScreenWindowingMode()) {
+ setBounds(newBounds);
} else if (overrideWindowingMode != WINDOWING_MODE_PINNED) {
// For pinned stack, resize is now part of the {@link WindowContainerTransaction}
resize(new Rect(newBounds), null /* tempTaskBounds */,
@@ -920,11 +882,7 @@
// warning toast about it.
mAtmService.getTaskChangeNotificationController()
.notifyActivityDismissingDockedStack();
- final ActivityStack primarySplitStack = display.getRootSplitScreenPrimaryTask();
- primarySplitStack.setWindowingModeInSurfaceTransaction(WINDOWING_MODE_UNDEFINED,
- false /* animate */, false /* showRecents */,
- false /* enteringSplitScreenMode */, true /* deferEnsuringVisibility */,
- primarySplitStack == this ? creating : false);
+ display.onSplitScreenModeDismissed();
}
}
@@ -1218,7 +1176,7 @@
display.getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN);
if (topFullScreenStack != null) {
final ActivityStack primarySplitScreenStack = display.getRootSplitScreenPrimaryTask();
- if (display.getIndexOf(topFullScreenStack)
+ if (primarySplitScreenStack != null && display.getIndexOf(topFullScreenStack)
> display.getIndexOf(primarySplitScreenStack)) {
primarySplitScreenStack.moveToFront(reason + " splitScreenToTop");
}
@@ -3994,17 +3952,6 @@
? ((WindowContainer) oldParent).getDisplayContent() : null;
super.onParentChanged(newParent, oldParent);
- if (display != null && inSplitScreenPrimaryWindowingMode()
- // only do this for the base stack
- && !newParent.inSplitScreenPrimaryWindowingMode()) {
- // If we created a docked stack we want to resize it so it resizes all other stacks
- // in the system.
- getStackDockedModeBounds(null /* dockedBounds */, null /* currentTempTaskBounds */,
- mTmpRect /* outStackBounds */, mTmpRect2 /* outTempTaskBounds */);
- mStackSupervisor.resizeDockedStackLocked(getRequestedOverrideBounds(), mTmpRect,
- mTmpRect2, null, null, PRESERVE_WINDOWS);
- }
-
// Resume next focusable stack after reparenting to another display if we aren't removing
// the prevous display.
if (oldDisplay != null && oldDisplay.isRemoving()) {
@@ -4950,6 +4897,12 @@
}
@Override
+ public SurfaceControl getParentSurfaceControl() {
+ // Tile is a "virtual" parent, so we need to intercept the parent surface here
+ return mTile != null ? mTile.getSurfaceControl() : super.getParentSurfaceControl();
+ }
+
+ @Override
void removeImmediately() {
// TODO(task-hierarchy): remove this override when tiles are in hierarchy
if (mTile != null) {
@@ -5002,6 +4955,10 @@
if (!matchParentBounds()) {
final Rect bounds = getRequestedOverrideBounds();
bounds.dumpDebug(proto, BOUNDS);
+ } else if (getStack().getTile() != null) {
+ // use tile's bounds here for cts.
+ final Rect bounds = getStack().getTile().getRequestedOverrideBounds();
+ bounds.dumpDebug(proto, BOUNDS);
}
getOverrideDisplayedBounds().dumpDebug(proto, DISPLAYED_BOUNDS);
mAdjustedBounds.dumpDebug(proto, ADJUSTED_BOUNDS);
@@ -5021,6 +4978,8 @@
proto.write(SURFACE_HEIGHT, mSurfaceControl.getHeight());
}
+ proto.write(CREATED_BY_ORGANIZER, this instanceof TaskTile);
+
proto.end(token);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index a582f21..a2e8801 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -2447,7 +2447,9 @@
// split-screen in split-screen.
mService.getTaskChangeNotificationController()
.notifyActivityDismissingDockedStack();
- moveTasksToFullscreenStackLocked(dockedStack, actualStack == dockedStack);
+ dockedStack.getDisplay().onSplitScreenModeDismissed();
+ dockedStack.getDisplay().ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS,
+ true /* notifyClients */);
}
return;
}
@@ -2809,7 +2811,7 @@
final DisplayContent display = task.getStack().getDisplay();
final ActivityStack topSecondaryStack =
display.getTopStackInWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
- if (topSecondaryStack.isActivityTypeHome()) {
+ if (topSecondaryStack != null && topSecondaryStack.isActivityTypeHome()) {
// If the home activity is the top split-screen secondary stack, then the
// primary split-screen stack is in the minimized mode which means it can't
// receive input keys, so we should move the focused app to the home app so that
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index f2917c5..f777e90 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -37,7 +37,6 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -229,6 +228,7 @@
import android.view.IRecentsAnimationRunner;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
+import android.view.WindowContainerTransaction;
import android.view.WindowManager;
import com.android.internal.R;
@@ -2269,6 +2269,9 @@
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
try {
+ if (WindowConfiguration.isSplitScreenWindowingMode(windowingMode)) {
+ return setTaskWindowingModeSplitScreen(taskId, windowingMode, toTop);
+ }
final Task task = mRootWindowContainer.anyTaskForId(taskId,
MATCH_TASK_IN_STACKS_ONLY);
if (task == null) {
@@ -2286,10 +2289,16 @@
}
final ActivityStack stack = task.getStack();
+ // Convert some windowing-mode changes into root-task reparents for split-screen.
+ if (stack.getTile() != null) {
+ stack.getDisplay().onSplitScreenModeDismissed();
+ }
if (toTop) {
stack.moveToFront("setTaskWindowingMode", task);
}
stack.setWindowingMode(windowingMode);
+ stack.getDisplay().ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS,
+ true /* notifyClients */);
return true;
} finally {
Binder.restoreCallingIdentity(ident);
@@ -2719,36 +2728,8 @@
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
try {
- if (isInLockTaskMode()) {
- Slog.w(TAG, "setTaskWindowingModeSplitScreenPrimary: Is in lock task mode="
- + getLockTaskModeState());
- return false;
- }
-
- final Task task = mRootWindowContainer.anyTaskForId(taskId,
- MATCH_TASK_IN_STACKS_ONLY);
- if (task == null) {
- Slog.w(TAG, "setTaskWindowingModeSplitScreenPrimary: No task for id=" + taskId);
- return false;
- }
- if (!task.isActivityTypeStandardOrUndefined()) {
- throw new IllegalArgumentException("setTaskWindowingMode: Attempt to move"
- + " non-standard task " + taskId + " to split-screen windowing mode");
- }
-
- if (DEBUG_STACK) Slog.d(TAG_STACK,
- "setTaskWindowingModeSplitScreenPrimary: moving task=" + taskId
- + " to createMode=" + createMode + " toTop=" + toTop);
- mWindowManager.setDockedStackCreateStateLocked(createMode, initialBounds);
- final int windowingMode = task.getWindowingMode();
- final ActivityStack stack = task.getStack();
- if (toTop) {
- stack.moveToFront("setTaskWindowingModeSplitScreenPrimary", task);
- }
- stack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, animate, showRecents,
- false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */,
- false /* creating */);
- return windowingMode != task.getWindowingMode();
+ return setTaskWindowingModeSplitScreen(taskId, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
+ toTop);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -2756,6 +2737,49 @@
}
/**
+ * Moves the specified task into a split-screen tile.
+ */
+ private boolean setTaskWindowingModeSplitScreen(int taskId, int windowingMode, boolean toTop) {
+ if (!WindowConfiguration.isSplitScreenWindowingMode(windowingMode)) {
+ throw new IllegalArgumentException("Calling setTaskWindowingModeSplitScreen with non"
+ + "split-screen mode: " + windowingMode);
+ }
+ if (isInLockTaskMode()) {
+ Slog.w(TAG, "setTaskWindowingModeSplitScreen: Is in lock task mode="
+ + getLockTaskModeState());
+ return false;
+ }
+
+ final Task task = mRootWindowContainer.anyTaskForId(taskId,
+ MATCH_TASK_IN_STACKS_ONLY);
+ if (task == null) {
+ Slog.w(TAG, "setTaskWindowingModeSplitScreenPrimary: No task for id=" + taskId);
+ return false;
+ }
+ if (!task.isActivityTypeStandardOrUndefined()) {
+ throw new IllegalArgumentException("setTaskWindowingMode: Attempt to move"
+ + " non-standard task " + taskId + " to split-screen windowing mode");
+ }
+
+ final int prevMode = task.getWindowingMode();
+ final ActivityStack stack = task.getStack();
+ TaskTile tile = null;
+ for (int i = stack.getDisplay().getStackCount() - 1; i >= 0; --i) {
+ tile = stack.getDisplay().getStackAt(i).asTile();
+ if (tile != null && tile.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+ break;
+ }
+ }
+ if (tile == null) {
+ throw new IllegalStateException("Can't enter split without associated tile");
+ }
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.reparent(stack.mRemoteToken, tile.mRemoteToken, toTop);
+ mTaskOrganizerController.applyContainerTransaction(wct, null);
+ return prevMode != task.getWindowingMode();
+ }
+
+ /**
* Removes stacks in the input windowing modes from the system if they are of activity type
* ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
*/
@@ -3963,46 +3987,6 @@
}
/**
- * Dismisses split-screen multi-window mode.
- * @param toTop If true the current primary split-screen stack will be placed or left on top.
- */
- @Override
- public void dismissSplitScreenMode(boolean toTop) {
- enforceCallerIsRecentsOrHasPermission(
- MANAGE_ACTIVITY_STACKS, "dismissSplitScreenMode()");
- final long ident = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- final ActivityStack stack =
- mRootWindowContainer.getDefaultDisplay().getRootSplitScreenPrimaryTask();
- if (stack == null) {
- Slog.w(TAG, "dismissSplitScreenMode: primary split-screen stack not found.");
- return;
- }
-
- if (toTop) {
- // Caller wants the current split-screen primary stack to be the top stack after
- // it goes fullscreen, so move it to the front.
- stack.moveToFront("dismissSplitScreenMode");
- } else {
- // In this case the current split-screen primary stack shouldn't be the top
- // stack after it goes fullscreen, so we move the focus to the top-most
- // split-screen secondary stack next to it.
- final ActivityStack otherStack = stack.getDisplay().getTopStackInWindowingMode(
- WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
- if (otherStack != null) {
- otherStack.moveToFront("dismissSplitScreenMode_other");
- }
- }
-
- stack.setWindowingMode(WINDOWING_MODE_UNDEFINED);
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- /**
* Dismisses Pip
* @param animate True if the dismissal should be animated.
* @param animationDuration The duration of the resize animation in milliseconds or -1 if the
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index b0c6c64..55a41ab 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -17,7 +17,6 @@
package com.android.server.wm;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -31,6 +30,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.app.WindowConfiguration.isSplitScreenWindowingMode;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -55,9 +55,6 @@
import static android.view.View.GONE;
import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
-import static android.view.WindowManager.DOCKED_BOTTOM;
-import static android.view.WindowManager.DOCKED_INVALID;
-import static android.view.WindowManager.DOCKED_TOP;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
@@ -2626,54 +2623,9 @@
void adjustForImeIfNeeded() {
final WindowState imeWin = mInputMethodWindow;
- final boolean imeVisible = imeWin != null && imeWin.isVisibleLw() && imeWin.isDisplayedLw()
- && !mDividerControllerLocked.isImeHideRequested();
- final ActivityStack dockedStack = getRootSplitScreenPrimaryTask();
- final boolean dockVisible = dockedStack != null;
- final Task topDockedTask = dockVisible ? dockedStack.getTask((t) -> true): null;
- final ActivityStack imeTargetStack = mWmService.getImeFocusStackLocked();
- final int imeDockSide = (dockVisible && imeTargetStack != null) ?
- imeTargetStack.getDockSide() : DOCKED_INVALID;
- final boolean imeOnTop = (imeDockSide == DOCKED_TOP);
- final boolean imeOnBottom = (imeDockSide == DOCKED_BOTTOM);
+ final boolean imeVisible = imeWin != null && imeWin.isVisibleLw()
+ && imeWin.isDisplayedLw();
final int imeHeight = mDisplayFrames.getInputMethodWindowVisibleHeight();
- final boolean imeHeightChanged = imeVisible &&
- imeHeight != mDividerControllerLocked.getImeHeightAdjustedFor();
-
- // This includes a case where the docked stack is unminimizing and IME is visible for the
- // bottom side stack. The condition prevents adjusting the override task bounds for IME to
- // the minimized docked stack bounds.
- final boolean dockMinimized = mDividerControllerLocked.isMinimizedDock()
- || (topDockedTask != null && imeOnBottom && !dockedStack.isAdjustedForIme()
- && dockedStack.getBounds().height() < topDockedTask.getBounds().height());
-
- // The divider could be adjusted for IME position, or be thinner than usual,
- // or both. There are three possible cases:
- // - If IME is visible, and focus is on top, divider is not moved for IME but thinner.
- // - If IME is visible, and focus is on bottom, divider is moved for IME and thinner.
- // - If IME is not visible, divider is not moved and is normal width.
-
- if (imeVisible && dockVisible && (imeOnTop || imeOnBottom) && !dockMinimized) {
- for (int i = mTaskContainers.getChildCount() - 1; i >= 0; --i) {
- final ActivityStack stack = mTaskContainers.getChildAt(i);
- final boolean isDockedOnBottom = stack.getDockSide() == DOCKED_BOTTOM;
- if (stack.isVisible() && (imeOnBottom || isDockedOnBottom)
- && stack.inSplitScreenWindowingMode()) {
- stack.setAdjustedForIme(imeWin, imeOnBottom && imeHeightChanged);
- } else {
- stack.resetAdjustedForIme(false);
- }
- }
- mDividerControllerLocked.setAdjustedForIme(
- imeOnBottom /*ime*/, true /*divider*/, true /*animate*/, imeWin, imeHeight);
- } else {
- for (int i = mTaskContainers.getChildCount() - 1; i >= 0; --i) {
- final ActivityStack stack = mTaskContainers.getChildAt(i);
- stack.resetAdjustedForIme(!dockVisible);
- }
- mDividerControllerLocked.setAdjustedForIme(
- false /*ime*/, false /*divider*/, dockVisible /*animate*/, imeWin, imeHeight);
- }
mPinnedStackControllerLocked.setAdjustedForIme(imeVisible, imeHeight);
}
@@ -3453,8 +3405,6 @@
mInputMethodTarget = target;
mInputMethodTargetWaitingAnim = targetWaitingAnim;
assignWindowLayers(false /* setLayoutNeeded */);
- mInputMethodControlTarget = computeImeControlTarget();
- mInsetsStateController.onImeControlTargetChanged(mInputMethodControlTarget);
updateImeParent();
}
@@ -3465,7 +3415,7 @@
*
* @see #getImeControlTarget()
*/
- void updateImeControlTarget(WindowState target) {
+ void updateImeControlTarget(InsetsControlTarget target) {
mInputMethodControlTarget = target;
mInsetsStateController.onImeControlTargetChanged(mInputMethodControlTarget);
}
@@ -3503,13 +3453,13 @@
* Computes which control-target the IME should be attached to.
*/
@VisibleForTesting
- InsetsControlTarget computeImeControlTarget() {
+ InsetsControlTarget computeImeControlTarget(InsetsControlTarget appTarget) {
if (!isImeAttachedToApp() && mRemoteInsetsControlTarget != null) {
return mRemoteInsetsControlTarget;
}
// Otherwise, we just use the ime target
- return mInputMethodTarget;
+ return appTarget;
}
void setLayoutNeeded() {
@@ -3897,7 +3847,7 @@
}
}
- /** @returns the orientation of the display when it's rotation is ROTATION_0. */
+ /** @return the orientation of the display when it's rotation is ROTATION_0. */
int getNaturalOrientation() {
return mBaseDisplayWidth < mBaseDisplayHeight
? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
@@ -4338,8 +4288,6 @@
}
} else {
mRootSplitScreenPrimaryTask = stack;
- mDisplayContent.onSplitScreenModeActivated();
- mDividerControllerLocked.notifyDockedStackExistsChanged(true);
}
}
}
@@ -4351,11 +4299,6 @@
mRootPinnedTask = null;
} else if (stack == mRootSplitScreenPrimaryTask) {
mRootSplitScreenPrimaryTask = null;
- mDisplayContent.onSplitScreenModeDismissed();
- // Re-set the split-screen create mode whenever the split-screen stack is removed.
- mWmService.setDockedStackCreateStateLocked(
- SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, null /* initialBounds */);
- mDividerControllerLocked.notifyDockedStackExistsChanged(false);
}
}
@@ -5771,6 +5714,33 @@
return createStackUnchecked(windowingMode, activityType, stackId, onTop, info, intent);
}
+ /** @return the tile to create the next stack in. */
+ private TaskTile updateLaunchTile(int windowingMode) {
+ if (!isSplitScreenWindowingMode(windowingMode)) {
+ // Only split-screen windowing modes interact with tiles.
+ return null;
+ }
+ for (int i = getStackCount() - 1; i >= 0; --i) {
+ final TaskTile t = getStackAt(i).asTile();
+ if (t == null || t.getRequestedOverrideWindowingMode() != windowingMode) {
+ continue;
+ }
+ // If not already set, pick a launch tile which is not the one we are launching
+ // into.
+ if (mLaunchTile == null) {
+ for (int j = 0, n = getStackCount(); j < n; ++j) {
+ TaskTile tt = getStackAt(j).asTile();
+ if (tt != t) {
+ mLaunchTile = tt;
+ break;
+ }
+ }
+ }
+ return t;
+ }
+ return mLaunchTile;
+ }
+
@VisibleForTesting
ActivityStack createStackUnchecked(int windowingMode, int activityType,
int stackId, boolean onTop, ActivityInfo info, Intent intent) {
@@ -5783,13 +5753,20 @@
info.applicationInfo = new ApplicationInfo();
}
+ TaskTile tile = updateLaunchTile(windowingMode);
+ if (tile != null) {
+ // Since this stack will be put into a tile, its windowingMode will be inherited.
+ windowingMode = WINDOWING_MODE_UNDEFINED;
+ }
final ActivityStack stack = new ActivityStack(this, stackId,
mRootWindowContainer.mStackSupervisor, activityType, info, intent);
addStack(stack, onTop ? POSITION_TOP : POSITION_BOTTOM);
stack.setWindowingMode(windowingMode, false /* animate */, false /* showRecents */,
false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */,
true /* creating */);
-
+ if (tile != null) {
+ tile.addChild(stack, 0 /* index */);
+ }
return stack;
}
@@ -6009,16 +5986,15 @@
void onSplitScreenModeDismissed() {
mAtmService.deferWindowLayout();
try {
- // Adjust the windowing mode of any stack in secondary split-screen to fullscreen.
+ mLaunchTile = null;
for (int i = getStackCount() - 1; i >= 0; --i) {
- final ActivityStack otherStack = getStackAt(i);
- if (!otherStack.inSplitScreenSecondaryWindowingMode()) {
- continue;
+ final TaskTile t = getStackAt(i).asTile();
+ if (t != null) {
+ t.removeAllChildren();
}
- otherStack.setWindowingMode(WINDOWING_MODE_UNDEFINED, false /* animate */,
- false /* showRecents */, false /* enteringSplitScreenMode */,
- true /* deferEnsuringVisibility */, false /* creating */);
}
+ mDividerControllerLocked.setMinimizedDockedStack(false /* minimized */,
+ false /* animate */);
} finally {
final ActivityStack topFullscreenStack =
getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN);
@@ -6036,27 +6012,6 @@
}
}
- void onSplitScreenModeActivated() {
- mAtmService.deferWindowLayout();
- try {
- // Adjust the windowing mode of any affected by split-screen to split-screen secondary.
- final ActivityStack splitScreenPrimaryStack = getRootSplitScreenPrimaryTask();
- for (int i = getStackCount() - 1; i >= 0; --i) {
- final ActivityStack otherStack = getStackAt(i);
- if (otherStack == splitScreenPrimaryStack
- || !otherStack.affectedBySplitScreenResize()) {
- continue;
- }
- otherStack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
- false /* animate */, false /* showRecents */,
- true /* enteringSplitScreenMode */, true /* deferEnsuringVisibility */,
- false /* creating */);
- }
- } finally {
- mAtmService.continueWindowLayout();
- }
- }
-
/**
* Returns true if the {@param windowingMode} is supported based on other parameters passed in.
* @param windowingMode The windowing mode we are checking support for.
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 38b0ca8..d719dbe3 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -24,7 +24,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
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.Display.TYPE_INTERNAL;
import static android.view.InsetsState.ITYPE_BOTTOM_GESTURES;
import static android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
import static android.view.InsetsState.ITYPE_LEFT_GESTURES;
@@ -2983,11 +2983,12 @@
/**
* 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.
+ *
+ * The radius is only valid for internal displays, since the corner radius of external displays
+ * is not known at build time when window corners are configured.
*/
float getWindowCornerRadius() {
- return mDisplayContent.getDisplay().getType() == TYPE_BUILT_IN
+ return mDisplayContent.getDisplay().getType() == TYPE_INTERNAL
? ScreenDecorationsUtils.getWindowCornerRadius(mContext.getResources()) : 0f;
}
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index e71371a..539853b 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -525,9 +525,15 @@
}
mService.mH.removeCallbacks(mDisplayRotationHandlerTimeout);
mIsWaitingForRemoteRotation = false;
- mDisplayContent.sendNewConfiguration();
- if (t != null) {
- mService.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null);
+ mService.mAtmService.deferWindowLayout();
+ try {
+ mDisplayContent.sendNewConfiguration();
+ if (t != null) {
+ mService.mAtmService.mTaskOrganizerController.applyContainerTransaction(t,
+ null /* organizer */);
+ }
+ } finally {
+ mService.mAtmService.continueWindowLayout();
}
}
}
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 872379e..6431e11 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -745,7 +745,7 @@
* @param minimizedDock Whether the docked stack is currently minimized.
* @param animate Whether to animate the change.
*/
- private void setMinimizedDockedStack(boolean minimizedDock, boolean animate) {
+ void setMinimizedDockedStack(boolean minimizedDock, boolean animate) {
final boolean wasMinimized = mMinimizedDock;
mMinimizedDock = minimizedDock;
if (minimizedDock == wasMinimized) {
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 00947d7..44034ed 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -31,14 +31,14 @@
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
+import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
+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.KeyguardControllerProto.AOD_SHOWING;
import static com.android.server.wm.KeyguardControllerProto.KEYGUARD_OCCLUDED_STATES;
import static com.android.server.wm.KeyguardControllerProto.KEYGUARD_SHOWING;
import static com.android.server.wm.KeyguardOccludedProto.DISPLAY_ID;
import static com.android.server.wm.KeyguardOccludedProto.KEYGUARD_OCCLUDED;
-import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import android.os.IBinder;
import android.os.RemoteException;
@@ -411,8 +411,7 @@
if (stack == null) {
return;
}
- mStackSupervisor.moveTasksToFullscreenStackLocked(stack,
- stack.isFocusedStackOnDisplay());
+ mRootWindowContainer.getDefaultDisplay().onSplitScreenModeDismissed();
}
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 944e0ae..be68c5d 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -20,7 +20,6 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
-import static android.view.WindowManager.DOCKED_INVALID;
import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
@@ -414,12 +413,7 @@
}
// Save the minimized home height
- final ActivityStack dockedStack =
- mDisplayContent.getRootSplitScreenPrimaryTask();
- mDisplayContent.getDockedDividerController().getHomeStackBoundsInDockedMode(
- mDisplayContent.getConfiguration(),
- dockedStack == null ? DOCKED_INVALID : dockedStack.getDockSide(),
- mMinimizedHomeBounds);
+ mMinimizedHomeBounds = mDisplayContent.getRootHomeTask().getBounds();
mService.mWindowPlacerLocked.performSurfacePlacement();
@@ -842,8 +836,8 @@
mTask = task;
mIsRecentTaskInvisible = isRecentTaskInvisible;
final WindowContainer container = mTask.getParent();
- container.getRelativeDisplayedPosition(mPosition);
mBounds.set(container.getDisplayedBounds());
+ mPosition.set(mBounds.left, mBounds.top);
}
RemoteAnimationTarget createRemoteAnimationTarget() {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 0aed691..c7f2cc7 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -199,6 +199,7 @@
static final int INVALID_MIN_SIZE = -1;
private float mShadowRadius = 0;
private final Rect mLastSurfaceCrop = new Rect();
+ private static final boolean ENABLE_FREEFORM_COMPOSITOR_SHADOWS = false;
/**
* The modes to control how the stack is moved to the front when calling {@link Task#reparent}.
@@ -2144,12 +2145,6 @@
// For floating tasks, calculate the smallest width from the bounds of the task
inOutConfig.smallestScreenWidthDp = (int) (
Math.min(mTmpFullBounds.width(), mTmpFullBounds.height()) / density);
- } else if (WindowConfiguration.isSplitScreenWindowingMode(windowingMode)) {
- // Iterating across all screen orientations, and return the minimum of the task
- // width taking into account that the bounds might change because the snap
- // algorithm snaps to a different value
- inOutConfig.smallestScreenWidthDp =
- getSmallestScreenWidthDpForDockedBounds(mTmpFullBounds);
}
// otherwise, it will just inherit
}
@@ -2553,8 +2548,10 @@
}
private void updateSurfaceCrop() {
+ // TODO(b/149585281) remove when root task has the correct bounds for freeform
// Only update the crop if we are drawing shadows on the task.
- if (mSurfaceControl == null || !mWmService.mRenderShadowsInCompositor) {
+ if (mSurfaceControl == null || !mWmService.mRenderShadowsInCompositor
+ || !isRootTask() || !ENABLE_FREEFORM_COMPOSITOR_SHADOWS) {
return;
}
@@ -2978,8 +2975,14 @@
}
@Override
+ void onSurfaceShown(SurfaceControl.Transaction t) {
+ super.onSurfaceShown(t);
+ t.unsetColor(mSurfaceControl);
+ }
+
+ @Override
SurfaceControl.Builder makeSurface() {
- return super.makeSurface().setMetadata(METADATA_TASK_ID, mTaskId);
+ return super.makeSurface().setColorLayer().setMetadata(METADATA_TASK_ID, mTaskId);
}
boolean isTaskAnimating() {
@@ -3248,6 +3251,10 @@
return this;
}
+ TaskTile asTile() {
+ return null;
+ }
+
// TODO(task-merge): Figure-out how this should work with hierarchy tasks.
boolean shouldBeVisible(ActivityRecord starting) {
return true;
@@ -3934,7 +3941,8 @@
return dipToPixel(PINNED_WINDOWING_MODE_ELEVATION_IN_DIP,
mDisplayContent.getDisplayMetrics());
}
- if (inFreeformWindowingMode()) {
+ // TODO(b/149585281) remove when root task has the correct bounds for freeform
+ if (ENABLE_FREEFORM_COMPOSITOR_SHADOWS && inFreeformWindowingMode()) {
final int elevation = taskIsFocused
? DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP : DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
return dipToPixel(elevation, mDisplayContent.getDisplayMetrics());
@@ -3949,7 +3957,7 @@
*/
private void updateShadowsRadius(boolean taskIsFocused,
SurfaceControl.Transaction pendingTransaction) {
- if (!mWmService.mRenderShadowsInCompositor) return;
+ if (!mWmService.mRenderShadowsInCompositor || !isRootTask()) return;
final float newShadowRadius = getShadowRadius(taskIsFocused);
if (mShadowRadius != newShadowRadius) {
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 4b13a0c..4d5621c 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -492,6 +492,10 @@
if (!(container instanceof Task)) {
throw new IllegalArgumentException("Invalid container in hierarchy op");
}
+ if (container.getDisplayContent() == null) {
+ Slog.w(TAG, "Container is no longer attached: " + container);
+ return 0;
+ }
if (hop.isReparent()) {
// special case for tiles since they are "virtual" parents
if (container instanceof ActivityStack && ((ActivityStack) container).isRootTask()) {
diff --git a/services/core/java/com/android/server/wm/TaskTile.java b/services/core/java/com/android/server/wm/TaskTile.java
index 369db05..74d5c33 100644
--- a/services/core/java/com/android/server/wm/TaskTile.java
+++ b/services/core/java/com/android/server/wm/TaskTile.java
@@ -31,7 +31,6 @@
import android.graphics.Rect;
import android.os.IBinder;
import android.util.Slog;
-import android.view.SurfaceControl;
import java.util.ArrayList;
import java.util.Comparator;
@@ -78,30 +77,9 @@
// Virtual parent, so don't notify children.
}
- /**
- * If there is a disconnection, this will clean up any vestigial surfaces left on the tile
- * leash by moving known children to a new surfacecontrol and then removing the old one.
- */
- void cleanupSurfaces() {
- if (mSurfaceControl == null) {
- return;
- }
- SurfaceControl oldSurface = mSurfaceControl;
- WindowContainer parentWin = getParent();
- if (parentWin == null) {
- return;
- }
- mSurfaceControl = parentWin.makeChildSurface(null).setName("TaskTile " + mTaskId + " - "
- + getRequestedOverrideWindowingMode()).setContainerLayer().build();
- SurfaceControl.Transaction t = parentWin.getPendingTransaction();
- t.show(mSurfaceControl);
- for (int i = 0; i < mChildren.size(); ++i) {
- if (mChildren.get(i).getSurfaceControl() == null) {
- continue;
- }
- mChildren.get(i).reparentSurfaceControl(t, mSurfaceControl);
- }
- t.remove(oldSurface);
+ @Override
+ TaskTile asTile() {
+ return this;
}
@Override
@@ -215,6 +193,12 @@
super.removeImmediately();
}
+ @Override
+ void taskOrganizerDied() {
+ super.taskOrganizerDied();
+ removeImmediately();
+ }
+
static TaskTile forToken(IBinder token) {
try {
return (TaskTile) ((TaskToken) token).getContainer();
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 0bb4e03..294c36a 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -365,6 +365,7 @@
// build a surface.
setSurfaceControl(makeSurface().build());
getPendingTransaction().show(mSurfaceControl);
+ onSurfaceShown(getPendingTransaction());
updateSurfacePosition();
} else {
// If we have a surface but a new parent, we just need to perform a reparent. Go through
@@ -383,6 +384,13 @@
scheduleAnimation();
}
+ /**
+ * Called when the surface is shown for the first time.
+ */
+ void onSurfaceShown(Transaction t) {
+ // do nothing
+ }
+
// Temp. holders for a chain of containers we are currently processing.
private final LinkedList<WindowContainer> mTmpChain1 = new LinkedList<>();
private final LinkedList<WindowContainer> mTmpChain2 = new LinkedList<>();
@@ -1908,7 +1916,7 @@
@Override
public Builder makeAnimationLeash() {
- return makeSurface();
+ return makeSurface().setContainerLayer();
}
@Override
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 2bb6703..1b0d177 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7320,7 +7320,9 @@
synchronized (mGlobalLock) {
final WindowState imeTarget = mWindowMap.get(imeTargetWindowToken);
if (imeTarget != null) {
- imeTarget.getDisplayContent().updateImeControlTarget(imeTarget);
+ InsetsControlTarget controlTarget =
+ imeTarget.getDisplayContent().computeImeControlTarget(imeTarget);
+ imeTarget.getDisplayContent().updateImeControlTarget(controlTarget);
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 46081c5..ed4e684 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2662,18 +2662,6 @@
mWmService.mTaskSnapshotController.onAppDied(win.mActivityRecord);
}
win.removeIfPossible(shouldKeepVisibleDeadAppWindow());
- if (win.mAttrs.type == TYPE_DOCK_DIVIDER) {
- // The owner of the docked divider died :( We reset the docked stack,
- // just in case they have the divider at an unstable position. Better
- // also reset drag resizing state, because the owner can't do it
- // anymore.
- final ActivityStack stack =
- dc.getRootSplitScreenPrimaryTask();
- if (stack != null) {
- stack.resetDockedStackToMiddle();
- }
- resetSplitScreenResizing = true;
- }
} else if (mHasSurface) {
Slog.e(TAG, "!!! LEAK !!! Window removed but surface still valid.");
WindowState.this.removeIfPossible();
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index d0eaa48..e1615af 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -645,10 +645,12 @@
Return<void> gnssSvStatusCb_2_0(const hidl_vec<IGnssCallback_V2_0::GnssSvInfo>& svInfoList) override {
return gnssSvStatusCbImpl(svInfoList);
}
+
+ // New in 2.1
Return<void> gnssSvStatusCb_2_1(const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList) override {
return gnssSvStatusCbImpl(svInfoList);
}
- Return<void> gnssSetCapabilitesCbImpl(uint32_t capabilities, bool hasSubHalCapabilityFlags);
+ Return<void> gnssSetCapabilitiesCb_2_1(uint32_t capabilities) override;
// TODO: Reconsider allocation cost vs threadsafety on these statics
static const char* sNmeaString;
@@ -851,6 +853,10 @@
return GnssCallback::gnssSetCapabilitesCb(capabilities);
}
+Return<void> GnssCallback::gnssSetCapabilitiesCb_2_1(uint32_t capabilities) {
+ return GnssCallback::gnssSetCapabilitesCb(capabilities);
+}
+
Return<void> GnssCallback::gnssAcquireWakelockCb() {
acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME);
return Void();
diff --git a/services/core/xsd/vts/Android.bp b/services/core/xsd/vts/Android.bp
index 5545656..636d110 100644
--- a/services/core/xsd/vts/Android.bp
+++ b/services/core/xsd/vts/Android.bp
@@ -31,4 +31,12 @@
"-Wall",
"-Werror",
],
+ data: [
+ ":default-permissions",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts-core"
+ ],
+ test_config: "vts_defaultPermissions_validate_test.xml",
}
diff --git a/services/core/xsd/vts/vts_defaultPermissions_validate_test.xml b/services/core/xsd/vts/vts_defaultPermissions_validate_test.xml
new file mode 100644
index 0000000..1ccacae
--- /dev/null
+++ b/services/core/xsd/vts/vts_defaultPermissions_validate_test.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+<configuration description="Runs vts_defaultPermissions_validate_test.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="default-permissions.xsd->/data/local/tmp/default-permissions.xsd" />
+ <option name="push" value="vts_defaultPermissions_validate_test->/data/local/tmp/vts_defaultPermissions_validate_test" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="vts_defaultPermissions_validate_test" />
+ </test>
+</configuration>
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index aa5dafc..063ad0f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -15230,15 +15230,27 @@
final ActiveAdmin admin =
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
previousCrossProfilePackages = admin.mCrossProfilePackages;
+ if (packageNames.equals(previousCrossProfilePackages)) {
+ return;
+ }
admin.mCrossProfilePackages = packageNames;
saveSettingsLocked(mInjector.userHandleGetCallingUserId());
}
+ logSetCrossProfilePackages(who, packageNames);
final CrossProfileApps crossProfileApps = mContext.getSystemService(CrossProfileApps.class);
mInjector.binderWithCleanCallingIdentity(
() -> crossProfileApps.resetInteractAcrossProfilesAppOps(
previousCrossProfilePackages, new HashSet<>(packageNames)));
}
+ private void logSetCrossProfilePackages(ComponentName who, List<String> packageNames) {
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_CROSS_PROFILE_PACKAGES)
+ .setAdmin(who)
+ .setStrings(packageNames.toArray(new String[packageNames.size()]))
+ .write();
+ }
+
@Override
public List<String> getCrossProfilePackages(ComponentName who) {
if (!mHasFeature) {
diff --git a/services/people/java/com/android/server/people/data/ConversationInfo.java b/services/people/java/com/android/server/people/data/ConversationInfo.java
index ce35366..859cdf2 100644
--- a/services/people/java/com/android/server/people/data/ConversationInfo.java
+++ b/services/people/java/com/android/server/people/data/ConversationInfo.java
@@ -137,6 +137,11 @@
return hasShortcutFlags(ShortcutInfo.FLAG_LONG_LIVED);
}
+ /** Whether the shortcut for this conversation is cached in Shortcut Service. */
+ public boolean isShortcutCached() {
+ return hasShortcutFlags(ShortcutInfo.FLAG_CACHED);
+ }
+
/** Whether this conversation is marked as important by the user. */
public boolean isImportant() {
return hasConversationFlags(FLAG_IMPORTANT);
@@ -213,6 +218,9 @@
if (isShortcutLongLived()) {
sb.append("Liv");
}
+ if (isShortcutCached()) {
+ sb.append("Cac");
+ }
sb.append("]");
sb.append(", conversationFlags=0x").append(Integer.toHexString(mConversationFlags));
sb.append(" [");
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index 2edfd74..dd9cbd0 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -101,8 +101,8 @@
private final SparseArray<NotificationListenerService> mNotificationListeners =
new SparseArray<>();
private final SparseArray<PackageMonitor> mPackageMonitors = new SparseArray<>();
- private final ContentObserver mCallLogContentObserver;
- private final ContentObserver mMmsSmsContentObserver;
+ private ContentObserver mCallLogContentObserver;
+ private ContentObserver mMmsSmsContentObserver;
private ShortcutServiceInternal mShortcutServiceInternal;
private PackageManagerInternal mPackageManagerInternal;
@@ -118,10 +118,6 @@
mContext = context;
mInjector = injector;
mUsageStatsQueryExecutor = mInjector.createScheduledExecutor();
- mCallLogContentObserver = new CallLogContentObserver(
- BackgroundThread.getHandler());
- mMmsSmsContentObserver = new MmsSmsContentObserver(
- BackgroundThread.getHandler());
mDiskReadWriterExecutor = mInjector.createScheduledExecutor();
}
@@ -189,9 +185,12 @@
// The call log and MMS/SMS messages are shared across user profiles. So only need to
// register the content observers once for the primary user.
// TODO: Register observers after the conversations and events being loaded from disk.
+ mCallLogContentObserver = new CallLogContentObserver(BackgroundThread.getHandler());
mContext.getContentResolver().registerContentObserver(
CallLog.CONTENT_URI, /* notifyForDescendants= */ true,
mCallLogContentObserver, UserHandle.USER_SYSTEM);
+
+ mMmsSmsContentObserver = new MmsSmsContentObserver(BackgroundThread.getHandler());
mContext.getContentResolver().registerContentObserver(
MmsSms.CONTENT_URI, /* notifyForDescendants= */ false,
mMmsSmsContentObserver, UserHandle.USER_SYSTEM);
@@ -226,8 +225,14 @@
mPackageMonitors.get(userId).unregister();
}
if (userId == UserHandle.USER_SYSTEM) {
- mContext.getContentResolver().unregisterContentObserver(mCallLogContentObserver);
- mContext.getContentResolver().unregisterContentObserver(mMmsSmsContentObserver);
+ if (mCallLogContentObserver != null) {
+ mContext.getContentResolver().unregisterContentObserver(mCallLogContentObserver);
+ mCallLogContentObserver = null;
+ }
+ if (mMmsSmsContentObserver != null) {
+ mContext.getContentResolver().unregisterContentObserver(mMmsSmsContentObserver);
+ mCallLogContentObserver = null;
+ }
}
DataMaintenanceService.cancelJob(mContext, userId);
@@ -635,6 +640,14 @@
private class NotificationListener extends NotificationListenerService {
@Override
+ public void onNotificationPosted(StatusBarNotification sbn) {
+ EventHistoryImpl eventHistory = getEventHistoryIfEligible(sbn);
+ if (eventHistory != null) {
+ eventHistory.addEvent(new Event(sbn.getPostTime(), Event.TYPE_NOTIFICATION_POSTED));
+ }
+ }
+
+ @Override
public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap,
int reason) {
if (reason != REASON_CLICK) {
@@ -683,7 +696,15 @@
break;
}
conversationStore.addOrUpdate(builder.build());
- // TODO: Cache the shortcut when a conversation's notification setting is changed.
+
+ if (modificationType == NOTIFICATION_CHANNEL_OR_GROUP_UPDATED
+ && conversationInfo.isShortcutLongLived()
+ && !conversationInfo.isShortcutCached()) {
+ mShortcutServiceInternal.cacheShortcuts(user.getIdentifier(),
+ mContext.getPackageName(), pkg,
+ Collections.singletonList(conversationInfo.getShortcutId()),
+ user.getIdentifier());
+ }
}
}
diff --git a/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java b/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java
index 6dcfaa0..72f1abb 100644
--- a/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java
+++ b/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java
@@ -80,10 +80,6 @@
addEventByShortcutId(packageData, e.getShortcutId(),
new Event(e.getTimeStamp(), Event.TYPE_SHORTCUT_INVOCATION));
break;
- case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
- addEventByNotificationChannelId(packageData, e.getNotificationChannelId(),
- new Event(e.getTimeStamp(), Event.TYPE_NOTIFICATION_POSTED));
- break;
case UsageEvents.Event.LOCUS_ID_SET:
onInAppConversationEnded(packageData, e);
LocusId locusId = e.getLocusId() != null ? new LocusId(e.getLocusId()) : null;
@@ -142,17 +138,4 @@
EventStore.CATEGORY_LOCUS_ID_BASED, locusId.getId());
eventHistory.addEvent(event);
}
-
- private void addEventByNotificationChannelId(PackageData packageData,
- String notificationChannelId, Event event) {
- ConversationInfo conversationInfo =
- packageData.getConversationStore().getConversationByNotificationChannelId(
- notificationChannelId);
- if (conversationInfo == null) {
- return;
- }
- EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateEventHistory(
- EventStore.CATEGORY_SHORTCUT_BASED, conversationInfo.getShortcutId());
- eventHistory.addEvent(event);
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index 8d5939a..7027185 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -202,7 +202,7 @@
assertEquals(1, displayIds.length);
final int displayId = displayIds[0];
DisplayInfo info = bs.getDisplayInfo(displayId);
- assertEquals(info.type, Display.TYPE_BUILT_IN);
+ assertEquals(info.type, Display.TYPE_INTERNAL);
displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
diff --git a/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java b/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java
index c0e7927..70d6cf8 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java
@@ -46,7 +46,7 @@
.setContactUri(CONTACT_URI)
.setContactPhoneNumber(PHONE_NUMBER)
.setNotificationChannelId(NOTIFICATION_CHANNEL_ID)
- .setShortcutFlags(ShortcutInfo.FLAG_LONG_LIVED)
+ .setShortcutFlags(ShortcutInfo.FLAG_LONG_LIVED | ShortcutInfo.FLAG_CACHED)
.setImportant(true)
.setNotificationSilenced(true)
.setBubbled(true)
@@ -62,6 +62,7 @@
assertEquals(PHONE_NUMBER, conversationInfo.getContactPhoneNumber());
assertEquals(NOTIFICATION_CHANNEL_ID, conversationInfo.getNotificationChannelId());
assertTrue(conversationInfo.isShortcutLongLived());
+ assertTrue(conversationInfo.isShortcutCached());
assertTrue(conversationInfo.isImportant());
assertTrue(conversationInfo.isNotificationSilenced());
assertTrue(conversationInfo.isBubbled());
@@ -83,6 +84,7 @@
assertNull(conversationInfo.getContactPhoneNumber());
assertNull(conversationInfo.getNotificationChannelId());
assertFalse(conversationInfo.isShortcutLongLived());
+ assertFalse(conversationInfo.isShortcutCached());
assertFalse(conversationInfo.isImportant());
assertFalse(conversationInfo.isNotificationSilenced());
assertFalse(conversationInfo.isBubbled());
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index 0bb984e..3ecd319 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -184,6 +184,7 @@
when(mStatusBarNotification.getNotification()).thenReturn(mNotification);
when(mStatusBarNotification.getPackageName()).thenReturn(TEST_PKG_NAME);
when(mStatusBarNotification.getUser()).thenReturn(UserHandle.of(USER_ID_PRIMARY));
+ when(mStatusBarNotification.getPostTime()).thenReturn(System.currentTimeMillis());
when(mNotification.getShortcutId()).thenReturn(TEST_SHORTCUT_ID);
mNotificationChannel = new NotificationChannel(
@@ -325,6 +326,28 @@
}
@Test
+ public void testNotificationPosted() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ mDataManager.onShortcutAddedOrUpdated(shortcut);
+
+ NotificationListenerService listenerService =
+ mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
+
+ listenerService.onNotificationPosted(mStatusBarNotification);
+
+ List<Range<Long>> activeNotificationOpenTimeSlots = new ArrayList<>();
+ mDataManager.forAllPackages(packageData ->
+ activeNotificationOpenTimeSlots.addAll(
+ packageData.getEventHistory(TEST_SHORTCUT_ID)
+ .getEventIndex(Event.TYPE_NOTIFICATION_POSTED)
+ .getActiveTimeSlots()));
+ assertEquals(1, activeNotificationOpenTimeSlots.size());
+ }
+
+ @Test
public void testNotificationOpened() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
@@ -393,6 +416,9 @@
assertTrue(conversationInfo.isImportant());
assertFalse(conversationInfo.isNotificationSilenced());
assertFalse(conversationInfo.isDemoted());
+ verify(mShortcutServiceInternal).cacheShortcuts(
+ anyInt(), any(), eq(TEST_PKG_NAME),
+ eq(Collections.singletonList(TEST_SHORTCUT_ID)), eq(USER_ID_PRIMARY));
}
@Test
@@ -589,6 +615,7 @@
when(mockContext.getUser()).thenReturn(UserHandle.of(userId));
ShortcutInfo.Builder builder = new ShortcutInfo.Builder(mockContext, id)
.setShortLabel(id)
+ .setLongLived(true)
.setIntent(new Intent("TestIntent"));
if (person != null) {
builder.setPersons(new Person[] {person});
diff --git a/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java b/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java
index dc4876b..d444466 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java
@@ -59,7 +59,6 @@
private static final String PKG_NAME = "pkg";
private static final String ACTIVITY_NAME = "TestActivity";
private static final String SHORTCUT_ID = "abc";
- private static final String NOTIFICATION_CHANNEL_ID = "test : abc";
private static final LocusId LOCUS_ID_1 = new LocusId("locus_1");
private static final LocusId LOCUS_ID_2 = new LocusId("locus_2");
@@ -83,7 +82,6 @@
scheduledExecutorService, testDir, helper);
mPackageData.mConversationStore.mConversationInfo = new ConversationInfo.Builder()
.setShortcutId(SHORTCUT_ID)
- .setNotificationChannelId(NOTIFICATION_CHANNEL_ID)
.setLocusId(LOCUS_ID_1)
.build();
@@ -114,19 +112,6 @@
}
@Test
- public void testQueryNotificationInterruptionEvent() {
- addUsageEvents(createNotificationInterruptionEvent(100L));
-
- assertTrue(mHelper.querySince(50L));
- assertEquals(100L, mHelper.getLastEventTimestamp());
- Event expectedEvent = new Event(100L, Event.TYPE_NOTIFICATION_POSTED);
- List<Event> events = mPackageData.mEventStore.mShortcutEventHistory.queryEvents(
- Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
- assertEquals(1, events.size());
- assertEquals(expectedEvent, events.get(0));
- }
-
- @Test
public void testInAppConversationSwitch() {
addUsageEvents(
createLocusIdSetEvent(100_000L, LOCUS_ID_1.getId()),
@@ -203,13 +188,6 @@
return e;
}
- private static UsageEvents.Event createNotificationInterruptionEvent(long timestamp) {
- UsageEvents.Event e = createUsageEvent(UsageEvents.Event.NOTIFICATION_INTERRUPTION,
- timestamp);
- e.mNotificationChannelId = NOTIFICATION_CHANNEL_ID;
- return e;
- }
-
private static UsageEvents.Event createLocusIdSetEvent(long timestamp, String locusId) {
UsageEvents.Event e = createUsageEvent(UsageEvents.Event.LOCUS_ID_SET, timestamp);
e.mClass = ACTIVITY_NAME;
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index e28d13a..9670658 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -37,6 +37,7 @@
import android.content.pm.parsing.ParsingPackage;
import android.os.Build;
import android.os.Process;
+import android.platform.test.annotations.Presubmit;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -57,6 +58,7 @@
import java.util.Map;
import java.util.Set;
+@Presubmit
@RunWith(JUnit4.class)
public class AppsFilterTest {
@@ -314,7 +316,8 @@
b -> b.setSigningDetails(frameworkSigningDetails));
PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"),
DUMMY_TARGET_UID,
- b -> b.setSigningDetails(frameworkSigningDetails));
+ b -> b.setSigningDetails(frameworkSigningDetails)
+ .setPkgFlags(ApplicationInfo.FLAG_SYSTEM));
PackageSetting calling = simulateAddPackage(appsFilter,
pkg("com.some.other.package"), DUMMY_CALLING_UID,
b -> b.setSigningDetails(otherSigningDetails));
diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
index 4d0481be..eef9012 100644
--- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
@@ -18,6 +18,7 @@
import static org.junit.Assert.assertEquals;
+import android.platform.test.annotations.Presubmit;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
@@ -48,8 +49,9 @@
* Build/Install/Run:
* atest FrameworksServicesTests:SystemConfigTest
*/
-@RunWith(AndroidJUnit4.class)
@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
public class SystemConfigTest {
private static final String LOG_TAG = "SystemConfigTest";
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryProtoHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryProtoHelperTest.java
index 458117d..6eaf546 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryProtoHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryProtoHelperTest.java
@@ -21,6 +21,7 @@
import android.app.NotificationHistory.HistoricalNotification;
import android.graphics.drawable.Icon;
import android.test.suitebuilder.annotation.SmallTest;
+import android.text.TextUtils;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
@@ -57,6 +58,10 @@
String expectedText = "text" + index;
Icon expectedIcon = Icon.createWithResource(InstrumentationRegistry.getContext(),
index);
+ String conversationId = null;
+ if (index % 2 == 0) {
+ conversationId = "convo" + index;
+ }
return new HistoricalNotification.Builder()
.setPackage(packageName)
@@ -68,6 +73,7 @@
.setTitle(expectedTitle)
.setText(expectedText)
.setIcon(expectedIcon)
+ .setConversationId(conversationId)
.build();
}
@@ -139,6 +145,9 @@
expectedStrings.add(n.getPackage());
expectedStrings.add(n.getChannelName());
expectedStrings.add(n.getChannelId());
+ if (!TextUtils.isEmpty(n.getConversationId())) {
+ expectedStrings.add(n.getConversationId());
+ }
expectedEntries.add(n);
}
history.addNotificationToWrite(n);
@@ -178,6 +187,9 @@
expectedStrings.add(n.getPackage());
expectedStrings.add(n.getChannelName());
expectedStrings.add(n.getChannelId());
+ if (!TextUtils.isEmpty(n.getConversationId())) {
+ expectedStrings.add(n.getConversationId());
+ }
expectedEntries.add(n);
}
history.addNotificationToWrite(n);
@@ -227,6 +239,9 @@
expectedStrings.add(n.getPackage());
expectedStrings.add(n.getChannelName());
expectedStrings.add(n.getChannelId());
+ if (n.getConversationId() != null) {
+ expectedStrings.add(n.getConversationId());
+ }
expectedEntries.add(n);
}
history.addNotificationToWrite(n);
@@ -264,6 +279,9 @@
expectedStrings.add(n.getPackage());
expectedStrings.add(n.getChannelName());
expectedStrings.add(n.getChannelId());
+ if (n.getConversationId() != null) {
+ expectedStrings.add(n.getConversationId());
+ }
history.addNotificationToWrite(n);
}
history.poolStringsFromNotifications();
@@ -279,6 +297,9 @@
expectedStrings.add(n.getPackage());
expectedStrings.add(n.getChannelName());
expectedStrings.add(n.getChannelId());
+ if (n.getConversationId() != null) {
+ expectedStrings.add(n.getConversationId());
+ }
actualHistory.addNotificationToWrite(n);
}
actualHistory.poolStringsFromNotifications();
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 bc33f08..e5ffb4d 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -1284,6 +1284,27 @@
}
@Test
+ public void testPostCancelPostNotifiesListeners() throws Exception {
+ // WHEN a notification is posted
+ final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", sbn.getId(),
+ sbn.getNotification(), sbn.getUserId());
+ // THEN it is canceled
+ mBinderService.cancelNotificationWithTag(PKG, PKG, "tag", sbn.getId(), sbn.getUserId());
+ // THEN it is posted again (before the cancel has a chance to finish)
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", sbn.getId(),
+ sbn.getNotification(), sbn.getUserId());
+ // THEN the later enqueue isn't swallowed by the cancel. I.e., ordering is respected
+ waitForIdle();
+
+ // The final enqueue made it to the listener instead of being canceled
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(PKG);
+ assertEquals(1, notifs.length);
+ assertEquals(1, mService.getNotificationRecordCount());
+ }
+
+ @Test
public void testCancelNotificationWhilePostedAndEnqueued() throws Exception {
mBinderService.enqueueNotificationWithTag(PKG, PKG,
"testCancelNotificationWhilePostedAndEnqueued", 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 fa182d6..d46975c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -29,6 +29,7 @@
import static android.app.ActivityManager.START_SWITCHES_CANCELED;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
@@ -64,8 +65,10 @@
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
+import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.IApplicationThread;
+import android.app.WindowConfiguration;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -80,6 +83,9 @@
import android.platform.test.annotations.Presubmit;
import android.service.voice.IVoiceInteractionSession;
import android.view.Gravity;
+import android.view.ITaskOrganizer;
+import android.view.IWindowContainer;
+import android.view.SurfaceControl;
import androidx.test.filters.SmallTest;
@@ -999,7 +1005,8 @@
assertThat(outActivity[0].inSplitScreenWindowingMode()).isFalse();
// Move activity to split-screen-primary stack and make sure it has the focus.
- top.getRootTask().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ TestSplitOrganizer splitOrg = new TestSplitOrganizer(mService, top.getDisplayId());
+ splitOrg.mPrimary.addChild(top.getRootTask(), 0 /* index */);
top.getRootTask().moveToFront("testWindowingModeOptionsLaunchAdjacent");
// Activity must landed on split-screen-secondary when launch adjacent.
@@ -1022,4 +1029,58 @@
verify(recentTasks, times(1)).add(any());
}
+
+ static class TestSplitOrganizer extends ITaskOrganizer.Stub {
+ final ActivityTaskManagerService mService;
+ TaskTile mPrimary;
+ TaskTile mSecondary;
+ boolean mInSplit = false;
+ int mDisplayId;
+ TestSplitOrganizer(ActivityTaskManagerService service, int displayId) {
+ mService = service;
+ mDisplayId = displayId;
+ mService.mTaskOrganizerController.registerTaskOrganizer(this,
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ mService.mTaskOrganizerController.registerTaskOrganizer(this,
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ IWindowContainer primary = mService.mTaskOrganizerController.createRootTask(
+ displayId, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY).token;
+ mPrimary = TaskTile.forToken(primary.asBinder());
+ IWindowContainer secondary = mService.mTaskOrganizerController.createRootTask(
+ displayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY).token;
+ mSecondary = TaskTile.forToken(secondary.asBinder());
+ }
+ @Override
+ public void taskAppeared(ActivityManager.RunningTaskInfo info) {
+ }
+ @Override
+ public void taskVanished(IWindowContainer wc) {
+ }
+ @Override
+ public void transactionReady(int id, SurfaceControl.Transaction t) {
+ }
+ @Override
+ public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
+ if (mInSplit) {
+ return;
+ }
+ if (info.topActivityType != ACTIVITY_TYPE_UNDEFINED) {
+ if (info.configuration.windowConfiguration.getWindowingMode()
+ == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+ mInSplit = true;
+ mService.mTaskOrganizerController.setLaunchRoot(mDisplayId,
+ mSecondary.mRemoteToken);
+ // move everything to secondary because test expects this but usually sysui
+ // does it.
+ DisplayContent dc = mService.mRootWindowContainer.getDisplayContent(mDisplayId);
+ for (int i = dc.getStackCount() - 1; i >= 0; --i) {
+ if (!WindowConfiguration.isSplitScreenWindowingMode(
+ dc.getStackAt(i).getWindowingMode())) {
+ mSecondary.addChild(dc.getStackAt(i), 0);
+ }
+ }
+ }
+ }
+ }
+ };
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index a672a95..4449069 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -1098,7 +1098,6 @@
assertSecurityException(expectCallable,
() -> mService.setTaskWindowingModeSplitScreenPrimary(0,
SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, true, true, new Rect(), true));
- assertSecurityException(expectCallable, () -> mService.dismissSplitScreenMode(true));
assertSecurityException(expectCallable, () -> mService.dismissPip(true, 0));
assertSecurityException(expectCallable,
() -> mService.moveTopActivityToPinnedStack(INVALID_STACK_ID, new Rect()));
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
index 0312df6..cd53ece 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
@@ -28,19 +28,17 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+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;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -49,6 +47,7 @@
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManager.StackInfo;
import android.app.PictureInPictureParams;
+import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Binder;
@@ -56,7 +55,6 @@
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import android.util.ArrayMap;
-import android.content.pm.ActivityInfo;
import android.util.Rational;
import android.view.Display;
import android.view.ITaskOrganizer;
@@ -458,9 +456,9 @@
private List<TaskTile> getTaskTiles(DisplayContent dc) {
ArrayList<TaskTile> out = new ArrayList<>();
for (int i = dc.getStackCount() - 1; i >= 0; --i) {
- final Task t = dc.getStackAt(i);
- if (t instanceof TaskTile) {
- out.add((TaskTile) t);
+ final TaskTile t = dc.getStackAt(i).asTile();
+ if (t != null) {
+ out.add(t);
}
}
return out;
diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java
index 9f2537c..203047f 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -22,11 +22,12 @@
import android.os.Parcel;
import android.telephony.gsm.GsmCellLocation;
import android.text.TextUtils;
+import android.util.ArraySet;
-import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
-import java.util.List;
import java.util.Objects;
+import java.util.Set;
/**
* CellIdentity to represent a unique GSM cell
@@ -50,7 +51,7 @@
private final int mBsic;
// a list of additional PLMN-IDs reported for this cell
- private final List<String> mAdditionalPlmns;
+ private final ArraySet<String> mAdditionalPlmns;
/**
* @hide
@@ -62,7 +63,7 @@
mCid = CellInfo.UNAVAILABLE;
mArfcn = CellInfo.UNAVAILABLE;
mBsic = CellInfo.UNAVAILABLE;
- mAdditionalPlmns = Collections.emptyList();
+ mAdditionalPlmns = new ArraySet<>();
}
/**
@@ -81,13 +82,13 @@
*/
public CellIdentityGsm(int lac, int cid, int arfcn, int bsic, @Nullable String mccStr,
@Nullable String mncStr, @Nullable String alphal, @Nullable String alphas,
- @NonNull List<String> additionalPlmns) {
+ @NonNull Collection<String> additionalPlmns) {
super(TAG, CellInfo.TYPE_GSM, mccStr, mncStr, alphal, alphas);
mLac = inRangeOrUnavailable(lac, 0, MAX_LAC);
mCid = inRangeOrUnavailable(cid, 0, MAX_CID);
mArfcn = inRangeOrUnavailable(arfcn, 0, MAX_ARFCN);
mBsic = inRangeOrUnavailable(bsic, 0, MAX_BSIC);
- mAdditionalPlmns = new ArrayList<>(additionalPlmns.size());
+ mAdditionalPlmns = new ArraySet<>(additionalPlmns.size());
for (String plmn : additionalPlmns) {
if (isValidPlmn(plmn)) {
mAdditionalPlmns.add(plmn);
@@ -99,7 +100,7 @@
public CellIdentityGsm(@NonNull android.hardware.radio.V1_0.CellIdentityGsm cid) {
this(cid.lac, cid.cid, cid.arfcn,
cid.bsic == (byte) 0xFF ? CellInfo.UNAVAILABLE : cid.bsic,
- cid.mcc, cid.mnc, "", "", Collections.emptyList());
+ cid.mcc, cid.mnc, "", "", new ArraySet<>());
}
/** @hide */
@@ -107,7 +108,7 @@
this(cid.base.lac, cid.base.cid, cid.base.arfcn,
cid.base.bsic == (byte) 0xFF ? CellInfo.UNAVAILABLE : cid.base.bsic, cid.base.mcc,
cid.base.mnc, cid.operatorNames.alphaLong, cid.operatorNames.alphaShort,
- Collections.emptyList());
+ new ArraySet<>());
}
/** @hide */
@@ -221,8 +222,8 @@
* @return a list of additional PLMN IDs supported by this cell.
*/
@NonNull
- public List<String> getAdditionalPlmns() {
- return mAdditionalPlmns;
+ public Set<String> getAdditionalPlmns() {
+ return Collections.unmodifiableSet(mAdditionalPlmns);
}
/**
@@ -296,7 +297,7 @@
dest.writeInt(mCid);
dest.writeInt(mArfcn);
dest.writeInt(mBsic);
- dest.writeList(mAdditionalPlmns);
+ dest.writeArraySet(mAdditionalPlmns);
}
/** Construct from Parcel, type has already been processed */
@@ -306,7 +307,7 @@
mCid = in.readInt();
mArfcn = in.readInt();
mBsic = in.readInt();
- mAdditionalPlmns = in.readArrayList(null);
+ mAdditionalPlmns = (ArraySet<String>) in.readArraySet(null);
if (DBG) log(toString());
}
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index a194ae3..e4198d1 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -23,11 +23,13 @@
import android.os.Parcel;
import android.telephony.gsm.GsmCellLocation;
import android.text.TextUtils;
+import android.util.ArraySet;
-import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
/**
* CellIdentity is to represent a unique LTE cell
@@ -54,7 +56,7 @@
private final int mBandwidth;
// a list of additional PLMN-IDs reported for this cell
- private final List<String> mAdditionalPlmns;
+ private final ArraySet<String> mAdditionalPlmns;
private ClosedSubscriberGroupInfo mCsgInfo;
@@ -69,7 +71,7 @@
mTac = CellInfo.UNAVAILABLE;
mEarfcn = CellInfo.UNAVAILABLE;
mBandwidth = CellInfo.UNAVAILABLE;
- mAdditionalPlmns = Collections.emptyList();
+ mAdditionalPlmns = new ArraySet<>();
mCsgInfo = null;
}
@@ -86,7 +88,7 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public CellIdentityLte(int mcc, int mnc, int ci, int pci, int tac) {
this(ci, pci, tac, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, String.valueOf(mcc),
- String.valueOf(mnc), null, null, Collections.emptyList(), null);
+ String.valueOf(mnc), null, null, new ArraySet<>(), null);
}
/**
@@ -107,7 +109,7 @@
*/
public CellIdentityLte(int ci, int pci, int tac, int earfcn, int bandwidth,
@Nullable String mccStr, @Nullable String mncStr, @Nullable String alphal,
- @Nullable String alphas, @NonNull List<String> additionalPlmns,
+ @Nullable String alphas, @NonNull Collection<String> additionalPlmns,
@Nullable ClosedSubscriberGroupInfo csgInfo) {
super(TAG, CellInfo.TYPE_LTE, mccStr, mncStr, alphal, alphas);
mCi = inRangeOrUnavailable(ci, 0, MAX_CI);
@@ -115,7 +117,7 @@
mTac = inRangeOrUnavailable(tac, 0, MAX_TAC);
mEarfcn = inRangeOrUnavailable(earfcn, 0, MAX_EARFCN);
mBandwidth = inRangeOrUnavailable(bandwidth, 0, MAX_BANDWIDTH);
- mAdditionalPlmns = new ArrayList<>(additionalPlmns.size());
+ mAdditionalPlmns = new ArraySet<>(additionalPlmns.size());
for (String plmn : additionalPlmns) {
if (isValidPlmn(plmn)) {
mAdditionalPlmns.add(plmn);
@@ -127,14 +129,14 @@
/** @hide */
public CellIdentityLte(@NonNull android.hardware.radio.V1_0.CellIdentityLte cid) {
this(cid.ci, cid.pci, cid.tac, cid.earfcn,
- CellInfo.UNAVAILABLE, cid.mcc, cid.mnc, "", "", Collections.emptyList(), null);
+ CellInfo.UNAVAILABLE, cid.mcc, cid.mnc, "", "", new ArraySet<>(), null);
}
/** @hide */
public CellIdentityLte(@NonNull android.hardware.radio.V1_2.CellIdentityLte cid) {
this(cid.base.ci, cid.base.pci, cid.base.tac, cid.base.earfcn, cid.bandwidth,
cid.base.mcc, cid.base.mnc, cid.operatorNames.alphaLong,
- cid.operatorNames.alphaShort, Collections.emptyList(), null);
+ cid.operatorNames.alphaShort, new ArraySet<>(), null);
}
/** @hide */
@@ -270,8 +272,8 @@
* @return a list of additional PLMN IDs supported by this cell.
*/
@NonNull
- public List<String> getAdditionalPlmns() {
- return mAdditionalPlmns;
+ public Set<String> getAdditionalPlmns() {
+ return Collections.unmodifiableSet(mAdditionalPlmns);
}
/**
@@ -361,7 +363,7 @@
dest.writeInt(mTac);
dest.writeInt(mEarfcn);
dest.writeInt(mBandwidth);
- dest.writeList(mAdditionalPlmns);
+ dest.writeArraySet(mAdditionalPlmns);
dest.writeParcelable(mCsgInfo, flags);
}
@@ -373,7 +375,7 @@
mTac = in.readInt();
mEarfcn = in.readInt();
mBandwidth = in.readInt();
- mAdditionalPlmns = in.readArrayList(null);
+ mAdditionalPlmns = (ArraySet<String>) in.readArraySet(null);
mCsgInfo = in.readParcelable(null);
if (DBG) log(toString());
}
diff --git a/telephony/java/android/telephony/CellIdentityNr.java b/telephony/java/android/telephony/CellIdentityNr.java
index a0ef5aa..cba500a 100644
--- a/telephony/java/android/telephony/CellIdentityNr.java
+++ b/telephony/java/android/telephony/CellIdentityNr.java
@@ -22,11 +22,14 @@
import android.os.Parcel;
import android.telephony.AccessNetworkConstants.NgranBands.NgranBand;
import android.telephony.gsm.GsmCellLocation;
+import android.util.ArraySet;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
/**
* Information to represent a unique NR(New Radio 5G) cell.
@@ -46,7 +49,7 @@
private final List<Integer> mBands;
// a list of additional PLMN-IDs reported for this cell
- private final List<String> mAdditionalPlmns;
+ private final ArraySet<String> mAdditionalPlmns;
/**
*
@@ -66,14 +69,14 @@
public CellIdentityNr(int pci, int tac, int nrArfcn, @NgranBand List<Integer> bands,
@Nullable String mccStr, @Nullable String mncStr, long nci,
@Nullable String alphal, @Nullable String alphas,
- @NonNull List<String> additionalPlmns) {
+ @NonNull Collection<String> additionalPlmns) {
super(TAG, CellInfo.TYPE_NR, mccStr, mncStr, alphal, alphas);
mPci = inRangeOrUnavailable(pci, 0, MAX_PCI);
mTac = inRangeOrUnavailable(tac, 0, MAX_TAC);
mNrArfcn = inRangeOrUnavailable(nrArfcn, 0, MAX_NRARFCN);
mBands = new ArrayList<>(bands);
mNci = inRangeOrUnavailable(nci, 0, MAX_NCI);
- mAdditionalPlmns = new ArrayList<>(additionalPlmns.size());
+ mAdditionalPlmns = new ArraySet<>(additionalPlmns.size());
for (String plmn : additionalPlmns) {
if (isValidPlmn(plmn)) {
mAdditionalPlmns.add(plmn);
@@ -85,7 +88,7 @@
public CellIdentityNr(@NonNull android.hardware.radio.V1_4.CellIdentityNr cid) {
this(cid.pci, cid.tac, cid.nrarfcn, Collections.emptyList(), cid.mcc, cid.mnc, cid.nci,
cid.operatorNames.alphaLong, cid.operatorNames.alphaShort,
- Collections.emptyList());
+ new ArraySet<>());
}
/** @hide */
@@ -212,8 +215,8 @@
* @return a list of additional PLMN IDs supported by this cell.
*/
@NonNull
- public List<String> getAdditionalPlmns() {
- return Collections.unmodifiableList(mAdditionalPlmns);
+ public Set<String> getAdditionalPlmns() {
+ return Collections.unmodifiableSet(mAdditionalPlmns);
}
@Override
@@ -241,7 +244,7 @@
dest.writeInt(mNrArfcn);
dest.writeList(mBands);
dest.writeLong(mNci);
- dest.writeList(mAdditionalPlmns);
+ dest.writeArraySet(mAdditionalPlmns);
}
/** Construct from Parcel, type has already been processed */
@@ -252,7 +255,7 @@
mNrArfcn = in.readInt();
mBands = in.readArrayList(null);
mNci = in.readLong();
- mAdditionalPlmns = in.readArrayList(null);
+ mAdditionalPlmns = (ArraySet<String>) in.readArraySet(null);
}
/** Implement the Parcelable interface */
diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java
index 531487a..30f98bc 100644
--- a/telephony/java/android/telephony/CellIdentityTdscdma.java
+++ b/telephony/java/android/telephony/CellIdentityTdscdma.java
@@ -20,11 +20,12 @@
import android.annotation.Nullable;
import android.os.Parcel;
import android.telephony.gsm.GsmCellLocation;
+import android.util.ArraySet;
-import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
-import java.util.List;
import java.util.Objects;
+import java.util.Set;
/**
* CellIdentity is to represent a unique TD-SCDMA cell
@@ -50,7 +51,7 @@
private final int mUarfcn;
// a list of additional PLMN-IDs reported for this cell
- private final List<String> mAdditionalPlmns;
+ private final ArraySet<String> mAdditionalPlmns;
private ClosedSubscriberGroupInfo mCsgInfo;
@@ -63,7 +64,7 @@
mCid = CellInfo.UNAVAILABLE;
mCpid = CellInfo.UNAVAILABLE;
mUarfcn = CellInfo.UNAVAILABLE;
- mAdditionalPlmns = Collections.emptyList();
+ mAdditionalPlmns = new ArraySet<>();
mCsgInfo = null;
}
@@ -85,13 +86,14 @@
*/
public CellIdentityTdscdma(@Nullable String mcc, @Nullable String mnc, int lac, int cid,
int cpid, int uarfcn, @Nullable String alphal, @Nullable String alphas,
- @NonNull List<String> additionalPlmns, @Nullable ClosedSubscriberGroupInfo csgInfo) {
+ @NonNull Collection<String> additionalPlmns,
+ @Nullable ClosedSubscriberGroupInfo csgInfo) {
super(TAG, CellInfo.TYPE_TDSCDMA, mcc, mnc, alphal, alphas);
mLac = inRangeOrUnavailable(lac, 0, MAX_LAC);
mCid = inRangeOrUnavailable(cid, 0, MAX_CID);
mCpid = inRangeOrUnavailable(cpid, 0, MAX_CPID);
mUarfcn = inRangeOrUnavailable(uarfcn, 0, MAX_UARFCN);
- mAdditionalPlmns = new ArrayList<>(additionalPlmns.size());
+ mAdditionalPlmns = new ArraySet<>(additionalPlmns.size());
for (String plmn : additionalPlmns) {
if (isValidPlmn(plmn)) {
mAdditionalPlmns.add(plmn);
@@ -208,8 +210,8 @@
* @return a list of additional PLMN IDs supported by this cell.
*/
@NonNull
- public List<String> getAdditionalPlmns() {
- return mAdditionalPlmns;
+ public Set<String> getAdditionalPlmns() {
+ return Collections.unmodifiableSet(mAdditionalPlmns);
}
/**
@@ -289,7 +291,7 @@
dest.writeInt(mCid);
dest.writeInt(mCpid);
dest.writeInt(mUarfcn);
- dest.writeList(mAdditionalPlmns);
+ dest.writeArraySet(mAdditionalPlmns);
dest.writeParcelable(mCsgInfo, flags);
}
@@ -300,7 +302,7 @@
mCid = in.readInt();
mCpid = in.readInt();
mUarfcn = in.readInt();
- mAdditionalPlmns = in.readArrayList(null);
+ mAdditionalPlmns = (ArraySet<String>) in.readArraySet(null);
mCsgInfo = in.readParcelable(null);
if (DBG) log(toString());
}
diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java
index 15e491b..9d2cb74 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -22,11 +22,12 @@
import android.os.Parcel;
import android.telephony.gsm.GsmCellLocation;
import android.text.TextUtils;
+import android.util.ArraySet;
-import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
-import java.util.List;
import java.util.Objects;
+import java.util.Set;
/**
* CellIdentity to represent a unique UMTS cell
@@ -51,7 +52,7 @@
private final int mUarfcn;
// a list of additional PLMN-IDs reported for this cell
- private final List<String> mAdditionalPlmns;
+ private final ArraySet<String> mAdditionalPlmns;
@Nullable
private final ClosedSubscriberGroupInfo mCsgInfo;
@@ -65,7 +66,7 @@
mCid = CellInfo.UNAVAILABLE;
mPsc = CellInfo.UNAVAILABLE;
mUarfcn = CellInfo.UNAVAILABLE;
- mAdditionalPlmns = Collections.emptyList();
+ mAdditionalPlmns = new ArraySet<>();
mCsgInfo = null;
}
@@ -86,13 +87,14 @@
*/
public CellIdentityWcdma(int lac, int cid, int psc, int uarfcn, @Nullable String mccStr,
@Nullable String mncStr, @Nullable String alphal, @Nullable String alphas,
- @NonNull List<String> additionalPlmns, @Nullable ClosedSubscriberGroupInfo csgInfo) {
+ @NonNull Collection<String> additionalPlmns,
+ @Nullable ClosedSubscriberGroupInfo csgInfo) {
super(TAG, CellInfo.TYPE_WCDMA, mccStr, mncStr, alphal, alphas);
mLac = inRangeOrUnavailable(lac, 0, MAX_LAC);
mCid = inRangeOrUnavailable(cid, 0, MAX_CID);
mPsc = inRangeOrUnavailable(psc, 0, MAX_PSC);
mUarfcn = inRangeOrUnavailable(uarfcn, 0, MAX_UARFCN);
- mAdditionalPlmns = new ArrayList<>(additionalPlmns.size());
+ mAdditionalPlmns = new ArraySet<>(additionalPlmns.size());
for (String plmn : additionalPlmns) {
if (isValidPlmn(plmn)) {
mAdditionalPlmns.add(plmn);
@@ -104,14 +106,14 @@
/** @hide */
public CellIdentityWcdma(@NonNull android.hardware.radio.V1_0.CellIdentityWcdma cid) {
this(cid.lac, cid.cid, cid.psc, cid.uarfcn, cid.mcc, cid.mnc, "", "",
- Collections.emptyList(), null);
+ new ArraySet<>(), null);
}
/** @hide */
public CellIdentityWcdma(@NonNull android.hardware.radio.V1_2.CellIdentityWcdma cid) {
this(cid.base.lac, cid.base.cid, cid.base.psc, cid.base.uarfcn,
cid.base.mcc, cid.base.mnc, cid.operatorNames.alphaLong,
- cid.operatorNames.alphaShort, Collections.emptyList(), null);
+ cid.operatorNames.alphaShort, new ArraySet<>(), null);
}
/** @hide */
@@ -232,8 +234,8 @@
* @return a list of additional PLMN IDs supported by this cell.
*/
@NonNull
- public List<String> getAdditionalPlmns() {
- return mAdditionalPlmns;
+ public Set<String> getAdditionalPlmns() {
+ return Collections.unmodifiableSet(mAdditionalPlmns);
}
/**
@@ -305,7 +307,7 @@
dest.writeInt(mCid);
dest.writeInt(mPsc);
dest.writeInt(mUarfcn);
- dest.writeList(mAdditionalPlmns);
+ dest.writeArraySet(mAdditionalPlmns);
dest.writeParcelable(mCsgInfo, flags);
}
@@ -316,7 +318,7 @@
mCid = in.readInt();
mPsc = in.readInt();
mUarfcn = in.readInt();
- mAdditionalPlmns = in.readArrayList(null);
+ mAdditionalPlmns = (ArraySet<String>) in.readArraySet(null);
mCsgInfo = in.readParcelable(null);
if (DBG) log(toString());
}
diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java
index f9de47d..c74e17f 100644
--- a/telephony/java/android/telephony/NetworkRegistrationInfo.java
+++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java
@@ -258,7 +258,7 @@
mCellIdentity = cellIdentity;
mEmergencyOnly = emergencyOnly;
mNrState = NR_STATE_NONE;
- mRplmn = (rplmn == null) ? "" : rplmn;
+ mRplmn = rplmn;
}
/**
@@ -408,13 +408,13 @@
* <p>If the device is registered, this will return the registered PLMN-ID. If registration
* has failed, then this will return the PLMN ID of the last attempted registration. If the
* device is not registered, or if is registered to a non-3GPP radio technology, then this
- * will return an empty string.
+ * will return null.
*
* <p>See 3GPP TS 23.122 for further information about the Registered PLMN.
*
- * @return the registered PLMN-ID or an empty string.
+ * @return the registered PLMN-ID or null.
*/
- @NonNull public String getRegisteredPlmn() {
+ @Nullable public String getRegisteredPlmn() {
return mRplmn;
}
@@ -892,7 +892,7 @@
* @return The same instance of the builder.
*/
public @NonNull Builder setRegisteredPlmn(@Nullable String rplmn) {
- mRplmn = (rplmn == null) ? "" : rplmn;
+ mRplmn = rplmn;
return this;
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 15103bf..68b6683 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -13135,7 +13135,7 @@
*/
static IPhoneSubInfo getSubscriberInfoService() {
// Keeps cache disabled until test fixes are checked into AOSP.
- if (true) {
+ if (!sServiceHandleCacheEnabled) {
return IPhoneSubInfo.Stub.asInterface(
TelephonyFrameworkInitializer
.getTelephonyServiceManager()
@@ -13169,7 +13169,7 @@
*/
static ISub getSubscriptionService() {
// Keeps cache disabled until test fixes are checked into AOSP.
- if (true) {
+ if (!sServiceHandleCacheEnabled) {
return ISub.Stub.asInterface(
TelephonyFrameworkInitializer
.getTelephonyServiceManager()
@@ -13203,7 +13203,7 @@
*/
static ISms getSmsService() {
// Keeps cache disabled until test fixes are checked into AOSP.
- if (true) {
+ if (!sServiceHandleCacheEnabled) {
return ISms.Stub.asInterface(
TelephonyFrameworkInitializer
.getTelephonyServiceManager()
diff --git a/tests/RollbackTest/RollbackTest/AndroidManifest.xml b/tests/RollbackTest/RollbackTest/AndroidManifest.xml
index 2b8c964..9274da2 100644
--- a/tests/RollbackTest/RollbackTest/AndroidManifest.xml
+++ b/tests/RollbackTest/RollbackTest/AndroidManifest.xml
@@ -17,6 +17,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.tests.rollback" >
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<application>
<receiver android:name="com.android.cts.install.lib.LocalIntentSender"
android:exported="true" />