Merge "Remove use of private SkBlurMask.h"
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index f3f4fa1..e2fff0f 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -862,7 +862,8 @@
public boolean KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED =
DEFAULT_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED;
- public int USE_TARE_POLICY = Settings.Global.DEFAULT_ENABLE_TARE;
+ public int USE_TARE_POLICY = EconomyManager.DEFAULT_ENABLE_POLICY_ALARM
+ ? EconomyManager.DEFAULT_ENABLE_TARE_MODE : EconomyManager.ENABLED_MODE_OFF;
/**
* The amount of temporary reserve quota to give apps on receiving the
@@ -1294,7 +1295,8 @@
KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED);
pw.println();
- pw.print(Settings.Global.ENABLE_TARE, USE_TARE_POLICY);
+ pw.print(Settings.Global.ENABLE_TARE,
+ EconomyManager.enabledModeToString(USE_TARE_POLICY));
pw.println();
pw.print(KEY_TEMPORARY_QUOTA_BUMP, TEMPORARY_QUOTA_BUMP);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 6a7904c..70bf353 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -636,7 +636,6 @@
public static final long DEFAULT_RUNTIME_USER_INITIATED_DATA_TRANSFER_LIMIT_MS =
Math.min(Long.MAX_VALUE, DEFAULT_RUNTIME_USER_INITIATED_LIMIT_MS);
static final boolean DEFAULT_PERSIST_IN_SPLIT_FILES = true;
- private static final boolean DEFAULT_USE_TARE_POLICY = false;
/**
* Minimum # of non-ACTIVE jobs for which the JMS will be happy running some work early.
@@ -802,7 +801,8 @@
/**
* If true, use TARE policy for job limiting. If false, use quotas.
*/
- public boolean USE_TARE_POLICY = DEFAULT_USE_TARE_POLICY;
+ public boolean USE_TARE_POLICY = EconomyManager.DEFAULT_ENABLE_POLICY_JOB_SCHEDULER
+ && EconomyManager.DEFAULT_ENABLE_TARE_MODE == EconomyManager.ENABLED_MODE_ON;
private void updateBatchingConstantsLocked() {
MIN_READY_NON_ACTIVE_JOBS_COUNT = DeviceConfig.getInt(
diff --git a/api/Android.bp b/api/Android.bp
index f49e6dd..78ddc6a 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -184,6 +184,18 @@
"$(location :frameworks-base-api-module-lib-current.txt)",
}
+genrule {
+ name: "frameworks-base-api-current.srcjar",
+ tools: ["merge_zips"],
+ out: ["current.srcjar"],
+ cmd: "$(location merge_zips) $(out) $(in)",
+ srcs: [
+ ":api-stubs-docs-non-updatable",
+ ":all-modules-public-stubs-source",
+ ],
+ visibility: ["//visibility:private"], // Used by make module in //development, mind
+}
+
// This produces the same annotations.zip as framework-doc-stubs, but by using
// outputs from individual modules instead of all the source code.
genrule_defaults {
diff --git a/api/api.go b/api/api.go
index c91ff81..077ab96 100644
--- a/api/api.go
+++ b/api/api.go
@@ -147,17 +147,6 @@
ctx.CreateModule(genrule.GenRuleFactory, &props)
}
-func createMergedStubsSrcjar(ctx android.LoadHookContext, modules []string) {
- props := genruleProps{}
- props.Name = proptools.StringPtr(ctx.ModuleName() + "-current.srcjar")
- props.Tools = []string{"merge_zips"}
- props.Out = []string{"current.srcjar"}
- props.Cmd = proptools.StringPtr("$(location merge_zips) $(out) $(in)")
- props.Srcs = append([]string{":api-stubs-docs-non-updatable"}, createSrcs(modules, "{.public.stubs.source}")...)
- props.Visibility = []string{"//visibility:private"} // Used by make module in //development, mind
- ctx.CreateModule(genrule.GenRuleFactory, &props)
-}
-
func createMergedAnnotationsFilegroups(ctx android.LoadHookContext, modules, system_server_modules []string) {
for _, i := range []struct{
name string
@@ -382,8 +371,6 @@
}
createMergedTxts(ctx, bootclasspath, system_server_classpath)
- createMergedStubsSrcjar(ctx, bootclasspath)
-
createMergedPublicStubs(ctx, bootclasspath)
createMergedSystemStubs(ctx, bootclasspath)
createMergedTestStubsForNonUpdatableModules(ctx)
diff --git a/core/api/current.txt b/core/api/current.txt
index 3eb543d..1aad4b7 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1857,6 +1857,7 @@
field public static final int windowMinWidthMajor = 16843606; // 0x1010356
field public static final int windowMinWidthMinor = 16843607; // 0x1010357
field public static final int windowNoDisplay = 16843294; // 0x101021e
+ field public static final int windowNoMoveAnimation;
field public static final int windowNoTitle = 16842838; // 0x1010056
field @Deprecated public static final int windowOverscan = 16843727; // 0x10103cf
field public static final int windowReenterTransition = 16843951; // 0x10104af
@@ -4510,6 +4511,7 @@
method @NonNull public final <T extends android.view.View> T requireViewById(@IdRes int);
method public final void runOnUiThread(Runnable);
method public void setActionBar(@Nullable android.widget.Toolbar);
+ method public void setAllowCrossUidActivitySwitchFromBelow(boolean);
method public void setContentTransitionManager(android.transition.TransitionManager);
method public void setContentView(@LayoutRes int);
method public void setContentView(android.view.View);
@@ -24425,7 +24427,7 @@
method public void registerTransferCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaRouter2.TransferCallback);
method public void setOnGetControllerHintsListener(@Nullable android.media.MediaRouter2.OnGetControllerHintsListener);
method public void setRouteListingPreference(@Nullable android.media.RouteListingPreference);
- method public void showSystemOutputSwitcher();
+ method public boolean showSystemOutputSwitcher();
method public void stop();
method public void transferTo(@NonNull android.media.MediaRoute2Info);
method public void unregisterControllerCallback(@NonNull android.media.MediaRouter2.ControllerCallback);
@@ -41244,6 +41246,7 @@
method public void setUiEnabled(boolean);
method public void show(android.os.Bundle, int);
method public void startAssistantActivity(android.content.Intent);
+ method public void startAssistantActivity(@NonNull android.content.Intent, @NonNull android.os.Bundle);
method public void startVoiceActivity(android.content.Intent);
method public final void unregisterVisibleActivityCallback(@NonNull android.service.voice.VoiceInteractionSession.VisibleActivityCallback);
field public static final String KEY_SHOW_SESSION_ID = "android.service.voice.SHOW_SESSION_ID";
@@ -53823,6 +53826,7 @@
ctor public WindowManager.LayoutParams(int, int, int, int, int, int, int);
ctor public WindowManager.LayoutParams(android.os.Parcel);
method public boolean areWallpaperTouchEventsEnabled();
+ method public boolean canPlayMoveAnimation();
method public final int copyFrom(android.view.WindowManager.LayoutParams);
method public String debug(String);
method public int describeContents();
@@ -53835,6 +53839,7 @@
method public boolean isHdrConversionEnabled();
method public static boolean mayUseInputMethod(int);
method public void setBlurBehindRadius(@IntRange(from=0) int);
+ method public void setCanPlayMoveAnimation(boolean);
method public void setColorMode(int);
method public void setFitInsetsIgnoringVisibility(boolean);
method public void setFitInsetsSides(int);
@@ -55942,7 +55947,7 @@
public final class TextBoundsInfoResult {
ctor public TextBoundsInfoResult(int);
- ctor public TextBoundsInfoResult(int, @NonNull android.view.inputmethod.TextBoundsInfo);
+ ctor public TextBoundsInfoResult(int, @Nullable android.view.inputmethod.TextBoundsInfo);
method public int getResultCode();
method @Nullable public android.view.inputmethod.TextBoundsInfo getTextBoundsInfo();
field public static final int CODE_CANCELLED = 3; // 0x3
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index dde964c..03be333 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2800,6 +2800,12 @@
ctor public VisibleActivityInfo(int, @NonNull android.os.IBinder);
}
+ public class VoiceInteractionService extends android.app.Service {
+ method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetectorForTest(@NonNull String, @NonNull java.util.Locale, @NonNull android.hardware.soundtrigger.SoundTrigger.ModuleProperties, @NonNull java.util.concurrent.Executor, @NonNull android.service.voice.AlwaysOnHotwordDetector.Callback);
+ method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetectorForTest(@NonNull String, @NonNull java.util.Locale, @Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull android.hardware.soundtrigger.SoundTrigger.ModuleProperties, @NonNull java.util.concurrent.Executor, @NonNull android.service.voice.AlwaysOnHotwordDetector.Callback);
+ method @NonNull public final java.util.List<android.hardware.soundtrigger.SoundTrigger.ModuleProperties> listModuleProperties();
+ }
+
public static class VoiceInteractionSession.ActivityId {
method @NonNull public android.os.IBinder getAssistToken();
method public int getTaskId();
@@ -3288,6 +3294,7 @@
public interface WindowManager extends android.view.ViewManager {
method public default int getDisplayImePolicy(int);
+ method public static boolean hasWindowExtensionsEnabled();
method public default void holdLock(android.os.IBinder, int);
method public default boolean isGlobalKey(int);
method public default boolean isTaskSnapshotSupported();
@@ -3306,7 +3313,6 @@
method public final void setWindowContextToken(@NonNull android.os.IBinder);
field public static final int ACCESSIBILITY_TITLE_CHANGED = 33554432; // 0x2000000
field public static final int FLAG_SLIPPERY = 536870912; // 0x20000000
- field public static final int PRIVATE_FLAG_NO_MOVE_ANIMATION = 64; // 0x40
field public CharSequence accessibilityTitle;
field public int privateFlags;
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 1d03d84..d9c7a27 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -9193,6 +9193,24 @@
}
/**
+ * Specifies whether the activities below this one in the task can also start other activities
+ * or finish the task.
+ * <p>
+ * Starting from Target SDK Level {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, apps
+ * are blocked from starting new activities or finishing their task unless the top activity of
+ * such task belong to the same UID for security reasons.
+ * <p>
+ * Setting this flag to {@code true} will allow the launching app to ignore the restriction if
+ * this activity is on top. Apps matching the UID of this activity are always exempt.
+ *
+ * @param allowed {@code true} to disable the UID restrictions; {@code false} to revert back to
+ * the default behaviour
+ */
+ public void setAllowCrossUidActivitySwitchFromBelow(boolean allowed) {
+ ActivityClient.getInstance().setAllowCrossUidActivitySwitchFromBelow(mToken, allowed);
+ }
+
+ /**
* Registers remote animations per transition type for this activity.
*
* @param definition The remote animation definition that defines which transition whould run
diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java
index 5354a44..44327af 100644
--- a/core/java/android/app/ActivityClient.java
+++ b/core/java/android/app/ActivityClient.java
@@ -478,6 +478,14 @@
}
}
+ void setAllowCrossUidActivitySwitchFromBelow(IBinder token, boolean allowed) {
+ try {
+ getActivityClientController().setAllowCrossUidActivitySwitchFromBelow(token, allowed);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
int setVrMode(IBinder token, boolean enabled, ComponentName packageName) {
try {
return getActivityClientController().setVrMode(token, enabled, packageName);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 543c5e7..b50245d 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2251,12 +2251,18 @@
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case BIND_SERVICE:
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind: "
+ + String.valueOf(msg.obj));
+ }
handleBindService((BindServiceData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case UNBIND_SERVICE:
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceUnbind");
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceUnbind: "
+ + String.valueOf(msg.obj));
+ }
handleUnbindService((BindServiceData)msg.obj);
schedulePurgeIdler();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
@@ -2270,13 +2276,19 @@
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case STOP_SERVICE:
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceStop");
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceStop: "
+ + String.valueOf(msg.obj));
+ }
handleStopService((IBinder)msg.obj);
schedulePurgeIdler();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case TIMEOUT_SERVICE:
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceTimeout");
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceTimeout: "
+ + String.valueOf(msg.obj));
+ }
handleTimeoutService((IBinder) msg.obj, msg.arg1);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
@@ -2288,7 +2300,12 @@
cci.context.performFinalCleanup(cci.who, cci.what);
break;
case GC_WHEN_IDLE:
- scheduleGcIdler();
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "gcWhenIdle");
+ try {
+ scheduleGcIdler();
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ }
break;
case DUMP_SERVICE:
handleDumpService((DumpComponentInfo)msg.obj);
@@ -2365,7 +2382,15 @@
handleTranslucentConversionComplete((IBinder)msg.obj, msg.arg1 == 1);
break;
case INSTALL_PROVIDER:
- handleInstallProvider((ProviderInfo) msg.obj);
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "providerInstall: "
+ + String.valueOf(msg.obj));
+ }
+ try {
+ handleInstallProvider((ProviderInfo) msg.obj);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ }
break;
case ON_NEW_ACTIVITY_OPTIONS:
Pair<IBinder, ActivityOptions> pair = (Pair<IBinder, ActivityOptions>) msg.obj;
diff --git a/core/java/android/app/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl
index 5e40399..5136b20 100644
--- a/core/java/android/app/IActivityClientController.aidl
+++ b/core/java/android/app/IActivityClientController.aidl
@@ -120,6 +120,7 @@
oneway void setShowWhenLocked(in IBinder token, boolean showWhenLocked);
oneway void setInheritShowWhenLocked(in IBinder token, boolean setInheritShownWhenLocked);
oneway void setTurnScreenOn(in IBinder token, boolean turnScreenOn);
+ oneway void setAllowCrossUidActivitySwitchFromBelow(in IBinder token, boolean allowed);
oneway void reportActivityFullyDrawn(in IBinder token, boolean restoredFromBundle);
oneway void overrideActivityTransition(IBinder token, boolean open, int enterAnim, int exitAnim,
int backgroundColor);
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index d207688..aa5b866 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -823,6 +823,7 @@
/** Blocks until all broadcast queues become idle. */
void waitForBroadcastIdle();
+ void waitForBroadcastBarrier();
/** Delays delivering broadcasts to the specified package. */
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.DUMP)")
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index 90ed4ce..f3ad9d1 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -368,8 +368,8 @@
* <li>App has a UID < {@link android.os.Process#FIRST_APPLICATION_UID}</li>
* <li>App is on Doze allowlist</li>
* <li>Device is running in <a href="https://android.googlesource.com/platform/frameworks/base/+/master/packages/SystemUI/docs/demo_mode.md">Demo Mode</a></li>
- * <li><a href="https://source.android.com/devices/tech/admin/provision">Device owner app</a><li>
- * <li><a href="https://source.android.com/devices/tech/admin/managed-profiles">Profile owner apps</a><li>
+ * <li><a href="https://source.android.com/devices/tech/admin/provision">Device owner app</a></li>
+ * <li><a href="https://source.android.com/devices/tech/admin/managed-profiles">Profile owner apps</a></li>
* <li>Persistent apps</li>
* <li><a href="https://source.android.com/docs/core/connect/carrier">Carrier privileged apps</a></li>
* <li>Apps that have the {@code android.app.role.RoleManager#ROLE_EMERGENCY} role</li>
diff --git a/core/java/android/credentials/OWNERS b/core/java/android/credentials/OWNERS
index e8f393e..f5de01d 100644
--- a/core/java/android/credentials/OWNERS
+++ b/core/java/android/credentials/OWNERS
@@ -3,3 +3,5 @@
sgjerry@google.com
leecam@google.com
akaphle@google.com
+omerozer@google.com
+jwillcox@google.com
diff --git a/core/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtils.java b/core/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtils.java
index 1b661e7..d681a2c 100644
--- a/core/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtils.java
+++ b/core/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtils.java
@@ -74,6 +74,9 @@
private static final String DPD_DELAY_SEC_KEY = "DPD_DELAY_SEC_KEY";
private static final String NATT_KEEPALIVE_DELAY_SEC_KEY = "NATT_KEEPALIVE_DELAY_SEC_KEY";
private static final String IKE_OPTIONS_KEY = "IKE_OPTIONS_KEY";
+ private static final String IP_VERSION_KEY = "IP_VERSION_KEY";
+ private static final String ENCAP_TYPE_KEY = "ENCAP_TYPE_KEY";
+ // TODO: add DSCP_KEY and IS_IKE_FRAGMENT_SUPPORTED_KEY.
// TODO: b/243181760 Use the IKE API when they are exposed
@VisibleForTesting(visibility = Visibility.PRIVATE)
@@ -156,6 +159,8 @@
result.putInt(SOFT_LIFETIME_SEC_KEY, params.getSoftLifetimeSeconds());
result.putInt(DPD_DELAY_SEC_KEY, params.getDpdDelaySeconds());
result.putInt(NATT_KEEPALIVE_DELAY_SEC_KEY, params.getNattKeepAliveDelaySeconds());
+ result.putInt(IP_VERSION_KEY, params.getIpVersion());
+ result.putInt(ENCAP_TYPE_KEY, params.getEncapType());
// TODO: b/185941731 Make sure IkeSessionParamsUtils is automatically updated when a new
// IKE_OPTION is defined in IKE module and added in the IkeSessionParams
@@ -207,6 +212,8 @@
in.getInt(HARD_LIFETIME_SEC_KEY), in.getInt(SOFT_LIFETIME_SEC_KEY));
builder.setDpdDelaySeconds(in.getInt(DPD_DELAY_SEC_KEY));
builder.setNattKeepAliveDelaySeconds(in.getInt(NATT_KEEPALIVE_DELAY_SEC_KEY));
+ builder.setIpVersion(in.getInt(IP_VERSION_KEY));
+ builder.setEncapType(in.getInt(ENCAP_TYPE_KEY));
final PersistableBundle configReqListBundle = in.getPersistableBundle(CONFIG_REQUESTS_KEY);
Objects.requireNonNull(configReqListBundle, "Config request list was null");
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7f651c5..54e4909 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -40,7 +40,6 @@
import android.app.NotificationManager;
import android.app.SearchManager;
import android.app.WallpaperManager;
-import android.app.tare.EconomyManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -14720,37 +14719,15 @@
= "forced_app_standby_for_small_battery_enabled";
/**
- * Whether to enable the TARE subsystem as a whole or not.
- * 1 means enable, 0 means disable.
+ * Whether to enable the TARE subsystem or not.
+ * Valid values are
+ * {@link android.app.tare.EconomyManager#ENABLE_TARE_ON EconomyManager.ENABLE_TARE_*}.
*
* @hide
*/
public static final String ENABLE_TARE = "enable_tare";
/**
- * Default value for {@link #ENABLE_TARE}.
- *
- * @hide
- */
- public static final int DEFAULT_ENABLE_TARE = EconomyManager.DEFAULT_ENABLE_TARE_MODE;
-
- /**
- * Whether to enable the TARE AlarmManager economic policy or not.
- * 1 means enable, 0 means disable.
- *
- * @hide
- */
- public static final String ENABLE_TARE_ALARM_MANAGER = "enable_tare_alarm_manager";
-
- /**
- * Default value for {@link #ENABLE_TARE_ALARM_MANAGER}.
- *
- * @hide
- */
- public static final int DEFAULT_ENABLE_TARE_ALARM_MANAGER =
- EconomyManager.DEFAULT_ENABLE_POLICY_ALARM ? 1 : 0;
-
- /**
* Settings for AlarmManager's TARE EconomicPolicy (list of its economic factors).
*
* Keys are listed in {@link android.app.tare.EconomyManager}.
@@ -14760,22 +14737,6 @@
public static final String TARE_ALARM_MANAGER_CONSTANTS = "tare_alarm_manager_constants";
/**
- * Whether to enable the TARE JobScheduler economic policy or not.
- * 1 means enable, 0 means disable.
- *
- * @hide
- */
- public static final String ENABLE_TARE_JOB_SCHEDULER = "enable_tare_job_scheduler";
-
- /**
- * Default value for {@link #ENABLE_TARE_JOB_SCHEDULER}.
- *
- * @hide
- */
- public static final int DEFAULT_ENABLE_TARE_JOB_SCHEDULER =
- EconomyManager.DEFAULT_ENABLE_POLICY_JOB_SCHEDULER ? 1 : 0;
-
- /**
* Settings for JobScheduler's TARE EconomicPolicy (list of its economic factors).
*
* Keys are listed in {@link android.app.tare.EconomyManager}.
diff --git a/core/java/android/service/quicksettings/TileService.java b/core/java/android/service/quicksettings/TileService.java
index d957029..94384b0 100644
--- a/core/java/android/service/quicksettings/TileService.java
+++ b/core/java/android/service/quicksettings/TileService.java
@@ -70,8 +70,8 @@
* <li>{@link #onTileAdded()} and {@link #onTileRemoved()} may be called outside of the
* {@link #onCreate()} - {@link #onDestroy()} window</li>
* </ul>
- * <p>TileService will be detected by tiles that match the {@value #ACTION_QS_TILE}
- * and require the permission "android.permission.BIND_QUICK_SETTINGS_TILE".
+ * <p>TileService will resolve against services that match the {@value #ACTION_QS_TILE} action
+ * and require the permission {@code android.permission.BIND_QUICK_SETTINGS_TILE}.
* The label and icon for the service will be used as the default label and
* icon for the tile. Here is an example TileService declaration.</p>
* <pre class="prettyprint">
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index bcc3f58..48b7a59 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -784,8 +784,13 @@
mSupportSandboxedDetectionService = supportSandboxedDetectionService;
}
+ // Do nothing. This method should not be abstract.
+ // TODO (b/269355519) un-subclass AOHD.
@Override
- void initialize(@Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory) {
+ void initialize(@Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory) {}
+
+ void initialize(@Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory,
+ @Nullable SoundTrigger.ModuleProperties moduleProperties) {
if (mSupportSandboxedDetectionService) {
initAndVerifyDetector(options, sharedMemory, mInternalCallback,
DETECTOR_TYPE_TRUSTED_HOTWORD_DSP);
@@ -793,9 +798,18 @@
try {
Identity identity = new Identity();
identity.packageName = ActivityThread.currentOpPackageName();
+ if (moduleProperties == null) {
+ List<SoundTrigger.ModuleProperties> modulePropList =
+ mModelManagementService.listModuleProperties(identity);
+ if (modulePropList.size() > 0) {
+ moduleProperties = modulePropList.get(0);
+ }
+ // (@atneya) intentionally let a null moduleProperties through until
+ // all CTS tests are fixed
+ }
mSoundTriggerSession =
mModelManagementService.createSoundTriggerSessionAsOriginator(
- identity, mBinder);
+ identity, mBinder, moduleProperties);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 12853cb..a684e41 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -24,6 +24,8 @@
import android.annotation.SdkConstant;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.app.ActivityThread;
import android.app.Service;
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
@@ -33,7 +35,9 @@
import android.content.Context;
import android.content.Intent;
import android.hardware.soundtrigger.KeyphraseEnrollmentInfo;
+import android.hardware.soundtrigger.SoundTrigger;
import android.media.voice.KeyphraseModelManager;
+import android.media.permission.Identity;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -386,6 +390,22 @@
}
/**
+ * List available ST modules to attach to for test purposes.
+ * @hide
+ */
+ @TestApi
+ @NonNull
+ public final List<SoundTrigger.ModuleProperties> listModuleProperties() {
+ Identity identity = new Identity();
+ identity.packageName = ActivityThread.currentOpPackageName();
+ try {
+ return mSystemService.listModuleProperties(identity);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Creates an {@link AlwaysOnHotwordDetector} for the given keyphrase and locale.
* This instance must be retained and used by the client.
* Calling this a second time invalidates the previously created hotword detector
@@ -422,7 +442,8 @@
@SuppressLint("MissingNullability") AlwaysOnHotwordDetector.Callback callback) {
return createAlwaysOnHotwordDetectorInternal(keyphrase, locale,
/* supportHotwordDetectionService= */ false, /* options= */ null,
- /* sharedMemory= */ null, /* executor= */ null, callback);
+ /* sharedMemory= */ null, /* moduleProperties */ null,
+ /* executor= */ null, callback);
}
/**
@@ -461,10 +482,37 @@
Objects.requireNonNull(callback);
return createAlwaysOnHotwordDetectorInternal(keyphrase, locale,
/* supportHotwordDetectionService= */ false, /* options= */ null,
- /* sharedMemory= */ null, executor, callback);
+ /* sharedMemory= */ null, /* moduleProperties */ null, executor, callback);
}
/**
+ * Same as {@link createAlwaysOnHotwordDetector(String, Locale, Executor,
+ * AlwaysOnHotwordDetector.Callback)}, but allow explicit selection of the underlying ST
+ * module to attach to.
+ * Use {@link listModuleProperties} to get available modules to attach to.
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(Manifest.permission.MANAGE_HOTWORD_DETECTION)
+ @NonNull
+ public final AlwaysOnHotwordDetector createAlwaysOnHotwordDetectorForTest(
+ @NonNull String keyphrase, @SuppressLint("UseIcu") @NonNull Locale locale,
+ @NonNull SoundTrigger.ModuleProperties moduleProperties,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull AlwaysOnHotwordDetector.Callback callback) {
+
+ Objects.requireNonNull(keyphrase);
+ Objects.requireNonNull(locale);
+ Objects.requireNonNull(moduleProperties);
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+ return createAlwaysOnHotwordDetectorInternal(keyphrase, locale,
+ /* supportHotwordDetectionService= */ false, /* options= */ null,
+ /* sharedMemory= */ null, moduleProperties, executor, callback);
+ }
+
+
+ /**
* Create an {@link AlwaysOnHotwordDetector} and trigger a {@link HotwordDetectionService}
* service, then it will also pass the read-only data to hotword detection service.
*
@@ -515,7 +563,7 @@
@SuppressLint("MissingNullability") AlwaysOnHotwordDetector.Callback callback) {
return createAlwaysOnHotwordDetectorInternal(keyphrase, locale,
/* supportHotwordDetectionService= */ true, options, sharedMemory,
- /* executor= */ null, callback);
+ /* modulProperties */ null, /* executor= */ null, callback);
}
/**
@@ -566,16 +614,46 @@
Objects.requireNonNull(executor);
Objects.requireNonNull(callback);
return createAlwaysOnHotwordDetectorInternal(keyphrase, locale,
- /* supportHotwordDetectionService= */ true, options, sharedMemory, executor,
- callback);
+ /* supportHotwordDetectionService= */ true, options, sharedMemory,
+ /* moduleProperties */ null, executor, callback);
}
+ /**
+ * Same as {@link createAlwaysOnHotwordDetector(String, Locale,
+ * PersistableBundle, SharedMemory, Executor, AlwaysOnHotwordDetector.Callback)},
+ * but allow explicit selection of the underlying ST module to attach to.
+ * Use {@link listModuleProperties} to get available modules to attach to.
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(Manifest.permission.MANAGE_HOTWORD_DETECTION)
+ @NonNull
+ public final AlwaysOnHotwordDetector createAlwaysOnHotwordDetectorForTest(
+ @NonNull String keyphrase, @SuppressLint("UseIcu") @NonNull Locale locale,
+ @Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory,
+ @NonNull SoundTrigger.ModuleProperties moduleProperties,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull AlwaysOnHotwordDetector.Callback callback) {
+
+ Objects.requireNonNull(keyphrase);
+ Objects.requireNonNull(locale);
+ Objects.requireNonNull(moduleProperties);
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+ return createAlwaysOnHotwordDetectorInternal(keyphrase, locale,
+ /* supportHotwordDetectionService= */ true, options, sharedMemory,
+ moduleProperties, executor, callback);
+ }
+
+
+
private AlwaysOnHotwordDetector createAlwaysOnHotwordDetectorInternal(
@SuppressLint("MissingNullability") String keyphrase, // TODO: nullability properly
@SuppressLint({"MissingNullability", "UseIcu"}) Locale locale,
boolean supportHotwordDetectionService,
@Nullable PersistableBundle options,
@Nullable SharedMemory sharedMemory,
+ @Nullable SoundTrigger.ModuleProperties moduleProperties,
@Nullable @CallbackExecutor Executor executor,
@SuppressLint("MissingNullability") AlwaysOnHotwordDetector.Callback callback) {
@@ -609,7 +687,8 @@
try {
dspDetector.registerOnDestroyListener(this::onHotwordDetectorDestroyed);
- dspDetector.initialize(options, sharedMemory);
+ // If moduleProperties is null, the default STModule is used.
+ dspDetector.initialize(options, sharedMemory, moduleProperties);
} catch (Exception e) {
mActiveDetectors.remove(dspDetector);
dspDetector.destroy();
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 5778518..cabcae3 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -26,6 +26,7 @@
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.app.Activity;
+import android.app.ActivityOptions;
import android.app.Dialog;
import android.app.DirectAction;
import android.app.Instrumentation;
@@ -1527,8 +1528,34 @@
* <p>By default, the system will create a window for the UI for this session. If you are using
* an assistant activity instead, then you can disable the window creation by calling
* {@link #setUiEnabled} in {@link #onPrepareShow(Bundle, int)}.</p>
+ *
+ * NOTE: if the app would like to override some options to start the Activity,
+ * use {@link #startAssistantActivity(Intent, Bundle)} instead.
*/
public void startAssistantActivity(Intent intent) {
+ startAssistantActivity(intent, ActivityOptions.makeBasic().toBundle());
+ }
+
+ /**
+ * <p>Ask that a new assistant activity be started. This will create a new task in the
+ * in activity manager: this means that
+ * {@link Intent#FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_NEW_TASK}
+ * will be set for you to make it a new task.</p>
+ *
+ * <p>The newly started activity will be displayed on top of other activities in the system
+ * in a new layer that is not affected by multi-window mode. Tasks started from this activity
+ * will go into the normal activity layer and not this new layer.</p>
+ *
+ * <p>By default, the system will create a window for the UI for this session. If you are using
+ * an assistant activity instead, then you can disable the window creation by calling
+ * {@link #setUiEnabled} in {@link #onPrepareShow(Bundle, int)}.</p>
+ *
+ * @param intent the intent used to start an assistant activity
+ * @param bundle Additional options for how the Activity should be started. See
+ * {@link ActivityOptions} for how to build the Bundle supplied here.
+ */
+ public void startAssistantActivity(@NonNull Intent intent, @NonNull Bundle bundle) {
+ Objects.requireNonNull(bundle);
if (mToken == null) {
throw new IllegalStateException("Can't call before onCreate()");
}
@@ -1537,7 +1564,7 @@
intent.prepareToLeaveProcess(mContext);
int res = mSystemService.startAssistantActivity(mToken, intent,
intent.resolveType(mContext.getContentResolver()),
- mContext.getAttributionTag());
+ mContext.getAttributionTag(), bundle);
Instrumentation.checkStartActivityResult(res, intent);
} catch (RemoteException e) {
}
diff --git a/core/java/android/text/method/InsertModeTransformationMethod.java b/core/java/android/text/method/InsertModeTransformationMethod.java
index fbdaa3d..0c933d9 100644
--- a/core/java/android/text/method/InsertModeTransformationMethod.java
+++ b/core/java/android/text/method/InsertModeTransformationMethod.java
@@ -80,12 +80,34 @@
*/
public InsertModeTransformationMethod(@IntRange(from = 0) int offset, boolean singleLine,
@NonNull TransformationMethod oldTransformationMethod) {
- mStart = offset;
- mEnd = offset;
+ this(offset, offset, singleLine, oldTransformationMethod);
+ }
+
+ private InsertModeTransformationMethod(int start, int end, boolean singleLine,
+ @NonNull TransformationMethod oldTransformationMethod) {
+ mStart = start;
+ mEnd = end;
mSingleLine = singleLine;
mOldTransformationMethod = oldTransformationMethod;
}
+ /**
+ * Create a new {@code InsertModeTransformation} with the given new inner
+ * {@code oldTransformationMethod} and the {@code singleLine} value. The returned
+ * {@link InsertModeTransformationMethod} will keep the highlight range.
+ *
+ * @param oldTransformationMethod the updated inner transformation method at the
+ * {@link android.widget.TextView}.
+ * @param singleLine the updated singleLine value.
+ * @return the new {@link InsertModeTransformationMethod} with the updated
+ * {@code oldTransformationMethod} and {@code singleLine} value.
+ */
+ public InsertModeTransformationMethod update(TransformationMethod oldTransformationMethod,
+ boolean singleLine) {
+ return new InsertModeTransformationMethod(mStart, mEnd, singleLine,
+ oldTransformationMethod);
+ }
+
public TransformationMethod getOldTransformationMethod() {
return mOldTransformationMethod;
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 4fcb137..e7cefd6 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -108,6 +108,7 @@
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.Log;
import android.util.proto.ProtoOutputStream;
@@ -1105,6 +1106,26 @@
public static final String PARCEL_KEY_SHORTCUTS_ARRAY = "shortcuts_array";
/**
+ * Whether the device supports the WindowManager Extensions.
+ * OEMs can enable this by having their device config to inherit window_extensions.mk, such as:
+ * <pre>
+ * $(call inherit-product, $(SRC_TARGET_DIR)/product/window_extensions.mk)
+ * </pre>
+ * @hide
+ */
+ boolean WINDOW_EXTENSIONS_ENABLED =
+ SystemProperties.getBoolean("persist.wm.extensions.enabled", false);
+
+ /**
+ * @see #WINDOW_EXTENSIONS_ENABLED
+ * @hide
+ */
+ @TestApi
+ static boolean hasWindowExtensionsEnabled() {
+ return WINDOW_EXTENSIONS_ENABLED;
+ }
+
+ /**
* Application-level
* {@link android.content.pm.PackageManager.Property PackageManager.Property}
* tag that specifies whether OEMs are permitted to provide activity
@@ -2785,10 +2806,10 @@
/**
* Never animate position changes of the window.
*
+ * @see android.R.attr#Window_windowNoMoveAnimation
* {@hide}
*/
@UnsupportedAppUsage
- @TestApi
public static final int PRIVATE_FLAG_NO_MOVE_ANIMATION = 0x00000040;
/** Window flag: special flag to limit the size of the window to be
@@ -4211,6 +4232,31 @@
}
/**
+ * Set whether animations can be played for position changes on this window. If disabled,
+ * the window will move to its new position instantly without animating.
+ *
+ * @attr ref android.R.attr#Window_windowNoMoveAnimation
+ */
+ public void setCanPlayMoveAnimation(boolean enable) {
+ if (enable) {
+ privateFlags &= ~PRIVATE_FLAG_NO_MOVE_ANIMATION;
+ } else {
+ privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION;
+ }
+ }
+
+ /**
+ * @return whether playing an animation during a position change is allowed on this window.
+ * This does not guarantee that an animation will be played in all such situations. For
+ * example, drag-resizing may move the window but not play an animation.
+ *
+ * @attr ref android.R.attr#Window_windowNoMoveAnimation
+ */
+ public boolean canPlayMoveAnimation() {
+ return (privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0;
+ }
+
+ /**
* @return the {@link WindowInsets.Type}s that this window is avoiding overlapping.
*/
public @InsetsType int getFitInsetsTypes() {
diff --git a/core/java/android/view/inputmethod/TextBoundsInfoResult.java b/core/java/android/view/inputmethod/TextBoundsInfoResult.java
index 62df17a..9dc11f0 100644
--- a/core/java/android/view/inputmethod/TextBoundsInfoResult.java
+++ b/core/java/android/view/inputmethod/TextBoundsInfoResult.java
@@ -17,7 +17,6 @@
package android.view.inputmethod;
import android.annotation.IntDef;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.RectF;
@@ -98,7 +97,7 @@
* is null.
*/
public TextBoundsInfoResult(@ResultCode int resultCode,
- @NonNull TextBoundsInfo textBoundsInfo) {
+ @Nullable TextBoundsInfo textBoundsInfo) {
if (resultCode == CODE_SUCCESS && textBoundsInfo == null) {
throw new IllegalStateException("TextBoundsInfo must be provided when the resultCode "
+ "is CODE_SUCCESS.");
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 616957a..b89dd31 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -8072,7 +8072,7 @@
final boolean isSingleLine = mTextView.isSingleLine();
mInsertModeTransformationMethod = new InsertModeTransformationMethod(offset,
isSingleLine, oldTransformationMethod);
- mTextView.setTransformationMethod(mInsertModeTransformationMethod);
+ mTextView.setTransformationMethodInternal(mInsertModeTransformationMethod);
Selection.setSelection((Spannable) mTextView.getText(), offset);
mIsInsertModeActive = true;
@@ -8083,11 +8083,6 @@
if (!mIsInsertModeActive) return;
if (mInsertModeTransformationMethod == null
|| mInsertModeTransformationMethod != mTextView.getTransformationMethod()) {
- // If mInsertionModeTransformationMethod doesn't match the one on TextView,
- // something else have changed the TextView's TransformationMethod while the
- // insertion mode is active. We don't need to restore the oldTransformationMethod.
- // TODO(265871733): support the case where setTransformationMethod is called in
- // the insert mode.
mIsInsertModeActive = false;
return;
}
@@ -8097,7 +8092,7 @@
final int selectionEnd = mTextView.getSelectionEnd();
final TransformationMethod oldTransformationMethod =
mInsertModeTransformationMethod.getOldTransformationMethod();
- mTextView.setTransformationMethod(oldTransformationMethod);
+ mTextView.setTransformationMethodInternal(oldTransformationMethod);
Selection.setSelection((Spannable) mTextView.getText(), selectionStart, selectionEnd);
mIsInsertModeActive = false;
}
@@ -8118,6 +8113,25 @@
layout.getSelection(highlightStart, highlightEnd, consumer);
}
}
+
+ /**
+ * Notify the {@link InsertModeController} before the TextView's
+ * {@link TransformationMethod} is updated. If it's not in the insert mode,
+ * the given method is directly returned. Otherwise, it will wrap the given transformation
+ * method with an {@link InsertModeTransformationMethod} and then return.
+ *
+ * @param oldTransformationMethod the new {@link TransformationMethod} to be set on the
+ * TextView.
+ * @return the updated {@link TransformationMethod} to be set on the Textview.
+ */
+ TransformationMethod updateTransformationMethod(
+ TransformationMethod oldTransformationMethod) {
+ if (!mIsInsertModeActive) return oldTransformationMethod;
+
+ mInsertModeTransformationMethod = mInsertModeTransformationMethod.update(
+ oldTransformationMethod, mTextView.isSingleLine());
+ return mInsertModeTransformationMethod;
+ }
}
boolean enterInsertMode(int offset) {
@@ -8134,6 +8148,26 @@
}
/**
+ * Called by the {@link TextView} when the {@link TransformationMethod} is updated.
+ *
+ * @param method the {@link TransformationMethod} to be set on the TextView.
+ */
+ void setTransformationMethod(TransformationMethod method) {
+ if (mInsertModeController == null || !mInsertModeController.mIsInsertModeActive) {
+ mTextView.setTransformationMethodInternal(method);
+ return;
+ }
+
+ // Changing TransformationMethod will reset selection range to [0, 0), we need to
+ // manually restore the old selection range.
+ final int selectionStart = mTextView.getSelectionStart();
+ final int selectionEnd = mTextView.getSelectionEnd();
+ method = mInsertModeController.updateTransformationMethod(method);
+ mTextView.setTransformationMethodInternal(method);
+ Selection.setSelection((Spannable) mTextView.getText(), selectionStart, selectionEnd);
+ }
+
+ /**
* Initializes the nodeInfo with smart actions.
*/
void onInitializeSmartActionsAccessibilityNodeInfo(AccessibilityNodeInfo nodeInfo) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 56bdea3..5de8079 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -2769,6 +2769,14 @@
* @attr ref android.R.styleable#TextView_singleLine
*/
public final void setTransformationMethod(TransformationMethod method) {
+ if (mEditor != null) {
+ mEditor.setTransformationMethod(method);
+ } else {
+ setTransformationMethodInternal(method);
+ }
+ }
+
+ void setTransformationMethodInternal(@Nullable TransformationMethod method) {
if (method == mTransformation) {
// Avoid the setText() below if the transformation is
// the same.
diff --git a/core/java/com/android/internal/app/ISoundTriggerService.aidl b/core/java/com/android/internal/app/ISoundTriggerService.aidl
index 3874de3..ab7f602 100644
--- a/core/java/com/android/internal/app/ISoundTriggerService.aidl
+++ b/core/java/com/android/internal/app/ISoundTriggerService.aidl
@@ -17,6 +17,7 @@
package com.android.internal.app;
import android.media.permission.Identity;
+import android.hardware.soundtrigger.SoundTrigger;
import com.android.internal.app.ISoundTriggerSession;
/**
@@ -43,6 +44,7 @@
* to clean-up whenever that happens.
*/
ISoundTriggerSession attachAsOriginator(in Identity originatorIdentity,
+ in SoundTrigger.ModuleProperties moduleProperties,
IBinder client);
/**
@@ -64,5 +66,12 @@
*/
ISoundTriggerSession attachAsMiddleman(in Identity middlemanIdentity,
in Identity originatorIdentity,
+ in SoundTrigger.ModuleProperties moduleProperties,
IBinder client);
+
+ /**
+ * Get available underlying SoundTrigger modules to attach to.
+ */
+ List<SoundTrigger.ModuleProperties> listModuleProperties(in Identity originatorIdentity);
+
}
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index 619b420..6b40d98 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -51,7 +51,7 @@
int startVoiceActivity(IBinder token, in Intent intent, String resolvedType,
String attributionTag);
int startAssistantActivity(IBinder token, in Intent intent, String resolvedType,
- String attributionTag);
+ String attributionTag, in Bundle bundle);
void setKeepAwake(IBinder token, boolean keepAwake);
void closeSystemDialogs(IBinder token);
void finish(IBinder token);
@@ -243,7 +243,14 @@
*/
IVoiceInteractionSoundTriggerSession createSoundTriggerSessionAsOriginator(
in Identity originatorIdentity,
- IBinder client);
+ IBinder client,
+ in SoundTrigger.ModuleProperties moduleProperties);
+
+ /**
+ * Lists properties of SoundTrigger modules that can be attached to by
+ * @{link createSoundTriggerSessionAsOriginator}.
+ */
+ List<SoundTrigger.ModuleProperties> listModuleProperties(in Identity originatorIdentity);
/**
* Set configuration and pass read-only data to hotword detection service.
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index a14e9a7..c41b822 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -2548,6 +2548,11 @@
}
params.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION;
}
+ if (a.getBoolean(
+ R.styleable.Window_windowNoMoveAnimation,
+ false)) {
+ params.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION;
+ }
final int sysUiVis = decor.getSystemUiVisibility();
final int statusLightFlag = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
final int statusFlag = a.getBoolean(R.styleable.Window_windowLightStatusBar, false)
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 826624a..b9e5366 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2221,6 +2221,10 @@
<!-- Elevation to use for the window. -->
<attr name="windowElevation" format="dimension" />
+ <!-- Flag indicating whether this window should skip movement animations.
+ See also {@link android.view.WindowManager.LayoutParams#setCanPlayMoveAnimation} -->
+ <attr name="windowNoMoveAnimation" format="boolean" />
+
<!-- Whether to clip window content to the outline of the window background. -->
<attr name="windowClipToOutline" format="boolean" />
@@ -9022,10 +9026,10 @@
{@link android.os.Build.VERSION_CODES#N} and not used in previous versions. -->
<attr name="supportsLocalInteraction" format="boolean" />
<!-- The service that provides {@link android.service.voice.HotwordDetectionService}.
- @hide @SystemApi -->
+ Expect a component name to be provided. @hide @SystemApi -->
<attr name="hotwordDetectionService" format="string" />
<!-- The service that provides {@link android.service.voice.VisualQueryDetectionService}.
- @hide @SystemApi -->
+ Expect a component name to be provided. @hide @SystemApi -->
<attr name="visualQueryDetectionService" format="string" />
</declare-styleable>
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index b23d5b4..ee02100 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -129,6 +129,7 @@
<public name="searchResultHighlightColor" />
<public name="focusedSearchResultHighlightColor" />
<public name="stylusHandwritingSettingsActivity" />
+ <public name="windowNoMoveAnimation" />
</staging-public-group>
<staging-public-group type="id" first-id="0x01cd0000">
diff --git a/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java b/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java
index 76e761a..90bb627 100644
--- a/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java
@@ -35,6 +35,8 @@
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.compatibility.common.util.SystemUtil;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -96,6 +98,7 @@
mContext.registerReceiverAsUser(receiver, UserHandle.of(mCrossUserId),
new IntentFilter(Intent.ACTION_USER_UNLOCKED), null, null);
ActivityManager.getService().startUserInBackground(mCrossUserId);
+ SystemUtil.runShellCommand("am wait-for-broadcast-barrier");
try {
if (!latch.await(TIMEOUT_USER_UNLOCK_SEC, TimeUnit.SECONDS)) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index c5e374a..19d8cfa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -499,6 +499,11 @@
break;
}
}
+ } else if (mSideStage.getChildCount() != 0) {
+ // There are chances the entering app transition got canceled by performing
+ // rotation transition. Checks if there is any child task existed in split
+ // screen before fallback to cancel entering flow.
+ openingToSide = true;
}
if (isEnteringSplit && !openingToSide) {
@@ -515,7 +520,7 @@
}
}
- if (!isEnteringSplit && openingToSide) {
+ if (!isEnteringSplit && apps != null) {
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
prepareEvictNonOpeningChildTasks(position, apps, evictWct);
mSyncQueue.queue(evictWct);
@@ -598,6 +603,11 @@
break;
}
}
+ } else if (mSideStage.getChildCount() != 0) {
+ // There are chances the entering app transition got canceled by performing
+ // rotation transition. Checks if there is any child task existed in split
+ // screen before fallback to cancel entering flow.
+ openingToSide = true;
}
if (isEnteringSplit && !openingToSide && apps != null) {
@@ -624,7 +634,7 @@
}
- if (!isEnteringSplit && openingToSide) {
+ if (!isEnteringSplit && apps != null) {
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
prepareEvictNonOpeningChildTasks(position, apps, evictWct);
mSyncQueue.queue(evictWct);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
index 3d1edc9..0a9c331 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
@@ -216,6 +216,7 @@
@Override
public void close() {
+ mInputEventReceiver.dispose();
mInputChannel.dispose();
try {
mWindowSession.remove(mFakeWindow);
diff --git a/libs/androidfw/ApkParsing.cpp b/libs/androidfw/ApkParsing.cpp
index cf4fbb9..32d2c5b 100644
--- a/libs/androidfw/ApkParsing.cpp
+++ b/libs/androidfw/ApkParsing.cpp
@@ -56,10 +56,8 @@
return nullptr;
}
- // Make sure there aren't subdirectories
- const char* abiOffset = fileName + APK_LIB_LEN;
- const size_t abiSize = lastSlash - abiOffset;
- if (memchr(abiOffset, '/', abiSize)) {
+ // Make sure there aren't subdirectories by checking if the next / after lib/ is the last slash
+ if (memchr(fileName + APK_LIB_LEN, '/', fileNameLen - APK_LIB_LEN) != lastSlash) {
return nullptr;
}
diff --git a/libs/androidfw/tests/ApkParsing_test.cpp b/libs/androidfw/tests/ApkParsing_test.cpp
index 4aab0a1..62e88c6 100644
--- a/libs/androidfw/tests/ApkParsing_test.cpp
+++ b/libs/androidfw/tests/ApkParsing_test.cpp
@@ -68,4 +68,10 @@
auto lastSlash = util::ValidLibraryPathLastSlash(path, false, false);
ASSERT_THAT(lastSlash, IsNull());
}
+
+TEST(ApkParsingTest, InvalidFileAtRoot) {
+ const char* path = "lib/library.so";
+ auto lastSlash = util::ValidLibraryPathLastSlash(path, false, false);
+ ASSERT_THAT(lastSlash, IsNull());
+}
}
\ No newline at end of file
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index d63b3e7..bcbe706 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -73,7 +73,6 @@
target: {
android: {
include_dirs: [
- "external/skia/src/effects",
"external/skia/src/image",
"external/skia/src/utils",
"external/skia/src/gpu",
@@ -386,9 +385,6 @@
"external/skia/include/private",
"external/skia/src/codec",
"external/skia/src/core",
- "external/skia/src/effects",
- "external/skia/src/image",
- "external/skia/src/images",
],
shared_libs: [
diff --git a/libs/hwui/jni/YuvToJpegEncoder.cpp b/libs/hwui/jni/YuvToJpegEncoder.cpp
index 6c070fe..8874ef1 100644
--- a/libs/hwui/jni/YuvToJpegEncoder.cpp
+++ b/libs/hwui/jni/YuvToJpegEncoder.cpp
@@ -2,9 +2,7 @@
#define LOG_TAG "YuvToJpegEncoder"
#include "CreateJavaOutputStreamAdaptor.h"
-#include "SkJPEGWriteUtility.h"
#include "SkStream.h"
-#include "SkTypes.h"
#include "YuvToJpegEncoder.h"
#include <ui/PixelFormat.h>
#include <hardware/hardware.h>
@@ -13,6 +11,15 @@
#include <csetjmp>
+extern "C" {
+ // We need to include stdio.h before jpeg because jpeg does not include it, but uses FILE
+ // See https://github.com/libjpeg-turbo/libjpeg-turbo/issues/17
+ #include <stdio.h>
+ #include "jpeglib.h"
+ #include "jerror.h"
+ #include "jmorecfg.h"
+}
+
YuvToJpegEncoder* YuvToJpegEncoder::create(int format, int* strides) {
// Only ImageFormat.NV21 and ImageFormat.YUY2 are supported
// for now.
@@ -39,11 +46,64 @@
longjmp(err->jmp, 1);
}
+/*
+ * Destination struct for directing decompressed pixels to a SkStream.
+ */
+static constexpr size_t kMgrBufferSize = 1024;
+struct skstream_destination_mgr : jpeg_destination_mgr {
+ skstream_destination_mgr(SkWStream* stream);
+
+ SkWStream* const fStream;
+
+ uint8_t fBuffer[kMgrBufferSize];
+};
+
+static void sk_init_destination(j_compress_ptr cinfo) {
+ skstream_destination_mgr* dest = (skstream_destination_mgr*)cinfo->dest;
+
+ dest->next_output_byte = dest->fBuffer;
+ dest->free_in_buffer = kMgrBufferSize;
+}
+
+static boolean sk_empty_output_buffer(j_compress_ptr cinfo) {
+ skstream_destination_mgr* dest = (skstream_destination_mgr*)cinfo->dest;
+
+ if (!dest->fStream->write(dest->fBuffer, kMgrBufferSize)) {
+ ERREXIT(cinfo, JERR_FILE_WRITE);
+ return FALSE;
+ }
+
+ dest->next_output_byte = dest->fBuffer;
+ dest->free_in_buffer = kMgrBufferSize;
+ return TRUE;
+}
+
+static void sk_term_destination(j_compress_ptr cinfo) {
+ skstream_destination_mgr* dest = (skstream_destination_mgr*)cinfo->dest;
+
+ size_t size = kMgrBufferSize - dest->free_in_buffer;
+ if (size > 0) {
+ if (!dest->fStream->write(dest->fBuffer, size)) {
+ ERREXIT(cinfo, JERR_FILE_WRITE);
+ return;
+ }
+ }
+
+ dest->fStream->flush();
+}
+
+skstream_destination_mgr::skstream_destination_mgr(SkWStream* stream)
+ : fStream(stream) {
+ this->init_destination = sk_init_destination;
+ this->empty_output_buffer = sk_empty_output_buffer;
+ this->term_destination = sk_term_destination;
+}
+
bool YuvToJpegEncoder::encode(SkWStream* stream, void* inYuv, int width,
int height, int* offsets, int jpegQuality) {
- jpeg_compress_struct cinfo;
- ErrorMgr err;
- skjpeg_destination_mgr sk_wstream(stream);
+ jpeg_compress_struct cinfo;
+ ErrorMgr err;
+ skstream_destination_mgr sk_wstream(stream);
cinfo.err = jpeg_std_error(&err.pub);
err.pub.error_exit = error_exit;
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index f739365..7eb0c76 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -94,5 +94,5 @@
void setSessionVolumeWithManager(IMediaRouter2Manager manager, int requestId,
String sessionId, int volume);
void releaseSessionWithManager(IMediaRouter2Manager manager, int requestId, String sessionId);
- void showMediaOutputSwitcher(String packageName);
+ boolean showMediaOutputSwitcher(String packageName);
}
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 24ec227..c620229 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -461,16 +461,30 @@
}
/**
- * Shows the system UI output switcher.
+ * Shows the system output switcher dialog.
+ *
+ * <p>Should only be called when the context of MediaRouter2 is in the foreground and visible on
+ * the screen.
+ *
+ * <p>The appearance and precise behaviour of the system output switcher dialog may vary across
+ * different devices, OS versions, and form factors, but the basic functionality stays the same.
+ *
+ * <p>See <a
+ * href="https://developer.android.com/guide/topics/media/media-routing#output-switcher">Output
+ * Switcher documentation</a> for more details.
+ *
+ * @return {@code true} if the output switcher dialog is being shown, or {@code false} if the
+ * call is ignored because the app is in the background.
*/
- public void showSystemOutputSwitcher() {
+ public boolean showSystemOutputSwitcher() {
synchronized (mLock) {
try {
- mMediaRouterService.showMediaOutputSwitcher(mPackageName);
+ return mMediaRouterService.showMediaOutputSwitcher(mPackageName);
} catch (RemoteException ex) {
ex.rethrowFromSystemServer();
}
}
+ return false;
}
/**
diff --git a/media/java/android/media/soundtrigger/SoundTriggerManager.java b/media/java/android/media/soundtrigger/SoundTriggerManager.java
index c473a01..0e9ef4c 100644
--- a/media/java/android/media/soundtrigger/SoundTriggerManager.java
+++ b/media/java/android/media/soundtrigger/SoundTriggerManager.java
@@ -32,6 +32,7 @@
import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
import android.hardware.soundtrigger.SoundTrigger.ModelParamRange;
+import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
import android.hardware.soundtrigger.SoundTrigger.SoundModel;
import android.media.permission.ClearCallingIdentityContext;
@@ -52,6 +53,7 @@
import com.android.internal.util.Preconditions;
import java.util.HashMap;
+import java.util.List;
import java.util.Objects;
import java.util.UUID;
@@ -92,8 +94,16 @@
originatorIdentity.packageName = ActivityThread.currentOpPackageName();
try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
- mSoundTriggerSession = soundTriggerService.attachAsOriginator(originatorIdentity,
- mBinderToken);
+ List<ModuleProperties> modulePropertiesList = soundTriggerService
+ .listModuleProperties(originatorIdentity);
+ if (!modulePropertiesList.isEmpty()) {
+ mSoundTriggerSession = soundTriggerService.attachAsOriginator(
+ originatorIdentity,
+ modulePropertiesList.get(0),
+ mBinderToken);
+ } else {
+ mSoundTriggerSession = null;
+ }
}
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
@@ -110,6 +120,9 @@
@RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
@Deprecated
public void updateModel(Model model) {
+ if (mSoundTriggerSession == null) {
+ throw new IllegalStateException("No underlying SoundTriggerModule available");
+ }
try {
mSoundTriggerSession.updateSoundModel(model.getGenericSoundModel());
} catch (RemoteException e) {
@@ -128,6 +141,9 @@
@Nullable
@Deprecated
public Model getModel(UUID soundModelId) {
+ if (mSoundTriggerSession == null) {
+ throw new IllegalStateException("No underlying SoundTriggerModule available");
+ }
try {
GenericSoundModel model =
mSoundTriggerSession.getSoundModel(new ParcelUuid(soundModelId));
@@ -149,6 +165,10 @@
@RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
@Deprecated
public void deleteModel(UUID soundModelId) {
+ if (mSoundTriggerSession == null) {
+ throw new IllegalStateException("No underlying SoundTriggerModule available");
+ }
+
try {
mSoundTriggerSession.deleteSoundModel(new ParcelUuid(soundModelId));
} catch (RemoteException e) {
@@ -176,7 +196,7 @@
@RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
public SoundTriggerDetector createSoundTriggerDetector(UUID soundModelId,
@NonNull SoundTriggerDetector.Callback callback, @Nullable Handler handler) {
- if (soundModelId == null) {
+ if (soundModelId == null || mSoundTriggerSession == null) {
return null;
}
@@ -342,7 +362,7 @@
@RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
@UnsupportedAppUsage
public int loadSoundModel(SoundModel soundModel) {
- if (soundModel == null) {
+ if (soundModel == null || mSoundTriggerSession == null) {
return STATUS_ERROR;
}
@@ -389,7 +409,9 @@
Preconditions.checkNotNull(soundModelId);
Preconditions.checkNotNull(detectionService);
Preconditions.checkNotNull(config);
-
+ if (mSoundTriggerSession == null) {
+ return STATUS_ERROR;
+ }
try {
return mSoundTriggerSession.startRecognitionForService(new ParcelUuid(soundModelId),
params, detectionService, config);
@@ -405,7 +427,7 @@
@RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int stopRecognition(UUID soundModelId) {
- if (soundModelId == null) {
+ if (soundModelId == null || mSoundTriggerSession == null) {
return STATUS_ERROR;
}
try {
@@ -422,7 +444,7 @@
@RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int unloadSoundModel(UUID soundModelId) {
- if (soundModelId == null) {
+ if (soundModelId == null || mSoundTriggerSession == null) {
return STATUS_ERROR;
}
try {
@@ -440,7 +462,7 @@
@RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
@UnsupportedAppUsage
public boolean isRecognitionActive(UUID soundModelId) {
- if (soundModelId == null) {
+ if (soundModelId == null || mSoundTriggerSession == null) {
return false;
}
try {
@@ -475,7 +497,7 @@
@RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
@UnsupportedAppUsage
public int getModelState(UUID soundModelId) {
- if (soundModelId == null) {
+ if (soundModelId == null || mSoundTriggerSession == null) {
return STATUS_ERROR;
}
try {
@@ -492,8 +514,10 @@
*/
@RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
@Nullable
- public SoundTrigger.ModuleProperties getModuleProperties() {
-
+ public ModuleProperties getModuleProperties() {
+ if (mSoundTriggerSession == null) {
+ return null;
+ }
try {
return mSoundTriggerSession.getModuleProperties();
} catch (RemoteException e) {
@@ -520,6 +544,10 @@
@RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
public int setParameter(@Nullable UUID soundModelId,
@ModelParams int modelParam, int value) {
+ if (mSoundTriggerSession == null) {
+ return SoundTrigger.STATUS_INVALID_OPERATION;
+ }
+
try {
return mSoundTriggerSession.setParameter(new ParcelUuid(soundModelId), modelParam,
value);
@@ -543,6 +571,10 @@
@RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
public int getParameter(@NonNull UUID soundModelId,
@ModelParams int modelParam) {
+ if (mSoundTriggerSession == null) {
+ throw new IllegalArgumentException("Sound model is not loaded: "
+ + soundModelId.toString());
+ }
try {
return mSoundTriggerSession.getParameter(new ParcelUuid(soundModelId), modelParam);
} catch (RemoteException e) {
@@ -563,6 +595,9 @@
@Nullable
public ModelParamRange queryParameter(@Nullable UUID soundModelId,
@ModelParams int modelParam) {
+ if (mSoundTriggerSession == null) {
+ return null;
+ }
try {
return mSoundTriggerSession.queryParameter(new ParcelUuid(soundModelId), modelParam);
} catch (RemoteException e) {
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java
index ba30e79..afc2bb1 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java
@@ -190,7 +190,9 @@
break;
}
case DO_SEND_TIME_SHIFT_MODE: {
- mSessionImpl.sendTimeShiftMode((Integer) msg.obj);
+ SomeArgs args = (SomeArgs) msg.obj;
+ mSessionImpl.sendTimeShiftMode(args.argi1);
+ args.recycle();
break;
}
case DO_SEND_AVAILABLE_SPEEDS: {
@@ -445,8 +447,7 @@
@Override
public void sendTimeShiftMode(int mode) {
- mCaller.executeOrSendMessage(
- mCaller.obtainMessageI(DO_SEND_TIME_SHIFT_MODE, mode));
+ mCaller.executeOrSendMessage(mCaller.obtainMessageI(DO_SEND_TIME_SHIFT_MODE, mode));
}
@Override
diff --git a/media/tests/MediaFrameworkTest/AndroidManifest.xml b/media/tests/MediaFrameworkTest/AndroidManifest.xml
index 41ac285..e886558 100644
--- a/media/tests/MediaFrameworkTest/AndroidManifest.xml
+++ b/media/tests/MediaFrameworkTest/AndroidManifest.xml
@@ -41,7 +41,6 @@
</activity>
<activity android:label="Camera2CtsActivity"
android:name="Camera2SurfaceViewActivity"
- android:screenOrientation="landscape"
android:configChanges="keyboardHidden|orientation|screenSize"
android:showWhenLocked="true"
android:turnScreenOn="true"
diff --git a/packages/PackageInstaller/AndroidManifest.xml b/packages/PackageInstaller/AndroidManifest.xml
index 9e249c4..45c4f88 100644
--- a/packages/PackageInstaller/AndroidManifest.xml
+++ b/packages/PackageInstaller/AndroidManifest.xml
@@ -148,7 +148,7 @@
<provider android:name=".wear.WearPackageIconProvider"
android:authorities="com.google.android.packageinstaller.wear.provider"
android:grantUriPermissions="true"
- android:exported="true" />
+ android:exported="false" />
</application>
</manifest>
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
index c702f59..11a8956 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
@@ -22,7 +22,7 @@
import com.android.settingslib.spa.framework.common.SpaEnvironment
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider
-import com.android.settingslib.spa.gallery.dialog.AlterDialogPageProvider
+import com.android.settingslib.spa.gallery.dialog.AlertDialogPageProvider
import com.android.settingslib.spa.gallery.home.HomePageProvider
import com.android.settingslib.spa.gallery.itemList.ItemListPageProvider
import com.android.settingslib.spa.gallery.itemList.ItemOperatePageProvider
@@ -80,7 +80,7 @@
ProgressBarPageProvider,
LoadingBarPageProvider,
ChartPageProvider,
- AlterDialogPageProvider,
+ AlertDialogPageProvider,
ItemListPageProvider,
ItemOperatePageProvider,
OperateListPageProvider,
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/AlterDialogPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/AlertDialogPage.kt
similarity index 89%
rename from packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/AlterDialogPage.kt
rename to packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/AlertDialogPage.kt
index 063b61c..1545a3e 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/AlterDialogPage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/AlertDialogPage.kt
@@ -28,14 +28,14 @@
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
-private const val TITLE = "AlterDialogPage"
+private const val TITLE = "AlertDialogPage"
-object AlterDialogPageProvider : SettingsPageProvider {
- override val name = "AlterDialogPage"
+object AlertDialogPageProvider : SettingsPageProvider {
+ override val name = "AlertDialogPage"
private val owner = createSettingsPage()
override fun buildEntry(arguments: Bundle?): List<SettingsEntry> = listOf(
- SettingsEntryBuilder.create("AlterDialog", owner).setUiLayoutFn {
+ SettingsEntryBuilder.create("AlertDialog", owner).setUiLayoutFn {
val alertDialogPresenter = rememberAlertDialogPresenter(
confirmButton = AlertDialogButton("Ok"),
dismissButton = AlertDialogButton("Cancel"),
@@ -43,7 +43,7 @@
text = { Text("Text") },
)
Preference(object : PreferenceModel {
- override val title = "Show AlterDialog"
+ override val title = "Show AlertDialog"
override val onClick = alertDialogPresenter::open
})
}.build(),
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePage.kt
index 0878fc0..c60ebfd 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePage.kt
@@ -28,7 +28,7 @@
import com.android.settingslib.spa.gallery.R
import com.android.settingslib.spa.gallery.SettingsPageProviderEnum
import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider
-import com.android.settingslib.spa.gallery.dialog.AlterDialogPageProvider
+import com.android.settingslib.spa.gallery.dialog.AlertDialogPageProvider
import com.android.settingslib.spa.gallery.itemList.OperateListPageProvider
import com.android.settingslib.spa.gallery.page.ArgumentPageModel
import com.android.settingslib.spa.gallery.page.ArgumentPageProvider
@@ -64,7 +64,7 @@
ProgressBarPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
LoadingBarPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
ChartPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
- AlterDialogPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
+ AlertDialogPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
)
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index be49940..e57cf3b 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -148,9 +148,8 @@
Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, PERCENTAGE_INTEGER_VALIDATOR);
VALIDATORS.put(Global.BLUETOOTH_ON, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.CLOCKWORK_HOME_READY, ANY_STRING_VALIDATOR);
- VALIDATORS.put(Global.ENABLE_TARE, BOOLEAN_VALIDATOR);
- VALIDATORS.put(Global.ENABLE_TARE_ALARM_MANAGER, BOOLEAN_VALIDATOR);
- VALIDATORS.put(Global.ENABLE_TARE_JOB_SCHEDULER, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.ENABLE_TARE,
+ new DiscreteValueValidator(new String[] {"0", "1", "2"}));
VALIDATORS.put(Global.TARE_ALARM_MANAGER_CONSTANTS, ANY_STRING_VALIDATOR);
VALIDATORS.put(Global.TARE_JOB_SCHEDULER_CONSTANTS, ANY_STRING_VALIDATOR);
VALIDATORS.put(Global.PRIVATE_DNS_MODE, ANY_STRING_VALIDATOR);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index e825e0e..c0176d5 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -266,8 +266,6 @@
Settings.Global.ENABLE_EPHEMERAL_FEATURE,
Settings.Global.ENABLE_RESTRICTED_BUCKET,
Settings.Global.ENABLE_TARE,
- Settings.Global.ENABLE_TARE_ALARM_MANAGER,
- Settings.Global.ENABLE_TARE_JOB_SCHEDULER,
Settings.Global.DYNAMIC_POWER_SAVINGS_ENABLED,
Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD,
Settings.Global.SMART_REPLIES_IN_NOTIFICATIONS_FLAGS,
diff --git a/packages/SystemUI/docs/device-entry/quickaffordance.md b/packages/SystemUI/docs/device-entry/quickaffordance.md
index 79d5718..d662649 100644
--- a/packages/SystemUI/docs/device-entry/quickaffordance.md
+++ b/packages/SystemUI/docs/device-entry/quickaffordance.md
@@ -52,6 +52,11 @@
* Unselect an already-selected quick affordance from a slot
* Unselect all already-selected quick affordances from a slot
+## Device Policy
+Returning `DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL` or
+`DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL` from
+`DevicePolicyManager#getKeyguardDisabledFeatures` will disable the keyguard quick affordance feature on the device.
+
## Testing
* Add a unit test for your implementation of `KeyguardQuickAffordanceConfig`
* Manually verify that your implementation works in multi-user environments from both the main user and a secondary user
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index addae72..13e2bca 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -683,7 +683,10 @@
public void onTrustManagedChanged(boolean managed, int userId) {
Assert.isMainThread();
mUserTrustIsManaged.put(userId, managed);
- mUserTrustIsUsuallyManaged.put(userId, mTrustManager.isTrustUsuallyManaged(userId));
+ boolean trustUsuallyManaged = mTrustManager.isTrustUsuallyManaged(userId);
+ mLogger.logTrustUsuallyManagedUpdated(userId, mUserTrustIsUsuallyManaged.get(userId),
+ trustUsuallyManaged, "onTrustManagedChanged");
+ mUserTrustIsUsuallyManaged.put(userId, trustUsuallyManaged);
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -2350,8 +2353,12 @@
updateSecondaryLockscreenRequirement(user);
List<UserInfo> allUsers = mUserManager.getUsers();
for (UserInfo userInfo : allUsers) {
+ boolean trustUsuallyManaged = mTrustManager.isTrustUsuallyManaged(userInfo.id);
+ mLogger.logTrustUsuallyManagedUpdated(userInfo.id,
+ mUserTrustIsUsuallyManaged.get(userInfo.id),
+ trustUsuallyManaged, "init from constructor");
mUserTrustIsUsuallyManaged.put(userInfo.id,
- mTrustManager.isTrustUsuallyManaged(userInfo.id));
+ trustUsuallyManaged);
}
updateAirplaneModeState();
@@ -2391,9 +2398,11 @@
}
private void updateFaceEnrolled(int userId) {
- mIsFaceEnrolled = mFaceManager != null && !mFaceSensorProperties.isEmpty()
+ Boolean isFaceEnrolled = mFaceManager != null && !mFaceSensorProperties.isEmpty()
&& mBiometricEnabledForUser.get(userId)
&& mAuthController.isFaceAuthEnrolled(userId);
+ mIsFaceEnrolled = isFaceEnrolled;
+ mLogger.logFaceEnrolledUpdated(mIsFaceEnrolled, isFaceEnrolled);
}
public boolean isFaceSupported() {
@@ -3052,9 +3061,13 @@
@VisibleForTesting
boolean isUnlockWithFingerprintPossible(int userId) {
// TODO (b/242022358), make this rely on onEnrollmentChanged event and update it only once.
- mIsUnlockWithFingerprintPossible.put(userId, mFpm != null
+ boolean fpEnrolled = mFpm != null
&& !mFingerprintSensorProperties.isEmpty()
- && !isFingerprintDisabled(userId) && mFpm.hasEnrolledTemplates(userId));
+ && !isFingerprintDisabled(userId) && mFpm.hasEnrolledTemplates(userId);
+ mLogger.logFpEnrolledUpdated(userId,
+ mIsUnlockWithFingerprintPossible.getOrDefault(userId, false),
+ fpEnrolled);
+ mIsUnlockWithFingerprintPossible.put(userId, fpEnrolled);
return mIsUnlockWithFingerprintPossible.get(userId);
}
@@ -3170,7 +3183,10 @@
void handleUserSwitching(int userId, IRemoteCallback reply) {
Assert.isMainThread();
clearBiometricRecognized();
- mUserTrustIsUsuallyManaged.put(userId, mTrustManager.isTrustUsuallyManaged(userId));
+ boolean trustUsuallyManaged = mTrustManager.isTrustUsuallyManaged(userId);
+ mLogger.logTrustUsuallyManagedUpdated(userId, mUserTrustIsUsuallyManaged.get(userId),
+ trustUsuallyManaged, "userSwitching");
+ mUserTrustIsUsuallyManaged.put(userId, trustUsuallyManaged);
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index c414c08..7707e1f 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -26,6 +26,7 @@
import com.android.keyguard.KeyguardListenModel
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.keyguard.TrustGrantFlags
+import com.android.systemui.log.dagger.KeyguardUpdateMonitorLog
import com.android.systemui.plugins.log.LogBuffer
import com.android.systemui.plugins.log.LogLevel
import com.android.systemui.plugins.log.LogLevel.DEBUG
@@ -33,18 +34,15 @@
import com.android.systemui.plugins.log.LogLevel.INFO
import com.android.systemui.plugins.log.LogLevel.VERBOSE
import com.android.systemui.plugins.log.LogLevel.WARNING
-import com.android.systemui.log.dagger.KeyguardUpdateMonitorLog
import com.google.errorprone.annotations.CompileTimeConstant
import javax.inject.Inject
private const val TAG = "KeyguardUpdateMonitorLog"
-/**
- * Helper class for logging for [com.android.keyguard.KeyguardUpdateMonitor]
- */
-class KeyguardUpdateMonitorLogger @Inject constructor(
- @KeyguardUpdateMonitorLog private val logBuffer: LogBuffer
-) {
+/** Helper class for logging for [com.android.keyguard.KeyguardUpdateMonitor] */
+class KeyguardUpdateMonitorLogger
+@Inject
+constructor(@KeyguardUpdateMonitorLog private val logBuffer: LogBuffer) {
fun d(@CompileTimeConstant msg: String) = log(msg, DEBUG)
fun e(@CompileTimeConstant msg: String) = log(msg, ERROR)
@@ -56,15 +54,16 @@
fun log(@CompileTimeConstant msg: String, level: LogLevel) = logBuffer.log(TAG, level, msg)
fun logActiveUnlockTriggered(reason: String?) {
- logBuffer.log("ActiveUnlock", DEBUG,
- { str1 = reason },
- { "initiate active unlock triggerReason=$str1" })
+ logBuffer.log(
+ "ActiveUnlock",
+ DEBUG,
+ { str1 = reason },
+ { "initiate active unlock triggerReason=$str1" }
+ )
}
fun logAuthInterruptDetected(active: Boolean) {
- logBuffer.log(TAG, DEBUG,
- { bool1 = active },
- { "onAuthInterruptDetected($bool1)" })
+ logBuffer.log(TAG, DEBUG, { bool1 = active }, { "onAuthInterruptDetected($bool1)" })
}
fun logBroadcastReceived(action: String?) {
@@ -72,9 +71,12 @@
}
fun logDeviceProvisionedState(deviceProvisioned: Boolean) {
- logBuffer.log(TAG, DEBUG,
- { bool1 = deviceProvisioned },
- { "DEVICE_PROVISIONED state = $bool1" })
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ { bool1 = deviceProvisioned },
+ { "DEVICE_PROVISIONED state = $bool1" }
+ )
}
fun logException(ex: Exception, @CompileTimeConstant logMsg: String) {
@@ -82,46 +84,56 @@
}
fun logFaceAcquired(acquireInfo: Int) {
- logBuffer.log(TAG, DEBUG,
- { int1 = acquireInfo },
- { "Face acquired acquireInfo=$int1" })
+ logBuffer.log(TAG, DEBUG, { int1 = acquireInfo }, { "Face acquired acquireInfo=$int1" })
}
fun logFaceAuthDisabledForUser(userId: Int) {
- logBuffer.log(TAG, DEBUG,
- { int1 = userId },
- { "Face authentication disabled by DPM for userId: $int1" })
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ { int1 = userId },
+ { "Face authentication disabled by DPM for userId: $int1" }
+ )
}
fun logFaceAuthError(msgId: Int, originalErrMsg: String) {
- logBuffer.log(TAG, DEBUG, {
- str1 = originalErrMsg
- int1 = msgId
- }, { "Face error received: $str1 msgId= $int1" })
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ str1 = originalErrMsg
+ int1 = msgId
+ },
+ { "Face error received: $str1 msgId= $int1" }
+ )
}
fun logFaceAuthForWrongUser(authUserId: Int) {
- logBuffer.log(TAG, DEBUG,
- { int1 = authUserId },
- { "Face authenticated for wrong user: $int1" })
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ { int1 = authUserId },
+ { "Face authenticated for wrong user: $int1" }
+ )
}
fun logFaceAuthHelpMsg(msgId: Int, helpMsg: String?) {
- logBuffer.log(TAG, DEBUG, {
- int1 = msgId
- str1 = helpMsg
- }, { "Face help received, msgId: $int1 msg: $str1" })
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ int1 = msgId
+ str1 = helpMsg
+ },
+ { "Face help received, msgId: $int1 msg: $str1" }
+ )
}
fun logFaceAuthRequested(reason: String?) {
- logBuffer.log(TAG, DEBUG, {
- str1 = reason
- }, { "requestFaceAuth() reason=$str1" })
+ logBuffer.log(TAG, DEBUG, { str1 = reason }, { "requestFaceAuth() reason=$str1" })
}
fun logFaceAuthSuccess(userId: Int) {
- logBuffer.log(TAG, DEBUG,
- { int1 = userId },
- { "Face auth succeeded for user $int1" })
+ logBuffer.log(TAG, DEBUG, { int1 = userId }, { "Face auth succeeded for user $int1" })
}
fun logFaceLockoutReset(@LockoutMode mode: Int) {
@@ -133,21 +145,30 @@
}
fun logFaceUnlockPossible(isFaceUnlockPossible: Boolean) {
- logBuffer.log(TAG, DEBUG,
- { bool1 = isFaceUnlockPossible },
- {"isUnlockWithFacePossible: $bool1"})
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ { bool1 = isFaceUnlockPossible },
+ { "isUnlockWithFacePossible: $bool1" }
+ )
}
fun logFingerprintAuthForWrongUser(authUserId: Int) {
- logBuffer.log(TAG, DEBUG,
- { int1 = authUserId },
- { "Fingerprint authenticated for wrong user: $int1" })
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ { int1 = authUserId },
+ { "Fingerprint authenticated for wrong user: $int1" }
+ )
}
fun logFingerprintDisabledForUser(userId: Int) {
- logBuffer.log(TAG, DEBUG,
- { int1 = userId },
- { "Fingerprint disabled by DPM for userId: $int1" })
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ { int1 = userId },
+ { "Fingerprint disabled by DPM for userId: $int1" }
+ )
}
fun logFingerprintLockoutReset(@LockoutMode mode: Int) {
@@ -155,42 +176,63 @@
}
fun logFingerprintRunningState(fingerprintRunningState: Int) {
- logBuffer.log(TAG, DEBUG,
- { int1 = fingerprintRunningState },
- { "fingerprintRunningState: $int1" })
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ { int1 = fingerprintRunningState },
+ { "fingerprintRunningState: $int1" }
+ )
}
fun logFingerprintSuccess(userId: Int, isStrongBiometric: Boolean) {
- logBuffer.log(TAG, DEBUG, {
- int1 = userId
- bool1 = isStrongBiometric
- }, {"Fingerprint auth successful: userId: $int1, isStrongBiometric: $bool1"})
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ int1 = userId
+ bool1 = isStrongBiometric
+ },
+ { "Fingerprint auth successful: userId: $int1, isStrongBiometric: $bool1" }
+ )
}
fun logFingerprintError(msgId: Int, originalErrMsg: String) {
- logBuffer.log(TAG, DEBUG, {
- str1 = originalErrMsg
- int1 = msgId
- }, { "Fingerprint error received: $str1 msgId= $int1" })
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ str1 = originalErrMsg
+ int1 = msgId
+ },
+ { "Fingerprint error received: $str1 msgId= $int1" }
+ )
}
fun logInvalidSubId(subId: Int) {
- logBuffer.log(TAG, INFO,
- { int1 = subId },
- { "Previously active sub id $int1 is now invalid, will remove" })
+ logBuffer.log(
+ TAG,
+ INFO,
+ { int1 = subId },
+ { "Previously active sub id $int1 is now invalid, will remove" }
+ )
}
fun logPrimaryKeyguardBouncerChanged(
- primaryBouncerIsOrWillBeShowing: Boolean,
- primaryBouncerFullyShown: Boolean
+ primaryBouncerIsOrWillBeShowing: Boolean,
+ primaryBouncerFullyShown: Boolean
) {
- logBuffer.log(TAG, DEBUG, {
- bool1 = primaryBouncerIsOrWillBeShowing
- bool2 = primaryBouncerFullyShown
- }, {
- "handlePrimaryBouncerChanged " +
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ bool1 = primaryBouncerIsOrWillBeShowing
+ bool2 = primaryBouncerFullyShown
+ },
+ {
+ "handlePrimaryBouncerChanged " +
"primaryBouncerIsOrWillBeShowing=$bool1 primaryBouncerFullyShown=$bool2"
- })
+ }
+ )
}
fun logKeyguardListenerModel(model: KeyguardListenModel) {
@@ -198,98 +240,134 @@
}
fun logKeyguardShowingChanged(showing: Boolean, occluded: Boolean, visible: Boolean) {
- logBuffer.log(TAG, DEBUG, {
- bool1 = showing
- bool2 = occluded
- bool3 = visible
- }, {
- "keyguardShowingChanged(showing=$bool1 occluded=$bool2 visible=$bool3)"
- })
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ bool1 = showing
+ bool2 = occluded
+ bool3 = visible
+ },
+ { "keyguardShowingChanged(showing=$bool1 occluded=$bool2 visible=$bool3)" }
+ )
}
fun logMissingSupervisorAppError(userId: Int) {
- logBuffer.log(TAG, ERROR,
- { int1 = userId },
- { "No Profile Owner or Device Owner supervision app found for User $int1" })
+ logBuffer.log(
+ TAG,
+ ERROR,
+ { int1 = userId },
+ { "No Profile Owner or Device Owner supervision app found for User $int1" }
+ )
}
fun logPhoneStateChanged(newState: String?) {
- logBuffer.log(TAG, DEBUG,
- { str1 = newState },
- { "handlePhoneStateChanged($str1)" })
+ logBuffer.log(TAG, DEBUG, { str1 = newState }, { "handlePhoneStateChanged($str1)" })
}
fun logRegisterCallback(callback: KeyguardUpdateMonitorCallback?) {
- logBuffer.log(TAG, VERBOSE,
- { str1 = "$callback" },
- { "*** register callback for $str1" })
+ logBuffer.log(TAG, VERBOSE, { str1 = "$callback" }, { "*** register callback for $str1" })
}
fun logRetryingAfterFaceHwUnavailable(retryCount: Int) {
- logBuffer.log(TAG, WARNING,
- { int1 = retryCount },
- { "Retrying face after HW unavailable, attempt $int1" })
+ logBuffer.log(
+ TAG,
+ WARNING,
+ { int1 = retryCount },
+ { "Retrying face after HW unavailable, attempt $int1" }
+ )
}
fun logRetryAfterFpErrorWithDelay(msgId: Int, errString: String?, delay: Int) {
- logBuffer.log(TAG, DEBUG, {
- int1 = msgId
- int2 = delay
- str1 = "$errString"
- }, {
- "Fingerprint scheduling retry auth after $int2 ms due to($int1) -> $str1"
- })
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ int1 = msgId
+ int2 = delay
+ str1 = "$errString"
+ },
+ { "Fingerprint scheduling retry auth after $int2 ms due to($int1) -> $str1" }
+ )
}
fun logRetryAfterFpHwUnavailable(retryCount: Int) {
- logBuffer.log(TAG, WARNING,
- { int1 = retryCount },
- { "Retrying fingerprint attempt: $int1" })
+ logBuffer.log(
+ TAG,
+ WARNING,
+ { int1 = retryCount },
+ { "Retrying fingerprint attempt: $int1" }
+ )
}
fun logSendPrimaryBouncerChanged(
primaryBouncerIsOrWillBeShowing: Boolean,
primaryBouncerFullyShown: Boolean,
) {
- logBuffer.log(TAG, DEBUG, {
- bool1 = primaryBouncerIsOrWillBeShowing
- bool2 = primaryBouncerFullyShown
- }, {
- "sendPrimaryBouncerChanged primaryBouncerIsOrWillBeShowing=$bool1 " +
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ bool1 = primaryBouncerIsOrWillBeShowing
+ bool2 = primaryBouncerFullyShown
+ },
+ {
+ "sendPrimaryBouncerChanged primaryBouncerIsOrWillBeShowing=$bool1 " +
"primaryBouncerFullyShown=$bool2"
- })
+ }
+ )
}
fun logServiceStateChange(subId: Int, serviceState: ServiceState?) {
- logBuffer.log(TAG, DEBUG, {
- int1 = subId
- str1 = "$serviceState"
- }, { "handleServiceStateChange(subId=$int1, serviceState=$str1)" })
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ int1 = subId
+ str1 = "$serviceState"
+ },
+ { "handleServiceStateChange(subId=$int1, serviceState=$str1)" }
+ )
}
fun logServiceStateIntent(action: String?, serviceState: ServiceState?, subId: Int) {
- logBuffer.log(TAG, VERBOSE, {
- str1 = action
- str2 = "$serviceState"
- int1 = subId
- }, { "action $str1 serviceState=$str2 subId=$int1" })
+ logBuffer.log(
+ TAG,
+ VERBOSE,
+ {
+ str1 = action
+ str2 = "$serviceState"
+ int1 = subId
+ },
+ { "action $str1 serviceState=$str2 subId=$int1" }
+ )
}
fun logSimState(subId: Int, slotId: Int, state: Int) {
- logBuffer.log(TAG, DEBUG, {
- int1 = subId
- int2 = slotId
- long1 = state.toLong()
- }, { "handleSimStateChange(subId=$int1, slotId=$int2, state=$long1)" })
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ int1 = subId
+ int2 = slotId
+ long1 = state.toLong()
+ },
+ { "handleSimStateChange(subId=$int1, slotId=$int2, state=$long1)" }
+ )
}
fun logSimStateFromIntent(action: String?, extraSimState: String?, slotId: Int, subId: Int) {
- logBuffer.log(TAG, VERBOSE, {
- str1 = action
- str2 = extraSimState
- int1 = slotId
- int2 = subId
- }, { "action $str1 state: $str2 slotId: $int1 subid: $int2" })
+ logBuffer.log(
+ TAG,
+ VERBOSE,
+ {
+ str1 = action
+ str2 = extraSimState
+ int1 = slotId
+ int2 = subId
+ },
+ { "action $str1 state: $str2 slotId: $int1 subid: $int2" }
+ )
}
fun logSimUnlocked(subId: Int) {
@@ -297,78 +375,98 @@
}
fun logStartedListeningForFace(faceRunningState: Int, faceAuthUiEvent: FaceAuthUiEvent) {
- logBuffer.log(TAG, VERBOSE, {
- int1 = faceRunningState
- str1 = faceAuthUiEvent.reason
- str2 = faceAuthUiEvent.extraInfoToString()
- }, { "startListeningForFace(): $int1, reason: $str1 $str2" })
+ logBuffer.log(
+ TAG,
+ VERBOSE,
+ {
+ int1 = faceRunningState
+ str1 = faceAuthUiEvent.reason
+ str2 = faceAuthUiEvent.extraInfoToString()
+ },
+ { "startListeningForFace(): $int1, reason: $str1 $str2" }
+ )
}
fun logStartedListeningForFaceFromWakeUp(faceRunningState: Int, @WakeReason pmWakeReason: Int) {
- logBuffer.log(TAG, VERBOSE, {
- int1 = faceRunningState
- str1 = PowerManager.wakeReasonToString(pmWakeReason)
- }, { "startListeningForFace(): $int1, reason: wakeUp-$str1" })
+ logBuffer.log(
+ TAG,
+ VERBOSE,
+ {
+ int1 = faceRunningState
+ str1 = PowerManager.wakeReasonToString(pmWakeReason)
+ },
+ { "startListeningForFace(): $int1, reason: wakeUp-$str1" }
+ )
}
fun logStoppedListeningForFace(faceRunningState: Int, faceAuthReason: String) {
- logBuffer.log(TAG, VERBOSE, {
- int1 = faceRunningState
- str1 = faceAuthReason
- }, { "stopListeningForFace(): currentFaceRunningState: $int1, reason: $str1" })
+ logBuffer.log(
+ TAG,
+ VERBOSE,
+ {
+ int1 = faceRunningState
+ str1 = faceAuthReason
+ },
+ { "stopListeningForFace(): currentFaceRunningState: $int1, reason: $str1" }
+ )
}
fun logSubInfo(subInfo: SubscriptionInfo?) {
- logBuffer.log(TAG, VERBOSE,
- { str1 = "$subInfo" },
- { "SubInfo:$str1" })
+ logBuffer.log(TAG, VERBOSE, { str1 = "$subInfo" }, { "SubInfo:$str1" })
}
fun logTimeFormatChanged(newTimeFormat: String?) {
- logBuffer.log(TAG, DEBUG,
- { str1 = newTimeFormat },
- { "handleTimeFormatUpdate timeFormat=$str1" })
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ { str1 = newTimeFormat },
+ { "handleTimeFormatUpdate timeFormat=$str1" }
+ )
}
fun logUdfpsPointerDown(sensorId: Int) {
- logBuffer.log(TAG, DEBUG,
- { int1 = sensorId },
- { "onUdfpsPointerDown, sensorId: $int1" })
+ logBuffer.log(TAG, DEBUG, { int1 = sensorId }, { "onUdfpsPointerDown, sensorId: $int1" })
}
fun logUdfpsPointerUp(sensorId: Int) {
- logBuffer.log(TAG, DEBUG,
- { int1 = sensorId },
- { "onUdfpsPointerUp, sensorId: $int1" })
+ logBuffer.log(TAG, DEBUG, { int1 = sensorId }, { "onUdfpsPointerUp, sensorId: $int1" })
}
fun logUnexpectedFaceCancellationSignalState(faceRunningState: Int, unlockPossible: Boolean) {
- logBuffer.log(TAG, ERROR, {
- int1 = faceRunningState
- bool1 = unlockPossible
- }, {
- "Cancellation signal is not null, high chance of bug in " +
- "face auth lifecycle management. " +
- "Face state: $int1, unlockPossible: $bool1"
- })
+ logBuffer.log(
+ TAG,
+ ERROR,
+ {
+ int1 = faceRunningState
+ bool1 = unlockPossible
+ },
+ {
+ "Cancellation signal is not null, high chance of bug in " +
+ "face auth lifecycle management. " +
+ "Face state: $int1, unlockPossible: $bool1"
+ }
+ )
}
fun logUnexpectedFpCancellationSignalState(
fingerprintRunningState: Int,
unlockPossible: Boolean
) {
- logBuffer.log(TAG, ERROR, {
- int1 = fingerprintRunningState
- bool1 = unlockPossible
- }, {
- "Cancellation signal is not null, high chance of bug in " +
- "fp auth lifecycle management. FP state: $int1, unlockPossible: $bool1"
- })
+ logBuffer.log(
+ TAG,
+ ERROR,
+ {
+ int1 = fingerprintRunningState
+ bool1 = unlockPossible
+ },
+ {
+ "Cancellation signal is not null, high chance of bug in " +
+ "fp auth lifecycle management. FP state: $int1, unlockPossible: $bool1"
+ }
+ )
}
fun logUnregisterCallback(callback: KeyguardUpdateMonitorCallback?) {
- logBuffer.log(TAG, VERBOSE,
- { str1 = "$callback" },
- { "*** unregister callback for $str1" })
+ logBuffer.log(TAG, VERBOSE, { str1 = "$callback" }, { "*** unregister callback for $str1" })
}
fun logUserRequestedUnlock(
@@ -376,75 +474,149 @@
reason: String?,
dismissKeyguard: Boolean
) {
- logBuffer.log("ActiveUnlock", DEBUG, {
- str1 = requestOrigin?.name
- str2 = reason
- bool1 = dismissKeyguard
- }, { "reportUserRequestedUnlock origin=$str1 reason=$str2 dismissKeyguard=$bool1" })
+ logBuffer.log(
+ "ActiveUnlock",
+ DEBUG,
+ {
+ str1 = requestOrigin?.name
+ str2 = reason
+ bool1 = dismissKeyguard
+ },
+ { "reportUserRequestedUnlock origin=$str1 reason=$str2 dismissKeyguard=$bool1" }
+ )
}
fun logTrustGrantedWithFlags(
- flags: Int,
- newlyUnlocked: Boolean,
- userId: Int,
- message: String?
+ flags: Int,
+ newlyUnlocked: Boolean,
+ userId: Int,
+ message: String?
) {
- logBuffer.log(TAG, DEBUG, {
- int1 = flags
- bool1 = newlyUnlocked
- int2 = userId
- str1 = message
- }, { "trustGrantedWithFlags[user=$int2] newlyUnlocked=$bool1 " +
- "flags=${TrustGrantFlags(int1)} message=$str1" })
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ int1 = flags
+ bool1 = newlyUnlocked
+ int2 = userId
+ str1 = message
+ },
+ {
+ "trustGrantedWithFlags[user=$int2] newlyUnlocked=$bool1 " +
+ "flags=${TrustGrantFlags(int1)} message=$str1"
+ }
+ )
}
- fun logTrustChanged(
- wasTrusted: Boolean,
- isNowTrusted: Boolean,
- userId: Int
- ) {
- logBuffer.log(TAG, DEBUG, {
- bool1 = wasTrusted
- bool2 = isNowTrusted
- int1 = userId
- }, { "onTrustChanged[user=$int1] wasTrusted=$bool1 isNowTrusted=$bool2" })
+ fun logTrustChanged(wasTrusted: Boolean, isNowTrusted: Boolean, userId: Int) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ bool1 = wasTrusted
+ bool2 = isNowTrusted
+ int1 = userId
+ },
+ { "onTrustChanged[user=$int1] wasTrusted=$bool1 isNowTrusted=$bool2" }
+ )
}
fun logKeyguardStateUpdate(
- secure: Boolean,
- canDismissLockScreen: Boolean,
- trusted: Boolean,
- trustManaged: Boolean
-
+ secure: Boolean,
+ canDismissLockScreen: Boolean,
+ trusted: Boolean,
+ trustManaged: Boolean
) {
- logBuffer.log("KeyguardState", DEBUG, {
- bool1 = secure
- bool2 = canDismissLockScreen
- bool3 = trusted
- bool4 = trustManaged
- }, { "#update secure=$bool1 canDismissKeyguard=$bool2" +
- " trusted=$bool3 trustManaged=$bool4" })
+ logBuffer.log(
+ "KeyguardState",
+ DEBUG,
+ {
+ bool1 = secure
+ bool2 = canDismissLockScreen
+ bool3 = trusted
+ bool4 = trustManaged
+ },
+ {
+ "#update secure=$bool1 canDismissKeyguard=$bool2" +
+ " trusted=$bool3 trustManaged=$bool4"
+ }
+ )
}
fun logSkipUpdateFaceListeningOnWakeup(@WakeReason pmWakeReason: Int) {
- logBuffer.log(TAG, VERBOSE, {
- str1 = PowerManager.wakeReasonToString(pmWakeReason)
- }, { "Skip updating face listening state on wakeup from $str1"})
+ logBuffer.log(
+ TAG,
+ VERBOSE,
+ { str1 = PowerManager.wakeReasonToString(pmWakeReason) },
+ { "Skip updating face listening state on wakeup from $str1" }
+ )
}
fun logTaskStackChangedForAssistant(assistantVisible: Boolean) {
- logBuffer.log(TAG, VERBOSE, {
- bool1 = assistantVisible
- }, {
- "TaskStackChanged for ACTIVITY_TYPE_ASSISTANT, assistant visible: $bool1"
- })
+ logBuffer.log(
+ TAG,
+ VERBOSE,
+ { bool1 = assistantVisible },
+ { "TaskStackChanged for ACTIVITY_TYPE_ASSISTANT, assistant visible: $bool1" }
+ )
}
fun logAssistantVisible(assistantVisible: Boolean) {
- logBuffer.log(TAG, VERBOSE, {
- bool1 = assistantVisible
- }, {
- "Updating mAssistantVisible to new value: $bool1"
- })
+ logBuffer.log(
+ TAG,
+ VERBOSE,
+ { bool1 = assistantVisible },
+ { "Updating mAssistantVisible to new value: $bool1" }
+ )
+ }
+
+ fun logFaceEnrolledUpdated(oldValue: Boolean, newValue: Boolean) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ bool1 = oldValue
+ bool2 = newValue
+ },
+ { "Face enrolled state changed: old: $bool1, new: $bool2" }
+ )
+ }
+
+ fun logFpEnrolledUpdated(userId: Int, oldValue: Boolean, newValue: Boolean) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ int1 = userId
+ bool1 = oldValue
+ bool2 = newValue
+ },
+ { "Fp enrolled state changed for userId: $int1 old: $bool1, new: $bool2" }
+ )
+ }
+
+ fun logTrustUsuallyManagedUpdated(
+ userId: Int,
+ oldValue: Boolean,
+ newValue: Boolean,
+ context: String
+ ) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ int1 = userId
+ bool1 = oldValue
+ bool2 = newValue
+ str1 = context
+ },
+ {
+ "trustUsuallyManaged changed for " +
+ "userId: $int1 " +
+ "old: $bool1, " +
+ "new: $bool2 " +
+ "context: $str1"
+ }
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
index d8d8c0e..3a3f9b4 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
@@ -134,7 +134,8 @@
super.onStop()
mExitToDream = false
- uiController.hide()
+ // parent is set in onStart, so the field is initialized when we get here
+ uiController.hide(parent)
controlsSettingsDialogManager.closeDialog()
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
index c1cec9d..58673bb 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
@@ -31,7 +31,13 @@
}
fun show(parent: ViewGroup, onDismiss: Runnable, activityContext: Context)
- fun hide()
+
+ /**
+ * Hide the controls content if it's attached to this parent.
+ */
+ fun hide(parent: ViewGroup)
+
+ val isShowing: Boolean
/**
* Returns the preferred activity to start, depending on if the user has favorited any
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 58f4835..9405c60 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -168,6 +168,9 @@
private lateinit var activityContext: Context
private lateinit var listingCallback: ControlsListingController.ControlsListingCallback
+ override val isShowing: Boolean
+ get() = !hidden
+
init {
dumpManager.registerDumpable(javaClass.name, this)
}
@@ -727,21 +730,27 @@
controlActionCoordinator.closeDialogs()
}
- override fun hide() {
- hidden = true
+ override fun hide(parent: ViewGroup) {
+ // We need to check for the parent because it's possible that we have started showing in a
+ // different activity. In that case, make sure to only clear things associated with the
+ // passed parent
+ if (parent == this.parent) {
+ Log.d(ControlsUiController.TAG, "hide()")
+ hidden = true
- closeDialogs(true)
- controlsController.get().unsubscribe()
- taskViewController?.dismiss()
- taskViewController = null
+ closeDialogs(true)
+ controlsController.get().unsubscribe()
+ taskViewController?.dismiss()
+ taskViewController = null
+ controlsById.clear()
+ controlViewsById.clear()
+
+ controlsListingController.get().removeCallback(listingCallback)
+
+ if (!retainCache) RenderInfo.clearCache()
+ }
parent.removeAllViews()
- controlsById.clear()
- controlViewsById.clear()
-
- controlsListingController.get().removeCallback(listingCallback)
-
- if (!retainCache) RenderInfo.clearCache()
}
override fun onRefreshState(componentName: ComponentName, controls: List<Control>) {
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index da59996..fa4caaf 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -59,7 +59,7 @@
)
// TODO(b/254512517): Tracking Bug
- val FSI_REQUIRES_KEYGUARD = unreleasedFlag(110, "fsi_requires_keyguard", teamfood = true)
+ val FSI_REQUIRES_KEYGUARD = releasedFlag(110, "fsi_requires_keyguard")
// TODO(b/259130119): Tracking Bug
val FSI_ON_DND_UPDATE = unreleasedFlag(259130119, "fsi_on_dnd_update", teamfood = true)
@@ -187,7 +187,7 @@
// TODO(b/262780002): Tracking Bug
@JvmField
- val REVAMPED_WALLPAPER_UI = unreleasedFlag(222, "revamped_wallpaper_ui", teamfood = false)
+ val REVAMPED_WALLPAPER_UI = unreleasedFlag(222, "revamped_wallpaper_ui", teamfood = true)
/** A different path for unocclusion transitions back to keyguard */
// TODO(b/262859270): Tracking Bug
@@ -217,6 +217,7 @@
unreleasedFlag(
228,
"lock_screen_long_press_enabled",
+ teamfood = true,
)
// 300 - power menu
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
index 680c504..27a5974 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
@@ -131,7 +131,7 @@
throw UnsupportedOperationException()
}
- return insertSelection(values)
+ return runBlocking(mainDispatcher) { insertSelection(values) }
}
override fun query(
@@ -171,7 +171,7 @@
throw UnsupportedOperationException()
}
- return deleteSelection(uri, selectionArgs)
+ return runBlocking(mainDispatcher) { deleteSelection(uri, selectionArgs) }
}
override fun call(method: String, arg: String?, extras: Bundle?): Bundle? {
@@ -189,7 +189,7 @@
}
}
- private fun insertSelection(values: ContentValues?): Uri? {
+ private suspend fun insertSelection(values: ContentValues?): Uri? {
if (values == null) {
throw IllegalArgumentException("Cannot insert selection, no values passed in!")
}
@@ -311,7 +311,7 @@
}
}
- private fun querySlots(): Cursor {
+ private suspend fun querySlots(): Cursor {
return MatrixCursor(
arrayOf(
Contract.LockScreenQuickAffordances.SlotTable.Columns.ID,
@@ -330,7 +330,7 @@
}
}
- private fun queryFlags(): Cursor {
+ private suspend fun queryFlags(): Cursor {
return MatrixCursor(
arrayOf(
Contract.FlagsTable.Columns.NAME,
@@ -353,7 +353,7 @@
}
}
- private fun deleteSelection(
+ private suspend fun deleteSelection(
uri: Uri,
selectionArgs: Array<out String>?,
): Int {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index fb0fb1f..dfbe1c2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -18,12 +18,14 @@
package com.android.systemui.keyguard.domain.interactor
import android.app.AlertDialog
+import android.app.admin.DevicePolicyManager
import android.content.Intent
import android.util.Log
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
@@ -41,13 +43,17 @@
import com.android.systemui.statusbar.policy.KeyguardStateController
import dagger.Lazy
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.withContext
+@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class KeyguardQuickAffordanceInteractor
@Inject
@@ -61,6 +67,8 @@
private val featureFlags: FeatureFlags,
private val repository: Lazy<KeyguardQuickAffordanceRepository>,
private val launchAnimator: DialogLaunchAnimator,
+ private val devicePolicyManager: DevicePolicyManager,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
) {
private val isUsingRepository: Boolean
get() = featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES)
@@ -74,9 +82,13 @@
get() = featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES)
/** Returns an observable for the quick affordance at the given position. */
- fun quickAffordance(
+ suspend fun quickAffordance(
position: KeyguardQuickAffordancePosition
): Flow<KeyguardQuickAffordanceModel> {
+ if (isFeatureDisabledByDevicePolicy()) {
+ return flowOf(KeyguardQuickAffordanceModel.Hidden)
+ }
+
return combine(
quickAffordanceAlwaysVisible(position),
keyguardInteractor.isDozing,
@@ -148,8 +160,11 @@
*
* @return `true` if the affordance was selected successfully; `false` otherwise.
*/
- fun select(slotId: String, affordanceId: String): Boolean {
+ suspend fun select(slotId: String, affordanceId: String): Boolean {
check(isUsingRepository)
+ if (isFeatureDisabledByDevicePolicy()) {
+ return false
+ }
val slots = repository.get().getSlotPickerRepresentations()
val slot = slots.find { it.id == slotId } ?: return false
@@ -187,8 +202,11 @@
* @return `true` if the affordance was successfully removed; `false` otherwise (for example, if
* the affordance was not on the slot to begin with).
*/
- fun unselect(slotId: String, affordanceId: String?): Boolean {
+ suspend fun unselect(slotId: String, affordanceId: String?): Boolean {
check(isUsingRepository)
+ if (isFeatureDisabledByDevicePolicy()) {
+ return false
+ }
val slots = repository.get().getSlotPickerRepresentations()
if (slots.find { it.id == slotId } == null) {
@@ -227,6 +245,10 @@
/** Returns affordance IDs indexed by slot ID, for all known slots. */
suspend fun getSelections(): Map<String, List<KeyguardQuickAffordancePickerRepresentation>> {
+ if (isFeatureDisabledByDevicePolicy()) {
+ return emptyMap()
+ }
+
val slots = repository.get().getSlotPickerRepresentations()
val selections = repository.get().getCurrentSelections()
val affordanceById =
@@ -351,13 +373,17 @@
return repository.get().getAffordancePickerRepresentations()
}
- fun getSlotPickerRepresentations(): List<KeyguardSlotPickerRepresentation> {
+ suspend fun getSlotPickerRepresentations(): List<KeyguardSlotPickerRepresentation> {
check(isUsingRepository)
+ if (isFeatureDisabledByDevicePolicy()) {
+ return emptyList()
+ }
+
return repository.get().getSlotPickerRepresentations()
}
- fun getPickerFlags(): List<KeyguardPickerFlag> {
+ suspend fun getPickerFlags(): List<KeyguardPickerFlag> {
return listOf(
KeyguardPickerFlag(
name = Contract.FlagsTable.FLAG_NAME_REVAMPED_WALLPAPER_UI,
@@ -365,7 +391,9 @@
),
KeyguardPickerFlag(
name = Contract.FlagsTable.FLAG_NAME_CUSTOM_LOCK_SCREEN_QUICK_AFFORDANCES_ENABLED,
- value = featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES),
+ value =
+ !isFeatureDisabledByDevicePolicy() &&
+ featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES),
),
KeyguardPickerFlag(
name = Contract.FlagsTable.FLAG_NAME_CUSTOM_CLOCKS_ENABLED,
@@ -382,6 +410,17 @@
)
}
+ private suspend fun isFeatureDisabledByDevicePolicy(): Boolean {
+ val flags =
+ withContext(backgroundDispatcher) {
+ devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId)
+ }
+ val flagsToCheck =
+ DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL or
+ DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL
+ return flagsToCheck and flags != 0
+ }
+
companion object {
private const val TAG = "KeyguardQuickAffordanceInteractor"
private const val DELIMITER = "::"
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 7067c220..a0be151 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -25,8 +25,6 @@
import android.content.res.Resources;
import android.graphics.Rect;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.Log;
@@ -108,6 +106,11 @@
private boolean mShouldMoveMediaOnExpansion = true;
private boolean mUsingCombinedHeaders = false;
private QSLogger mQsLogger;
+ /**
+ * Specifies if we can collapse to QQS in current state. In split shade that should be always
+ * false. It influences available accessibility actions.
+ */
+ private boolean mCanCollapse = true;
public QSPanel(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -650,7 +653,9 @@
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
- info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE);
+ if (mCanCollapse) {
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE);
+ }
}
@Override
@@ -669,15 +674,11 @@
mCollapseExpandAction = action;
}
- private class H extends Handler {
- private static final int ANNOUNCE_FOR_ACCESSIBILITY = 1;
-
- @Override
- public void handleMessage(Message msg) {
- if (msg.what == ANNOUNCE_FOR_ACCESSIBILITY) {
- announceForAccessibility((CharSequence) msg.obj);
- }
- }
+ /**
+ * Specifies if these expanded QS can collapse to QQS.
+ */
+ public void setCanCollapse(boolean canCollapse) {
+ mCanCollapse = canCollapse;
}
public interface QSTileLayout {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index cabe1da..01dbb18 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -148,9 +148,10 @@
}
@Override
- protected void onSplitShadeChanged() {
+ protected void onSplitShadeChanged(boolean shouldUseSplitNotificationShade) {
((PagedTileLayout) mView.getOrCreateTileLayout())
.forceTilesRedistribution("Split shade state changed");
+ mView.setCanCollapse(!shouldUseSplitNotificationShade);
}
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index e85d0a3..bbdf6cc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -104,14 +104,14 @@
switchTileLayoutIfNeeded();
onConfigurationChanged();
if (previousSplitShadeState != mShouldUseSplitNotificationShade) {
- onSplitShadeChanged();
+ onSplitShadeChanged(mShouldUseSplitNotificationShade);
}
}
};
protected void onConfigurationChanged() { }
- protected void onSplitShadeChanged() { }
+ protected void onSplitShadeChanged(boolean shouldUseSplitNotificationShade) { }
private final Function1<Boolean, Unit> mMediaHostVisibilityListener = (visible) -> {
if (mMediaVisibilityChangedListener != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
index 80f7c36..9d8ed46 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
@@ -17,6 +17,7 @@
package com.android.systemui.shade
import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
import android.annotation.IdRes
import android.app.StatusBarManager
import android.content.res.Configuration
@@ -145,6 +146,14 @@
updateListeners()
}
+ private var customizing = false
+ set(value) {
+ if (field != value) {
+ field = value
+ updateVisibility()
+ }
+ }
+
/**
* Whether the QQS/QS part of the shade is visible. This is particularly important in
* Lockscreen, as the shade is visible but QS is not.
@@ -176,14 +185,9 @@
*/
var shadeExpandedFraction = -1f
set(value) {
- if (field != value) {
- val oldAlpha = header.alpha
+ if (qsVisible && field != value) {
header.alpha = ShadeInterpolation.getContentAlpha(value)
field = value
- if ((oldAlpha == 0f && header.alpha > 0f) ||
- (oldAlpha > 0f && header.alpha == 0f)) {
- updateVisibility()
- }
}
}
@@ -318,6 +322,7 @@
dumpManager.registerDumpable(this)
configurationController.addCallback(configurationControllerListener)
demoModeController.addCallback(demoModeReceiver)
+ statusBarIconController.addIconGroup(iconManager)
}
override fun onViewDetached() {
@@ -325,6 +330,7 @@
dumpManager.unregisterDumpable(this::class.java.simpleName)
configurationController.removeCallback(configurationControllerListener)
demoModeController.removeCallback(demoModeReceiver)
+ statusBarIconController.removeIconGroup(iconManager)
}
fun disable(state1: Int, state2: Int, animate: Boolean) {
@@ -339,31 +345,10 @@
.setDuration(duration)
.alpha(if (show) 0f else 1f)
.setInterpolator(if (show) Interpolators.ALPHA_OUT else Interpolators.ALPHA_IN)
- .setUpdateListener {
- updateVisibility()
- }
- .setListener(endAnimationListener)
+ .setListener(CustomizerAnimationListener(show))
.start()
}
- private val endAnimationListener = object : Animator.AnimatorListener {
- override fun onAnimationCancel(animation: Animator?) {
- clearListeners()
- }
-
- override fun onAnimationEnd(animation: Animator?) {
- clearListeners()
- }
-
- override fun onAnimationRepeat(animation: Animator?) {}
-
- override fun onAnimationStart(animation: Animator?) {}
-
- private fun clearListeners() {
- header.animate().setListener(null).setUpdateListener(null)
- }
- }
-
private fun loadConstraints() {
if (header is MotionLayout) {
// Use resources.getXml instead of passing the resource id due to bug b/205018300
@@ -453,7 +438,7 @@
private fun updateVisibility() {
val visibility = if (!largeScreenActive && !combinedHeaders || qsDisabled) {
View.GONE
- } else if (qsVisible && header.alpha > 0f) {
+ } else if (qsVisible && !customizing) {
View.VISIBLE
} else {
View.INVISIBLE
@@ -502,10 +487,8 @@
if (visible) {
updateSingleCarrier(qsCarrierGroupController.isSingleCarrier)
qsCarrierGroupController.setOnSingleCarrierChangedListener { updateSingleCarrier(it) }
- statusBarIconController.addIconGroup(iconManager)
} else {
qsCarrierGroupController.setOnSingleCarrierChangedListener(null)
- statusBarIconController.removeIconGroup(iconManager)
}
}
@@ -578,4 +561,23 @@
@VisibleForTesting
internal fun simulateViewDetached() = this.onViewDetached()
+
+ inner class CustomizerAnimationListener(
+ private val enteringCustomizing: Boolean,
+ ) : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator?) {
+ super.onAnimationEnd(animation)
+ header.animate().setListener(null)
+ if (enteringCustomizing) {
+ customizing = true
+ }
+ }
+
+ override fun onAnimationStart(animation: Animator?) {
+ super.onAnimationStart(animation)
+ if (!enteringCustomizing) {
+ customizing = false
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index aafd7e6..5ed11c2 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -4636,11 +4636,6 @@
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
- if (mTracking) {
- // TODO(b/247126247) fix underlying issue. Should be ACTION_POINTER_DOWN.
- mShadeLog.d("Don't intercept down event while already tracking");
- return false;
- }
mCentralSurfaces.userActivity();
mAnimatingOnDown = mHeightAnimator != null && !mIsSpringBackAnimation;
mMinExpandHeight = 0.0f;
@@ -4728,11 +4723,6 @@
"onTouch: duplicate down event detected... ignoring");
return true;
}
- if (mTracking) {
- // TODO(b/247126247) fix underlying issue. Should be ACTION_POINTER_DOWN.
- mShadeLog.d("Don't handle down event while already tracking");
- return true;
- }
mLastTouchDownTime = event.getDownTime();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
index 21f4cb5..49f17b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
@@ -88,6 +88,7 @@
mSeenNotifsFooterTextView = findViewById(R.id.unlock_prompt_footer);
updateResources();
updateText();
+ updateColors();
}
public void setFooterLabelTextAndIcon(@StringRes int text, @DrawableRes int icon) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
index 85f9961..aa90e2a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
@@ -29,6 +29,7 @@
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
+import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
import com.android.systemui.R
@@ -328,7 +329,7 @@
)
.isTrue()
- underTest.hide()
+ underTest.hide(parent)
clearInvocations(controlsListingController, taskViewFactory)
controlsSettingsRepository.setAllowActionOnTrivialControlsInLockscreen(false)
@@ -387,6 +388,28 @@
assertThat(underTest.resolveActivity()).isEqualTo(ControlsActivity::class.java)
}
+ @Test
+ fun testRemoveViewsOnlyForParentPassedInHide() {
+ underTest.show(parent, {}, context)
+ parent.addView(View(context))
+
+ val mockParent: ViewGroup = mock()
+
+ underTest.hide(mockParent)
+
+ verify(mockParent).removeAllViews()
+ assertThat(parent.childCount).isGreaterThan(0)
+ }
+
+ @Test
+ fun testHideDifferentParentDoesntCancelListeners() {
+ underTest.show(parent, {}, context)
+ underTest.hide(mock())
+
+ verify(controlsController, never()).unsubscribe()
+ verify(controlsListingController, never()).removeCallback(any())
+ }
+
private fun setUpPanel(panel: SelectedItem.PanelItem): ControlsServiceInfo {
val activity = ComponentName("pkg", "activity")
sharedPreferences
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
index 15a454b..a4e5bca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard
+import android.app.admin.DevicePolicyManager
import android.content.ContentValues
import android.content.pm.PackageManager
import android.content.pm.ProviderInfo
@@ -61,7 +62,6 @@
import com.android.systemui.util.settings.FakeSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
@@ -90,6 +90,7 @@
@Mock private lateinit var previewSurfacePackage: SurfaceControlViewHost.SurfacePackage
@Mock private lateinit var launchAnimator: DialogLaunchAnimator
@Mock private lateinit var commandQueue: CommandQueue
+ @Mock private lateinit var devicePolicyManager: DevicePolicyManager
private lateinit var underTest: CustomizationProvider
private lateinit var testScope: TestScope
@@ -102,7 +103,7 @@
whenever(backgroundHandler.looper).thenReturn(TestableLooper.get(this).looper)
underTest = CustomizationProvider()
- val testDispatcher = StandardTestDispatcher()
+ val testDispatcher = UnconfinedTestDispatcher()
testScope = TestScope(testDispatcher)
val localUserSelectionManager =
KeyguardQuickAffordanceLocalUserSelectionManager(
@@ -183,6 +184,8 @@
featureFlags = featureFlags,
repository = { quickAffordanceRepository },
launchAnimator = launchAnimator,
+ devicePolicyManager = devicePolicyManager,
+ backgroundDispatcher = testDispatcher,
)
underTest.previewManager =
KeyguardRemotePreviewManager(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index 23e06ec..84ec125 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.domain.interactor
+import android.app.admin.DevicePolicyManager
import android.content.Intent
import android.os.UserHandle
import androidx.test.filters.SmallTest
@@ -54,7 +55,10 @@
import com.android.systemui.util.settings.FakeSettings
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.test.runBlockingTest
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -70,6 +74,7 @@
import org.mockito.Mockito.verifyZeroInteractions
import org.mockito.MockitoAnnotations
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(Parameterized::class)
class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
@@ -219,8 +224,10 @@
@Mock private lateinit var expandable: Expandable
@Mock private lateinit var launchAnimator: DialogLaunchAnimator
@Mock private lateinit var commandQueue: CommandQueue
+ @Mock private lateinit var devicePolicyManager: DevicePolicyManager
private lateinit var underTest: KeyguardQuickAffordanceInteractor
+ private lateinit var testScope: TestScope
@JvmField @Parameter(0) var needStrongAuthAfterBoot: Boolean = false
@JvmField @Parameter(1) var canShowWhileLocked: Boolean = false
@@ -292,6 +299,8 @@
set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false)
set(Flags.FACE_AUTH_REFACTOR, true)
}
+ val testDispatcher = StandardTestDispatcher()
+ testScope = TestScope(testDispatcher)
underTest =
KeyguardQuickAffordanceInteractor(
keyguardInteractor =
@@ -322,58 +331,61 @@
featureFlags = featureFlags,
repository = { quickAffordanceRepository },
launchAnimator = launchAnimator,
+ devicePolicyManager = devicePolicyManager,
+ backgroundDispatcher = testDispatcher,
)
}
@Test
- fun onQuickAffordanceTriggered() = runBlockingTest {
- setUpMocks(
- needStrongAuthAfterBoot = needStrongAuthAfterBoot,
- keyguardIsUnlocked = keyguardIsUnlocked,
- )
+ fun onQuickAffordanceTriggered() =
+ testScope.runTest {
+ setUpMocks(
+ needStrongAuthAfterBoot = needStrongAuthAfterBoot,
+ keyguardIsUnlocked = keyguardIsUnlocked,
+ )
- homeControls.setState(
- lockScreenState =
- KeyguardQuickAffordanceConfig.LockScreenState.Visible(
- icon = DRAWABLE,
- )
- )
- homeControls.onTriggeredResult =
+ homeControls.setState(
+ lockScreenState =
+ KeyguardQuickAffordanceConfig.LockScreenState.Visible(
+ icon = DRAWABLE,
+ )
+ )
+ homeControls.onTriggeredResult =
+ if (startActivity) {
+ KeyguardQuickAffordanceConfig.OnTriggeredResult.StartActivity(
+ intent = INTENT,
+ canShowWhileLocked = canShowWhileLocked,
+ )
+ } else {
+ KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+ }
+
+ underTest.onQuickAffordanceTriggered(
+ configKey = BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS,
+ expandable = expandable,
+ )
+
if (startActivity) {
- KeyguardQuickAffordanceConfig.OnTriggeredResult.StartActivity(
- intent = INTENT,
- canShowWhileLocked = canShowWhileLocked,
- )
+ if (needsToUnlockFirst) {
+ verify(activityStarter)
+ .postStartActivityDismissingKeyguard(
+ any(),
+ /* delay= */ eq(0),
+ same(animationController),
+ )
+ } else {
+ verify(activityStarter)
+ .startActivity(
+ any(),
+ /* dismissShade= */ eq(true),
+ same(animationController),
+ /* showOverLockscreenWhenLocked= */ eq(true),
+ )
+ }
} else {
- KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+ verifyZeroInteractions(activityStarter)
}
-
- underTest.onQuickAffordanceTriggered(
- configKey = BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS,
- expandable = expandable,
- )
-
- if (startActivity) {
- if (needsToUnlockFirst) {
- verify(activityStarter)
- .postStartActivityDismissingKeyguard(
- any(),
- /* delay= */ eq(0),
- same(animationController),
- )
- } else {
- verify(activityStarter)
- .startActivity(
- any(),
- /* dismissShade= */ eq(true),
- same(animationController),
- /* showOverLockscreenWhenLocked= */ eq(true),
- )
- }
- } else {
- verifyZeroInteractions(activityStarter)
}
- }
private fun setUpMocks(
needStrongAuthAfterBoot: Boolean = true,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index 1b8c627..62c9e5f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.domain.interactor
+import android.app.admin.DevicePolicyManager
import android.os.UserHandle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -78,6 +79,7 @@
@Mock private lateinit var activityStarter: ActivityStarter
@Mock private lateinit var launchAnimator: DialogLaunchAnimator
@Mock private lateinit var commandQueue: CommandQueue
+ @Mock private lateinit var devicePolicyManager: DevicePolicyManager
private lateinit var underTest: KeyguardQuickAffordanceInteractor
@@ -184,6 +186,8 @@
featureFlags = featureFlags,
repository = { quickAffordanceRepository },
launchAnimator = launchAnimator,
+ devicePolicyManager = devicePolicyManager,
+ backgroundDispatcher = testDispatcher,
)
}
@@ -239,6 +243,44 @@
}
@Test
+ fun `quickAffordance - hidden when all features are disabled by device policy`() =
+ testScope.runTest {
+ whenever(devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId))
+ .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL)
+ quickAccessWallet.setState(
+ KeyguardQuickAffordanceConfig.LockScreenState.Visible(
+ icon = ICON,
+ )
+ )
+
+ val collectedValue by
+ collectLastValue(
+ underTest.quickAffordance(KeyguardQuickAffordancePosition.BOTTOM_END)
+ )
+
+ assertThat(collectedValue).isInstanceOf(KeyguardQuickAffordanceModel.Hidden::class.java)
+ }
+
+ @Test
+ fun `quickAffordance - hidden when shortcuts feature is disabled by device policy`() =
+ testScope.runTest {
+ whenever(devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId))
+ .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL)
+ quickAccessWallet.setState(
+ KeyguardQuickAffordanceConfig.LockScreenState.Visible(
+ icon = ICON,
+ )
+ )
+
+ val collectedValue by
+ collectLastValue(
+ underTest.quickAffordance(KeyguardQuickAffordancePosition.BOTTOM_END)
+ )
+
+ assertThat(collectedValue).isInstanceOf(KeyguardQuickAffordanceModel.Hidden::class.java)
+ }
+
+ @Test
fun `quickAffordance - bottom start affordance hidden while dozing`() =
testScope.runTest {
repository.setDozing(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index 6afeddd..8bd8be5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.ui.viewmodel
+import android.app.admin.DevicePolicyManager
import android.content.Intent
import android.os.UserHandle
import androidx.test.filters.SmallTest
@@ -87,6 +88,7 @@
@Mock private lateinit var activityStarter: ActivityStarter
@Mock private lateinit var launchAnimator: DialogLaunchAnimator
@Mock private lateinit var commandQueue: CommandQueue
+ @Mock private lateinit var devicePolicyManager: DevicePolicyManager
private lateinit var underTest: KeyguardBottomAreaViewModel
@@ -140,6 +142,7 @@
bouncerRepository = FakeKeyguardBouncerRepository(),
)
whenever(userTracker.userHandle).thenReturn(mock())
+ whenever(userTracker.userId).thenReturn(10)
whenever(lockPatternUtils.getStrongAuthForUser(anyInt()))
.thenReturn(LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED)
val testDispatcher = StandardTestDispatcher()
@@ -205,6 +208,8 @@
featureFlags = featureFlags,
repository = { quickAffordanceRepository },
launchAnimator = launchAnimator,
+ devicePolicyManager = devicePolicyManager,
+ backgroundDispatcher = testDispatcher,
),
bottomAreaInteractor = KeyguardBottomAreaInteractor(repository = repository),
burnInHelperWrapper = burnInHelperWrapper,
@@ -240,6 +245,39 @@
}
@Test
+ fun `startButton - hidden when device policy disables all keyguard features`() =
+ testScope.runTest {
+ whenever(devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId))
+ .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL)
+ repository.setKeyguardShowing(true)
+ val latest by collectLastValue(underTest.startButton)
+
+ val testConfig =
+ TestConfig(
+ isVisible = true,
+ isClickable = true,
+ isActivated = true,
+ icon = mock(),
+ canShowWhileLocked = false,
+ intent = Intent("action"),
+ )
+ val configKey =
+ setUpQuickAffordanceModel(
+ position = KeyguardQuickAffordancePosition.BOTTOM_START,
+ testConfig = testConfig,
+ )
+
+ assertQuickAffordanceViewModel(
+ viewModel = latest,
+ testConfig =
+ TestConfig(
+ isVisible = false,
+ ),
+ configKey = configKey,
+ )
+ }
+
+ @Test
fun `startButton - in preview mode - visible even when keyguard not showing`() =
testScope.runTest {
underTest.enablePreviewMode(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
index 6cf642c..09156d6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
@@ -70,7 +70,7 @@
whenever(brightnessSliderFactory.create(any(), any())).thenReturn(brightnessSlider)
whenever(brightnessControllerFactory.create(any())).thenReturn(brightnessController)
- testableResources.addOverride(R.bool.config_use_split_notification_shade, false)
+ setShouldUseSplitShade(false)
whenever(qsPanel.resources).thenReturn(testableResources.resources)
whenever(qsPanel.getOrCreateTileLayout()).thenReturn(pagedTileLayout)
whenever(statusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(false)
@@ -133,12 +133,31 @@
@Test
fun configurationChange_onlySplitShadeConfigChanges_tileAreRedistributed() {
- testableResources.addOverride(R.bool.config_use_split_notification_shade, false)
+ setShouldUseSplitShade(false)
controller.mOnConfigurationChangedListener.onConfigurationChange(configuration)
verify(pagedTileLayout, never()).forceTilesRedistribution(any())
- testableResources.addOverride(R.bool.config_use_split_notification_shade, true)
+ setShouldUseSplitShade(true)
controller.mOnConfigurationChangedListener.onConfigurationChange(configuration)
verify(pagedTileLayout).forceTilesRedistribution("Split shade state changed")
}
+
+ @Test
+ fun configurationChange_onlySplitShadeConfigChanges_qsPanelCanBeCollapsed() {
+ setShouldUseSplitShade(false)
+ controller.mOnConfigurationChangedListener.onConfigurationChange(configuration)
+ verify(qsPanel, never()).setCanCollapse(anyBoolean())
+
+ setShouldUseSplitShade(true)
+ controller.mOnConfigurationChangedListener.onConfigurationChange(configuration)
+ verify(qsPanel).setCanCollapse(false)
+
+ setShouldUseSplitShade(false)
+ controller.mOnConfigurationChangedListener.onConfigurationChange(configuration)
+ verify(qsPanel).setCanCollapse(true)
+ }
+
+ private fun setShouldUseSplitShade(shouldUse: Boolean) {
+ testableResources.addOverride(R.bool.config_use_split_notification_shade, shouldUse)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
index d52b296..a8cfb25 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
@@ -37,6 +37,7 @@
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -196,6 +197,16 @@
qsPanel.setSquishinessFraction(0.5f)
}
+ @Test
+ fun testSplitShade_CollapseAccessibilityActionNotAnnounced() {
+ qsPanel.setCanCollapse(false)
+ val accessibilityInfo = mock(AccessibilityNodeInfo::class.java)
+ qsPanel.onInitializeAccessibilityNodeInfo(accessibilityInfo)
+
+ val actionCollapse = AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE
+ verify(accessibilityInfo, never()).addAction(actionCollapse)
+ }
+
private infix fun View.isLeftOf(other: View): Boolean {
val rect = Rect()
getBoundsOnScreen(rect)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt
index 6175df9..e684007 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt
@@ -1,7 +1,6 @@
package com.android.systemui.shade
import android.animation.Animator
-import android.animation.ValueAnimator
import android.app.StatusBarManager
import android.content.Context
import android.testing.AndroidTestingRunner
@@ -44,6 +43,7 @@
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.Mockito.mock
+import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
import org.mockito.junit.MockitoJUnit
@@ -158,10 +158,20 @@
fun updateListeners_registersWhenVisible() {
makeShadeVisible()
verify(qsCarrierGroupController).setListening(true)
+ }
+
+ @Test
+ fun statusIconsAddedWhenAttached() {
verify(statusBarIconController).addIconGroup(any())
}
@Test
+ fun statusIconsRemovedWhenDettached() {
+ mLargeScreenShadeHeaderController.simulateViewDetached()
+ verify(statusBarIconController).removeIconGroup(any())
+ }
+
+ @Test
fun shadeExpandedFraction_updatesAlpha() {
makeShadeVisible()
mLargeScreenShadeHeaderController.shadeExpandedFraction = 0.5f
@@ -169,16 +179,6 @@
}
@Test
- fun alphaChangesUpdateVisibility() {
- makeShadeVisible()
- mLargeScreenShadeHeaderController.shadeExpandedFraction = 0f
- assertThat(viewVisibility).isEqualTo(View.INVISIBLE)
-
- mLargeScreenShadeHeaderController.shadeExpandedFraction = 1f
- assertThat(viewVisibility).isEqualTo(View.VISIBLE)
- }
-
- @Test
fun singleCarrier_enablesCarrierIconsInStatusIcons() {
whenever(qsCarrierGroupController.isSingleCarrier).thenReturn(true)
@@ -263,40 +263,32 @@
}
@Test
- fun testShadeExpanded_true_alpha_zero_invisible() {
- view.alpha = 0f
- mLargeScreenShadeHeaderController.largeScreenActive = true
- mLargeScreenShadeHeaderController.qsVisible = true
+ fun customizerAnimatorChangesViewVisibility() {
+ makeShadeVisible()
- assertThat(viewVisibility).isEqualTo(View.INVISIBLE)
- }
-
- @Test
- fun animatorCallsUpdateVisibilityOnUpdate() {
val animator = mock(ViewPropertyAnimator::class.java, Answers.RETURNS_SELF)
+ val duration = 1000L
whenever(view.animate()).thenReturn(animator)
+ val listenerCaptor = argumentCaptor<Animator.AnimatorListener>()
- mLargeScreenShadeHeaderController.startCustomizingAnimation(show = false, 0L)
-
- val updateCaptor = argumentCaptor<ValueAnimator.AnimatorUpdateListener>()
- verify(animator).setUpdateListener(capture(updateCaptor))
-
- mLargeScreenShadeHeaderController.largeScreenActive = true
- mLargeScreenShadeHeaderController.qsVisible = true
-
- view.alpha = 1f
- updateCaptor.value.onAnimationUpdate(mock())
-
- assertThat(viewVisibility).isEqualTo(View.VISIBLE)
-
- view.alpha = 0f
- updateCaptor.value.onAnimationUpdate(mock())
-
+ mLargeScreenShadeHeaderController.startCustomizingAnimation(show = true, duration)
+ verify(animator).setListener(capture(listenerCaptor))
+ // Start and end the animation
+ listenerCaptor.value.onAnimationStart(mock())
+ listenerCaptor.value.onAnimationEnd(mock())
assertThat(viewVisibility).isEqualTo(View.INVISIBLE)
+
+ reset(animator)
+ mLargeScreenShadeHeaderController.startCustomizingAnimation(show = false, duration)
+ verify(animator).setListener(capture(listenerCaptor))
+ // Start and end the animation
+ listenerCaptor.value.onAnimationStart(mock())
+ listenerCaptor.value.onAnimationEnd(mock())
+ assertThat(viewVisibility).isEqualTo(View.VISIBLE)
}
@Test
- fun animatorListenersClearedAtEnd() {
+ fun animatorListenerClearedAtEnd() {
val animator = mock(ViewPropertyAnimator::class.java, Answers.RETURNS_SELF)
whenever(view.animate()).thenReturn(animator)
@@ -306,21 +298,6 @@
listenerCaptor.value.onAnimationEnd(mock())
verify(animator).setListener(null)
- verify(animator).setUpdateListener(null)
- }
-
- @Test
- fun animatorListenersClearedOnCancel() {
- val animator = mock(ViewPropertyAnimator::class.java, Answers.RETURNS_SELF)
- whenever(view.animate()).thenReturn(animator)
-
- mLargeScreenShadeHeaderController.startCustomizingAnimation(show = true, 0L)
- val listenerCaptor = argumentCaptor<Animator.AnimatorListener>()
- verify(animator).setListener(capture(listenerCaptor))
-
- listenerCaptor.value.onAnimationCancel(mock())
- verify(animator).setListener(null)
- verify(animator).setUpdateListener(null)
}
@Test
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 328b971..cde820a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -4577,6 +4577,17 @@
return false;
}
+ /**
+ * Called when always on magnification feature flag flips to check if the feature should be
+ * enabled for current user state.
+ */
+ public void updateAlwaysOnMagnification() {
+ synchronized (mLock) {
+ readAlwaysOnMagnificationLocked(getCurrentUserState());
+ }
+ }
+
+ @GuardedBy("mLock")
boolean readAlwaysOnMagnificationLocked(AccessibilityUserState userState) {
final boolean isSettingsAlwaysOnEnabled = Settings.Secure.getIntForUser(
mContext.getContentResolver(),
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/AlwaysOnMagnificationFeatureFlag.java b/services/accessibility/java/com/android/server/accessibility/magnification/AlwaysOnMagnificationFeatureFlag.java
index ed45e7b..16d2e6b 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/AlwaysOnMagnificationFeatureFlag.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/AlwaysOnMagnificationFeatureFlag.java
@@ -16,10 +16,13 @@
package com.android.server.accessibility.magnification;
+import android.annotation.NonNull;
import android.provider.DeviceConfig;
import com.android.internal.annotations.VisibleForTesting;
+import java.util.concurrent.Executor;
+
/**
* Encapsulates the feature flags for always on magnification. {@see DeviceConfig}
*
@@ -50,4 +53,39 @@
Boolean.toString(isEnabled),
/* makeDefault= */ false);
}
+
+ /**
+ * Adds a listener for when the feature flag changes.
+ *
+ * <p>{@see DeviceConfig#addOnPropertiesChangedListener(
+ * String, Executor, DeviceConfig.OnPropertiesChangedListener)}
+ */
+ @NonNull
+ public static DeviceConfig.OnPropertiesChangedListener addOnChangedListener(
+ @NonNull Executor executor, @NonNull Runnable listener) {
+ DeviceConfig.OnPropertiesChangedListener onChangedListener =
+ properties -> {
+ if (properties.getKeyset().contains(
+ FEATURE_NAME_ENABLE_ALWAYS_ON_MAGNIFICATION)) {
+ listener.run();
+ }
+ };
+ DeviceConfig.addOnPropertiesChangedListener(
+ NAMESPACE,
+ executor,
+ onChangedListener);
+
+ return onChangedListener;
+ }
+
+ /**
+ * Remove a listener for when the feature flag changes.
+ *
+ * <p>{@see DeviceConfig#addOnPropertiesChangedListener(String, Executor,
+ * DeviceConfig.OnPropertiesChangedListener)}
+ */
+ public static void removeOnChangedListener(
+ @NonNull DeviceConfig.OnPropertiesChangedListener onChangedListener) {
+ DeviceConfig.removeOnPropertiesChangedListener(onChangedListener);
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index a6e6bd7..4753a54 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -48,6 +48,7 @@
import com.android.internal.accessibility.util.AccessibilityStatsLogUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ConcurrentUtils;
import com.android.server.LocalServices;
import com.android.server.accessibility.AccessibilityManagerService;
import com.android.server.wm.WindowManagerInternal;
@@ -149,6 +150,9 @@
.getAccessibilityController().setUiChangesForAccessibilityCallbacks(this);
mSupportWindowMagnification = context.getPackageManager().hasSystemFeature(
FEATURE_WINDOW_MAGNIFICATION);
+
+ AlwaysOnMagnificationFeatureFlag.addOnChangedListener(
+ ConcurrentUtils.DIRECT_EXECUTOR, mAms::updateAlwaysOnMagnification);
}
@VisibleForTesting
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index e35e4b3..af6b24e 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -1142,7 +1142,7 @@
} else {
// For any widget other then the first one, we just send update intent
// as we normally would.
- sendUpdateIntentLocked(provider, new int[]{appWidgetId});
+ sendUpdateIntentLocked(provider, new int[]{appWidgetId}, true);
}
// Schedule the future updates.
@@ -2379,7 +2379,7 @@
if (!canSendCombinedBroadcast) {
// If this function is called by mistake, send two separate broadcasts instead
sendEnableIntentLocked(p);
- sendUpdateIntentLocked(p, appWidgetIds);
+ sendUpdateIntentLocked(p, appWidgetIds, true);
return;
}
@@ -2399,12 +2399,12 @@
sendBroadcastAsUser(intent, p.id.getProfile(), true);
}
- private void sendUpdateIntentLocked(Provider provider, int[] appWidgetIds) {
+ private void sendUpdateIntentLocked(Provider provider, int[] appWidgetIds,
+ boolean interactive) {
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
intent.setComponent(provider.id.componentName);
- // Periodic background widget update heartbeats are not an interactive use case
- sendBroadcastAsUser(intent, provider.id.getProfile(), false);
+ sendBroadcastAsUser(intent, provider.id.getProfile(), interactive);
}
private void sendDeletedIntentLocked(Widget widget) {
@@ -3589,7 +3589,7 @@
scheduleNotifyProviderChangedLocked(widget);
}
// Now that we've told the host, push out an update.
- sendUpdateIntentLocked(provider, appWidgetIds);
+ sendUpdateIntentLocked(provider, appWidgetIds, false);
}
}
providersUpdated = true;
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 1c75380..e536076 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -159,6 +159,8 @@
);
public static final String[] AIDL_INTERFACE_PREFIXES_OF_INTEREST = new String[] {
+ "android.hardware.audio.core.IModule/",
+ "android.hardware.audio.core.IConfig/",
"android.hardware.biometrics.face.IFace/",
"android.hardware.biometrics.fingerprint.IFingerprint/",
"android.hardware.bluetooth.IBluetoothHci/",
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index ede4c5e..3a93cb3 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -7786,7 +7786,10 @@
r.mIsFgsDelegate,
r.mFgsDelegation != null ? r.mFgsDelegation.mOptions.mClientUid : INVALID_UID,
r.mFgsDelegation != null ? r.mFgsDelegation.mOptions.mDelegationService
- : ForegroundServiceDelegationOptions.DELEGATION_SERVICE_DEFAULT);
+ : ForegroundServiceDelegationOptions.DELEGATION_SERVICE_DEFAULT,
+ 0,
+ null,
+ null);
int event = 0;
if (state == FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 176690d..96d5f38 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -18843,6 +18843,11 @@
}
}
+ @Override
+ public void waitForBroadcastBarrier() {
+ waitForBroadcastBarrier(/* printWriter= */ null, false);
+ }
+
public void waitForBroadcastBarrier(@Nullable PrintWriter pw, boolean flushBroadcastLoopers) {
enforceCallingPermission(permission.DUMP, "waitForBroadcastBarrier()");
if (flushBroadcastLoopers) {
diff --git a/services/core/java/com/android/server/am/BroadcastConstants.java b/services/core/java/com/android/server/am/BroadcastConstants.java
index fe5888d..53fcddf 100644
--- a/services/core/java/com/android/server/am/BroadcastConstants.java
+++ b/services/core/java/com/android/server/am/BroadcastConstants.java
@@ -225,7 +225,7 @@
public int MAX_HISTORY_COMPLETE_SIZE = DEFAULT_MAX_HISTORY_COMPLETE_SIZE;
private static final String KEY_MAX_HISTORY_COMPLETE_SIZE = "bcast_max_history_complete_size";
private static final int DEFAULT_MAX_HISTORY_COMPLETE_SIZE =
- ActivityManager.isLowRamDeviceStatic() ? 10 : 50;
+ ActivityManager.isLowRamDeviceStatic() ? 64 : 256;
/**
* For {@link BroadcastQueueModernImpl}: Maximum number of summarized
@@ -234,16 +234,7 @@
public int MAX_HISTORY_SUMMARY_SIZE = DEFAULT_MAX_HISTORY_SUMMARY_SIZE;
private static final String KEY_MAX_HISTORY_SUMMARY_SIZE = "bcast_max_history_summary_size";
private static final int DEFAULT_MAX_HISTORY_SUMMARY_SIZE =
- ActivityManager.isLowRamDeviceStatic() ? 25 : 300;
-
- /**
- * For {@link BroadcastQueueModernImpl}: Maximum number of broadcast receivers to process in a
- * single synchronized block. Up to this many messages may be dispatched in a single binder
- * call. Set this to 1 (or zero) for pre-batch behavior.
- */
- public int MAX_BROADCAST_BATCH_SIZE = DEFAULT_MAX_BROADCAST_BATCH_SIZE;
- private static final String KEY_MAX_BROADCAST_BATCH_SIZE = "bcast_max_batch_size";
- private static final int DEFAULT_MAX_BROADCAST_BATCH_SIZE = 1;
+ ActivityManager.isLowRamDeviceStatic() ? 256 : 1024;
// Settings override tracking for this instance
private String mSettingsKey;
@@ -382,8 +373,6 @@
DEFAULT_MAX_HISTORY_COMPLETE_SIZE);
MAX_HISTORY_SUMMARY_SIZE = getDeviceConfigInt(KEY_MAX_HISTORY_SUMMARY_SIZE,
DEFAULT_MAX_HISTORY_SUMMARY_SIZE);
- MAX_BROADCAST_BATCH_SIZE = getDeviceConfigInt(KEY_MAX_BROADCAST_BATCH_SIZE,
- DEFAULT_MAX_BROADCAST_BATCH_SIZE);
}
}
@@ -429,7 +418,6 @@
MAX_CONSECUTIVE_URGENT_DISPATCHES).println();
pw.print(KEY_MAX_CONSECUTIVE_NORMAL_DISPATCHES,
MAX_CONSECUTIVE_NORMAL_DISPATCHES).println();
- pw.print(KEY_MAX_BROADCAST_BATCH_SIZE, MAX_BROADCAST_BATCH_SIZE).println();
pw.decreaseIndent();
pw.println();
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueueImpl.java b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
index 85d535b..c085706 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
@@ -76,7 +76,6 @@
import android.util.SparseIntArray;
import android.util.proto.ProtoOutputStream;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.TimeoutRecord;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.LocalServices;
@@ -190,14 +189,6 @@
}
}
- /**
- * This single object allows the queue to dispatch receivers using scheduleReceiverList
- * without constantly allocating new ReceiverInfo objects or ArrayLists. This queue
- * implementation is known to have a maximum size of one entry.
- */
- @VisibleForTesting
- final BroadcastReceiverBatch mReceiverBatch = new BroadcastReceiverBatch(1);
-
BroadcastQueueImpl(ActivityManagerService service, Handler handler,
String name, BroadcastConstants constants, boolean allowDelayBehindServices,
int schedGroup) {
@@ -402,13 +393,13 @@
mService.notifyPackageUse(r.intent.getComponent().getPackageName(),
PackageManager.NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER);
final boolean assumeDelivered = false;
- thread.scheduleReceiverList(mReceiverBatch.manifestReceiver(
+ thread.scheduleReceiver(
prepareReceiverIntent(r.intent, r.curFilteredExtras),
r.curReceiver, null /* compatInfo (unused but need to keep method signature) */,
r.resultCode, r.resultData, r.resultExtras, r.ordered, assumeDelivered,
r.userId, r.shareIdentity ? r.callingUid : Process.INVALID_UID,
- r.shareIdentity ? r.callerPackage : null,
- app.mState.getReportedProcState()));
+ app.mState.getReportedProcState(),
+ r.shareIdentity ? r.callerPackage : null);
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Process cur broadcast " + r + " DELIVERED for app " + app);
started = true;
@@ -756,12 +747,12 @@
// correctly ordered with other one-way calls.
try {
final boolean assumeDelivered = !ordered;
- thread.scheduleReceiverList(mReceiverBatch.registeredReceiver(
+ thread.scheduleRegisteredReceiver(
receiver, intent, resultCode,
data, extras, ordered, sticky, assumeDelivered, sendingUser,
+ app.mState.getReportedProcState(),
shareIdentity ? callingUid : Process.INVALID_UID,
- shareIdentity ? callingPackage : null,
- app.mState.getReportedProcState()));
+ shareIdentity ? callingPackage : null);
} catch (RemoteException ex) {
// Failed to call into the process. It's either dying or wedged. Kill it gently.
synchronized (mService) {
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index b016890..5c7fde7 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -145,11 +145,6 @@
// We configure runnable size only once at boot; it'd be too complex to
// try resizing dynamically at runtime
mRunning = new BroadcastProcessQueue[mConstants.getMaxRunningQueues()];
-
- // Set up the statistics for batched broadcasts.
- final int batchSize = mConstants.MAX_BROADCAST_BATCH_SIZE;
- mReceiverBatch = new BroadcastReceiverBatch(batchSize);
- Slog.i(TAG, "maximum broadcast batch size " + batchSize);
}
/**
@@ -220,15 +215,6 @@
private final BroadcastConstants mBgConstants;
/**
- * The sole instance of BroadcastReceiverBatch that is used by scheduleReceiverWarmLocked().
- * The class is not a true singleton but only one instance is needed for the broadcast queue.
- * Although this is guarded by mService, it should never be accessed by any other function.
- */
- @VisibleForTesting
- @GuardedBy("mService")
- final BroadcastReceiverBatch mReceiverBatch;
-
- /**
* Timestamp when last {@link #testAllProcessQueues} failure was observed;
* used for throttling log messages.
*/
@@ -248,19 +234,10 @@
private void enqueueFinishReceiver(@NonNull BroadcastProcessQueue queue,
@DeliveryState int deliveryState, @NonNull String reason) {
- enqueueFinishReceiver(queue, queue.getActive(), queue.getActiveIndex(),
- deliveryState, reason);
- }
-
- private void enqueueFinishReceiver(@NonNull BroadcastProcessQueue queue,
- @NonNull BroadcastRecord r, int index,
- @DeliveryState int deliveryState, @NonNull String reason) {
final SomeArgs args = SomeArgs.obtain();
args.arg1 = queue;
args.argi1 = deliveryState;
args.arg2 = reason;
- args.arg3 = r;
- args.argi2 = index;
mLocalHandler.sendMessage(Message.obtain(mLocalHandler, MSG_FINISH_RECEIVER, args));
}
@@ -308,10 +285,8 @@
final BroadcastProcessQueue queue = (BroadcastProcessQueue) args.arg1;
final int deliveryState = args.argi1;
final String reason = (String) args.arg2;
- final BroadcastRecord r = (BroadcastRecord) args.arg3;
- final int index = args.argi2;
args.recycle();
- finishReceiverLocked(queue, deliveryState, reason, r, index);
+ finishReceiverActiveLocked(queue, deliveryState, reason);
}
return true;
}
@@ -767,7 +742,7 @@
return;
}
- if (maybeSkipReceiver(queue, null, r, index)) {
+ if (maybeSkipReceiver(queue, r, index)) {
mRunningColdStart = null;
return;
}
@@ -811,22 +786,20 @@
@GuardedBy("mService")
private void scheduleReceiverWarmLocked(@NonNull BroadcastProcessQueue queue) {
checkState(queue.isActive(), "isActive");
- BroadcastReceiverBatch batch = mReceiverBatch;
- batch.reset();
- while (collectReceiverList(queue, batch)) {
- if (batch.isFull()) {
- break;
- }
- if (!shouldContinueScheduling(queue)) {
- break;
- }
- if (queue.isEmpty()) {
- break;
- }
- queue.makeActiveNextPending();
+ final BroadcastRecord r = queue.getActive();
+ final int index = queue.getActiveIndex();
+
+ if (r.terminalCount == 0) {
+ r.dispatchTime = SystemClock.uptimeMillis();
+ r.dispatchRealTime = SystemClock.elapsedRealtime();
+ r.dispatchClockTime = System.currentTimeMillis();
}
- processReceiverList(queue, batch);
+
+ if (maybeSkipReceiver(queue, r, index)) {
+ return;
+ }
+ dispatchReceivers(queue, r, index);
}
/**
@@ -834,14 +807,10 @@
* skipped (and therefore no more work is required).
*/
private boolean maybeSkipReceiver(@NonNull BroadcastProcessQueue queue,
- @Nullable BroadcastReceiverBatch batch, @NonNull BroadcastRecord r, int index) {
+ @NonNull BroadcastRecord r, int index) {
final String reason = shouldSkipReceiver(queue, r, index);
if (reason != null) {
- if (batch == null) {
- enqueueFinishReceiver(queue, r, index, BroadcastRecord.DELIVERY_SKIPPED, reason);
- } else {
- batch.finish(r, index, BroadcastRecord.DELIVERY_SKIPPED, reason);
- }
+ enqueueFinishReceiver(queue, BroadcastRecord.DELIVERY_SKIPPED, reason);
return true;
}
return false;
@@ -884,147 +853,6 @@
}
/**
- * Collect receivers into a list, to be dispatched in a single receiver list call. Return
- * true if remaining receivers in the queue should be examined, and false if the current list
- * is complete.
- */
- private boolean collectReceiverList(@NonNull BroadcastProcessQueue queue,
- @NonNull BroadcastReceiverBatch batch) {
- final ProcessRecord app = queue.app;
- final BroadcastRecord r = queue.getActive();
- final int index = queue.getActiveIndex();
- final Object receiver = r.receivers.get(index);
- final Intent receiverIntent = r.getReceiverIntent(receiver);
-
- if (r.terminalCount == 0) {
- r.dispatchTime = SystemClock.uptimeMillis();
- r.dispatchRealTime = SystemClock.elapsedRealtime();
- r.dispatchClockTime = System.currentTimeMillis();
- }
- if (maybeSkipReceiver(queue, batch, r, index)) {
- return true;
- }
-
- final IApplicationThread thread = app.getOnewayThread();
- if (thread == null) {
- batch.finish(r, index, BroadcastRecord.DELIVERY_FAILURE, "missing IApplicationThread");
- return true;
- }
-
- final boolean assumeDelivered = isAssumedDelivered(r, index);
- if (receiver instanceof BroadcastFilter) {
- batch.schedule(((BroadcastFilter) receiver).receiverList.receiver,
- receiverIntent, r.resultCode, r.resultData, r.resultExtras,
- r.ordered, r.initialSticky, assumeDelivered, r.userId,
- r.shareIdentity ? r.callingUid : Process.INVALID_UID,
- r.shareIdentity ? r.callerPackage : null,
- app.mState.getReportedProcState(), r, index);
- // TODO: consider making registered receivers of unordered
- // broadcasts report results to detect ANRs
- if (assumeDelivered) {
- batch.success(r, index, BroadcastRecord.DELIVERY_DELIVERED, "assuming delivered");
- return true;
- }
- } else {
- batch.schedule(receiverIntent, ((ResolveInfo) receiver).activityInfo,
- null, r.resultCode, r.resultData, r.resultExtras, r.ordered, assumeDelivered,
- r.userId, r.shareIdentity ? r.callingUid : Process.INVALID_UID,
- r.shareIdentity ? r.callerPackage : null,
- app.mState.getReportedProcState(), r, index);
- if (assumeDelivered) {
- batch.success(r, index, BroadcastRecord.DELIVERY_DELIVERED, "assuming delivered");
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Process the information in a BroadcastReceiverBatch. Elements in the finish and success
- * lists are sent to enqueueFinishReceiver(). Elements in the receivers list are transmitted
- * to the target in a single binder call.
- */
- private void processReceiverList(@NonNull BroadcastProcessQueue queue,
- @NonNull BroadcastReceiverBatch batch) {
- // Transmit the receiver list.
- final ProcessRecord app = queue.app;
- final IApplicationThread thread = app.getOnewayThread();
-
- batch.recordBatch(thread instanceof SameProcessApplicationThread);
-
- // Mark all the receivers that were discarded. None of these have actually been scheduled.
- for (int i = 0; i < batch.finished().size(); i++) {
- final var finish = batch.finished().get(i);
- enqueueFinishReceiver(queue, finish.r, finish.index, finish.deliveryState,
- finish.reason);
- }
- // Prepare for delivery of all receivers that are about to be scheduled.
- for (int i = 0; i < batch.cookies().size(); i++) {
- final var cookie = batch.cookies().get(i);
- prepareToDispatch(queue, cookie.r, cookie.index);
- }
-
- // Notify on dispatch. Note that receiver/cookies are recorded only if the thread is
- // non-null and the list will therefore be sent.
- for (int i = 0; i < batch.cookies().size(); i++) {
- // Cookies and receivers are 1:1
- final var cookie = batch.cookies().get(i);
- final BroadcastRecord r = cookie.r;
- final int index = cookie.index;
- final Object receiver = r.receivers.get(index);
-
- if (r.shareIdentity) {
- mService.mPackageManagerInt.grantImplicitAccess(r.userId, r.intent,
- UserHandle.getAppId(app.uid), r.callingUid, true);
- }
- if (receiver instanceof BroadcastFilter) {
- notifyScheduleRegisteredReceiver(queue.app, r, (BroadcastFilter) receiver);
- } else {
- notifyScheduleReceiver(queue.app, r, (ResolveInfo) receiver);
- }
- }
-
- // Transmit the enqueued receivers. The thread cannot be null because the lock has been
- // held since collectReceiverList(), which will not add any receivers if the thread is null.
- boolean remoteFailed = false;
- if (batch.receivers().size() > 0) {
- try {
- thread.scheduleReceiverList(batch.receivers());
- } catch (RemoteException e) {
- // Log the failure of the first receiver in the list. Note that there must be at
- // least one receiver/cookie to reach this point in the code, which means
- // cookie[0] is a valid element.
- final var info = batch.cookies().get(0);
- final BroadcastRecord r = info.r;
- final int index = info.index;
- final Object receiver = r.receivers.get(index);
- final String msg = "Failed to schedule " + r + " to " + receiver
- + " via " + app + ": " + e;
- logw(msg);
- app.killLocked("Can't deliver broadcast", ApplicationExitInfo.REASON_OTHER, true);
- remoteFailed = true;
- }
- }
-
- if (!remoteFailed) {
- // If transmission succeed, report all receivers that are assumed to be delivered.
- for (int i = 0; i < batch.success().size(); i++) {
- final var finish = batch.success().get(i);
- enqueueFinishReceiver(queue, finish.r, finish.index, finish.deliveryState,
- finish.reason);
- }
- } else {
- // If transmission failed, fail all receivers in the list.
- for (int i = 0; i < batch.cookies().size(); i++) {
- final var cookie = batch.cookies().get(i);
- enqueueFinishReceiver(queue, cookie.r, cookie.index,
- BroadcastRecord.DELIVERY_FAILURE, "remote app");
- }
- }
- }
-
- /**
* Return true if this receiver should be assumed to have been delivered.
*/
private boolean isAssumedDelivered(BroadcastRecord r, int index) {
@@ -1035,7 +863,7 @@
/**
* A receiver is about to be dispatched. Start ANR timers, if necessary.
*/
- private void prepareToDispatch(@NonNull BroadcastProcessQueue queue,
+ private void dispatchReceivers(@NonNull BroadcastProcessQueue queue,
@NonNull BroadcastRecord r, int index) {
final ProcessRecord app = queue.app;
final Object receiver = r.receivers.get(index);
@@ -1074,6 +902,47 @@
if (DEBUG_BROADCAST) logv("Scheduling " + r + " to warm " + app);
setDeliveryState(queue, app, r, index, receiver, BroadcastRecord.DELIVERY_SCHEDULED,
"scheduleReceiverWarmLocked");
+
+ final Intent receiverIntent = r.getReceiverIntent(receiver);
+ final IApplicationThread thread = app.getOnewayThread();
+ if (thread != null) {
+ try {
+ if (receiver instanceof BroadcastFilter) {
+ notifyScheduleRegisteredReceiver(app, r, (BroadcastFilter) receiver);
+ thread.scheduleRegisteredReceiver(
+ ((BroadcastFilter) receiver).receiverList.receiver,
+ receiverIntent, r.resultCode, r.resultData, r.resultExtras,
+ r.ordered, r.initialSticky, assumeDelivered, r.userId,
+ app.mState.getReportedProcState(),
+ r.shareIdentity ? r.callingUid : Process.INVALID_UID,
+ r.shareIdentity ? r.callerPackage : null);
+ // TODO: consider making registered receivers of unordered
+ // broadcasts report results to detect ANRs
+ if (assumeDelivered) {
+ enqueueFinishReceiver(queue, BroadcastRecord.DELIVERY_DELIVERED,
+ "assuming delivered");
+ }
+ } else {
+ notifyScheduleReceiver(app, r, (ResolveInfo) receiver);
+ thread.scheduleReceiver(receiverIntent, ((ResolveInfo) receiver).activityInfo,
+ null, r.resultCode, r.resultData, r.resultExtras, r.ordered,
+ assumeDelivered, r.userId,
+ app.mState.getReportedProcState(),
+ r.shareIdentity ? r.callingUid : Process.INVALID_UID,
+ r.shareIdentity ? r.callerPackage : null);
+ }
+ } catch (RemoteException e) {
+ final String msg = "Failed to schedule " + r + " to " + receiver
+ + " via " + app + ": " + e;
+ logw(msg);
+ app.killLocked("Can't deliver broadcast", ApplicationExitInfo.REASON_OTHER,
+ ApplicationExitInfo.SUBREASON_UNDELIVERED_BROADCAST, true);
+ enqueueFinishReceiver(queue, BroadcastRecord.DELIVERY_FAILURE, "remote app");
+ }
+ } else {
+ enqueueFinishReceiver(queue, BroadcastRecord.DELIVERY_FAILURE,
+ "missing IApplicationThread");
+ }
}
/**
@@ -1093,13 +962,13 @@
}
try {
final boolean assumeDelivered = true;
- thread.scheduleReceiverList(mReceiverBatch.registeredReceiver(
+ thread.scheduleRegisteredReceiver(
r.resultTo, r.intent,
r.resultCode, r.resultData, r.resultExtras, false, r.initialSticky,
assumeDelivered, r.userId,
+ app.mState.getReportedProcState(),
r.shareIdentity ? r.callingUid : Process.INVALID_UID,
- r.shareIdentity ? r.callerPackage : null,
- app.mState.getReportedProcState()));
+ r.shareIdentity ? r.callerPackage : null);
} catch (RemoteException e) {
final String msg = "Failed to schedule result of " + r + " via " + app + ": " + e;
logw(msg);
@@ -1187,33 +1056,20 @@
return false;
}
- final BroadcastRecord r = queue.getActive();
- final int index = queue.getActiveIndex();
- return finishReceiverLocked(queue, deliveryState, reason, r, index);
- }
-
- private boolean finishReceiverLocked(@NonNull BroadcastProcessQueue queue,
- @DeliveryState int deliveryState, @NonNull String reason,
- BroadcastRecord r, int index) {
- if (!queue.isActive()) {
- logw("Ignoring finish; no active broadcast for " + queue);
- return false;
- }
-
final int cookie = traceBegin("finishReceiver");
final ProcessRecord app = queue.app;
+ final BroadcastRecord r = queue.getActive();
+ final int index = queue.getActiveIndex();
final Object receiver = r.receivers.get(index);
setDeliveryState(queue, app, r, index, receiver, deliveryState, reason);
- final boolean early = r != queue.getActive() || index != queue.getActiveIndex();
-
if (deliveryState == BroadcastRecord.DELIVERY_TIMEOUT) {
r.anrCount++;
if (app != null && !app.isDebugging()) {
mService.appNotResponding(queue.app, TimeoutRecord.forBroadcastReceiver(r.intent));
}
- } else if (!early) {
+ } else {
mLocalHandler.removeMessages(MSG_DELIVERY_TIMEOUT_SOFT, queue);
mLocalHandler.removeMessages(MSG_DELIVERY_TIMEOUT_HARD, queue);
}
@@ -1221,13 +1077,6 @@
// Given that a receiver just finished, check if the "waitingFor" conditions are met.
checkAndRemoveWaitingFor();
- if (early) {
- // This is an early receiver that was transmitted as part of a group. The delivery
- // state has been updated but don't make any further decisions.
- traceEnd(cookie);
- return false;
- }
-
final boolean res = shouldContinueScheduling(queue);
if (res) {
// We're on a roll; move onto the next broadcast for this process
@@ -1980,17 +1829,6 @@
ipw.decreaseIndent();
ipw.println();
- ipw.println("Batch statistics:");
- ipw.increaseIndent();
- {
- final var stats = mReceiverBatch.getStatistics();
- ipw.println("Finished " + Arrays.toString(stats.finish));
- ipw.println("DispatchedLocal " + Arrays.toString(stats.local));
- ipw.println("DispatchedRemote " + Arrays.toString(stats.remote));
- }
- ipw.decreaseIndent();
- ipw.println();
-
if (dumpConstants) {
mConstants.dump(ipw);
}
diff --git a/services/core/java/com/android/server/am/BroadcastReceiverBatch.java b/services/core/java/com/android/server/am/BroadcastReceiverBatch.java
deleted file mode 100644
index 63575ba..0000000
--- a/services/core/java/com/android/server/am/BroadcastReceiverBatch.java
+++ /dev/null
@@ -1,357 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.am;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.ReceiverInfo;
-import android.content.IIntentReceiver;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.res.CompatibilityInfo;
-import android.os.Bundle;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.util.ArrayList;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- * A batch of receiver instructions. This includes a list of finish requests and a list of
- * receivers. The instructions are for a single queue. It is constructed and consumed in a single
- * call to {@link BroadcastQueueModernImpl#scheduleReceiverWarmLocked}. The list size is bounded by
- * {@link BroadcastConstants#MAX_BROADCAST_BATCH_SIZE}. Because this class is ephemeral and its use
- * is bounded, it is pre-allocated to avoid allocating new objects every time it is used.
- *
- * The {@link #single} methods support the use of this class in {@link BroadcastQueueImpl}. These
- * methods simplify the use of {@link IApplicationThread#scheduleReceiverList} as a replacement
- * for scheduleReceiver and scheduleRegisteredReceiver.
- *
- * This class is designed to be allocated once and constantly reused (call {@link #reset} between
- * uses). Objects needed by the instance are kept in a pool - all object allocation occurs when
- * the instance is created, and no allocation occurs thereafter. However, if the variable
- * mDeepReceiverCopy is set true (it is false by default) then the method {@link #receivers} always
- * returns newly allocated objects. This is required for mock testing.
- *
- * This class is not thread-safe. Instances must be protected by the caller.
- * @hide
- */
-final class BroadcastReceiverBatch {
-
- /**
- * If this is true then receivers() returns a deep copy of the ReceiveInfo array. If this is
- * false, receivers() returns a reference to the array. A deep copy is needed only for the
- * broadcast queue mocking tests.
- */
- @VisibleForTesting
- boolean mDeepReceiverCopy = false;
-
- /**
- * A private pool implementation this class.
- */
- private static class Pool<T> {
- final int size;
- final ArrayList<T> pool;
- int next;
- Pool(int n, @NonNull Class<T> c) {
- size = n;
- pool = new ArrayList<>(size);
- try {
- for (int i = 0; i < size; i++) {
- pool.add(c.getDeclaredConstructor().newInstance());
- }
- } catch (Exception e) {
- // This class is only used locally. Turn any exceptions into something fatal.
- throw new RuntimeException(e);
- }
- }
- T next() {
- return pool.get(next++);
- }
- void reset() {
- next = 0;
- }
- }
-
- /**
- * The information needed to finish off a receiver. This is valid only in the context of
- * a queue.
- */
- static class FinishInfo {
- BroadcastRecord r = null;
- int index = 0;
- int deliveryState = 0;
- String reason = null;
- FinishInfo set(@Nullable BroadcastRecord r, int index,
- int deliveryState, @Nullable String reason) {
- this.r = r;
- this.index = index;
- this.deliveryState = deliveryState;
- this.reason = reason;
- return this;
- }
- }
-
- /**
- * The information needed to recreate a receiver info. The broadcast record can be null if the
- * caller does not expect to need it later.
- */
- static class ReceiverCookie {
- BroadcastRecord r = null;
- int index = 0;
- ReceiverCookie set(@Nullable BroadcastRecord r, int index) {
- this.r = r;
- this.index = index;
- return this;
- }
- }
-
- // The object pools.
- final int mSize;
- private final Pool<ReceiverInfo> receiverPool;
- private final Pool<FinishInfo> finishPool;
- private final Pool<ReceiverCookie> cookiePool;
-
- // The accumulated data. The receivers should be an ArrayList to be directly compatible
- // with scheduleReceiverList(). The receivers array is not final because a new array must
- // be created for every new call to scheduleReceiverList().
- private final ArrayList<ReceiverInfo> mReceivers;
- private final ArrayList<ReceiverCookie> mCookies;
- private final ArrayList<FinishInfo> mFinished;
- // The list of finish records to complete if the binder succeeds or fails.
- private final ArrayList<FinishInfo> mSuccess;
-
- BroadcastReceiverBatch(int size) {
- mSize = size;
- mReceivers = new ArrayList<>(mSize);
- mCookies = new ArrayList<>(mSize);
- mFinished = new ArrayList<>(mSize);
- mSuccess = new ArrayList<>(mSize);
-
- receiverPool = new Pool<>(mSize, ReceiverInfo.class);
- finishPool = new Pool<>(mSize, FinishInfo.class);
- cookiePool = new Pool<>(mSize, ReceiverCookie.class);
- mStats = new Statistics(mSize);
- reset();
- }
-
- void reset() {
- mReceivers.clear();
- mCookies.clear();
- mFinished.clear();
- mSuccess.clear();
-
- receiverPool.reset();
- finishPool.reset();
- cookiePool.reset();
- }
-
- void finish(@Nullable BroadcastRecord r, int index,
- int deliveryState, @Nullable String reason) {
- mFinished.add(finishPool.next().set(r, index, deliveryState, reason));
- }
- void success(@Nullable BroadcastRecord r, int index,
- int deliveryState, @Nullable String reason) {
- mSuccess.add(finishPool.next().set(r, index, deliveryState, reason));
- }
- // Add a ReceiverInfo for a registered receiver.
- void schedule(@Nullable IIntentReceiver receiver, Intent intent,
- int resultCode, @Nullable String data, @Nullable Bundle extras, boolean ordered,
- boolean sticky, boolean assumeDelivered, int sendingUser, int sendingUid,
- @Nullable String sendingPackage, int processState, @Nullable BroadcastRecord r,
- int index) {
- ReceiverInfo ri = new ReceiverInfo();
- ri.intent = intent;
- ri.data = data;
- ri.extras = extras;
- ri.assumeDelivered = assumeDelivered;
- ri.sendingUser = sendingUser;
- ri.processState = processState;
- ri.resultCode = resultCode;
- ri.registered = true;
- ri.receiver = receiver;
- ri.ordered = ordered;
- ri.sticky = sticky;
- ri.sendingUid = sendingUid;
- ri.sendingPackage = sendingPackage;
-
- mReceivers.add(ri);
- mCookies.add(cookiePool.next().set(r, index));
- }
- // Add a ReceiverInfo for a manifest receiver.
- void schedule(@Nullable Intent intent, @Nullable ActivityInfo activityInfo,
- @Nullable CompatibilityInfo compatInfo, int resultCode, @Nullable String data,
- @Nullable Bundle extras, boolean sync, boolean assumeDelivered, int sendingUser,
- int sendingUid, @Nullable String sendingPackage, int processState,
- @Nullable BroadcastRecord r, int index) {
- ReceiverInfo ri = new ReceiverInfo();
- ri.intent = intent;
- ri.data = data;
- ri.extras = extras;
- ri.assumeDelivered = assumeDelivered;
- ri.sendingUser = sendingUser;
- ri.processState = processState;
- ri.resultCode = resultCode;
- ri.registered = false;
- ri.activityInfo = activityInfo;
- ri.compatInfo = compatInfo;
- ri.sync = sync;
- ri.sendingUid = sendingUid;
- ri.sendingPackage = sendingPackage;
- mReceivers.add(ri);
- mCookies.add(cookiePool.next().set(r, index));
- }
-
- /**
- * Two convenience functions for dispatching a single receiver. The functions start with a
- * reset. Then they create the ReceiverInfo array and return it. Statistics are not
- * collected.
- */
- ArrayList<ReceiverInfo> registeredReceiver(@Nullable IIntentReceiver receiver,
- @Nullable Intent intent, int resultCode, @Nullable String data,
- @Nullable Bundle extras, boolean ordered, boolean sticky, boolean assumeDelivered,
- int sendingUser, int sendingUid, @Nullable String sendingPackage, int processState) {
- reset();
- schedule(receiver, intent, resultCode, data, extras, ordered, sticky, assumeDelivered,
- sendingUser, sendingUid, sendingPackage, processState, null, 0);
- return receivers();
- }
-
- ArrayList<ReceiverInfo> manifestReceiver(@Nullable Intent intent,
- @Nullable ActivityInfo activityInfo, @Nullable CompatibilityInfo compatInfo,
- int resultCode, @Nullable String data, @Nullable Bundle extras, boolean sync,
- boolean assumeDelivered, int sendingUser, int sendingUid,
- @Nullable String sendingPackage, int processState) {
- reset();
- schedule(intent, activityInfo, compatInfo, resultCode, data, extras, sync, assumeDelivered,
- sendingUser, sendingUid, sendingPackage, processState, null, 0);
- return receivers();
- }
-
- // Return true if the batch is full. Adding any more entries will throw an exception.
- boolean isFull() {
- return (mFinished.size() + mReceivers.size()) >= mSize;
- }
- int finishCount() {
- return mFinished.size() + mSuccess.size();
- }
- int receiverCount() {
- return mReceivers.size();
- }
-
- /**
- * Create a deep copy of the receiver list. This is only for testing which is confused when
- * objects are reused.
- */
- private ArrayList<ReceiverInfo> copyReceiverInfo() {
- ArrayList<ReceiverInfo> copy = new ArrayList<>();
- for (int i = 0; i < mReceivers.size(); i++) {
- final ReceiverInfo r = mReceivers.get(i);
- final ReceiverInfo n = new ReceiverInfo();
- n.intent = r.intent;
- n.data = r.data;
- n.extras = r.extras;
- n.assumeDelivered = r.assumeDelivered;
- n.sendingUser = r.sendingUser;
- n.processState = r.processState;
- n.resultCode = r.resultCode;
- n.registered = r.registered;
- n.receiver = r.receiver;
- n.ordered = r.ordered;
- n.sticky = r.sticky;
- n.activityInfo = r.activityInfo;
- n.compatInfo = r.compatInfo;
- n.sync = r.sync;
- copy.add(n);
- }
- return copy;
- }
-
- /**
- * Accessors for the accumulated instructions. The important accessor is receivers(), since
- * it can be modified to return a deep copy of the mReceivers array.
- */
- @NonNull
- ArrayList<ReceiverInfo> receivers() {
- if (!mDeepReceiverCopy) {
- return mReceivers;
- } else {
- return copyReceiverInfo();
- }
- }
- @NonNull
- ArrayList<ReceiverCookie> cookies() {
- return mCookies;
- }
- @NonNull
- ArrayList<FinishInfo> finished() {
- return mFinished;
- }
- @NonNull
- ArrayList<FinishInfo> success() {
- return mSuccess;
- }
-
- /**
- * A simple POD for statistics. The parameter is the size of the BroadcastReceiverBatch.
- */
- static class Statistics {
- final int[] finish;
- final int[] local;
- final int[] remote;
- Statistics(int size) {
- finish = new int[size+1];
- local = new int[size+1];
- remote = new int[size+1];
- }
- }
-
- private final Statistics mStats;
-
- /**
- * A unique counter that identifies individual transmission groups. This is only used for
- * debugging. It is used to determine which receivers were sent in the same batch, and in
- * which order. This is static to distinguish between batches across all queues in the
- * system.
- */
- private static final AtomicInteger sTransmitGroup = new AtomicInteger(0);
-
- /**
- * Record statistics for this batch of instructions. This updates the local statistics and it
- * updates the transmitGroup and transmitOrder fields of the BroadcastRecords being
- * dispatched.
- */
- void recordBatch(boolean local) {
- final int group = sTransmitGroup.addAndGet(1);
- for (int i = 0; i < cookies().size(); i++) {
- final var cookie = cookies().get(i);
- cookie.r.transmitGroup[cookie.index] = group;
- cookie.r.transmitOrder[cookie.index] = i;
- }
- mStats.finish[finishCount()]++;
- if (local) {
- mStats.local[receiverCount()]++;
- } else {
- mStats.remote[receiverCount()]++;
- }
- }
-
- @NonNull
- Statistics getStatistics() {
- return mStats;
- }
-}
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index bc2077a..67783be 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -120,8 +120,6 @@
@UptimeMillisLong long finishTime; // when broadcast finished
final @UptimeMillisLong long[] scheduledTime; // when each receiver was scheduled
final @UptimeMillisLong long[] terminalTime; // when each receiver was terminal
- final int[] transmitGroup; // the batch group for each receiver
- final int[] transmitOrder; // the position of the receiver in the group
final boolean timeoutExempt; // true if this broadcast is not subject to receiver timeouts
int resultCode; // current result code value.
@Nullable String resultData; // current result data value.
@@ -400,8 +398,6 @@
blockedUntilTerminalCount = calculateBlockedUntilTerminalCount(receivers, _serialized);
scheduledTime = new long[delivery.length];
terminalTime = new long[delivery.length];
- transmitGroup = new int[delivery.length];
- transmitOrder = new int[delivery.length];
resultToApp = _resultToApp;
resultTo = _resultTo;
resultCode = _resultCode;
@@ -457,8 +453,6 @@
blockedUntilTerminalCount = from.blockedUntilTerminalCount;
scheduledTime = from.scheduledTime;
terminalTime = from.terminalTime;
- transmitGroup = from.transmitGroup;
- transmitOrder = from.transmitOrder;
resultToApp = from.resultToApp;
resultTo = from.resultTo;
enqueueTime = from.enqueueTime;
diff --git a/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java b/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
index bd1cbee..c0cb7d9 100644
--- a/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
+++ b/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
@@ -25,9 +25,11 @@
import static android.app.ActivityManager.FOREGROUND_SERVICE_API_TYPE_MICROPHONE;
import static android.app.ActivityManager.FOREGROUND_SERVICE_API_TYPE_PHONE_CALL;
import static android.app.ActivityManager.FOREGROUND_SERVICE_API_TYPE_USB;
+import static android.os.Process.INVALID_UID;
import android.annotation.IntDef;
import android.app.ActivityManager.ForegroundServiceApiType;
+import android.app.ForegroundServiceDelegationOptions;
import android.content.ComponentName;
import android.content.pm.ServiceInfo;
import android.util.ArrayMap;
@@ -35,6 +37,7 @@
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -430,33 +433,32 @@
public void logFgsApiEvent(ServiceRecord r, int fgsState,
@FgsApiState int apiState,
@ForegroundServiceApiType int[] apiType, long[] timestamp) {
- // TODO: Uncomment when atom changes are in
-// FrameworkStatsLog.write(FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
-// r.appInfo.uid,
-// r.shortInstanceName,
-// fgsState, // FGS State
-// r.mAllowWhileInUsePermissionInFgs, // allowWhileInUsePermissionInFgs
-// r.mAllowStartForeground, // fgsStartReasonCode
-// r.appInfo.targetSdkVersion,
-// r.mRecentCallingUid,
-// 0, // callerTargetSdkVersion
-// r.mInfoTempFgsAllowListReason != null
-// ? r.mInfoTempFgsAllowListReason.mCallingUid : INVALID_UID,
-// r.mFgsNotificationWasDeferred,
-// r.mFgsNotificationShown,
-// 0, // durationMs
-// r.mStartForegroundCount,
-// ActivityManagerUtils.hashComponentNameForAtom(r.shortInstanceName),
-// r.mFgsHasNotificationPermission,
-// r.foregroundServiceType,
-// 0,
-// r.mIsFgsDelegate,
-// r.mFgsDelegation != null ? r.mFgsDelegation.mOptions.mClientUid : INVALID_UID,
-// r.mFgsDelegation != null ? r.mFgsDelegation.mOptions.mDelegationService
-// : ForegroundServiceDelegationOptions.DELEGATION_SERVICE_DEFAULT,
-// apiState,
-// apiType,
-// timestamp);
+ FrameworkStatsLog.write(FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
+ r.appInfo.uid,
+ r.shortInstanceName,
+ fgsState, // FGS State
+ r.mAllowWhileInUsePermissionInFgs, // allowWhileInUsePermissionInFgs
+ r.mAllowStartForeground, // fgsStartReasonCode
+ r.appInfo.targetSdkVersion,
+ r.mRecentCallingUid,
+ 0, // callerTargetSdkVersion
+ r.mInfoTempFgsAllowListReason != null
+ ? r.mInfoTempFgsAllowListReason.mCallingUid : INVALID_UID,
+ r.mFgsNotificationWasDeferred,
+ r.mFgsNotificationShown,
+ 0, // durationMs
+ r.mStartForegroundCount,
+ ActivityManagerUtils.hashComponentNameForAtom(r.shortInstanceName),
+ r.mFgsHasNotificationPermission,
+ r.foregroundServiceType,
+ 0,
+ r.mIsFgsDelegate,
+ r.mFgsDelegation != null ? r.mFgsDelegation.mOptions.mClientUid : INVALID_UID,
+ r.mFgsDelegation != null ? r.mFgsDelegation.mOptions.mDelegationService
+ : ForegroundServiceDelegationOptions.DELEGATION_SERVICE_DEFAULT,
+ apiState,
+ apiType,
+ timestamp);
}
/**
@@ -467,31 +469,30 @@
public void logFgsApiEventWithNoFgs(int uid,
@FgsApiState int apiState,
@ForegroundServiceApiType int[] apiType, long[] timestamp) {
- // TODO: Uncomment when atom changes are in
-// FrameworkStatsLog.write(FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
-// uid,
-// null,
-// FGS_STATE_CHANGED_API_CALL,
-// false, // allowWhileInUsePermissionInFgs
-// 0, // fgsStartReasonCode
-// 0,
-// uid,
-// 0, // callerTargetSdkVersion
-// 0,
-// false,
-// false,
-// 0, // durationMs
-// 0,
-// 0,
-// false,
-// 0,
-// 0,
-// false,
-// 0,
-// 0,
-// apiState,
-// apiType,
-// timestamp);
+ FrameworkStatsLog.write(FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
+ uid,
+ null,
+ FGS_STATE_CHANGED_API_CALL,
+ false, // allowWhileInUsePermissionInFgs
+ 0, // fgsStartReasonCode
+ 0,
+ uid,
+ 0, // callerTargetSdkVersion
+ 0,
+ false,
+ false,
+ 0, // durationMs
+ 0,
+ 0,
+ false,
+ 0,
+ 0,
+ false,
+ 0,
+ 0,
+ apiState,
+ apiType,
+ timestamp);
}
/**
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index b001f3d..1268156 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -1263,6 +1263,10 @@
sendILMsg(MSG_IL_BTA2DP_TIMEOUT, SENDMSG_QUEUE, a2dpCodec, address, delayMs);
}
+ /*package*/ void setLeAudioTimeout(String address, int device, int delayMs) {
+ sendILMsg(MSG_IL_BTLEAUDIO_TIMEOUT, SENDMSG_QUEUE, device, address, delayMs);
+ }
+
/*package*/ void setAvrcpAbsoluteVolumeSupported(boolean supported) {
synchronized (mDeviceStateLock) {
mBtHelper.setAvrcpAbsoluteVolumeSupported(supported);
@@ -1467,6 +1471,13 @@
mDeviceInventory.onMakeA2dpDeviceUnavailableNow((String) msg.obj, msg.arg1);
}
break;
+ case MSG_IL_BTLEAUDIO_TIMEOUT:
+ // msg.obj == address of LE Audio device
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.onMakeLeAudioDeviceUnavailableNow(
+ (String) msg.obj, msg.arg1);
+ }
+ break;
case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
final BluetoothDevice btDevice = (BluetoothDevice) msg.obj;
synchronized (mDeviceStateLock) {
@@ -1556,6 +1567,7 @@
case MSG_I_BT_SERVICE_DISCONNECTED_PROFILE:
if (msg.arg1 != BluetoothProfile.HEADSET) {
synchronized (mDeviceStateLock) {
+ mBtHelper.onBtProfileDisconnected(msg.arg1);
mDeviceInventory.onBtProfileDisconnected(msg.arg1);
}
} else {
@@ -1703,12 +1715,14 @@
private static final int MSG_IL_SAVE_NDEF_DEVICE_FOR_STRATEGY = 47;
private static final int MSG_IL_SAVE_REMOVE_NDEF_DEVICE_FOR_STRATEGY = 48;
+ private static final int MSG_IL_BTLEAUDIO_TIMEOUT = 49;
private static boolean isMessageHandledUnderWakelock(int msgId) {
switch(msgId) {
case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
case MSG_L_SET_BT_ACTIVE_DEVICE:
case MSG_IL_BTA2DP_TIMEOUT:
+ case MSG_IL_BTLEAUDIO_TIMEOUT:
case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
case MSG_TOGGLE_HDMI:
case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT:
@@ -1800,6 +1814,7 @@
case MSG_L_SET_BT_ACTIVE_DEVICE:
case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
case MSG_IL_BTA2DP_TIMEOUT:
+ case MSG_IL_BTLEAUDIO_TIMEOUT:
case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
if (sLastDeviceConnectMsgTime >= time) {
// add a little delay to make sure messages are ordered as expected
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index f9270c9..aae1d38 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -395,7 +395,7 @@
case BluetoothProfile.LE_AUDIO:
case BluetoothProfile.LE_AUDIO_BROADCAST:
if (switchToUnavailable) {
- makeLeAudioDeviceUnavailable(address, btInfo.mAudioSystemDevice);
+ makeLeAudioDeviceUnavailableNow(address, btInfo.mAudioSystemDevice);
} else if (switchToAvailable) {
makeLeAudioDeviceAvailable(address, BtHelper.getName(btInfo.mDevice),
streamType, btInfo.mVolume == -1 ? -1 : btInfo.mVolume * 10,
@@ -507,6 +507,12 @@
}
}
+ /*package*/ void onMakeLeAudioDeviceUnavailableNow(String address, int device) {
+ synchronized (mDevicesLock) {
+ makeLeAudioDeviceUnavailableNow(address, device);
+ }
+ }
+
/*package*/ void onReportNewRoutes() {
int n = mRoutesObservers.beginBroadcast();
if (n > 0) {
@@ -1027,10 +1033,11 @@
new MediaMetrics.Item(mMetricsId + "disconnectLeAudio")
.record();
if (toRemove.size() > 0) {
- final int delay = checkSendBecomingNoisyIntentInt(device, 0,
+ final int delay = checkSendBecomingNoisyIntentInt(device,
+ AudioService.CONNECTION_STATE_DISCONNECTED,
AudioSystem.DEVICE_NONE);
toRemove.stream().forEach(deviceAddress ->
- makeLeAudioDeviceUnavailable(deviceAddress, device)
+ makeLeAudioDeviceUnavailableLater(deviceAddress, device, delay)
);
}
}
@@ -1331,9 +1338,21 @@
*/
mDeviceBroker.setBluetoothA2dpOnInt(true, false /*fromA2dp*/, eventSource);
- AudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(device, address, name),
+ final int res = AudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
+ device, address, name),
AudioSystem.DEVICE_STATE_AVAILABLE,
AudioSystem.AUDIO_FORMAT_DEFAULT);
+ if (res != AudioSystem.AUDIO_STATUS_OK) {
+ AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+ "APM failed to make available LE Audio device addr=" + address
+ + " error=" + res).printLog(TAG));
+ // TODO: connection failed, stop here
+ // TODO: return;
+ } else {
+ AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+ "LE Audio device addr=" + address + " now available").printLog(TAG));
+ }
+
mConnectedDevices.put(DeviceInfo.makeDeviceListKey(device, address),
new DeviceInfo(device, name, address, AudioSystem.AUDIO_FORMAT_DEFAULT));
mDeviceBroker.postAccessoryPlugMediaUnmute(device);
@@ -1354,11 +1373,23 @@
}
@GuardedBy("mDevicesLock")
- private void makeLeAudioDeviceUnavailable(String address, int device) {
+ private void makeLeAudioDeviceUnavailableNow(String address, int device) {
if (device != AudioSystem.DEVICE_NONE) {
- AudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(device, address),
+ final int res = AudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
+ device, address),
AudioSystem.DEVICE_STATE_UNAVAILABLE,
AudioSystem.AUDIO_FORMAT_DEFAULT);
+
+ if (res != AudioSystem.AUDIO_STATUS_OK) {
+ AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+ "APM failed to make unavailable LE Audio device addr=" + address
+ + " error=" + res).printLog(TAG));
+ // TODO: failed to disconnect, stop here
+ // TODO: return;
+ } else {
+ AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+ "LE Audio device addr=" + address + " made unavailable").printLog(TAG));
+ }
mConnectedDevices.remove(DeviceInfo.makeDeviceListKey(device, address));
}
@@ -1366,6 +1397,14 @@
}
@GuardedBy("mDevicesLock")
+ private void makeLeAudioDeviceUnavailableLater(String address, int device, int delayMs) {
+ // the device will be made unavailable later, so consider it disconnected right away
+ mConnectedDevices.remove(DeviceInfo.makeDeviceListKey(device, address));
+ // send the delayed message to make the device unavailable later
+ mDeviceBroker.setLeAudioTimeout(address, device, delayMs);
+ }
+
+ @GuardedBy("mDevicesLock")
private void setCurrentAudioRouteNameIfPossible(String name, boolean fromA2dp) {
synchronized (mCurAudioRoutes) {
if (TextUtils.equals(mCurAudioRoutes.bluetoothName, name)) {
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index df65dbd..2dcdc54 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -278,7 +278,11 @@
}
AudioService.sVolumeLogger.enqueue(new AudioServiceEvents.VolumeEvent(
AudioServiceEvents.VolumeEvent.VOL_SET_AVRCP_VOL, index));
- mA2dp.setAvrcpAbsoluteVolume(index);
+ try {
+ mA2dp.setAvrcpAbsoluteVolume(index);
+ } catch (Exception e) {
+ Log.e(TAG, "Exception while changing abs volume", e);
+ }
}
/*package*/ synchronized @AudioSystem.AudioFormatNativeEnumForBtCodec int getA2dpCodec(
@@ -286,7 +290,12 @@
if (mA2dp == null) {
return AudioSystem.AUDIO_FORMAT_DEFAULT;
}
- final BluetoothCodecStatus btCodecStatus = mA2dp.getCodecStatus(device);
+ BluetoothCodecStatus btCodecStatus = null;
+ try {
+ btCodecStatus = mA2dp.getCodecStatus(device);
+ } catch (Exception e) {
+ Log.e(TAG, "Exception while getting status of " + device, e);
+ }
if (btCodecStatus == null) {
return AudioSystem.AUDIO_FORMAT_DEFAULT;
}
@@ -420,7 +429,11 @@
}
AudioService.sVolumeLogger.enqueue(new AudioServiceEvents.VolumeEvent(
AudioServiceEvents.VolumeEvent.VOL_SET_LE_AUDIO_VOL, index, maxIndex));
- mLeAudio.setVolume(volume);
+ try {
+ mLeAudio.setVolume(volume);
+ } catch (Exception e) {
+ Log.e(TAG, "Exception while setting LE volume", e);
+ }
}
/*package*/ synchronized void setHearingAidVolume(int index, int streamType,
@@ -446,7 +459,11 @@
AudioService.sVolumeLogger.enqueue(new AudioServiceEvents.VolumeEvent(
AudioServiceEvents.VolumeEvent.VOL_SET_HEARING_AID_VOL, index, gainDB));
}
- mHearingAid.setVolume(gainDB);
+ try {
+ mHearingAid.setVolume(gainDB);
+ } catch (Exception e) {
+ Log.i(TAG, "Exception while setting hearing aid volume", e);
+ }
}
/*package*/ synchronized void onBroadcastScoConnectionState(int state) {
@@ -471,7 +488,7 @@
}
// @GuardedBy("AudioDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void resetBluetoothSco() {
mScoAudioState = SCO_STATE_INACTIVE;
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
@@ -486,6 +503,35 @@
mBluetoothHeadset = null;
}
+ //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ /*package*/ synchronized void onBtProfileDisconnected(int profile) {
+ switch (profile) {
+ case BluetoothProfile.A2DP:
+ mA2dp = null;
+ break;
+ case BluetoothProfile.HEARING_AID:
+ mHearingAid = null;
+ break;
+ case BluetoothProfile.LE_AUDIO:
+ mLeAudio = null;
+ break;
+
+ case BluetoothProfile.A2DP_SINK:
+ case BluetoothProfile.LE_AUDIO_BROADCAST:
+ // shouldn't be received here as profile doesn't involve BtHelper
+ Log.e(TAG, "onBtProfileDisconnected: Not a profile handled by BtHelper "
+ + BluetoothProfile.getProfileName(profile));
+ break;
+
+ default:
+ // Not a valid profile to disconnect
+ Log.e(TAG, "onBtProfileDisconnected: Not a valid profile to disconnect "
+ + BluetoothProfile.getProfileName(profile));
+ break;
+ }
+ }
+
+ @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void onBtProfileConnected(int profile, BluetoothProfile proxy) {
if (profile == BluetoothProfile.HEADSET) {
onHeadsetProfileConnected((BluetoothHeadset) proxy);
@@ -671,7 +717,6 @@
public void onServiceConnected(int profile, BluetoothProfile proxy) {
switch(profile) {
case BluetoothProfile.A2DP:
- case BluetoothProfile.A2DP_SINK:
case BluetoothProfile.HEADSET:
case BluetoothProfile.HEARING_AID:
case BluetoothProfile.LE_AUDIO:
@@ -681,6 +726,10 @@
mDeviceBroker.postBtProfileConnected(profile, proxy);
break;
+ case BluetoothProfile.A2DP_SINK:
+ // no A2DP sink functionality handled by BtHelper
+ case BluetoothProfile.LE_AUDIO_BROADCAST:
+ // no broadcast functionality handled by BtHelper
default:
break;
}
@@ -689,14 +738,19 @@
switch (profile) {
case BluetoothProfile.A2DP:
- case BluetoothProfile.A2DP_SINK:
case BluetoothProfile.HEADSET:
case BluetoothProfile.HEARING_AID:
case BluetoothProfile.LE_AUDIO:
- case BluetoothProfile.LE_AUDIO_BROADCAST:
+ AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+ "BT profile service: disconnecting "
+ + BluetoothProfile.getProfileName(profile) + " profile"));
mDeviceBroker.postBtProfileDisconnected(profile);
break;
+ case BluetoothProfile.A2DP_SINK:
+ // no A2DP sink functionality handled by BtHelper
+ case BluetoothProfile.LE_AUDIO_BROADCAST:
+ // no broadcast functionality handled by BtHelper
default:
break;
}
diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java
index 748c4ce5..4e8e704 100644
--- a/services/core/java/com/android/server/audio/SoundDoseHelper.java
+++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java
@@ -108,6 +108,8 @@
private static final String PERSIST_CSD_RECORD_SEPARATOR_CHAR = "|";
private static final String PERSIST_CSD_RECORD_SEPARATOR = "\\|";
+ private static final long GLOBAL_TIME_OFFSET_UNINITIALIZED = -1;
+
private final EventLogger mLogger = new EventLogger(AudioService.LOG_NB_EVENTS_SOUND_DOSE,
"CSD updates");
@@ -168,6 +170,11 @@
@GuardedBy("mCsdStateLock")
private final List<SoundDoseRecord> mDoseRecords = new ArrayList<>();
+ // time in seconds reported by System.currentTimeInMillis used as an offset to convert between
+ // boot time and global time
+ @GuardedBy("mCsdStateLock")
+ private long mGlobalTimeOffsetInSecs = GLOBAL_TIME_OFFSET_UNINITIALIZED;
+
private final Context mContext;
private final ISoundDoseCallback.Stub mSoundDoseCallback = new ISoundDoseCallback.Stub() {
@@ -590,16 +597,24 @@
Log.v(TAG, "Initializing sound dose");
synchronized (mCsdStateLock) {
+ if (mGlobalTimeOffsetInSecs == GLOBAL_TIME_OFFSET_UNINITIALIZED) {
+ mGlobalTimeOffsetInSecs = System.currentTimeMillis() / 1000L;
+ }
+
+ float prevCsd = mCurrentCsd;
// Restore persisted values
mCurrentCsd = parseGlobalSettingFloat(
Settings.Global.AUDIO_SAFE_CSD_CURRENT_VALUE, /* defaultValue= */0.f);
- mNextCsdWarning = parseGlobalSettingFloat(
- Settings.Global.AUDIO_SAFE_CSD_NEXT_WARNING, /* defaultValue= */1.f);
- final List<SoundDoseRecord> records = persistedStringToRecordList(
- mSettings.getGlobalString(mAudioService.getContentResolver(),
- Settings.Global.AUDIO_SAFE_CSD_DOSE_RECORDS));
- if (records != null) {
- mDoseRecords.addAll(records);
+ if (mCurrentCsd != prevCsd) {
+ mNextCsdWarning = parseGlobalSettingFloat(
+ Settings.Global.AUDIO_SAFE_CSD_NEXT_WARNING, /* defaultValue= */1.f);
+ final List<SoundDoseRecord> records = persistedStringToRecordList(
+ mSettings.getGlobalString(mAudioService.getContentResolver(),
+ Settings.Global.AUDIO_SAFE_CSD_DOSE_RECORDS),
+ mGlobalTimeOffsetInSecs);
+ if (records != null) {
+ mDoseRecords.addAll(records);
+ }
}
}
@@ -774,8 +789,13 @@
mLogger.enqueue(SoundDoseEvent.getDoseUpdateEvent(currentCsd, totalDuration));
}
+ @SuppressWarnings("GuardedBy") // avoid limitation with intra-procedural analysis of lambdas
private void onPersistSoundDoseRecords() {
synchronized (mCsdStateLock) {
+ if (mGlobalTimeOffsetInSecs == GLOBAL_TIME_OFFSET_UNINITIALIZED) {
+ mGlobalTimeOffsetInSecs = System.currentTimeMillis() / 1000L;
+ }
+
mSettings.putGlobalString(mAudioService.getContentResolver(),
Settings.Global.AUDIO_SAFE_CSD_CURRENT_VALUE,
Float.toString(mCurrentCsd));
@@ -785,28 +805,41 @@
mSettings.putGlobalString(mAudioService.getContentResolver(),
Settings.Global.AUDIO_SAFE_CSD_DOSE_RECORDS,
mDoseRecords.stream().map(
- SoundDoseHelper::recordToPersistedString).collect(
+ record -> SoundDoseHelper.recordToPersistedString(record,
+ mGlobalTimeOffsetInSecs)).collect(
Collectors.joining(PERSIST_CSD_RECORD_SEPARATOR_CHAR)));
}
}
- private static String recordToPersistedString(SoundDoseRecord record) {
- return record.timestamp
+ private static String recordToPersistedString(SoundDoseRecord record,
+ long globalTimeOffsetInSecs) {
+ return convertToGlobalTime(record.timestamp, globalTimeOffsetInSecs)
+ PERSIST_CSD_RECORD_FIELD_SEPARATOR + record.duration
+ PERSIST_CSD_RECORD_FIELD_SEPARATOR + record.value
+ PERSIST_CSD_RECORD_FIELD_SEPARATOR + record.averageMel;
}
- private static List<SoundDoseRecord> persistedStringToRecordList(String records) {
+ private static long convertToGlobalTime(long bootTimeInSecs, long globalTimeOffsetInSecs) {
+ return bootTimeInSecs + globalTimeOffsetInSecs;
+ }
+
+ private static long convertToBootTime(long globalTimeInSecs, long globalTimeOffsetInSecs) {
+ return globalTimeInSecs - globalTimeOffsetInSecs;
+ }
+
+ private static List<SoundDoseRecord> persistedStringToRecordList(String records,
+ long globalTimeOffsetInSecs) {
if (records == null || records.isEmpty()) {
return null;
}
return Arrays.stream(TextUtils.split(records, PERSIST_CSD_RECORD_SEPARATOR)).map(
- SoundDoseHelper::persistedStringToRecord).filter(Objects::nonNull).collect(
+ record -> SoundDoseHelper.persistedStringToRecord(record,
+ globalTimeOffsetInSecs)).filter(Objects::nonNull).collect(
Collectors.toList());
}
- private static SoundDoseRecord persistedStringToRecord(String record) {
+ private static SoundDoseRecord persistedStringToRecord(String record,
+ long globalTimeOffsetInSecs) {
if (record == null || record.isEmpty()) {
return null;
}
@@ -818,7 +851,8 @@
final SoundDoseRecord sdRecord = new SoundDoseRecord();
try {
- sdRecord.timestamp = Long.parseLong(fields[0]);
+ sdRecord.timestamp = convertToBootTime(Long.parseLong(fields[0]),
+ globalTimeOffsetInSecs);
sdRecord.duration = Integer.parseInt(fields[1]);
sdRecord.value = Float.parseFloat(fields[2]);
sdRecord.averageMel = Float.parseFloat(fields[3]);
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 850f6da..81e550e 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -466,6 +466,8 @@
private boolean mIsDocked;
private boolean mIsDreaming;
+ private boolean mBootCompleted = false;
+
private final BroadcastReceiver mIdleModeReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -602,6 +604,12 @@
}
}
} else if (phase == PHASE_BOOT_COMPLETED) {
+ synchronized (mSyncRoot) {
+ mBootCompleted = true;
+ for (int i = 0; i < mDisplayPowerControllers.size(); i++) {
+ mDisplayPowerControllers.valueAt(i).onBootCompleted();
+ }
+ }
mDisplayModeDirector.onBootCompleted();
mLogicalDisplayMapper.onBootCompleted();
}
@@ -2998,12 +3006,12 @@
displayPowerController = new DisplayPowerController2(
mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
- () -> handleBrightnessChange(display), hbmMetadata);
+ () -> handleBrightnessChange(display), hbmMetadata, mBootCompleted);
} else {
displayPowerController = new DisplayPowerController(
mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
- () -> handleBrightnessChange(display), hbmMetadata);
+ () -> handleBrightnessChange(display), hbmMetadata, mBootCompleted);
}
mDisplayPowerControllers.append(display.getDisplayIdLocked(), displayPowerController);
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 82e6e30..40c879a 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -139,6 +139,7 @@
private static final int MSG_BRIGHTNESS_RAMP_DONE = 12;
private static final int MSG_STATSD_HBM_BRIGHTNESS = 13;
private static final int MSG_SWITCH_USER = 14;
+ private static final int MSG_BOOT_COMPLETED = 15;
private static final int PROXIMITY_UNKNOWN = -1;
private static final int PROXIMITY_NEGATIVE = 0;
@@ -518,6 +519,8 @@
private final SparseArray<DisplayPowerControllerInterface> mDisplayBrightnessFollowers =
new SparseArray<>();
+ private boolean mBootCompleted;
+
/**
* Creates the display power controller.
*/
@@ -525,7 +528,8 @@
DisplayPowerCallbacks callbacks, Handler handler,
SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay,
BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting,
- Runnable onBrightnessChangeRunnable, HighBrightnessModeMetadata hbmMetadata) {
+ Runnable onBrightnessChangeRunnable, HighBrightnessModeMetadata hbmMetadata,
+ boolean bootCompleted) {
mInjector = injector != null ? injector : new Injector();
mClock = mInjector.getClock();
@@ -662,6 +666,7 @@
mTemporaryAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT;
mPendingAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ mBootCompleted = bootCompleted;
}
private void applyReduceBrightColorsSplineAdjustment() {
@@ -1448,7 +1453,7 @@
// Initialize things the first time the power state is changed.
if (mustInitialize) {
- initialize(state);
+ initialize(readyToUpdateDisplayState() ? state : Display.STATE_UNKNOWN);
}
// Animate the screen state change unless already animating.
@@ -2154,7 +2159,8 @@
}
}
- if (!reportOnly && mPowerState.getScreenState() != state) {
+ if (!reportOnly && mPowerState.getScreenState() != state
+ && readyToUpdateDisplayState()) {
Trace.traceCounter(Trace.TRACE_TAG_POWER, "ScreenState", state);
// TODO(b/153319140) remove when we can get this from the above trace invocation
SystemProperties.set("debug.tracing.screen_state", String.valueOf(state));
@@ -2567,6 +2573,12 @@
mBrightnessSetting.setBrightness(brightnessValue);
}
+ @Override
+ public void onBootCompleted() {
+ Message msg = mHandler.obtainMessage(MSG_BOOT_COMPLETED);
+ mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
+ }
+
private void updateScreenBrightnessSetting(float brightnessValue) {
if (!isValidBrightnessValue(brightnessValue)
|| brightnessValue == mCurrentScreenBrightnessSetting) {
@@ -2712,6 +2724,17 @@
}
};
+ /**
+ * Indicates whether the display state is ready to update. If this is the default display, we
+ * want to update it right away so that we can draw the boot animation on it. If it is not
+ * the default display, drawing the boot animation on it would look incorrect, so we need
+ * to wait until boot is completed.
+ * @return True if the display state is ready to update
+ */
+ private boolean readyToUpdateDisplayState() {
+ return mDisplayId == Display.DEFAULT_DISPLAY || mBootCompleted;
+ }
+
@Override
public void dump(final PrintWriter pw) {
synchronized (mLock) {
@@ -3235,6 +3258,11 @@
case MSG_SWITCH_USER:
handleOnSwitchUser(msg.arg1);
break;
+
+ case MSG_BOOT_COMPLETED:
+ mBootCompleted = true;
+ updatePowerState();
+ break;
}
}
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 0861a37..5ea8aaa 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -137,6 +137,7 @@
private static final int MSG_BRIGHTNESS_RAMP_DONE = 10;
private static final int MSG_STATSD_HBM_BRIGHTNESS = 11;
private static final int MSG_SWITCH_USER = 12;
+ private static final int MSG_BOOT_COMPLETED = 13;
private static final int BRIGHTNESS_CHANGE_STATSD_REPORT_INTERVAL_MS = 500;
@@ -425,6 +426,8 @@
private SparseArray<DisplayPowerControllerInterface> mDisplayBrightnessFollowers =
new SparseArray();
+ private boolean mBootCompleted;
+
/**
* Creates the display power controller.
*/
@@ -432,7 +435,8 @@
DisplayPowerCallbacks callbacks, Handler handler,
SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay,
BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting,
- Runnable onBrightnessChangeRunnable, HighBrightnessModeMetadata hbmMetadata) {
+ Runnable onBrightnessChangeRunnable, HighBrightnessModeMetadata hbmMetadata,
+ boolean bootCompleted) {
mInjector = injector != null ? injector : new Injector();
mClock = mInjector.getClock();
@@ -555,6 +559,7 @@
mTemporaryAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT;
mPendingAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ mBootCompleted = bootCompleted;
}
private void applyReduceBrightColorsSplineAdjustment() {
@@ -1190,7 +1195,7 @@
// Initialize things the first time the power state is changed.
if (mustInitialize) {
- initialize(state);
+ initialize(readyToUpdateDisplayState() ? state : Display.STATE_UNKNOWN);
}
// Animate the screen state change unless already animating.
@@ -1720,6 +1725,12 @@
}
}
+ @Override
+ public void onBootCompleted() {
+ Message msg = mHandler.obtainMessage(MSG_BOOT_COMPLETED);
+ mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
+ }
+
private boolean saveBrightnessInfo(float brightness) {
return saveBrightnessInfo(brightness, brightness);
}
@@ -1861,7 +1872,8 @@
}
}
- if (!reportOnly && mPowerState.getScreenState() != state) {
+ if (!reportOnly && mPowerState.getScreenState() != state
+ && readyToUpdateDisplayState()) {
Trace.traceCounter(Trace.TRACE_TAG_POWER, "ScreenState", state);
// TODO(b/153319140) remove when we can get this from the above trace invocation
SystemProperties.set("debug.tracing.screen_state", String.valueOf(state));
@@ -2519,6 +2531,17 @@
}
}
+ /**
+ * Indicates whether the display state is ready to update. If this is the default display, we
+ * want to update it right away so that we can draw the boot animation on it. If it is not
+ * the default display, drawing the boot animation on it would look incorrect, so we need
+ * to wait until boot is completed.
+ * @return True if the display state is ready to update
+ */
+ private boolean readyToUpdateDisplayState() {
+ return mDisplayId == Display.DEFAULT_DISPLAY || mBootCompleted;
+ }
+
// Return bucket index of range_[left]_[right] where
// left <= nits < right
private int nitsToRangeIndex(float nits) {
@@ -2738,6 +2761,11 @@
case MSG_SWITCH_USER:
handleOnSwitchUser(msg.arg1);
break;
+
+ case MSG_BOOT_COMPLETED:
+ mBootCompleted = true;
+ updatePowerState();
+ break;
}
}
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
index 0bc8154..73edb97 100644
--- a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
+++ b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
@@ -206,4 +206,9 @@
* @param follower The DPC to remove from the followers list
*/
void removeDisplayBrightnessFollower(DisplayPowerControllerInterface follower);
+
+ /**
+ * Indicate that boot has been completed and the screen is ready to update.
+ */
+ void onBootCompleted();
}
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index a9b9b54..4d134b6 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -16,6 +16,8 @@
package com.android.server.media;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -248,12 +250,17 @@
// Binder call
@Override
- public void showMediaOutputSwitcher(String packageName) {
+ public boolean showMediaOutputSwitcher(String packageName) {
if (!validatePackageName(Binder.getCallingUid(), packageName)) {
throw new SecurityException("packageName must match the calling identity");
}
final long token = Binder.clearCallingIdentity();
try {
+ if (mContext.getSystemService(ActivityManager.class).getPackageImportance(packageName)
+ > IMPORTANCE_FOREGROUND) {
+ Slog.w(TAG, "showMediaOutputSwitcher only works when called from foreground");
+ return false;
+ }
synchronized (mLock) {
StatusBarManagerInternal statusBar =
LocalServices.getService(StatusBarManagerInternal.class);
@@ -262,6 +269,7 @@
} finally {
Binder.restoreCallingIdentity(token);
}
+ return true;
}
// Binder call
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index bba1dbe..a01b2c1 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -8695,6 +8695,9 @@
if (interceptBefore && !record.isIntercepted()
&& record.isNewEnoughForAlerting(System.currentTimeMillis())) {
buzzBeepBlinkLocked(record);
+
+ // Log alert after change in intercepted state to Zen Log as well
+ ZenLog.traceAlertOnUpdatedIntercept(record);
}
}
if (changed) {
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 2ea6c40..1cfcb4e 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -114,6 +114,8 @@
// is this notification currently being intercepted by Zen Mode?
private boolean mIntercept;
+ // has the intercept value been set explicitly? we only want to log it if new or changed
+ private boolean mInterceptSet;
// is this notification hidden since the app pkg is suspended?
private boolean mHidden;
@@ -929,6 +931,7 @@
public boolean setIntercepted(boolean intercept) {
mIntercept = intercept;
+ mInterceptSet = true;
return mIntercept;
}
@@ -949,6 +952,10 @@
return mIntercept;
}
+ public boolean hasInterceptBeenSet() {
+ return mInterceptSet;
+ }
+
public boolean isNewEnoughForAlerting(long now) {
return getFreshnessMs(now) <= MAX_SOUND_DELAY_MS;
}
diff --git a/services/core/java/com/android/server/notification/ZenLog.java b/services/core/java/com/android/server/notification/ZenLog.java
index c0bc474..35b94e7 100644
--- a/services/core/java/com/android/server/notification/ZenLog.java
+++ b/services/core/java/com/android/server/notification/ZenLog.java
@@ -68,20 +68,23 @@
private static final int TYPE_MATCHES_CALL_FILTER = 18;
private static final int TYPE_RECORD_CALLER = 19;
private static final int TYPE_CHECK_REPEAT_CALLER = 20;
+ private static final int TYPE_ALERT_ON_UPDATED_INTERCEPT = 21;
private static int sNext;
private static int sSize;
public static void traceIntercepted(NotificationRecord record, String reason) {
- if (record != null && record.isIntercepted()) return; // already logged
append(TYPE_INTERCEPTED, record.getKey() + "," + reason);
}
public static void traceNotIntercepted(NotificationRecord record, String reason) {
- if (record != null && record.isUpdate) return; // already logged
append(TYPE_NOT_INTERCEPTED, record.getKey() + "," + reason);
}
+ public static void traceAlertOnUpdatedIntercept(NotificationRecord record) {
+ append(TYPE_ALERT_ON_UPDATED_INTERCEPT, record.getKey());
+ }
+
public static void traceSetRingerModeExternal(int ringerModeOld, int ringerModeNew,
String caller, int ringerModeInternalIn, int ringerModeInternalOut) {
append(TYPE_SET_RINGER_MODE_EXTERNAL, caller + ",e:" +
@@ -219,6 +222,7 @@
case TYPE_MATCHES_CALL_FILTER: return "matches_call_filter";
case TYPE_RECORD_CALLER: return "record_caller";
case TYPE_CHECK_REPEAT_CALLER: return "check_repeat_caller";
+ case TYPE_ALERT_ON_UPDATED_INTERCEPT: return "alert_on_updated_intercept";
default: return "unknown";
}
}
diff --git a/services/core/java/com/android/server/notification/ZenModeFiltering.java b/services/core/java/com/android/server/notification/ZenModeFiltering.java
index db0ce2e..5b7b0c1 100644
--- a/services/core/java/com/android/server/notification/ZenModeFiltering.java
+++ b/services/core/java/com/android/server/notification/ZenModeFiltering.java
@@ -155,85 +155,85 @@
if (isCritical(record)) {
// Zen mode is ignored for critical notifications.
- ZenLog.traceNotIntercepted(record, "criticalNotification");
+ maybeLogInterceptDecision(record, false, "criticalNotification");
return false;
}
// Make an exception to policy for the notification saying that policy has changed
if (NotificationManager.Policy.areAllVisualEffectsSuppressed(policy.suppressedVisualEffects)
&& "android".equals(record.getSbn().getPackageName())
&& SystemMessageProto.SystemMessage.NOTE_ZEN_UPGRADE == record.getSbn().getId()) {
- ZenLog.traceNotIntercepted(record, "systemDndChangedNotification");
+ maybeLogInterceptDecision(record, false, "systemDndChangedNotification");
return false;
}
switch (zen) {
case Global.ZEN_MODE_NO_INTERRUPTIONS:
// #notevenalarms
- ZenLog.traceIntercepted(record, "none");
+ maybeLogInterceptDecision(record, true, "none");
return true;
case Global.ZEN_MODE_ALARMS:
if (isAlarm(record)) {
// Alarms only
- ZenLog.traceNotIntercepted(record, "alarm");
+ maybeLogInterceptDecision(record, false, "alarm");
return false;
}
- ZenLog.traceIntercepted(record, "alarmsOnly");
+ maybeLogInterceptDecision(record, true, "alarmsOnly");
return true;
case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
// allow user-prioritized packages through in priority mode
if (record.getPackagePriority() == Notification.PRIORITY_MAX) {
- ZenLog.traceNotIntercepted(record, "priorityApp");
+ maybeLogInterceptDecision(record, false, "priorityApp");
return false;
}
if (isAlarm(record)) {
if (!policy.allowAlarms()) {
- ZenLog.traceIntercepted(record, "!allowAlarms");
+ maybeLogInterceptDecision(record, true, "!allowAlarms");
return true;
}
- ZenLog.traceNotIntercepted(record, "allowedAlarm");
+ maybeLogInterceptDecision(record, false, "allowedAlarm");
return false;
}
if (isEvent(record)) {
if (!policy.allowEvents()) {
- ZenLog.traceIntercepted(record, "!allowEvents");
+ maybeLogInterceptDecision(record, true, "!allowEvents");
return true;
}
- ZenLog.traceNotIntercepted(record, "allowedEvent");
+ maybeLogInterceptDecision(record, false, "allowedEvent");
return false;
}
if (isReminder(record)) {
if (!policy.allowReminders()) {
- ZenLog.traceIntercepted(record, "!allowReminders");
+ maybeLogInterceptDecision(record, true, "!allowReminders");
return true;
}
- ZenLog.traceNotIntercepted(record, "allowedReminder");
+ maybeLogInterceptDecision(record, false, "allowedReminder");
return false;
}
if (isMedia(record)) {
if (!policy.allowMedia()) {
- ZenLog.traceIntercepted(record, "!allowMedia");
+ maybeLogInterceptDecision(record, true, "!allowMedia");
return true;
}
- ZenLog.traceNotIntercepted(record, "allowedMedia");
+ maybeLogInterceptDecision(record, false, "allowedMedia");
return false;
}
if (isSystem(record)) {
if (!policy.allowSystem()) {
- ZenLog.traceIntercepted(record, "!allowSystem");
+ maybeLogInterceptDecision(record, true, "!allowSystem");
return true;
}
- ZenLog.traceNotIntercepted(record, "allowedSystem");
+ maybeLogInterceptDecision(record, false, "allowedSystem");
return false;
}
if (isConversation(record)) {
if (policy.allowConversations()) {
if (policy.priorityConversationSenders == CONVERSATION_SENDERS_ANYONE) {
- ZenLog.traceNotIntercepted(record, "conversationAnyone");
+ maybeLogInterceptDecision(record, false, "conversationAnyone");
return false;
} else if (policy.priorityConversationSenders
== NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT
&& record.getChannel().isImportantConversation()) {
- ZenLog.traceNotIntercepted(record, "conversationMatches");
+ maybeLogInterceptDecision(record, false, "conversationMatches");
return false;
}
}
@@ -244,31 +244,59 @@
if (policy.allowRepeatCallers()
&& REPEAT_CALLERS.isRepeat(
mContext, extras(record), record.getPhoneNumbers())) {
- ZenLog.traceNotIntercepted(record, "repeatCaller");
+ maybeLogInterceptDecision(record, false, "repeatCaller");
return false;
}
if (!policy.allowCalls()) {
- ZenLog.traceIntercepted(record, "!allowCalls");
+ maybeLogInterceptDecision(record, true, "!allowCalls");
return true;
}
return shouldInterceptAudience(policy.allowCallsFrom(), record);
}
if (isMessage(record)) {
if (!policy.allowMessages()) {
- ZenLog.traceIntercepted(record, "!allowMessages");
+ maybeLogInterceptDecision(record, true, "!allowMessages");
return true;
}
return shouldInterceptAudience(policy.allowMessagesFrom(), record);
}
- ZenLog.traceIntercepted(record, "!priority");
+ maybeLogInterceptDecision(record, true, "!priority");
return true;
default:
- ZenLog.traceNotIntercepted(record, "unknownZenMode");
+ maybeLogInterceptDecision(record, false, "unknownZenMode");
return false;
}
}
+ // Consider logging the decision of shouldIntercept for the given record.
+ // This will log the outcome if one of the following is true:
+ // - it's the first time the intercept decision is set for the record
+ // - OR it's not the first time, but the intercept decision changed
+ private static void maybeLogInterceptDecision(NotificationRecord record, boolean intercept,
+ String reason) {
+ boolean interceptBefore = record.isIntercepted();
+ if (record.hasInterceptBeenSet() && (interceptBefore == intercept)) {
+ // this record has already been evaluated for whether it should be intercepted, and
+ // the decision has not changed.
+ return;
+ }
+
+ // add a note to the reason indicating whether it's new or updated
+ String annotatedReason = reason;
+ if (!record.hasInterceptBeenSet()) {
+ annotatedReason = "new:" + reason;
+ } else if (interceptBefore != intercept) {
+ annotatedReason = "updated:" + reason;
+ }
+
+ if (intercept) {
+ ZenLog.traceIntercepted(record, annotatedReason);
+ } else {
+ ZenLog.traceNotIntercepted(record, annotatedReason);
+ }
+ }
+
/**
* Check if the notification is too critical to be suppressed.
*
@@ -285,10 +313,10 @@
private static boolean shouldInterceptAudience(int source, NotificationRecord record) {
float affinity = record.getContactAffinity();
if (!audienceMatches(source, affinity)) {
- ZenLog.traceIntercepted(record, "!audienceMatches,affinity=" + affinity);
+ maybeLogInterceptDecision(record, true, "!audienceMatches,affinity=" + affinity);
return true;
}
- ZenLog.traceNotIntercepted(record, "affinity=" + affinity);
+ maybeLogInterceptDecision(record, false, "affinity=" + affinity);
return false;
}
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 7a95987..01e9522 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -1365,6 +1365,20 @@
}
}
+ public void setAllowCrossUidActivitySwitchFromBelow(IBinder token, boolean allowed) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
+ if (r != null) {
+ r.setAllowCrossUidActivitySwitchFromBelow(allowed);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
@Override
public void reportActivityFullyDrawn(IBinder token, boolean restoredFromBundle) {
final long origId = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 828848b..89b7968 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -314,6 +314,7 @@
import android.util.EventLog;
import android.util.Log;
import android.util.MergedConfiguration;
+import android.util.Pair;
import android.util.Slog;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
@@ -701,6 +702,11 @@
private boolean mInheritShownWhenLocked;
private boolean mTurnScreenOn;
+ /** Allow activity launches which would otherwise be blocked by
+ * {@link ActivityTransitionSecurityController#checkActivityAllowedToStart}
+ */
+ private boolean mAllowCrossUidActivitySwitchFromBelow;
+
/** Have we been asked to have this token keep the screen frozen? */
private boolean mFreezingScreen;
@@ -2153,7 +2159,7 @@
boolean appActivityEmbeddingEnabled = false;
try {
- appActivityEmbeddingEnabled = WindowManagerService.sWindowExtensionsEnabled
+ appActivityEmbeddingEnabled = WindowManager.hasWindowExtensionsEnabled()
&& mAtmService.mContext.getPackageManager()
.getProperty(PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED, packageName)
.getBoolean();
@@ -9836,6 +9842,54 @@
mTurnScreenOn = turnScreenOn;
}
+ void setAllowCrossUidActivitySwitchFromBelow(boolean allowed) {
+ mAllowCrossUidActivitySwitchFromBelow = allowed;
+ }
+
+ /**
+ * Determines if a source is allowed to add or remove activities from the task,
+ * if the current ActivityRecord is above it in the stack
+ *
+ * A transition is blocked ({@code false} returned) if all of the following are met:
+ * <pre>
+ * 1. The source activity and the current activity record belong to different apps
+ * (i.e, have different UIDs).
+ * 2. Both the source activity and the current activity target U+
+ * 3. The current activity has not set
+ * {@link ActivityRecord#setAllowCrossUidActivitySwitchFromBelow(boolean)} to {@code true}
+ * </pre>
+ *
+ * Returns a pair where the elements mean:
+ * <pre>
+ * First: {@code false} if we should actually block the transition (takes into consideration
+ * feature flag and targetSdk).
+ * Second: {@code false} if we should warn about the transition via toasts. This happens if
+ * the transition would be blocked in case both the app was targeting U+ and the feature was
+ * enabled.
+ * </pre>
+ *
+ * @param sourceUid The source (s) activity performing the state change
+ */
+ Pair<Boolean, Boolean> allowCrossUidActivitySwitchFromBelow(int sourceUid) {
+ int myUid = info.applicationInfo.uid;
+ if (sourceUid == myUid) {
+ return new Pair<>(true, true);
+ }
+
+ // If mAllowCrossUidActivitySwitchFromBelow is set, honor it.
+ if (mAllowCrossUidActivitySwitchFromBelow) {
+ return new Pair<>(true, true);
+ }
+
+ // If it is not set, default to true if both records target ≥ U, false otherwise
+ // TODO(b/258792202) Replace with CompatChanges and replace Pair with boolean once feature
+ // flag is removed
+ boolean restrictActivitySwitch =
+ ActivitySecurityModelFeatureFlags.shouldRestrictActivitySwitch(myUid)
+ && ActivitySecurityModelFeatureFlags.shouldRestrictActivitySwitch(sourceUid);
+ return new Pair<>(!restrictActivitySwitch, false);
+ }
+
boolean getTurnScreenOnFlag() {
return mTurnScreenOn || containsTurnScreenOnWindow();
}
@@ -9944,7 +9998,8 @@
super.dumpDebug(proto, WINDOW_TOKEN, logLevel);
proto.write(LAST_SURFACE_SHOWING, mLastSurfaceShowing);
proto.write(IS_WAITING_FOR_TRANSITION_START, isWaitingForTransitionStart());
- proto.write(IS_ANIMATING, isAnimating(PARENTS, ANIMATION_TYPE_APP_TRANSITION));
+ proto.write(IS_ANIMATING, isAnimating(TRANSITION | PARENTS | CHILDREN,
+ ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_WINDOW_ANIMATION));
if (mThumbnail != null){
mThumbnail.dumpDebug(proto, THUMBNAIL);
}
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 1d5fcd7..32dac49 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -125,6 +125,7 @@
import android.os.UserManager;
import android.service.voice.IVoiceInteractionSession;
import android.text.TextUtils;
+import android.util.Pair;
import android.util.Pools.SynchronizedPool;
import android.util.Slog;
import android.widget.Toast;
@@ -1908,36 +1909,21 @@
* Log activity starts which violate one of the following rules of the
* activity security model (ASM):
* See go/activity-security for rationale behind the rules.
- * We don't currently block, but these checks may later become blocks
* 1. Within a task, only an activity matching a top UID of the task can start activities
* 2. Only activities within a foreground task, which match a top UID of the task, can
* create a new task or bring an existing one into the foreground
*/
private boolean checkActivitySecurityModel(ActivityRecord r, boolean newTask, Task targetTask) {
+ // BAL Exception allowed in all cases
+ if (mBalCode == BAL_ALLOW_ALLOWLISTED_UID) {
+ return true;
+ }
+
// Intents with FLAG_ACTIVITY_NEW_TASK will always be considered as creating a new task
// even if the intent is delivered to an existing task.
boolean taskToFront = newTask
|| (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == FLAG_ACTIVITY_NEW_TASK;
- if (mSourceRecord != null) {
- boolean passesAsmChecks = true;
- Task sourceTask = mSourceRecord.getTask();
-
- // Don't allow launches into a new task if the current task is not foreground.
- if (taskToFront) {
- passesAsmChecks = sourceTask != null && sourceTask.isVisible();
- }
-
- Task taskToCheck = taskToFront ? sourceTask : targetTask;
- passesAsmChecks = passesAsmChecks && ActivityTaskSupervisor
- .doesTopActivityMatchingUidExistForAsm(taskToCheck, mSourceRecord.getUid(),
- mSourceRecord);
-
- if (passesAsmChecks) {
- return true;
- }
- }
-
// BAL exception only allowed for new tasks
if (taskToFront) {
if (mBalCode == BAL_ALLOW_ALLOWLISTED_COMPONENT
@@ -1949,9 +1935,34 @@
}
}
- // BAL Exception allowed in all cases
- if (mBalCode == BAL_ALLOW_ALLOWLISTED_UID) {
- return true;
+ boolean shouldBlockActivityStart = true;
+ // Used for logging/toasts. Would we block the start if target sdk was U and feature was
+ // enabled?
+ boolean wouldBlockActivityStartIgnoringFlags = true;
+
+ if (mSourceRecord != null) {
+ boolean passesAsmChecks = true;
+ Task sourceTask = mSourceRecord.getTask();
+
+ // Don't allow launches into a new task if the current task is not foreground.
+ if (taskToFront) {
+ passesAsmChecks = sourceTask != null && sourceTask.isVisible();
+ }
+
+ if (passesAsmChecks) {
+ Task taskToCheck = taskToFront ? sourceTask : targetTask;
+ // first == false means Should Block
+ // second == false means Would Block disregarding flags
+ Pair<Boolean, Boolean> pair = ActivityTaskSupervisor
+ .doesTopActivityMatchingUidExistForAsm(taskToCheck, mSourceRecord.getUid(),
+ mSourceRecord);
+ shouldBlockActivityStart = !pair.first;
+ wouldBlockActivityStartIgnoringFlags = !pair.second;
+ }
+
+ if (!wouldBlockActivityStartIgnoringFlags) {
+ return true;
+ }
}
// ASM rules have failed. Log why
@@ -1996,19 +2007,20 @@
mBalCode
);
- boolean shouldBlockActivityStart =
- ActivitySecurityModelFeatureFlags.shouldRestrictActivitySwitch(mCallingUid);
+ boolean blockActivityStartAndFeatureEnabled = ActivitySecurityModelFeatureFlags
+ .shouldRestrictActivitySwitch(mCallingUid)
+ && shouldBlockActivityStart;
if (ActivitySecurityModelFeatureFlags.shouldShowToast(mCallingUid)) {
UiThread.getHandler().post(() -> Toast.makeText(mService.mContext,
"Activity start from " + r.launchedFromPackage
- + (shouldBlockActivityStart ? " " : " would be ")
+ + (blockActivityStartAndFeatureEnabled ? " " : " would be ")
+ "blocked by " + ActivitySecurityModelFeatureFlags.DOC_LINK,
Toast.LENGTH_SHORT).show());
}
- if (shouldBlockActivityStart) {
+ if (blockActivityStartAndFeatureEnabled) {
Slog.e(TAG, "Abort Launching r: " + r
+ " as source: "
+ (mSourceRecord != null ? mSourceRecord : r.launchedFromPackage)
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 3876290..ef47b6e 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -133,6 +133,7 @@
import android.provider.MediaStore;
import android.util.ArrayMap;
import android.util.MergedConfiguration;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
@@ -1635,14 +1636,18 @@
// Prevent recursion.
return;
}
- boolean passesAsmChecks = true;
+ boolean shouldBlockActivitySwitchIfFeatureEnabled = false;
+ boolean wouldBlockActivitySwitchIgnoringFlags = false;
// We may have already checked that the callingUid has additional clearTask privileges, and
// cleared the calling identify. If so, we infer we do not need further restrictions here.
// TODO(b/263368846) Move to live with the rest of the ASM logic.
if (callingUid != SYSTEM_UID) {
- passesAsmChecks = doesTopActivityMatchingUidExistForAsm(task, callingUid,
+ Pair<Boolean, Boolean> pair = doesTopActivityMatchingUidExistForAsm(task,
+ callingUid,
null);
- if (!passesAsmChecks) {
+ shouldBlockActivitySwitchIfFeatureEnabled = !pair.first;
+ wouldBlockActivitySwitchIgnoringFlags = !pair.second;
+ if (wouldBlockActivitySwitchIgnoringFlags) {
ActivityRecord topActivity = task.getActivity(ar ->
!ar.isState(FINISHING) && !ar.isAlwaysOnTop());
FrameworkStatsLog.write(FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED,
@@ -1685,13 +1690,13 @@
if (task.isPersistable) {
mService.notifyTaskPersisterLocked(null, true);
}
- if (!passesAsmChecks) {
- boolean shouldRestrictActivitySwitch =
- ActivitySecurityModelFeatureFlags.shouldRestrictActivitySwitch(callingUid);
-
+ if (wouldBlockActivitySwitchIgnoringFlags) {
+ boolean restrictActivitySwitch = ActivitySecurityModelFeatureFlags
+ .shouldRestrictActivitySwitch(callingUid)
+ && shouldBlockActivitySwitchIfFeatureEnabled;
if (ActivitySecurityModelFeatureFlags.shouldShowToast(callingUid)) {
UiThread.getHandler().post(() -> Toast.makeText(mService.mContext,
- (shouldRestrictActivitySwitch
+ (restrictActivitySwitch
? "Returning home due to "
: "Would return home due to ")
+ ActivitySecurityModelFeatureFlags.DOC_LINK,
@@ -1701,7 +1706,7 @@
// If the activity switch should be restricted, return home rather than the
// previously top task, to prevent users from being confused which app they're
// viewing
- if (shouldRestrictActivitySwitch) {
+ if (restrictActivitySwitch) {
Slog.w(TAG, "Return to home as source uid: " + callingUid
+ "is not on top of task t: " + task);
task.getTaskDisplayArea().moveHomeActivityToTop("taskRemoved");
@@ -1721,14 +1726,18 @@
*
* The 'sourceRecord' can be considered top even if it is 'finishing'
*
- * TODO(b/263368846) Shift to BackgroundActivityStartController once class is ready
+ * @return A pair where the first value is the return value matching the checks above, and the
+ * second value is the return value disregarding the feature flag or target api levels. Use the
+ * first value for blocking launches - the second value is only used to determine if a toast
+ * should be displayed, and will be used alongside a feature flag in {@link ActivityStarter}.
*/
+ // TODO(b/263368846) Shift to BackgroundActivityStartController once class is ready
@Nullable
- static boolean doesTopActivityMatchingUidExistForAsm(@Nullable Task task,
+ static Pair<Boolean, Boolean> doesTopActivityMatchingUidExistForAsm(@Nullable Task task,
int uid, @Nullable ActivityRecord sourceRecord) {
// If the source is visible, consider it 'top'.
if (sourceRecord != null && sourceRecord.isVisible()) {
- return true;
+ return new Pair<>(true, true);
}
// Consider the source activity, whether or not it is finishing. Do not consider any other
@@ -1739,28 +1748,33 @@
// Check top of stack (or the first task fragment for embedding).
ActivityRecord topActivity = task.getActivity(topOfStackPredicate);
if (topActivity == null) {
- return false;
+ return new Pair<>(false, false);
}
- if (topActivity.getUid() == uid) {
- return true;
+ Pair<Boolean, Boolean> pair = topActivity.allowCrossUidActivitySwitchFromBelow(uid);
+ if (pair.first) {
+ return new Pair<>(true, pair.second);
}
// Even if the top activity is not a match, we may be in an embedded activity scenario with
// an adjacent task fragment. Get the second fragment.
TaskFragment taskFragment = topActivity.getTaskFragment();
if (taskFragment == null) {
- return false;
+ return new Pair<>(false, false);
}
TaskFragment adjacentTaskFragment = taskFragment.getAdjacentTaskFragment();
if (adjacentTaskFragment == null) {
- return false;
+ return new Pair<>(false, false);
}
// Check the second fragment.
topActivity = adjacentTaskFragment.getActivity(topOfStackPredicate);
- return topActivity != null && topActivity.getUid() == uid;
+ if (topActivity == null) {
+ return new Pair<>(false, false);
+ }
+
+ return topActivity.allowCrossUidActivitySwitchFromBelow(uid);
}
void cleanUpRemovedTaskLocked(Task task, boolean killProcess, boolean removeFromRecents) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d5bf9f9..c30aef4 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -442,16 +442,6 @@
static final boolean ENABLE_FIXED_ROTATION_TRANSFORM =
SystemProperties.getBoolean("persist.wm.fixed_rotation_transform", true);
- /**
- * Whether the device supports the WindowManager Extensions.
- * OEMs can enable this by having their device config to inherit window_extensions.mk, such as:
- * <pre>
- * $(call inherit-product, $(SRC_TARGET_DIR)/product/window_extensions.mk)
- * </pre>
- */
- static final boolean sWindowExtensionsEnabled =
- SystemProperties.getBoolean("persist.wm.extensions.enabled", false);
-
// Enums for animation scale update types.
@Retention(RetentionPolicy.SOURCE)
@IntDef({WINDOW_ANIMATION_SCALE, TRANSITION_ANIMATION_SCALE, ANIMATION_DURATION_SCALE})
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index a23f889..684ede3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -4550,7 +4550,13 @@
@Override
public boolean addCrossProfileWidgetProvider(ComponentName admin, String callerPackageName,
String packageName) {
- final CallerIdentity caller = getCallerIdentity(admin, callerPackageName);
+ CallerIdentity caller;
+
+ if (isPermissionCheckFlagEnabled()) {
+ caller = getCallerIdentity(admin, callerPackageName);
+ } else {
+ caller = getCallerIdentity(admin);
+ }
ActiveAdmin activeAdmin;
if (isPermissionCheckFlagEnabled()) {
@@ -4598,7 +4604,13 @@
@Override
public boolean removeCrossProfileWidgetProvider(ComponentName admin, String callerPackageName,
String packageName) {
- final CallerIdentity caller = getCallerIdentity(admin, callerPackageName);
+ CallerIdentity caller;
+ if (isPermissionCheckFlagEnabled()) {
+ caller = getCallerIdentity(admin, callerPackageName);
+ } else {
+ caller = getCallerIdentity(admin);
+ }
+
ActiveAdmin activeAdmin;
if (isPermissionCheckFlagEnabled()) {
@@ -4646,7 +4658,12 @@
@Override
public List<String> getCrossProfileWidgetProviders(ComponentName admin,
String callerPackageName) {
- final CallerIdentity caller = getCallerIdentity(admin, callerPackageName);
+ CallerIdentity caller;
+ if (isPermissionCheckFlagEnabled()) {
+ caller = getCallerIdentity(admin, callerPackageName);
+ } else {
+ caller = getCallerIdentity(admin);
+ }
ActiveAdmin activeAdmin;
if (isPermissionCheckFlagEnabled()) {
@@ -7367,7 +7384,12 @@
if (!mHasFeature && !hasCallingOrSelfPermission(permission.MASTER_CLEAR)) {
return;
}
- final CallerIdentity caller = getCallerIdentity(callerPackageName);
+ CallerIdentity caller;
+ if (isPermissionCheckFlagEnabled()) {
+ caller = getCallerIdentity(callerPackageName);
+ } else {
+ caller = getCallerIdentity();
+ }
ActiveAdmin admin;
boolean calledByProfileOwnerOnOrgOwnedDevice =
@@ -8647,7 +8669,12 @@
return;
}
- final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+ CallerIdentity caller;
+ if (isPermissionCheckFlagEnabled()) {
+ caller = getCallerIdentity(who, callerPackageName);
+ } else {
+ caller = getCallerIdentity(who);
+ }
if (isPermissionCheckFlagEnabled()) {
// The effect of this policy is device-wide.
@@ -8676,8 +8703,12 @@
if (!mHasFeature) {
return false;
}
-
- final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+ CallerIdentity caller;
+ if (isPermissionCheckFlagEnabled()) {
+ caller = getCallerIdentity(who, callerPackageName);
+ } else {
+ caller = getCallerIdentity(who);
+ }
if (isPermissionCheckFlagEnabled()) {
enforceCanQuery(caller.getPackageName(), SET_TIME, UserHandle.USER_ALL);
@@ -8701,7 +8732,12 @@
return;
}
- final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+ CallerIdentity caller;
+ if (isPermissionCheckFlagEnabled()) {
+ caller = getCallerIdentity(who, callerPackageName);
+ } else {
+ caller = getCallerIdentity(who);
+ }
if (useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
// The effect of this policy is device-wide.
@@ -8742,7 +8778,12 @@
return false;
}
- final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+ CallerIdentity caller;
+ if (isPermissionCheckFlagEnabled()) {
+ caller = getCallerIdentity(who, callerPackageName);
+ } else {
+ caller = getCallerIdentity(who);
+ }
if (isPermissionCheckFlagEnabled()) {
// The effect of this policy is device-wide.
@@ -8966,7 +9007,12 @@
if (!mHasFeature) {
return false;
}
- final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+ CallerIdentity caller;
+ if (isPermissionCheckFlagEnabled()) {
+ caller = getCallerIdentity(who, callerPackageName);
+ } else {
+ caller = getCallerIdentity(who);
+ }
if (isPermissionCheckFlagEnabled()) {
Preconditions.checkCallAuthorization(
hasFullCrossUsersPermission(caller, userHandle)
@@ -10899,7 +10945,12 @@
@Override
public void addPersistentPreferredActivity(ComponentName who, String callerPackageName,
IntentFilter filter, ComponentName activity) {
- final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+ CallerIdentity caller;
+ if (isPermissionCheckFlagEnabled()) {
+ caller = getCallerIdentity(who, callerPackageName);
+ } else {
+ caller = getCallerIdentity(who);
+ }
final int userId = caller.getUserId();
if (useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
@@ -10949,7 +11000,12 @@
@Override
public void clearPackagePersistentPreferredActivities(ComponentName who,
String callerPackageName, String packageName) {
- final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+ CallerIdentity caller;
+ if (isPermissionCheckFlagEnabled()) {
+ caller = getCallerIdentity(who, callerPackageName);
+ } else {
+ caller = getCallerIdentity(who);
+ }
final int userId = caller.getUserId();
if (useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
@@ -11028,7 +11084,13 @@
@Override
public void setDefaultSmsApplication(ComponentName admin, String callerPackageName,
String packageName, boolean parent) {
- final CallerIdentity caller = getCallerIdentity(admin, callerPackageName);
+ CallerIdentity caller;
+ if (isPermissionCheckFlagEnabled()) {
+ caller = getCallerIdentity(admin, callerPackageName);
+ } else {
+ caller = getCallerIdentity(admin);
+ }
+
final int userId;
if (isPermissionCheckFlagEnabled()) {
@@ -11341,7 +11403,12 @@
@Override
public void addCrossProfileIntentFilter(ComponentName who, String callerPackageName,
IntentFilter filter, int flags) {
- final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+ CallerIdentity caller;
+ if (isPermissionCheckFlagEnabled()) {
+ caller = getCallerIdentity(who, callerPackageName);
+ } else {
+ caller = getCallerIdentity(who);
+ }
int callingUserId = caller.getUserId();
if (isPermissionCheckFlagEnabled()) {
@@ -11400,7 +11467,12 @@
@Override
public void clearCrossProfileIntentFilters(ComponentName who, String callerPackageName) {
- final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+ CallerIdentity caller;
+ if (isPermissionCheckFlagEnabled()) {
+ caller = getCallerIdentity(who, callerPackageName);
+ } else {
+ caller = getCallerIdentity(who);
+ }
int callingUserId = caller.getUserId();
if (isPermissionCheckFlagEnabled()) {
@@ -12712,7 +12784,12 @@
ComponentName who, String callerPackage, String key, boolean enabledFromThisOwner,
boolean parent) {
- final CallerIdentity caller = getCallerIdentity(who, callerPackage);
+ CallerIdentity caller;
+ if (isPermissionCheckFlagEnabled()) {
+ caller = getCallerIdentity(who, callerPackage);
+ } else {
+ caller = getCallerIdentity(who);
+ }
int userId = caller.getUserId();
if (!UserRestrictionsUtils.isValidRestriction(key)) {
@@ -12928,8 +13005,12 @@
if (!mHasFeature) {
return null;
}
-
- final CallerIdentity caller = getCallerIdentity(who, callerPackage);
+ CallerIdentity caller;
+ if (isPermissionCheckFlagEnabled()) {
+ caller = getCallerIdentity(who, callerPackage);
+ } else {
+ caller = getCallerIdentity(who);
+ }
if (useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
EnforcingAdmin admin = getEnforcingAdminForCaller(who, callerPackage);
@@ -13406,7 +13487,12 @@
if (!mHasFeature) {
return;
}
- final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+ CallerIdentity caller;
+ if (isPermissionCheckFlagEnabled()) {
+ caller = getCallerIdentity(who, callerPackageName);
+ } else {
+ caller = getCallerIdentity(who);
+ }
synchronized (getLockObject()) {
final ActiveAdmin ap;
if (isPermissionCheckFlagEnabled()) {
@@ -13456,12 +13542,19 @@
if (!mHasFeature) {
return null;
}
+ CallerIdentity caller;
Preconditions.checkArgumentNonnegative(userId, "Invalid userId");
- final CallerIdentity caller = getCallerIdentity(callerPackageName);
- if (!hasPermission(MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT, caller.getPackageName(), userId)
- && !hasFullCrossUsersPermission(caller, userId)) {
- throw new SecurityException("Caller does not have permission to call this on user: "
- + userId);
+ if (isPermissionCheckFlagEnabled()) {
+ caller = getCallerIdentity(callerPackageName);
+ if (!hasPermission(MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT,
+ caller.getPackageName(), userId)
+ && !hasFullCrossUsersPermission(caller, userId)) {
+ throw new SecurityException("Caller does not have permission to call this on user: "
+ + userId);
+ }
+ } else {
+ caller = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userId));
}
synchronized (getLockObject()) {
@@ -14047,11 +14140,13 @@
public void setLockTaskPackages(ComponentName who, String callerPackageName, String[] packages)
throws SecurityException {
Objects.requireNonNull(packages, "packages is null");
- final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
- checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_LOCK_TASK_PACKAGES);
- synchronized (getLockObject()) {
- enforceCanCallLockTaskLocked(caller);
+ CallerIdentity caller;
+ if (isPermissionCheckFlagEnabled()) {
+ caller = getCallerIdentity(who, callerPackageName);
+ } else {
+ caller = getCallerIdentity(who);
}
+ checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_LOCK_TASK_PACKAGES);
if (useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
EnforcingAdmin enforcingAdmin;
@@ -14103,7 +14198,12 @@
@Override
public String[] getLockTaskPackages(ComponentName who, String callerPackageName) {
- final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+ CallerIdentity caller;
+ if (isPermissionCheckFlagEnabled()) {
+ caller = getCallerIdentity(who, callerPackageName);
+ } else {
+ caller = getCallerIdentity(who);
+ }
final int userHandle = caller.getUserId();
if (useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
@@ -14165,9 +14265,16 @@
Preconditions.checkArgument(hasHome || !hasNotification,
"Cannot use LOCK_TASK_FEATURE_NOTIFICATIONS without LOCK_TASK_FEATURE_HOME");
- final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+ CallerIdentity caller;
+ if (isPermissionCheckFlagEnabled()) {
+ caller = getCallerIdentity(who, callerPackageName);
+ } else {
+ caller = getCallerIdentity(who);
+ }
final int userHandle = caller.getUserId();
- checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_LOCK_TASK_FEATURES);
+ synchronized (getLockObject()) {
+ checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_LOCK_TASK_FEATURES);
+ }
if (useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
EnforcingAdmin enforcingAdmin;
@@ -14211,7 +14318,12 @@
@Override
public int getLockTaskFeatures(ComponentName who, String callerPackageName) {
- final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+ CallerIdentity caller;
+ if (isPermissionCheckFlagEnabled()) {
+ caller = getCallerIdentity(who, callerPackageName);
+ } else {
+ caller = getCallerIdentity(who);
+ }
final int userHandle = caller.getUserId();
if (useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
@@ -14506,11 +14618,13 @@
@Override
public boolean setTime(@Nullable ComponentName who, String callerPackageName, long millis) {
- final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+ CallerIdentity caller;
if (isPermissionCheckFlagEnabled()) {
+ caller = getCallerIdentity(who, callerPackageName);
// This is a global action.
enforcePermission(SET_TIME, caller.getPackageName(), UserHandle.USER_ALL);
} else {
+ caller = getCallerIdentity(who);
Objects.requireNonNull(who, "ComponentName is null");
Preconditions.checkCallAuthorization(
isDefaultDeviceOwner(caller)
@@ -14532,11 +14646,13 @@
@Override
public boolean setTimeZone(@Nullable ComponentName who, String callerPackageName,
String timeZone) {
- final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+ CallerIdentity caller;
if (isPermissionCheckFlagEnabled()) {
+ caller = getCallerIdentity(who, callerPackageName);
// This is a global action.
enforcePermission(SET_TIME_ZONE, caller.getPackageName(), UserHandle.USER_ALL);
} else {
+ caller = getCallerIdentity(who);
Objects.requireNonNull(who, "ComponentName is null");
Preconditions.checkCallAuthorization(
isDefaultDeviceOwner(caller)
@@ -14745,7 +14861,12 @@
@Override
public boolean setStatusBarDisabled(ComponentName who, String callerPackageName,
boolean disabled) {
- final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+ CallerIdentity caller;
+ if (isPermissionCheckFlagEnabled()) {
+ caller = getCallerIdentity(who, callerPackageName);
+ } else {
+ caller = getCallerIdentity(who);
+ }
if (isPermissionCheckFlagEnabled()) {
enforcePermission(MANAGE_DEVICE_POLICY_STATUS_BAR, caller.getPackageName(),
UserHandle.USER_ALL);
@@ -16515,10 +16636,11 @@
if (!mHasFeature) {
return;
}
- final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+ CallerIdentity caller;
ActiveAdmin admin;
if (isPermissionCheckFlagEnabled()) {
+ caller = getCallerIdentity(who, callerPackageName);
EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
who,
MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE,
@@ -16526,6 +16648,7 @@
caller.getUserId());
admin = enforcingAdmin.getActiveAdmin();
} else {
+ caller = getCallerIdentity(who);
Objects.requireNonNull(who, "ComponentName is null");
synchronized (getLockObject()) {
admin = getActiveAdminForUidLocked(who, caller.getUid());
@@ -16550,10 +16673,11 @@
if (!mHasFeature) {
return null;
}
- final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+ CallerIdentity caller;
ActiveAdmin admin;
if (isPermissionCheckFlagEnabled()) {
+ caller = getCallerIdentity(who, callerPackageName);
EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
who,
MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE,
@@ -16561,6 +16685,7 @@
caller.getUserId());
admin = enforcingAdmin.getActiveAdmin();
} else {
+ caller = getCallerIdentity(who);
Objects.requireNonNull(who, "ComponentName is null");
synchronized (getLockObject()) {
admin = getActiveAdminForUidLocked(who, caller.getUid());
@@ -18157,7 +18282,12 @@
if (token == null || token.length < 32) {
throw new IllegalArgumentException("token must be at least 32-byte long");
}
- final CallerIdentity caller = getCallerIdentity(admin, callerPackageName);
+ CallerIdentity caller;
+ if (isPermissionCheckFlagEnabled()) {
+ caller = getCallerIdentity(admin, callerPackageName);
+ } else {
+ caller = getCallerIdentity(admin);
+ }
final int userId = caller.getUserId();
if (useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
@@ -18215,7 +18345,12 @@
if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return false;
}
- final CallerIdentity caller = getCallerIdentity(admin, callerPackageName);
+ CallerIdentity caller;
+ if (isPermissionCheckFlagEnabled()) {
+ caller = getCallerIdentity(admin, callerPackageName);
+ } else {
+ caller = getCallerIdentity(admin);
+ }
final int userId = caller.getUserId();
boolean result = false;
@@ -18257,7 +18392,12 @@
if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return false;
}
- final CallerIdentity caller = getCallerIdentity(admin, callerPackageName);
+ CallerIdentity caller;
+ if (isPermissionCheckFlagEnabled()) {
+ caller = getCallerIdentity(admin, callerPackageName);
+ } else {
+ caller = getCallerIdentity(admin);
+ }
int userId = caller.getUserId();
if (useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
@@ -18301,7 +18441,12 @@
}
Objects.requireNonNull(token);
- final CallerIdentity caller = getCallerIdentity(admin, callerPackageName);
+ CallerIdentity caller;
+ if (isPermissionCheckFlagEnabled()) {
+ caller = getCallerIdentity(admin, callerPackageName);
+ } else {
+ caller = getCallerIdentity(admin);
+ }
int userId = caller.getUserId();
boolean result = false;
@@ -19483,7 +19628,12 @@
public void setUserControlDisabledPackages(ComponentName who, String callerPackageName,
List<String> packages) {
Objects.requireNonNull(packages, "packages is null");
- final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+ CallerIdentity caller;
+ if (isPermissionCheckFlagEnabled()) {
+ caller = getCallerIdentity(who, callerPackageName);
+ } else {
+ caller = getCallerIdentity(who);
+ }
checkCanExecuteOrThrowUnsafe(
DevicePolicyManager.OPERATION_SET_USER_CONTROL_DISABLED_PACKAGES);
@@ -19560,7 +19710,12 @@
@Override
public List<String> getUserControlDisabledPackages(ComponentName who,
String callerPackageName) {
- final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+ CallerIdentity caller;
+ if (isPermissionCheckFlagEnabled()) {
+ caller = getCallerIdentity(who, callerPackageName);
+ } else {
+ caller = getCallerIdentity(who);
+ }
if (useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
enforceCanQuery(
@@ -19588,7 +19743,12 @@
@Override
public void setCommonCriteriaModeEnabled(ComponentName who, String callerPackageName,
boolean enabled) {
- final CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+ CallerIdentity caller;
+ if (isPermissionCheckFlagEnabled()) {
+ caller = getCallerIdentity(who, callerPackageName);
+ } else {
+ caller = getCallerIdentity(who);
+ }
final ActiveAdmin admin;
if (isPermissionCheckFlagEnabled()) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index 6bc2d1f..e7b3dd9 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -53,7 +53,6 @@
import android.app.BackgroundStartPrivileges;
import android.app.BroadcastOptions;
import android.app.IApplicationThread;
-import android.app.ReceiverInfo;
import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStatsManagerInternal;
import android.content.ComponentName;
@@ -66,7 +65,6 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
-import android.content.res.CompatibilityInfo;
import android.os.Binder;
import android.os.Bundle;
import android.os.DeadObjectException;
@@ -298,16 +296,12 @@
};
if (mImpl == Impl.DEFAULT) {
- var q = new BroadcastQueueImpl(mAms, mHandlerThread.getThreadHandler(), TAG,
+ mQueue = new BroadcastQueueImpl(mAms, mHandlerThread.getThreadHandler(), TAG,
mConstants, mSkipPolicy, emptyHistory, false,
ProcessList.SCHED_GROUP_DEFAULT);
- q.mReceiverBatch.mDeepReceiverCopy = true;
- mQueue = q;
} else if (mImpl == Impl.MODERN) {
- var q = new BroadcastQueueModernImpl(mAms, mHandlerThread.getThreadHandler(),
+ mQueue = new BroadcastQueueModernImpl(mAms, mHandlerThread.getThreadHandler(),
mConstants, mConstants, mSkipPolicy, emptyHistory);
- q.mReceiverBatch.mDeepReceiverCopy = true;
- mQueue = q;
} else {
throw new UnsupportedOperationException();
}
@@ -406,43 +400,6 @@
UnaryOperator.identity());
}
- private void doRegisteredReceiver(ProcessRecord r, boolean wedge, boolean abort,
- UnaryOperator<Bundle> extrasOperator, ReceiverInfo info) {
- final Intent intent = info.intent;
- final Bundle extras = info.extras;
- final boolean assumeDelivered = info.assumeDelivered;
- mScheduledBroadcasts.add(makeScheduledBroadcast(r, intent));
- if (!wedge && !assumeDelivered) {
- assertTrue(r.mReceivers.numberOfCurReceivers() > 0);
- assertNotEquals(ProcessList.SCHED_GROUP_UNDEFINED,
- mQueue.getPreferredSchedulingGroupLocked(r));
- mHandlerThread.getThreadHandler().post(() -> {
- synchronized (mAms) {
- mQueue.finishReceiverLocked(r, Activity.RESULT_OK,
- null, extrasOperator.apply(extras), abort, false);
- }
- });
- }
- }
-
- private void doManifestReceiver(ProcessRecord r, boolean wedge, boolean abort,
- UnaryOperator<Bundle> extrasOperator, ReceiverInfo info) {
- final Intent intent = info.intent;
- final Bundle extras = info.extras;
- mScheduledBroadcasts.add(makeScheduledBroadcast(r, intent));
- if (!wedge) {
- assertTrue(r.mReceivers.numberOfCurReceivers() > 0);
- assertNotEquals(ProcessList.SCHED_GROUP_UNDEFINED,
- mQueue.getPreferredSchedulingGroupLocked(r));
- mHandlerThread.getThreadHandler().post(() -> {
- synchronized (mAms) {
- mQueue.finishReceiverLocked(r, Activity.RESULT_OK, null,
- extrasOperator.apply(extras), abort, false);
- }
- });
- }
- }
-
private ProcessRecord makeActiveProcessRecord(ApplicationInfo ai, String processName,
ProcessBehavior behavior, UnaryOperator<Bundle> extrasOperator) throws Exception {
final boolean wedge = (behavior == ProcessBehavior.WEDGE);
@@ -486,22 +443,47 @@
if (dead) return r;
doAnswer((invocation) -> {
- Log.v(TAG, "Intercepting scheduleReceiverList() for "
+ Log.v(TAG, "Intercepting scheduleReceiver() for "
+ Arrays.toString(invocation.getArguments()));
- final List<ReceiverInfo> data = invocation.getArgument(0);
- for (int i = 0; i < data.size(); i++) {
- ReceiverInfo info = data.get(i);
- // The logic here mimics the logic in ActivityThread: elements of the list are
- // forwarded to a handler for manifest receivers or to a handler for registered
- // receivers.
- if (info.registered) {
- doRegisteredReceiver(r, wedge, abort, extrasOperator, info);
- } else {
- doManifestReceiver(r, wedge, abort, extrasOperator, info);
- }
+ final Intent intent = invocation.getArgument(0);
+ final Bundle extras = invocation.getArgument(5);
+ mScheduledBroadcasts.add(makeScheduledBroadcast(r, intent));
+ if (!wedge) {
+ assertTrue(r.mReceivers.numberOfCurReceivers() > 0);
+ assertNotEquals(ProcessList.SCHED_GROUP_UNDEFINED,
+ mQueue.getPreferredSchedulingGroupLocked(r));
+ mHandlerThread.getThreadHandler().post(() -> {
+ synchronized (mAms) {
+ mQueue.finishReceiverLocked(r, Activity.RESULT_OK, null,
+ extrasOperator.apply(extras), abort, false);
+ }
+ });
}
return null;
- }).when(thread).scheduleReceiverList(any());
+ }).when(thread).scheduleReceiver(any(), any(), any(), anyInt(), any(), any(), anyBoolean(),
+ anyBoolean(), anyInt(), anyInt(), anyInt(), any());
+
+ doAnswer((invocation) -> {
+ Log.v(TAG, "Intercepting scheduleRegisteredReceiver() for "
+ + Arrays.toString(invocation.getArguments()));
+ final Intent intent = invocation.getArgument(1);
+ final Bundle extras = invocation.getArgument(4);
+ final boolean ordered = invocation.getArgument(5);
+ mScheduledBroadcasts.add(makeScheduledBroadcast(r, intent));
+ if (!wedge && ordered) {
+ assertTrue(r.mReceivers.numberOfCurReceivers() > 0);
+ assertNotEquals(ProcessList.SCHED_GROUP_UNDEFINED,
+ mQueue.getPreferredSchedulingGroupLocked(r));
+ mHandlerThread.getThreadHandler().post(() -> {
+ synchronized (mAms) {
+ mQueue.finishReceiverLocked(r, Activity.RESULT_OK,
+ null, extrasOperator.apply(extras), abort, false);
+ }
+ });
+ }
+ return null;
+ }).when(thread).scheduleRegisteredReceiver(any(), any(), anyInt(), any(), any(),
+ anyBoolean(), anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyInt(), any());
return r;
}
@@ -643,131 +625,6 @@
};
}
- private static <T> boolean matchElement(T a, T b) {
- return a == null || a.equals(b);
- }
- private static <T> boolean matchObject(ArgumentMatcher<T> m, T b) {
- return m == null || m.matches(b);
- }
-
- /**
- * Create an ArgumentMatcher for a manifest receiver. The parameters are in the order of
- * {@link IApplicationThread#scheduleReceiver} but the names correspond to the field names in
- * {@link ReceiverInfo}. For every parameter, a null means "don't care".
- */
- private ArgumentMatcher<ReceiverInfo> manifestReceiverMatcher(
- ArgumentMatcher<Intent> intent,
- ArgumentMatcher<ActivityInfo> activityInfo,
- ArgumentMatcher<CompatibilityInfo> compatInfo,
- Integer resultCode,
- ArgumentMatcher<String> data,
- ArgumentMatcher<Bundle> extras,
- Boolean sync,
- Boolean assumeDelivered,
- Integer sendingUser,
- Integer processState) {
- return (test) -> {
- return test.registered == false
- && matchObject(intent, test.intent)
- && matchObject(activityInfo, test.activityInfo)
- && matchObject(compatInfo, test.compatInfo)
- && matchElement(resultCode, test.resultCode)
- && matchObject(data, test.data)
- && matchObject(extras, test.extras)
- && matchElement(sync, test.sync)
- && matchElement(assumeDelivered, test.assumeDelivered)
- && matchElement(sendingUser, test.sendingUser)
- && matchElement(processState, test.processState);
- };
- }
-
-
- /**
- * Create an argument suitable for the verify() mock methods, when the goal is to find a call
- * containing a manifest receiver.
- */
- private List<ReceiverInfo> manifestReceiver(
- ArgumentMatcher<Intent> intent,
- ArgumentMatcher<ActivityInfo> activityInfo,
- ArgumentMatcher<CompatibilityInfo> compatInfo,
- Integer resultCode,
- ArgumentMatcher<String> data,
- ArgumentMatcher<Bundle> extras,
- Boolean sync,
- Boolean assumeDelivered,
- Integer sendingUser,
- Integer processState) {
- return argThat(receiverList(manifestReceiverMatcher(intent, activityInfo, compatInfo,
- resultCode, data, extras, sync, assumeDelivered,
- sendingUser, processState)));
- }
-
- /**
- * Create an ArgumentMatcher for a registered receiver. The parameters are in the order of
- * {@link IApplicationThread#scheduleRegisteredReceiver} but the names correspond to the field
- * names in {@link ReceiverInfo}. For every parameter, a null means "don't care".
- */
- private ArgumentMatcher<ReceiverInfo> registeredReceiverMatcher(
- ArgumentMatcher<IIntentReceiver> receiver,
- ArgumentMatcher<Intent> intent,
- Integer resultCode,
- ArgumentMatcher<String> data,
- ArgumentMatcher<Bundle> extras,
- Boolean ordered,
- Boolean sticky,
- Boolean assumeDelivered,
- Integer sendingUser,
- Integer processState) {
- return (test) -> {
- return test.registered == true
- && matchObject(receiver, test.receiver)
- && matchObject(intent, test.intent)
- && matchElement(resultCode, test.resultCode)
- && matchObject(data, test.data)
- && matchObject(extras, test.extras)
- && matchElement(ordered, test.ordered)
- && matchElement(sticky, test.sticky)
- && matchElement(assumeDelivered, test.assumeDelivered)
- && matchElement(sendingUser, test.sendingUser)
- && matchElement(processState, test.processState);
- };
- }
-
- /**
- * Create an argument suitable for the verify() mock methods, when the goal is to find a call
- * containing a registered receiver.
- */
- private List<ReceiverInfo> registeredReceiver(
- ArgumentMatcher<IIntentReceiver> receiver,
- ArgumentMatcher<Intent> intent,
- Integer resultCode,
- ArgumentMatcher<String> data,
- ArgumentMatcher<Bundle> extras,
- Boolean ordered,
- Boolean sticky,
- Boolean assumeDelivered,
- Integer sendingUser,
- Integer processState) {
- return argThat(receiverList(registeredReceiverMatcher(receiver, intent, resultCode,
- data, extras, ordered, sticky, assumeDelivered,
- sendingUser, processState)));
- }
-
- /**
- * Apply a matcher to every element in a ReceiverInfo list.
- */
- private ArgumentMatcher<List<ReceiverInfo>> receiverList(ArgumentMatcher<ReceiverInfo> a) {
- return (test) -> {
- for (int i = 0; i < test.size(); i++) {
- ReceiverInfo r = test.get(i);
- if (a.matches(r)) {
- return true;
- }
- }
- return false;
- };
- }
-
private ArgumentMatcher<Bundle> bundleEquals(Bundle bundle) {
return (test) -> {
// TODO: check values in addition to keys
@@ -802,37 +659,42 @@
ComponentName component) throws Exception {
final Intent targetedIntent = new Intent(intent);
targetedIntent.setComponent(component);
- verify(app.getThread(), mode).scheduleReceiverList(
- manifestReceiver(filterEquals(targetedIntent),
- null, null, null, null, null, null, null, UserHandle.USER_SYSTEM, null));
+ verify(app.getThread(), mode).scheduleReceiver(
+ argThat(filterEquals(targetedIntent)), any(), any(),
+ anyInt(), any(), any(), eq(false), anyBoolean(), eq(UserHandle.USER_SYSTEM),
+ anyInt(), anyInt(), any());
}
private void verifyScheduleReceiver(VerificationMode mode, ProcessRecord app,
Intent intent, int userId) throws Exception {
- verify(app.getThread(), mode).scheduleReceiverList(
- manifestReceiver(filterEqualsIgnoringComponent(intent),
- null, null, null, null, null, null, null, userId, null));
+ verify(app.getThread(), mode).scheduleReceiver(
+ argThat(filterEqualsIgnoringComponent(intent)), any(), any(),
+ anyInt(), any(), any(), anyBoolean(), anyBoolean(), eq(userId),
+ anyInt(), anyInt(), any());
}
private void verifyScheduleReceiver(VerificationMode mode, ProcessRecord app,
int userId) throws Exception {
- verify(app.getThread(), mode).scheduleReceiverList(
- manifestReceiver(null,
- null, null, null, null, null, null, null, userId, null));
+ verify(app.getThread(), mode).scheduleReceiver(
+ any(), any(), any(),
+ anyInt(), any(), any(), anyBoolean(), anyBoolean(), eq(userId),
+ anyInt(), anyInt(), any());
}
private void verifyScheduleRegisteredReceiver(ProcessRecord app,
Intent intent) throws Exception {
- verify(app.getThread()).scheduleReceiverList(
- registeredReceiver(null, filterEqualsIgnoringComponent(intent),
- null, null, null, null, null, null, UserHandle.USER_SYSTEM, null));
+ verify(app.getThread()).scheduleRegisteredReceiver(
+ any(), argThat(filterEqualsIgnoringComponent(intent)),
+ anyInt(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(),
+ eq(UserHandle.USER_SYSTEM), anyInt(), anyInt(), any());
}
private void verifyScheduleRegisteredReceiver(VerificationMode mode, ProcessRecord app,
int userId) throws Exception {
- verify(app.getThread(), mode).scheduleReceiverList(
- registeredReceiver(null, null,
- null, null, null, null, null, null, userId, null));
+ verify(app.getThread(), mode).scheduleRegisteredReceiver(
+ any(), any(),
+ anyInt(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(),
+ eq(userId), anyInt(), anyInt(), any());
}
static final int USER_GUEST = 11;
@@ -1485,24 +1347,25 @@
final InOrder inOrder = inOrder(greenThread, blueThread, yellowThread, redThread);
final Bundle expectedExtras = new Bundle();
expectedExtras.putBoolean(PACKAGE_RED, true);
- inOrder.verify(greenThread).scheduleReceiverList(manifestReceiver(
- filterEqualsIgnoringComponent(airplane), null, null,
- Activity.RESULT_OK, null, bundleEquals(expectedExtras), true, false,
- UserHandle.USER_SYSTEM, null));
- inOrder.verify(blueThread).scheduleReceiverList(manifestReceiver(
- filterEqualsIgnoringComponent(airplane), null, null,
- Activity.RESULT_OK, null, bundleEquals(expectedExtras), true, false,
- UserHandle.USER_SYSTEM, null));
+ inOrder.verify(greenThread).scheduleReceiver(
+ argThat(filterEqualsIgnoringComponent(airplane)), any(), any(),
+ eq(Activity.RESULT_OK), any(), argThat(bundleEquals(expectedExtras)),
+ eq(true), eq(false), eq(UserHandle.USER_SYSTEM), anyInt(), anyInt(), any());
+ inOrder.verify(blueThread).scheduleReceiver(
+ argThat(filterEqualsIgnoringComponent(airplane)), any(), any(),
+ eq(Activity.RESULT_OK), any(), argThat(bundleEquals(expectedExtras)),
+ eq(true), eq(false), eq(UserHandle.USER_SYSTEM), anyInt(), anyInt(), any());
expectedExtras.putBoolean(PACKAGE_BLUE, true);
- inOrder.verify(yellowThread).scheduleReceiverList(manifestReceiver(
- filterEqualsIgnoringComponent(airplane), null, null,
- Activity.RESULT_OK, null, bundleEquals(expectedExtras), true, false,
- UserHandle.USER_SYSTEM, null));
+ inOrder.verify(yellowThread).scheduleReceiver(
+ argThat(filterEqualsIgnoringComponent(airplane)), any(), any(),
+ eq(Activity.RESULT_OK), any(), argThat(bundleEquals(expectedExtras)),
+ eq(true), eq(false), eq(UserHandle.USER_SYSTEM), anyInt(), anyInt(), any());
expectedExtras.putBoolean(PACKAGE_YELLOW, true);
- inOrder.verify(redThread).scheduleReceiverList(registeredReceiver(
- null, filterEquals(airplane),
- Activity.RESULT_OK, null, bundleEquals(expectedExtras), false,
- null, true, UserHandle.USER_SYSTEM, null));
+ inOrder.verify(redThread).scheduleRegisteredReceiver(
+ any(), argThat(filterEquals(airplane)),
+ eq(Activity.RESULT_OK), any(), argThat(bundleEquals(expectedExtras)),
+ eq(false), anyBoolean(), eq(true), eq(UserHandle.USER_SYSTEM), anyInt(),
+ anyInt(), any());
// Finally, verify that we thawed the final receiver
verify(mAms.mOomAdjuster.mCachedAppOptimizer).unfreezeTemporarily(eq(callerApp),
@@ -1565,24 +1428,25 @@
// have invoked or skipped the second receiver depending on the intent
// flag policy; we always deliver to final receiver regardless of abort
final InOrder inOrder = inOrder(greenThread, blueThread, redThread);
- inOrder.verify(greenThread).scheduleReceiverList(manifestReceiver(
- filterEqualsIgnoringComponent(intent), null, null,
- Activity.RESULT_OK, null, null, true, false, UserHandle.USER_SYSTEM,
- null));
+ inOrder.verify(greenThread).scheduleReceiver(
+ argThat(filterEqualsIgnoringComponent(intent)), any(), any(),
+ eq(Activity.RESULT_OK), any(), any(), eq(true), eq(false),
+ eq(UserHandle.USER_SYSTEM), anyInt(), anyInt(), any());
if ((intent.getFlags() & Intent.FLAG_RECEIVER_NO_ABORT) != 0) {
- inOrder.verify(blueThread).scheduleReceiverList(manifestReceiver(
- filterEqualsIgnoringComponent(intent), null, null,
- Activity.RESULT_OK, null, null, true, false, UserHandle.USER_SYSTEM,
- null));
+ inOrder.verify(blueThread).scheduleReceiver(
+ argThat(filterEqualsIgnoringComponent(intent)), any(), any(),
+ eq(Activity.RESULT_OK), any(), any(), eq(true), eq(false),
+ eq(UserHandle.USER_SYSTEM), anyInt(), anyInt(), any());
} else {
- inOrder.verify(blueThread, never()).scheduleReceiverList(manifestReceiver(
- null, null, null, null, null,
- null, null, null, null, null));
+ inOrder.verify(blueThread, never()).scheduleReceiver(
+ any(), any(), any(), anyInt(), any(), any(),
+ anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyInt(), any());
}
- inOrder.verify(redThread).scheduleReceiverList(registeredReceiver(
- null, filterEquals(intent),
- Activity.RESULT_OK, null, bundleEquals(expectedExtras),
- false, null, true, UserHandle.USER_SYSTEM, null));
+ inOrder.verify(redThread).scheduleRegisteredReceiver(
+ any(), argThat(filterEquals(intent)),
+ eq(Activity.RESULT_OK), any(), argThat(bundleEquals(expectedExtras)),
+ eq(false), anyBoolean(), eq(true), eq(UserHandle.USER_SYSTEM),
+ anyInt(), anyInt(), any());
}
/**
@@ -1602,10 +1466,11 @@
orderedResultTo, orderedExtras));
waitForIdle();
- verify(callerThread).scheduleReceiverList(registeredReceiver(
- null, filterEquals(airplane),
- Activity.RESULT_OK, null, bundleEquals(orderedExtras), false,
- null, true, UserHandle.USER_SYSTEM, null));
+ verify(callerThread).scheduleRegisteredReceiver(
+ any(), argThat(filterEquals(airplane)),
+ eq(Activity.RESULT_OK), any(), argThat(bundleEquals(orderedExtras)),
+ eq(false), anyBoolean(), eq(true), eq(UserHandle.USER_SYSTEM),
+ anyInt(), anyInt(), any());
}
/**
@@ -1623,10 +1488,11 @@
makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE)), resultTo));
waitForIdle();
- verify(callerThread).scheduleReceiverList(registeredReceiver(
- null, filterEquals(airplane),
- Activity.RESULT_OK, null, null, false,
- null, true, UserHandle.USER_SYSTEM, null));
+ verify(callerThread).scheduleRegisteredReceiver(
+ any(), argThat(filterEquals(airplane)),
+ eq(Activity.RESULT_OK), any(), any(),
+ eq(false), anyBoolean(), eq(true), eq(UserHandle.USER_SYSTEM),
+ anyInt(), anyInt(), any());
}
/**
@@ -1790,28 +1656,34 @@
final InOrder inOrder = inOrder(callerThread, blueThread);
// First broadcast is canceled
- inOrder.verify(callerThread).scheduleReceiverList(registeredReceiver(null,
- filterAndExtrasEquals(timezoneFirst), Activity.RESULT_CANCELED, null,
- null, false, null, true, UserHandle.USER_SYSTEM, null));
+ inOrder.verify(callerThread).scheduleRegisteredReceiver(
+ any(), argThat(filterAndExtrasEquals(timezoneFirst)),
+ eq(Activity.RESULT_CANCELED), any(), any(),
+ eq(false), anyBoolean(), eq(true), eq(UserHandle.USER_SYSTEM),
+ anyInt(), anyInt(), any());
// We deliver second broadcast to app
timezoneSecond.setClassName(PACKAGE_BLUE, CLASS_GREEN);
- inOrder.verify(blueThread).scheduleReceiverList(manifestReceiver(
- filterAndExtrasEquals(timezoneSecond),
- null, null, null, null, null, true, false, null, null));
+ inOrder.verify(blueThread).scheduleReceiver(
+ argThat(filterAndExtrasEquals(timezoneSecond)), any(), any(),
+ anyInt(), any(), any(), eq(true), eq(false), anyInt(),
+ anyInt(), anyInt(), any());
// Second broadcast is finished
timezoneSecond.setComponent(null);
- inOrder.verify(callerThread).scheduleReceiverList(registeredReceiver(null,
- filterAndExtrasEquals(timezoneSecond), Activity.RESULT_OK, null,
- null, false, null, true, UserHandle.USER_SYSTEM, null));
+ inOrder.verify(callerThread).scheduleRegisteredReceiver(
+ any(), argThat(filterAndExtrasEquals(timezoneSecond)),
+ eq(Activity.RESULT_OK), any(), any(),
+ eq(false), anyBoolean(), eq(true), eq(UserHandle.USER_SYSTEM),
+ anyInt(), anyInt(), any());
// Since we "replaced" the first broadcast in its original position,
// only now do we see the airplane broadcast
airplane.setClassName(PACKAGE_BLUE, CLASS_RED);
- inOrder.verify(blueThread).scheduleReceiverList(manifestReceiver(
- filterEquals(airplane),
- null, null, null, null, null, false, false, null, null));
+ inOrder.verify(blueThread).scheduleReceiver(
+ argThat(filterEquals(airplane)), any(), any(),
+ anyInt(), any(), any(), eq(false), eq(false), anyInt(),
+ anyInt(), anyInt(), any());
}
@Test
@@ -1873,15 +1745,17 @@
// First broadcast is delivered.
timeTickFirst.setClassName(PACKAGE_BLUE, CLASS_BLUE);
- inOrder.verify(blueThread).scheduleReceiverList(manifestReceiver(
- filterAndExtrasEquals(timeTickFirst),
- null, null, null, null, null, false, false, null, null));
+ inOrder.verify(blueThread).scheduleReceiver(
+ argThat(filterAndExtrasEquals(timeTickFirst)), any(), any(),
+ anyInt(), any(), any(), eq(false), eq(false), anyInt(),
+ anyInt(), anyInt(), any());
// Second broadcast should be replaced by third broadcast.
timeTickThird.setClassName(PACKAGE_BLUE, CLASS_BLUE);
- inOrder.verify(blueThread).scheduleReceiverList(manifestReceiver(
- filterAndExtrasEquals(timeTickThird),
- null, null, null, null, null, false, false, null, null));
+ inOrder.verify(blueThread).scheduleReceiver(
+ argThat(filterAndExtrasEquals(timeTickThird)), any(), any(),
+ anyInt(), any(), any(), eq(false), eq(false), anyInt(),
+ anyInt(), anyInt(), any());
}
@Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
index 6e63315..3d03b47 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
@@ -460,6 +460,25 @@
verify(secondFollowerDpc.animator).animateTo(eq(brightness), anyFloat(), anyFloat());
}
+ @Test
+ public void testDoesNotSetScreenStateForNonDefaultDisplayUntilBootCompleted() {
+ // We should still set screen state for the default display
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1);
+ verify(mHolder.displayPowerState).setScreenState(anyInt());
+
+ mHolder = createDisplayPowerController(42, UNIQUE_ID);
+
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1);
+ verify(mHolder.displayPowerState, never()).setScreenState(anyInt());
+
+ mHolder.dpc.onBootCompleted();
+ advanceTime(1);
+ verify(mHolder.displayPowerState).setScreenState(anyInt());
+ }
+
private DisplayPowerControllerHolder createDisplayPowerController(int displayId,
String uniqueId) {
final DisplayPowerState displayPowerState = mock(DisplayPowerState.class);
@@ -487,7 +506,7 @@
mContextSpy, injector, mDisplayPowerCallbacksMock, mHandler,
mSensorManagerMock, mDisplayBlankerMock, display,
mBrightnessTrackerMock, brightnessSetting, () -> {},
- hbmMetadata);
+ hbmMetadata, false);
return new DisplayPowerControllerHolder(dpc, displayPowerState, brightnessSetting, animator,
automaticBrightnessController, wakelockController);
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
index a8c3e4e..841128e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -464,6 +464,25 @@
verify(secondFollowerHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat());
}
+ @Test
+ public void testDoesNotSetScreenStateForNonDefaultDisplayUntilBootCompleted() {
+ // We should still set screen state for the default display
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1);
+ verify(mHolder.displayPowerState).setScreenState(anyInt());
+
+ mHolder = createDisplayPowerController(42, UNIQUE_ID);
+
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1);
+ verify(mHolder.displayPowerState, never()).setScreenState(anyInt());
+
+ mHolder.dpc.onBootCompleted();
+ advanceTime(1);
+ verify(mHolder.displayPowerState).setScreenState(anyInt());
+ }
+
private DisplayPowerControllerHolder createDisplayPowerController(int displayId,
String uniqueId) {
final DisplayPowerState displayPowerState = mock(DisplayPowerState.class);
@@ -489,7 +508,7 @@
mContextSpy, injector, mDisplayPowerCallbacksMock, mHandler,
mSensorManagerMock, mDisplayBlankerMock, display,
mBrightnessTrackerMock, brightnessSetting, () -> {},
- hbmMetadata);
+ hbmMetadata, false);
return new DisplayPowerControllerHolder(dpc, displayPowerState, brightnessSetting, animator,
automaticBrightnessController);
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 95009e6..0b6756d 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -1122,7 +1122,7 @@
@Nullable
private UserInfo getUser(int id) {
- List<UserInfo> list = mUserManager.getAliveUsers();
+ List<UserInfo> list = mUserManager.getUsers();
for (UserInfo user : list) {
if (user.id == id) {
@@ -1277,7 +1277,7 @@
@MediumTest
@Test
public void testConcurrentUserCreate() throws Exception {
- int userCount = mUserManager.getAliveUsers().size();
+ int userCount = mUserManager.getUsers().size();
int maxSupportedUsers = UserManager.getMaxSupportedUsers();
int canBeCreatedCount = maxSupportedUsers - userCount;
// Test exceeding the limit while running in parallel
@@ -1300,7 +1300,7 @@
"Could not create " + createUsersCount + " users in " + timeout + " seconds")
.that(es.awaitTermination(timeout, TimeUnit.SECONDS))
.isTrue();
- assertThat(mUserManager.getAliveUsers().size()).isEqualTo(maxSupportedUsers);
+ assertThat(mUserManager.getUsers().size()).isEqualTo(maxSupportedUsers);
assertThat(created.get()).isEqualTo(canBeCreatedCount);
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserRemovalWaiter.java b/services/tests/servicestests/src/com/android/server/pm/UserRemovalWaiter.java
index 9e1af0c..40eb84e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserRemovalWaiter.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserRemovalWaiter.java
@@ -23,7 +23,6 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.os.UserHandle;
-import android.os.UserManager;
import android.util.Log;
import java.io.Closeable;
@@ -36,9 +35,8 @@
public class UserRemovalWaiter extends BroadcastReceiver implements Closeable {
private final Context mContext;
- private final UserManager mUserManager;
private final String mTag;
- private final long mTimeoutMillis;
+ private final long mTimeoutInSeconds;
private final Map<Integer, CountDownLatch> mMap = new ConcurrentHashMap<>();
private CountDownLatch getLatch(final int userId) {
@@ -47,9 +45,8 @@
public UserRemovalWaiter(Context context, String tag, int timeoutInSeconds) {
mContext = context;
- mUserManager = UserManager.get(mContext);
mTag = tag;
- mTimeoutMillis = timeoutInSeconds * 1000L;
+ mTimeoutInSeconds = timeoutInSeconds;
mContext.registerReceiver(this, new IntentFilter(Intent.ACTION_USER_REMOVED));
}
@@ -73,28 +70,17 @@
*/
public void waitFor(int userId) {
Log.i(mTag, "Waiting for user " + userId + " to be removed");
- CountDownLatch latch = getLatch(userId);
+
long startTime = System.currentTimeMillis();
- while (System.currentTimeMillis() - startTime < mTimeoutMillis) {
- if (hasUserGone(userId) || waitLatchForOneSecond(latch)) {
+ try {
+ if (getLatch(userId).await(mTimeoutInSeconds, TimeUnit.SECONDS)) {
Log.i(mTag, "User " + userId + " is removed in "
+ (System.currentTimeMillis() - startTime) + " ms");
- return;
+ } else {
+ fail("Timeout waiting for user removal. userId = " + userId);
}
- }
- fail("Timeout waiting for user removal. userId = " + userId);
- }
-
- private boolean hasUserGone(int userId) {
- return mUserManager.getAliveUsers().stream().noneMatch(x -> x.id == userId);
- }
-
- private boolean waitLatchForOneSecond(CountDownLatch latch) {
- try {
- return latch.await(1, TimeUnit.SECONDS);
} catch (InterruptedException e) {
- Log.e(mTag, "Thread interrupted unexpectedly.", e);
- return false;
+ throw new AssertionError("Thread interrupted unexpectedly.", e);
}
}
}
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
index b716690..5efd158 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
@@ -56,9 +56,12 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
+import java.util.function.Function;
+import java.util.function.Supplier;
/**
* Helper for {@link SoundTrigger} APIs. Supports two types of models:
@@ -74,6 +77,9 @@
static final String TAG = "SoundTriggerHelper";
static final boolean DBG = false;
+ // Module ID if there is no available module to connect to.
+ public static final int INVALID_MODULE_ID = -1;
+
/**
* Return codes for {@link #startRecognition(int, KeyphraseSoundModel,
* IRecognitionStatusCallback, RecognitionConfig)},
@@ -84,10 +90,6 @@
private static final int INVALID_VALUE = Integer.MIN_VALUE;
- /** The {@link ModuleProperties} for the system, or null if none exists. */
- final ModuleProperties mModuleProperties;
-
- /** The properties for the DSP module */
private SoundTriggerModule mModule;
private final Object mLock = new Object();
private final Context mContext;
@@ -114,7 +116,6 @@
private PowerSaveModeListener mPowerSaveModeListener;
- private final SoundTriggerModuleProvider mModuleProvider;
// Handler to process call state changes will delay to allow time for the audio
// and sound trigger HALs to process the end of call notifications
@@ -123,46 +124,28 @@
private static final int MSG_CALL_STATE_CHANGED = 0;
private static final int CALL_INACTIVE_MSG_DELAY_MS = 1000;
- /**
- * Provider interface for retrieving SoundTriggerModule instances
- */
- public interface SoundTriggerModuleProvider {
- /**
- * Populate module properties for all available modules
- *
- * @param modules List of ModuleProperties to be populated
- * @return Status int 0 on success.
- */
- int listModuleProperties(@NonNull ArrayList<SoundTrigger.ModuleProperties> modules);
+ // TODO(b/269366605) Temporary solution to query correct moduleProperties
+ private final int mModuleId;
+ private final Function<SoundTrigger.StatusListener, SoundTriggerModule> mModuleProvider;
+ private final Supplier<List<ModuleProperties>> mModulePropertiesProvider;
- /**
- * Get SoundTriggerModule based on {@link SoundTrigger.ModuleProperties#getId()}
- *
- * @param moduleId Module ID
- * @param statusListener Client listener to be associated with the returned module
- * @return Module associated with moduleId
- */
- SoundTriggerModule getModule(int moduleId, SoundTrigger.StatusListener statusListener);
- }
-
- SoundTriggerHelper(Context context, SoundTriggerModuleProvider moduleProvider) {
- ArrayList <ModuleProperties> modules = new ArrayList<>();
- mModuleProvider = moduleProvider;
- int status = mModuleProvider.listModuleProperties(modules);
+ SoundTriggerHelper(Context context,
+ @NonNull Function<SoundTrigger.StatusListener, SoundTriggerModule> moduleProvider,
+ int moduleId,
+ @NonNull Supplier<List<ModuleProperties>> modulePropertiesProvider) {
+ mModuleId = moduleId;
mContext = context;
mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mModelDataMap = new HashMap<UUID, ModelData>();
mKeyphraseUuidMap = new HashMap<Integer, UUID>();
- if (status != SoundTrigger.STATUS_OK || modules.size() == 0) {
- Slog.w(TAG, "listModules status=" + status + ", # of modules=" + modules.size());
- mModuleProperties = null;
+ mModuleProvider = moduleProvider;
+ mModulePropertiesProvider = modulePropertiesProvider;
+ if (moduleId == INVALID_MODULE_ID) {
mModule = null;
} else {
- // TODO: Figure out how to determine which module corresponds to the DSP hardware.
- mModuleProperties = modules.get(0);
+ mModule = mModuleProvider.apply(this);
}
-
Looper looper = Looper.myLooper();
if (looper == null) {
looper = Looper.getMainLooper();
@@ -245,7 +228,6 @@
+ " soundModel=" + soundModel + ", callback=" + callback.asBinder()
+ ", recognitionConfig=" + recognitionConfig
+ ", runInBatterySaverMode=" + runInBatterySaverMode);
- Slog.d(TAG, "moduleProperties=" + mModuleProperties);
dumpModelStateLocked();
}
@@ -290,11 +272,8 @@
private int prepareForRecognition(ModelData modelData) {
if (mModule == null) {
- mModule = mModuleProvider.getModule(mModuleProperties.getId(), this);
- if (mModule == null) {
- Slog.w(TAG, "prepareForRecognition: cannot attach to sound trigger module");
- return STATUS_ERROR;
- }
+ Slog.w(TAG, "prepareForRecognition: cannot attach to sound trigger module");
+ return STATUS_ERROR;
}
// Load the model if it is not loaded.
if (!modelData.isModelLoaded()) {
@@ -336,11 +315,6 @@
IRecognitionStatusCallback callback, RecognitionConfig recognitionConfig,
int keyphraseId, boolean runInBatterySaverMode) {
synchronized (mLock) {
- if (mModuleProperties == null) {
- Slog.w(TAG, "Attempting startRecognition without the capability");
- return STATUS_ERROR;
- }
-
IRecognitionStatusCallback oldCallback = modelData.getCallback();
if (oldCallback != null && oldCallback.asBinder() != callback.asBinder()) {
Slog.w(TAG, "Canceling previous recognition for model id: "
@@ -486,8 +460,8 @@
if (callback == null) {
return STATUS_ERROR;
}
- if (mModuleProperties == null || mModule == null) {
- Slog.w(TAG, "Attempting stopRecognition without the capability");
+ if (mModule == null) {
+ Slog.w(TAG, "Attempting stopRecognition after detach");
return STATUS_ERROR;
}
@@ -563,7 +537,13 @@
}
public ModuleProperties getModuleProperties() {
- return mModuleProperties;
+ for (ModuleProperties moduleProperties : mModulePropertiesProvider.get()) {
+ if (moduleProperties.getId() == mModuleId) {
+ return moduleProperties;
+ }
+ }
+ Slog.e(TAG, "Module properties not found for existing moduleId " + mModuleId);
+ return null;
}
int unloadKeyphraseSoundModel(int keyphraseId) {
@@ -1027,7 +1007,7 @@
internalClearGlobalStateLocked();
if (mModule != null) {
mModule.detach();
- mModule = null;
+ mModule = mModuleProvider.apply(this);
}
}
}
@@ -1098,7 +1078,6 @@
void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
synchronized (mLock) {
pw.print(" module properties=");
- pw.println(mModuleProperties == null ? "null" : mModuleProperties);
pw.print(" call active=");
pw.println(mCallActive);
pw.println(" SoundTrigger Power State=" + mSoundTriggerPowerSaveMode);
@@ -1444,7 +1423,7 @@
// Computes whether we have any recognition running at all (voice or generic). Sets
// the mRecognitionRequested variable with the result.
private boolean computeRecognitionRequestedLocked() {
- if (mModuleProperties == null || mModule == null) {
+ if (mModule == null) {
mRecognitionRequested = false;
return mRecognitionRequested;
}
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerInternal.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerInternal.java
index 7071e23a..cc398d9 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerInternal.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerInternal.java
@@ -26,12 +26,14 @@
import android.hardware.soundtrigger.SoundTrigger.ModelParamRange;
import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
+import android.media.permission.Identity;
import android.os.IBinder;
import com.android.server.voiceinteraction.VoiceInteractionManagerService;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.List;
/**
* Provides a local service for managing voice-related recoginition models. This is primarily used
@@ -46,7 +48,11 @@
int STATUS_ERROR = SoundTrigger.STATUS_ERROR;
int STATUS_OK = SoundTrigger.STATUS_OK;
- Session attach(@NonNull IBinder client);
+ // Attach to a specific underlying STModule
+ Session attach(@NonNull IBinder client, ModuleProperties underlyingModule);
+
+ // Enumerate possible STModules to attach to
+ List<ModuleProperties> listModuleProperties(Identity originatorIdentity);
/**
* Dumps service-wide information.
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index 7824c69..495a433 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -51,7 +51,6 @@
import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
import android.hardware.soundtrigger.SoundTrigger.SoundModel;
-import android.hardware.soundtrigger.SoundTriggerModule;
import android.media.AudioAttributes;
import android.media.AudioFormat;
import android.media.AudioRecord;
@@ -89,6 +88,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
@@ -215,47 +215,73 @@
}
}
- private SoundTriggerHelper newSoundTriggerHelper() {
+ private SoundTriggerHelper newSoundTriggerHelper(ModuleProperties moduleProperties) {
Identity middlemanIdentity = new Identity();
middlemanIdentity.packageName = ActivityThread.currentOpPackageName();
-
Identity originatorIdentity = IdentityContext.getNonNull();
- return new SoundTriggerHelper(mContext,
- new SoundTriggerHelper.SoundTriggerModuleProvider() {
- @Override
- public int listModuleProperties(ArrayList<ModuleProperties> modules) {
- return SoundTrigger.listModulesAsMiddleman(modules, middlemanIdentity,
- originatorIdentity);
- }
+ ArrayList<ModuleProperties> moduleList = new ArrayList<>();
+ SoundTrigger.listModulesAsMiddleman(moduleList, middlemanIdentity,
+ originatorIdentity);
- @Override
- public SoundTriggerModule getModule(int moduleId,
- SoundTrigger.StatusListener statusListener) {
- return SoundTrigger.attachModuleAsMiddleman(moduleId, statusListener, null,
- middlemanIdentity, originatorIdentity);
- }
+ // Don't fail existing CTS tests which run without a ST module
+ final int moduleId = (moduleProperties != null) ?
+ moduleProperties.getId() : SoundTriggerHelper.INVALID_MODULE_ID;
+
+ if (moduleId != SoundTriggerHelper.INVALID_MODULE_ID) {
+ if (!moduleList.contains(moduleProperties)) {
+ throw new IllegalArgumentException("Invalid module properties");
+ }
+ }
+
+ return new SoundTriggerHelper(
+ mContext,
+ (SoundTrigger.StatusListener statusListener) ->
+ SoundTrigger.attachModuleAsMiddleman(
+ moduleId, statusListener, null /* handler */,
+ middlemanIdentity, originatorIdentity),
+ moduleId,
+ () -> {
+ ArrayList<ModuleProperties> modulePropList = new ArrayList<>();
+ SoundTrigger.listModulesAsMiddleman(modulePropList, middlemanIdentity,
+ originatorIdentity);
+ return modulePropList;
});
}
class SoundTriggerServiceStub extends ISoundTriggerService.Stub {
@Override
- public ISoundTriggerSession attachAsOriginator(Identity originatorIdentity,
+ public ISoundTriggerSession attachAsOriginator(@NonNull Identity originatorIdentity,
+ @NonNull ModuleProperties moduleProperties,
@NonNull IBinder client) {
try (SafeCloseable ignored = PermissionUtil.establishIdentityDirect(
originatorIdentity)) {
- return new SoundTriggerSessionStub(client);
+ return new SoundTriggerSessionStub(client, newSoundTriggerHelper(moduleProperties));
}
}
@Override
- public ISoundTriggerSession attachAsMiddleman(Identity originatorIdentity,
- Identity middlemanIdentity,
+ public ISoundTriggerSession attachAsMiddleman(@NonNull Identity originatorIdentity,
+ @NonNull Identity middlemanIdentity,
+ @NonNull ModuleProperties moduleProperties,
@NonNull IBinder client) {
try (SafeCloseable ignored = PermissionUtil.establishIdentityIndirect(mContext,
SOUNDTRIGGER_DELEGATE_IDENTITY, middlemanIdentity,
originatorIdentity)) {
- return new SoundTriggerSessionStub(client);
+ return new SoundTriggerSessionStub(client, newSoundTriggerHelper(moduleProperties));
+ }
+ }
+
+ @Override
+ public List<ModuleProperties> listModuleProperties(@NonNull Identity originatorIdentity) {
+ try (SafeCloseable ignored = PermissionUtil.establishIdentityDirect(
+ originatorIdentity)) {
+ Identity middlemanIdentity = new Identity();
+ middlemanIdentity.packageName = ActivityThread.currentOpPackageName();
+ ArrayList<ModuleProperties> moduleList = new ArrayList<>();
+ SoundTrigger.listModulesAsMiddleman(moduleList, middlemanIdentity,
+ originatorIdentity);
+ return moduleList;
}
}
}
@@ -269,8 +295,8 @@
private final Object mCallbacksLock = new Object();
private final TreeMap<UUID, IRecognitionStatusCallback> mCallbacks = new TreeMap<>();
- SoundTriggerSessionStub(@NonNull IBinder client) {
- mSoundTriggerHelper = newSoundTriggerHelper();
+ SoundTriggerSessionStub(@NonNull IBinder client, SoundTriggerHelper soundTriggerHelper) {
+ mSoundTriggerHelper = soundTriggerHelper;
mClient = client;
mOriginatorIdentity = IdentityContext.getNonNull();
try {
@@ -1615,8 +1641,18 @@
}
@Override
- public Session attach(@NonNull IBinder client) {
- return new SessionImpl(newSoundTriggerHelper(), client);
+ public Session attach(@NonNull IBinder client, ModuleProperties underlyingModule) {
+ return new SessionImpl(newSoundTriggerHelper(underlyingModule), client);
+ }
+
+ @Override
+ public List<ModuleProperties> listModuleProperties(Identity originatorIdentity) {
+ Identity middlemanIdentity = new Identity();
+ middlemanIdentity.packageName = ActivityThread.currentOpPackageName();
+ ArrayList<ModuleProperties> moduleList = new ArrayList<>();
+ SoundTrigger.listModulesAsMiddleman(moduleList, middlemanIdentity,
+ originatorIdentity);
+ return moduleList;
}
@Override
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 717f4e7..fd54293 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -377,7 +377,8 @@
@Override
public @NonNull IVoiceInteractionSoundTriggerSession createSoundTriggerSessionAsOriginator(
- @NonNull Identity originatorIdentity, IBinder client) {
+ @NonNull Identity originatorIdentity, IBinder client,
+ @NonNull ModuleProperties moduleProperties) {
Objects.requireNonNull(originatorIdentity);
boolean forHotwordDetectionService;
synchronized (VoiceInteractionManagerServiceStub.this) {
@@ -396,7 +397,7 @@
originatorIdentity.uid = Binder.getCallingUid();
originatorIdentity.pid = Binder.getCallingPid();
session = new SoundTriggerSessionPermissionsDecorator(
- createSoundTriggerSessionForSelfIdentity(client),
+ createSoundTriggerSessionForSelfIdentity(client, moduleProperties),
mContext,
originatorIdentity);
} else {
@@ -405,25 +406,35 @@
}
try (SafeCloseable ignored = PermissionUtil.establishIdentityDirect(
originatorIdentity)) {
- session = new SoundTriggerSession(mSoundTriggerInternal.attach(client));
+ session = new SoundTriggerSession(mSoundTriggerInternal.attach(client,
+ moduleProperties));
}
}
return new SoundTriggerSessionBinderProxy(session);
}
private IVoiceInteractionSoundTriggerSession createSoundTriggerSessionForSelfIdentity(
- IBinder client) {
+ IBinder client, ModuleProperties moduleProperties) {
Identity identity = new Identity();
identity.uid = Process.myUid();
identity.pid = Process.myPid();
identity.packageName = ActivityThread.currentOpPackageName();
return Binder.withCleanCallingIdentity(() -> {
try (SafeCloseable ignored = IdentityContext.create(identity)) {
- return new SoundTriggerSession(mSoundTriggerInternal.attach(client));
+ return new SoundTriggerSession(
+ mSoundTriggerInternal.attach(client, moduleProperties));
}
});
}
+ @Override
+ public List<ModuleProperties> listModuleProperties(Identity originatorIdentity) {
+ synchronized (VoiceInteractionManagerServiceStub.this) {
+ enforceIsCurrentVoiceInteractionService();
+ }
+ return mSoundTriggerInternal.listModuleProperties(originatorIdentity);
+ }
+
// TODO: VI Make sure the caller is the current user or profile
void startLocalVoiceInteraction(@NonNull final IBinder token,
@Nullable String attributionTag, @Nullable Bundle options) {
@@ -1038,7 +1049,8 @@
@Override
public int startAssistantActivity(@NonNull IBinder token, @NonNull Intent intent,
- @Nullable String resolvedType, @Nullable String attributionTag) {
+ @Nullable String resolvedType, @NonNull String attributionTag,
+ @NonNull Bundle bundle) {
synchronized (this) {
if (mImpl == null) {
Slog.w(TAG, "startAssistantActivity without running voice interaction service");
@@ -1049,7 +1061,7 @@
final long caller = Binder.clearCallingIdentity();
try {
return mImpl.startAssistantActivityLocked(attributionTag, callingPid,
- callingUid, token, intent, resolvedType);
+ callingUid, token, intent, resolvedType, bundle);
} finally {
Binder.restoreCallingIdentity(caller);
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index ad0e921..96b69f8 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -29,7 +29,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
-import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
import android.app.AppGlobals;
import android.app.ApplicationExitInfo;
@@ -376,7 +375,8 @@
@GuardedBy("this")
public int startAssistantActivityLocked(@Nullable String callingFeatureId, int callingPid,
- int callingUid, IBinder token, Intent intent, String resolvedType) {
+ int callingUid, IBinder token, Intent intent, String resolvedType,
+ @NonNull Bundle bundle) {
try {
if (mActiveSession == null || token != mActiveSession.mToken) {
Slog.w(TAG, "startAssistantActivity does not match active session");
@@ -388,10 +388,10 @@
}
intent = new Intent(intent);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- final ActivityOptions options = ActivityOptions.makeBasic();
- options.setLaunchActivityType(ACTIVITY_TYPE_ASSISTANT);
+ // TODO: make the key public hidden
+ bundle.putInt("android.activity.activityType", ACTIVITY_TYPE_ASSISTANT);
return mAtm.startAssistantActivity(mComponent.getPackageName(), callingFeatureId,
- callingPid, callingUid, intent, resolvedType, options.toBundle(), mUser);
+ callingPid, callingUid, intent, resolvedType, bundle, mUser);
} catch (RemoteException e) {
throw new IllegalStateException("Unexpected remote error", e);
}