Merge changes I958029a4,I45653246,I49161c8f,I1c862e86,I5e8c0bbe, ...
* changes:
Add action to query <Set Audio Volume Level> support
Add feature action for sending <Give Features>
Update CEC network with feature support on receiving <Report Features>
Create class for building and parsing <Report Features>
Validate all messages on construction
Keep DeviceFeatures updated in local devices.
Add CEC feature tracking to HdmiDeviceInfo
Refactor HdmiDeviceInfo construction to use a builder
diff --git a/Android.bp b/Android.bp
index 070bb73..e96c731 100644
--- a/Android.bp
+++ b/Android.bp
@@ -434,7 +434,6 @@
name: "framework-ike-shared-srcs",
visibility: ["//packages/modules/IPsec"],
srcs: [
- "core/java/android/net/annotations/PolicyDirection.java",
"core/java/com/android/internal/util/HexDump.java",
"core/java/com/android/internal/util/WakeupMessage.java",
"services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java",
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 3fb1fad..12a8654 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1556,7 +1556,7 @@
Slog.d(TAG, "UID " + uid + " bias changed from " + prevBias + " to " + newBias);
}
for (int c = 0; c < mControllers.size(); ++c) {
- mControllers.get(c).onUidBiasChangedLocked(uid, newBias);
+ mControllers.get(c).onUidBiasChangedLocked(uid, prevBias, newBias);
}
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java
index f733849..63781ae 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java
@@ -18,6 +18,14 @@
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+import android.annotation.NonNull;
+import android.app.job.JobInfo;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+import android.os.BatteryManagerInternal;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
@@ -27,6 +35,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.server.JobSchedulerBackgroundThread;
+import com.android.server.LocalServices;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.StateControllerProto;
@@ -42,10 +51,26 @@
private static final boolean DEBUG = JobSchedulerService.DEBUG
|| Log.isLoggable(TAG, Log.DEBUG);
+ @GuardedBy("mLock")
private final ArraySet<JobStatus> mTrackedTasks = new ArraySet<>();
+ /**
+ * List of jobs that started while the UID was in the TOP state.
+ */
+ @GuardedBy("mLock")
+ private final ArraySet<JobStatus> mTopStartedJobs = new ArraySet<>();
+
+ private final PowerTracker mPowerTracker;
+
+ /**
+ * Helper set to avoid too much GC churn from frequent calls to
+ * {@link #maybeReportNewChargingStateLocked()}.
+ */
+ private final ArraySet<JobStatus> mChangedJobs = new ArraySet<>();
public BatteryController(JobSchedulerService service) {
super(service);
+ mPowerTracker = new PowerTracker();
+ mPowerTracker.startTracking();
}
@Override
@@ -54,8 +79,15 @@
final long nowElapsed = sElapsedRealtimeClock.millis();
mTrackedTasks.add(taskStatus);
taskStatus.setTrackingController(JobStatus.TRACKING_BATTERY);
- taskStatus.setChargingConstraintSatisfied(nowElapsed,
- mService.isBatteryCharging() && mService.isBatteryNotLow());
+ if (taskStatus.hasChargingConstraint()) {
+ if (hasTopExemptionLocked(taskStatus)) {
+ taskStatus.setChargingConstraintSatisfied(nowElapsed,
+ mPowerTracker.isPowerConnected());
+ } else {
+ taskStatus.setChargingConstraintSatisfied(nowElapsed,
+ mService.isBatteryCharging() && mService.isBatteryNotLow());
+ }
+ }
taskStatus.setBatteryNotLowConstraintSatisfied(nowElapsed, mService.isBatteryNotLow());
}
}
@@ -66,9 +98,32 @@
}
@Override
+ @GuardedBy("mLock")
+ public void prepareForExecutionLocked(JobStatus jobStatus) {
+ if (DEBUG) {
+ Slog.d(TAG, "Prepping for " + jobStatus.toShortString());
+ }
+
+ final int uid = jobStatus.getSourceUid();
+ if (mService.getUidBias(uid) == JobInfo.BIAS_TOP_APP) {
+ if (DEBUG) {
+ Slog.d(TAG, jobStatus.toShortString() + " is top started job");
+ }
+ mTopStartedJobs.add(jobStatus);
+ }
+ }
+
+ @Override
+ @GuardedBy("mLock")
+ public void unprepareFromExecutionLocked(JobStatus jobStatus) {
+ mTopStartedJobs.remove(jobStatus);
+ }
+
+ @Override
public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob, boolean forUpdate) {
if (taskStatus.clearTrackingController(JobStatus.TRACKING_BATTERY)) {
mTrackedTasks.remove(taskStatus);
+ mTopStartedJobs.remove(taskStatus);
}
}
@@ -90,33 +145,124 @@
});
}
+ @Override
+ @GuardedBy("mLock")
+ public void onUidBiasChangedLocked(int uid, int prevBias, int newBias) {
+ if (prevBias == JobInfo.BIAS_TOP_APP || newBias == JobInfo.BIAS_TOP_APP) {
+ maybeReportNewChargingStateLocked();
+ }
+ }
+
+ @GuardedBy("mLock")
+ private boolean hasTopExemptionLocked(@NonNull JobStatus taskStatus) {
+ return mService.getUidBias(taskStatus.getSourceUid()) == JobInfo.BIAS_TOP_APP
+ || mTopStartedJobs.contains(taskStatus);
+ }
+
@GuardedBy("mLock")
private void maybeReportNewChargingStateLocked() {
+ final boolean powerConnected = mPowerTracker.isPowerConnected();
final boolean stablePower = mService.isBatteryCharging() && mService.isBatteryNotLow();
final boolean batteryNotLow = mService.isBatteryNotLow();
if (DEBUG) {
- Slog.d(TAG, "maybeReportNewChargingStateLocked: " + stablePower);
+ Slog.d(TAG, "maybeReportNewChargingStateLocked: "
+ + powerConnected + "/" + stablePower + "/" + batteryNotLow);
}
final long nowElapsed = sElapsedRealtimeClock.millis();
- boolean reportChange = false;
for (int i = mTrackedTasks.size() - 1; i >= 0; i--) {
final JobStatus ts = mTrackedTasks.valueAt(i);
- reportChange |= ts.setChargingConstraintSatisfied(nowElapsed, stablePower);
- reportChange |= ts.setBatteryNotLowConstraintSatisfied(nowElapsed, batteryNotLow);
+ if (ts.hasChargingConstraint()) {
+ if (hasTopExemptionLocked(ts)
+ && ts.getEffectivePriority() >= JobInfo.PRIORITY_DEFAULT) {
+ // If the job started while the app was on top or the app is currently on top,
+ // let the job run as long as there's power connected, even if the device isn't
+ // officially charging.
+ // For user requested/initiated jobs, users may be confused when the task stops
+ // running even though the device is plugged in.
+ // Low priority jobs don't need to be exempted.
+ if (ts.setChargingConstraintSatisfied(nowElapsed, powerConnected)) {
+ mChangedJobs.add(ts);
+ }
+ } else if (ts.setChargingConstraintSatisfied(nowElapsed, stablePower)) {
+ mChangedJobs.add(ts);
+ }
+ }
+ if (ts.hasBatteryNotLowConstraint()
+ && ts.setBatteryNotLowConstraintSatisfied(nowElapsed, batteryNotLow)) {
+ mChangedJobs.add(ts);
+ }
}
if (stablePower || batteryNotLow) {
// If one of our conditions has been satisfied, always schedule any newly ready jobs.
mStateChangedListener.onRunJobNow(null);
- } else if (reportChange) {
+ } else if (mChangedJobs.size() > 0) {
// Otherwise, just let the job scheduler know the state has changed and take care of it
// as it thinks is best.
- mStateChangedListener.onControllerStateChanged(mTrackedTasks);
+ mStateChangedListener.onControllerStateChanged(mChangedJobs);
+ }
+ mChangedJobs.clear();
+ }
+
+ private final class PowerTracker extends BroadcastReceiver {
+ /**
+ * Track whether there is power connected. It doesn't mean the device is charging.
+ * Use {@link JobSchedulerService#isBatteryCharging()} to determine if the device is
+ * charging.
+ */
+ private boolean mPowerConnected;
+
+ PowerTracker() {
+ }
+
+ void startTracking() {
+ IntentFilter filter = new IntentFilter();
+
+ filter.addAction(Intent.ACTION_POWER_CONNECTED);
+ filter.addAction(Intent.ACTION_POWER_DISCONNECTED);
+ mContext.registerReceiver(this, filter);
+
+ // Initialize tracker state.
+ BatteryManagerInternal batteryManagerInternal =
+ LocalServices.getService(BatteryManagerInternal.class);
+ mPowerConnected = batteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
+ }
+
+ boolean isPowerConnected() {
+ return mPowerConnected;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ synchronized (mLock) {
+ final String action = intent.getAction();
+
+ if (Intent.ACTION_POWER_CONNECTED.equals(action)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Power connected @ " + sElapsedRealtimeClock.millis());
+ }
+ if (mPowerConnected) {
+ return;
+ }
+ mPowerConnected = true;
+ } else if (Intent.ACTION_POWER_DISCONNECTED.equals(action)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Power disconnected @ " + sElapsedRealtimeClock.millis());
+ }
+ if (!mPowerConnected) {
+ return;
+ }
+ mPowerConnected = false;
+ }
+
+ maybeReportNewChargingStateLocked();
+ }
}
}
@Override
public void dumpControllerStateLocked(IndentingPrintWriter pw,
Predicate<JobStatus> predicate) {
+ pw.println("Power connected: " + mPowerTracker.isPowerConnected());
pw.println("Stable power: " + (mService.isBatteryCharging() && mService.isBatteryNotLow()));
pw.println("Not low: " + mService.isBatteryNotLow());
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index 9fb7ab5..c678755 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -517,7 +517,7 @@
@GuardedBy("mLock")
@Override
- public void onUidBiasChangedLocked(int uid, int newBias) {
+ public void onUidBiasChangedLocked(int uid, int prevBias, int newBias) {
UidStats uidStats = mUidStats.get(uid);
if (uidStats != null && uidStats.baseBias != newBias) {
uidStats.baseBias = newBias;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java
index 9749c80..428f2cb 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java
@@ -40,7 +40,6 @@
import android.util.Log;
import android.util.Slog;
import android.util.SparseArrayMap;
-import android.util.SparseBooleanArray;
import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
@@ -81,9 +80,6 @@
*/
@GuardedBy("mLock")
private final SparseArrayMap<String, Long> mEstimatedLaunchTimes = new SparseArrayMap<>();
- /** Cached list of UIDs in the TOP state. */
- @GuardedBy("mLock")
- private final SparseBooleanArray mTopUids = new SparseBooleanArray();
private final ThresholdAlarmListener mThresholdAlarmListener;
/**
@@ -186,15 +182,9 @@
@GuardedBy("mLock")
@Override
- public void onUidBiasChangedLocked(int uid, int newBias) {
+ public void onUidBiasChangedLocked(int uid, int prevBias, int newBias) {
final boolean isNowTop = newBias == JobInfo.BIAS_TOP_APP;
- final boolean wasTop = mTopUids.get(uid);
- if (isNowTop) {
- mTopUids.put(uid, true);
- } else {
- // Delete entries of non-top apps so the set doesn't get too large.
- mTopUids.delete(uid);
- }
+ final boolean wasTop = prevBias == JobInfo.BIAS_TOP_APP;
if (isNowTop != wasTop) {
mHandler.obtainMessage(MSG_PROCESS_TOP_STATE_CHANGE, uid, 0).sendToTarget();
}
@@ -314,7 +304,8 @@
// 3. The app is not open but has an active widget (we can't tell if a widget displays
// status/data, so this assumes the prefetch job is to update the data displayed on
// the widget).
- final boolean appIsOpen = mTopUids.get(jobStatus.getSourceUid());
+ final boolean appIsOpen =
+ mService.getUidBias(jobStatus.getSourceUid()) == JobInfo.BIAS_TOP_APP;
final boolean satisfied;
if (!appIsOpen) {
final int userId = jobStatus.getSourceUserId();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java
index 509fb69..2a2d602 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java
@@ -144,7 +144,7 @@
* important the UID is.
*/
@GuardedBy("mLock")
- public void onUidBiasChangedLocked(int uid, int newBias) {
+ public void onUidBiasChangedLocked(int uid, int prevBias, int newBias) {
}
protected boolean wouldBeReadyWithConstraintLocked(JobStatus jobStatus, int constraint) {
diff --git a/api/api.go b/api/api.go
index 3b0e300..e4c1b96 100644
--- a/api/api.go
+++ b/api/api.go
@@ -111,7 +111,7 @@
props.Tools = []string{"metalava"}
props.Out = []string{filename}
props.Cmd = proptools.StringPtr(metalavaCmd + "$(in) --api $(out)")
- props.Srcs = createSrcs(txt.BaseTxt, txt.Modules, txt.ModuleTag)
+ props.Srcs = append([]string{txt.BaseTxt}, createSrcs(txt.Modules, txt.ModuleTag)...)
props.Dists = []android.Dist{
{
Targets: []string{"droidcore"},
@@ -134,7 +134,7 @@
props.Tools = []string{"merge_zips"}
props.Out = []string{"current.srcjar"}
props.Cmd = proptools.StringPtr("$(location merge_zips) $(out) $(in)")
- props.Srcs = createSrcs(":api-stubs-docs-non-updatable", modules, "{.public.stubs.source}")
+ 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)
}
@@ -150,7 +150,7 @@
props.Out = []string{"annotations.zip"}
props.Cmd = proptools.StringPtr("$(location merge_annotation_zips) $(genDir)/out $(in) && " +
"$(location soong_zip) -o $(out) -C $(genDir)/out -D $(genDir)/out")
- props.Srcs = createSrcs(":android-non-updatable-doc-stubs{.annotations.zip}", modules, "{.public.annotations.zip}")
+ props.Srcs = append([]string{":android-non-updatable-doc-stubs{.annotations.zip}"}, createSrcs(modules, "{.public.annotations.zip}")...)
ctx.CreateModule(genrule.GenRuleFactory, &props)
}
@@ -172,7 +172,7 @@
// Note: order matters: first parameter is the full api-versions.xml
// after that the stubs files in any order
// stubs files are all modules that export API surfaces EXCEPT ART
- props.Srcs = createSrcs(":framework-doc-stubs{.api_versions.xml}", modules, ".stubs{.jar}")
+ props.Srcs = append([]string{":framework-doc-stubs{.api_versions.xml}"}, createSrcs(modules, ".stubs{.jar}")...)
props.Dists = []android.Dist{{Targets: []string{"sdk"}}}
ctx.CreateModule(genrule.GenRuleFactory, &props)
}
@@ -182,46 +182,12 @@
modules = removeAll(modules, []string{art, conscrypt, i18n})
props := libraryProps{}
props.Name = proptools.StringPtr("framework-updatable-stubs-module_libs_api")
- props.Static_libs = appendStr(modules, ".stubs.module_lib")
+ props.Static_libs = transformArray(modules, "", ".stubs.module_lib")
props.Sdk_version = proptools.StringPtr("module_current")
props.Visibility = []string{"//frameworks/base"}
ctx.CreateModule(java.LibraryFactory, &props)
}
-func appendStr(modules []string, s string) []string {
- a := make([]string, 0, len(modules))
- for _, module := range modules {
- a = append(a, module+s)
- }
- return a
-}
-
-func createSrcs(base string, modules []string, tag string) []string {
- a := make([]string, 0, len(modules)+1)
- a = append(a, base)
- for _, module := range modules {
- a = append(a, ":"+module+tag)
- }
- return a
-}
-
-func removeAll(s []string, vs []string) []string {
- for _, v := range vs {
- s = remove(s, v)
- }
- return s
-}
-
-func remove(s []string, v string) []string {
- s2 := make([]string, 0, len(s))
- for _, sv := range s {
- if sv != v {
- s2 = append(s2, sv)
- }
- }
- return s2
-}
-
func createMergedTxts(ctx android.LoadHookContext, bootclasspath, system_server_classpath []string) {
var textFiles []MergedTxtDefinition
// Two module libraries currently do not support @SystemApi so only have the public scope.
@@ -287,3 +253,36 @@
android.AddLoadHook(module, func(ctx android.LoadHookContext) { module.createInternalModules(ctx) })
return module
}
+
+// Various utility methods below.
+
+// Creates an array of ":<m><tag>" for each m in <modules>.
+func createSrcs(modules []string, tag string) []string {
+ return transformArray(modules, ":", tag)
+}
+
+// Creates an array of "<prefix><m><suffix>", for each m in <modules>.
+func transformArray(modules []string, prefix, suffix string) []string {
+ a := make([]string, 0, len(modules))
+ for _, module := range modules {
+ a = append(a, prefix+module+suffix)
+ }
+ return a
+}
+
+func removeAll(s []string, vs []string) []string {
+ for _, v := range vs {
+ s = remove(s, v)
+ }
+ return s
+}
+
+func remove(s []string, v string) []string {
+ s2 := make([]string, 0, len(s))
+ for _, sv := range s {
+ if sv != v {
+ s2 = append(s2, sv)
+ }
+ }
+ return s2
+}
diff --git a/core/api/current.txt b/core/api/current.txt
index 4f15fa9..385899a 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -17,6 +17,7 @@
field public static final String ACCESS_MEDIA_LOCATION = "android.permission.ACCESS_MEDIA_LOCATION";
field public static final String ACCESS_NETWORK_STATE = "android.permission.ACCESS_NETWORK_STATE";
field public static final String ACCESS_NOTIFICATION_POLICY = "android.permission.ACCESS_NOTIFICATION_POLICY";
+ field public static final String ACCESS_SUPPLEMENTAL_APIS = "android.permission.ACCESS_SUPPLEMENTAL_APIS";
field public static final String ACCESS_WIFI_STATE = "android.permission.ACCESS_WIFI_STATE";
field public static final String ACCOUNT_MANAGER = "android.permission.ACCOUNT_MANAGER";
field public static final String ACTIVITY_RECOGNITION = "android.permission.ACTIVITY_RECOGNITION";
@@ -18330,10 +18331,12 @@
ctor public BiometricPrompt.CryptoObject(@NonNull java.security.Signature);
ctor public BiometricPrompt.CryptoObject(@NonNull javax.crypto.Cipher);
ctor public BiometricPrompt.CryptoObject(@NonNull javax.crypto.Mac);
- ctor public BiometricPrompt.CryptoObject(@NonNull android.security.identity.IdentityCredential);
+ ctor @Deprecated public BiometricPrompt.CryptoObject(@NonNull android.security.identity.IdentityCredential);
+ ctor public BiometricPrompt.CryptoObject(@NonNull android.security.identity.PresentationSession);
method public javax.crypto.Cipher getCipher();
- method @Nullable public android.security.identity.IdentityCredential getIdentityCredential();
+ method @Deprecated @Nullable public android.security.identity.IdentityCredential getIdentityCredential();
method public javax.crypto.Mac getMac();
+ method @Nullable public android.security.identity.PresentationSession getPresentationSession();
method public java.security.Signature getSignature();
}
@@ -20149,6 +20152,24 @@
field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssAntennaInfo.SphericalCorrections> CREATOR;
}
+ public final class GnssAutomaticGainControl implements android.os.Parcelable {
+ method public int describeContents();
+ method @IntRange(from=0) public long getCarrierFrequencyHz();
+ method public int getConstellationType();
+ method @FloatRange(from=0xffffd8f0, to=10000) public double getLevelDb();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssAutomaticGainControl> CREATOR;
+ }
+
+ public static final class GnssAutomaticGainControl.Builder {
+ ctor public GnssAutomaticGainControl.Builder();
+ ctor public GnssAutomaticGainControl.Builder(@NonNull android.location.GnssAutomaticGainControl);
+ method @NonNull public android.location.GnssAutomaticGainControl build();
+ method @NonNull public android.location.GnssAutomaticGainControl.Builder setCarrierFrequencyHz(@IntRange(from=0) long);
+ method @NonNull public android.location.GnssAutomaticGainControl.Builder setConstellationType(int);
+ method @NonNull public android.location.GnssAutomaticGainControl.Builder setLevelDb(@FloatRange(from=0xffffd8f0, to=10000) double);
+ }
+
public final class GnssCapabilities implements android.os.Parcelable {
method public int describeContents();
method public boolean hasAntennaInfo();
@@ -20205,7 +20226,7 @@
method public double getAccumulatedDeltaRangeMeters();
method public int getAccumulatedDeltaRangeState();
method public double getAccumulatedDeltaRangeUncertaintyMeters();
- method public double getAutomaticGainControlLevelDb();
+ method @Deprecated public double getAutomaticGainControlLevelDb();
method @FloatRange(from=0, to=63) public double getBasebandCn0DbHz();
method @Deprecated public long getCarrierCycles();
method public float getCarrierFrequencyHz();
@@ -20227,7 +20248,7 @@
method public int getState();
method public int getSvid();
method public double getTimeOffsetNanos();
- method public boolean hasAutomaticGainControlLevelDb();
+ method @Deprecated public boolean hasAutomaticGainControlLevelDb();
method public boolean hasBasebandCn0DbHz();
method @Deprecated public boolean hasCarrierCycles();
method public boolean hasCarrierFrequencyHz();
@@ -20289,11 +20310,21 @@
public final class GnssMeasurementsEvent implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.location.GnssClock getClock();
+ method @NonNull public java.util.Collection<android.location.GnssAutomaticGainControl> getGnssAutomaticGainControls();
method @NonNull public java.util.Collection<android.location.GnssMeasurement> getMeasurements();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssMeasurementsEvent> CREATOR;
}
+ public static final class GnssMeasurementsEvent.Builder {
+ ctor public GnssMeasurementsEvent.Builder();
+ ctor public GnssMeasurementsEvent.Builder(@NonNull android.location.GnssMeasurementsEvent);
+ method @NonNull public android.location.GnssMeasurementsEvent build();
+ method @NonNull public android.location.GnssMeasurementsEvent.Builder setClock(@NonNull android.location.GnssClock);
+ method @NonNull public android.location.GnssMeasurementsEvent.Builder setGnssAutomaticGainControls(@NonNull java.util.Collection<android.location.GnssAutomaticGainControl>);
+ method @NonNull public android.location.GnssMeasurementsEvent.Builder setMeasurements(@NonNull java.util.Collection<android.location.GnssMeasurement>);
+ }
+
public abstract static class GnssMeasurementsEvent.Callback {
ctor public GnssMeasurementsEvent.Callback();
method public void onGnssMeasurementsReceived(android.location.GnssMeasurementsEvent);
@@ -20965,6 +20996,7 @@
method @NonNull public java.util.List<android.media.AudioPlaybackConfiguration> getActivePlaybackConfigurations();
method @NonNull public java.util.List<android.media.AudioRecordingConfiguration> getActiveRecordingConfigurations();
method public int getAllowedCapturePolicy();
+ method @NonNull public java.util.List<android.media.AudioDeviceInfo> getAudioDevicesForAttributes(@NonNull android.media.AudioAttributes);
method public int getAudioHwSyncForSession(int);
method @NonNull public java.util.List<android.media.AudioDeviceInfo> getAvailableCommunicationDevices();
method @Nullable public android.media.AudioDeviceInfo getCommunicationDevice();
@@ -27724,6 +27756,8 @@
ctor public VcnCellUnderlyingNetworkTemplate.Builder();
method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate build();
method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setMetered(int);
+ method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setMinDownstreamBandwidthKbps(int, int);
+ method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setMinUpstreamBandwidthKbps(int, int);
method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setOperatorPlmnIds(@NonNull java.util.Set<java.lang.String>);
method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setOpportunistic(int);
method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setRoaming(int);
@@ -27784,6 +27818,10 @@
public abstract class VcnUnderlyingNetworkTemplate {
method public int getMetered();
+ method public int getMinEntryDownstreamBandwidthKbps();
+ method public int getMinEntryUpstreamBandwidthKbps();
+ method public int getMinExitDownstreamBandwidthKbps();
+ method public int getMinExitUpstreamBandwidthKbps();
field public static final int MATCH_ANY = 0; // 0x0
field public static final int MATCH_FORBIDDEN = 2; // 0x2
field public static final int MATCH_REQUIRED = 1; // 0x1
@@ -27797,6 +27835,8 @@
ctor public VcnWifiUnderlyingNetworkTemplate.Builder();
method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate build();
method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate.Builder setMetered(int);
+ method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate.Builder setMinDownstreamBandwidthKbps(int, int);
+ method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate.Builder setMinUpstreamBandwidthKbps(int, int);
method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate.Builder setSsids(@NonNull java.util.Set<java.lang.String>);
}
@@ -38029,6 +38069,51 @@
ctor public CipherSuiteNotSupportedException(@NonNull String, @NonNull Throwable);
}
+ public class CredentialDataRequest {
+ method @NonNull public java.util.Map<java.lang.String,java.util.Collection<java.lang.String>> getDeviceSignedEntriesToRequest();
+ method @NonNull public java.util.Map<java.lang.String,java.util.Collection<java.lang.String>> getIssuerSignedEntriesToRequest();
+ method @Nullable public byte[] getReaderSignature();
+ method @Nullable public byte[] getRequestMessage();
+ method public boolean isAllowUsingExhaustedKeys();
+ method public boolean isAllowUsingExpiredKeys();
+ method public boolean isIncrementUseCount();
+ }
+
+ public static final class CredentialDataRequest.Builder {
+ ctor public CredentialDataRequest.Builder();
+ method @NonNull public android.security.identity.CredentialDataRequest build();
+ method @NonNull public android.security.identity.CredentialDataRequest.Builder setAllowUsingExhaustedKeys(boolean);
+ method @NonNull public android.security.identity.CredentialDataRequest.Builder setAllowUsingExpiredKeys(boolean);
+ method @NonNull public android.security.identity.CredentialDataRequest.Builder setDeviceSignedEntriesToRequest(@NonNull java.util.Map<java.lang.String,java.util.Collection<java.lang.String>>);
+ method @NonNull public android.security.identity.CredentialDataRequest.Builder setIncrementUseCount(boolean);
+ method @NonNull public android.security.identity.CredentialDataRequest.Builder setIssuerSignedEntriesToRequest(@NonNull java.util.Map<java.lang.String,java.util.Collection<java.lang.String>>);
+ method @NonNull public android.security.identity.CredentialDataRequest.Builder setReaderSignature(@NonNull byte[]);
+ method @NonNull public android.security.identity.CredentialDataRequest.Builder setRequestMessage(@NonNull byte[]);
+ }
+
+ public abstract class CredentialDataResult {
+ method @Nullable public abstract byte[] getDeviceMac();
+ method @NonNull public abstract byte[] getDeviceNameSpaces();
+ method @NonNull public abstract android.security.identity.CredentialDataResult.Entries getDeviceSignedEntries();
+ method @NonNull public abstract android.security.identity.CredentialDataResult.Entries getIssuerSignedEntries();
+ method @NonNull public abstract byte[] getStaticAuthenticationData();
+ }
+
+ public static interface CredentialDataResult.Entries {
+ method @Nullable public byte[] getEntry(@NonNull String, @NonNull String);
+ method @NonNull public java.util.Collection<java.lang.String> getEntryNames(@NonNull String);
+ method @NonNull public java.util.Collection<java.lang.String> getNamespaces();
+ method @NonNull public java.util.Collection<java.lang.String> getRetrievedEntryNames(@NonNull String);
+ method public int getStatus(@NonNull String, @NonNull String);
+ field public static final int STATUS_NOT_IN_REQUEST_MESSAGE = 3; // 0x3
+ field public static final int STATUS_NOT_REQUESTED = 2; // 0x2
+ field public static final int STATUS_NO_ACCESS_CONTROL_PROFILES = 6; // 0x6
+ field public static final int STATUS_NO_SUCH_ENTRY = 1; // 0x1
+ field public static final int STATUS_OK = 0; // 0x0
+ field public static final int STATUS_READER_AUTHENTICATION_FAILED = 5; // 0x5
+ field public static final int STATUS_USER_AUTHENTICATION_FAILED = 4; // 0x4
+ }
+
public class DocTypeNotSupportedException extends android.security.identity.IdentityCredentialException {
ctor public DocTypeNotSupportedException(@NonNull String);
ctor public DocTypeNotSupportedException(@NonNull String, @NonNull Throwable);
@@ -38040,19 +38125,19 @@
}
public abstract class IdentityCredential {
- method @NonNull public abstract java.security.KeyPair createEphemeralKeyPair();
- method @NonNull public abstract byte[] decryptMessageFromReader(@NonNull byte[]) throws android.security.identity.MessageDecryptionException;
+ method @Deprecated @NonNull public abstract java.security.KeyPair createEphemeralKeyPair();
+ method @Deprecated @NonNull public abstract byte[] decryptMessageFromReader(@NonNull byte[]) throws android.security.identity.MessageDecryptionException;
method @NonNull public byte[] delete(@NonNull byte[]);
- method @NonNull public abstract byte[] encryptMessageToReader(@NonNull byte[]);
+ method @Deprecated @NonNull public abstract byte[] encryptMessageToReader(@NonNull byte[]);
method @NonNull public abstract java.util.Collection<java.security.cert.X509Certificate> getAuthKeysNeedingCertification();
method @NonNull public abstract int[] getAuthenticationDataUsageCount();
method @NonNull public abstract java.util.Collection<java.security.cert.X509Certificate> getCredentialKeyCertificateChain();
- method @NonNull public abstract android.security.identity.ResultData getEntries(@Nullable byte[], @NonNull java.util.Map<java.lang.String,java.util.Collection<java.lang.String>>, @Nullable byte[], @Nullable byte[]) throws android.security.identity.EphemeralPublicKeyNotFoundException, android.security.identity.InvalidReaderSignatureException, android.security.identity.InvalidRequestMessageException, android.security.identity.NoAuthenticationKeyAvailableException, android.security.identity.SessionTranscriptMismatchException;
+ method @Deprecated @NonNull public abstract android.security.identity.ResultData getEntries(@Nullable byte[], @NonNull java.util.Map<java.lang.String,java.util.Collection<java.lang.String>>, @Nullable byte[], @Nullable byte[]) throws android.security.identity.EphemeralPublicKeyNotFoundException, android.security.identity.InvalidReaderSignatureException, android.security.identity.InvalidRequestMessageException, android.security.identity.NoAuthenticationKeyAvailableException, android.security.identity.SessionTranscriptMismatchException;
method @NonNull public byte[] proveOwnership(@NonNull byte[]);
- method public abstract void setAllowUsingExhaustedKeys(boolean);
- method public void setAllowUsingExpiredKeys(boolean);
+ method @Deprecated public abstract void setAllowUsingExhaustedKeys(boolean);
+ method @Deprecated public void setAllowUsingExpiredKeys(boolean);
method public abstract void setAvailableAuthenticationKeys(int, int);
- method public abstract void setReaderEphemeralPublicKey(@NonNull java.security.PublicKey) throws java.security.InvalidKeyException;
+ method @Deprecated public abstract void setReaderEphemeralPublicKey(@NonNull java.security.PublicKey) throws java.security.InvalidKeyException;
method @Deprecated public abstract void storeStaticAuthenticationData(@NonNull java.security.cert.X509Certificate, @NonNull byte[]) throws android.security.identity.UnknownAuthenticationKeyException;
method public void storeStaticAuthenticationData(@NonNull java.security.cert.X509Certificate, @NonNull java.time.Instant, @NonNull byte[]) throws android.security.identity.UnknownAuthenticationKeyException;
method @NonNull public byte[] update(@NonNull android.security.identity.PersonalizationData);
@@ -38065,6 +38150,7 @@
public abstract class IdentityCredentialStore {
method @NonNull public abstract android.security.identity.WritableIdentityCredential createCredential(@NonNull String, @NonNull String) throws android.security.identity.AlreadyPersonalizedException, android.security.identity.DocTypeNotSupportedException;
+ method @NonNull public android.security.identity.PresentationSession createPresentationSession(int) throws android.security.identity.CipherSuiteNotSupportedException;
method @Deprecated @Nullable public abstract byte[] deleteCredentialByName(@NonNull String);
method @Nullable public abstract android.security.identity.IdentityCredential getCredentialByName(@NonNull String, int) throws android.security.identity.CipherSuiteNotSupportedException;
method @Nullable public static android.security.identity.IdentityCredentialStore getDirectAccessInstance(@NonNull android.content.Context);
@@ -38103,22 +38189,29 @@
method @NonNull public android.security.identity.PersonalizationData.Builder putEntry(@NonNull String, @NonNull String, @NonNull java.util.Collection<android.security.identity.AccessControlProfileId>, @NonNull byte[]);
}
- public abstract class ResultData {
- method @NonNull public abstract byte[] getAuthenticatedData();
- method @Nullable public abstract byte[] getEntry(@NonNull String, @NonNull String);
- method @Nullable public abstract java.util.Collection<java.lang.String> getEntryNames(@NonNull String);
- method @Nullable public abstract byte[] getMessageAuthenticationCode();
- method @NonNull public abstract java.util.Collection<java.lang.String> getNamespaces();
- method @Nullable public abstract java.util.Collection<java.lang.String> getRetrievedEntryNames(@NonNull String);
- method @NonNull public abstract byte[] getStaticAuthenticationData();
- method public abstract int getStatus(@NonNull String, @NonNull String);
- field public static final int STATUS_NOT_IN_REQUEST_MESSAGE = 3; // 0x3
- field public static final int STATUS_NOT_REQUESTED = 2; // 0x2
- field public static final int STATUS_NO_ACCESS_CONTROL_PROFILES = 6; // 0x6
- field public static final int STATUS_NO_SUCH_ENTRY = 1; // 0x1
- field public static final int STATUS_OK = 0; // 0x0
- field public static final int STATUS_READER_AUTHENTICATION_FAILED = 5; // 0x5
- field public static final int STATUS_USER_AUTHENTICATION_FAILED = 4; // 0x4
+ public abstract class PresentationSession {
+ method @Nullable public abstract android.security.identity.CredentialDataResult getCredentialData(@NonNull String, @NonNull android.security.identity.CredentialDataRequest) throws android.security.identity.EphemeralPublicKeyNotFoundException, android.security.identity.InvalidReaderSignatureException, android.security.identity.InvalidRequestMessageException, android.security.identity.NoAuthenticationKeyAvailableException;
+ method @NonNull public abstract java.security.KeyPair getEphemeralKeyPair();
+ method public abstract void setReaderEphemeralPublicKey(@NonNull java.security.PublicKey) throws java.security.InvalidKeyException;
+ method public abstract void setSessionTranscript(@NonNull byte[]);
+ }
+
+ @Deprecated public abstract class ResultData {
+ method @Deprecated @NonNull public abstract byte[] getAuthenticatedData();
+ method @Deprecated @Nullable public abstract byte[] getEntry(@NonNull String, @NonNull String);
+ method @Deprecated @Nullable public abstract java.util.Collection<java.lang.String> getEntryNames(@NonNull String);
+ method @Deprecated @Nullable public abstract byte[] getMessageAuthenticationCode();
+ method @Deprecated @NonNull public abstract java.util.Collection<java.lang.String> getNamespaces();
+ method @Deprecated @Nullable public abstract java.util.Collection<java.lang.String> getRetrievedEntryNames(@NonNull String);
+ method @Deprecated @NonNull public abstract byte[] getStaticAuthenticationData();
+ method @Deprecated public abstract int getStatus(@NonNull String, @NonNull String);
+ field @Deprecated public static final int STATUS_NOT_IN_REQUEST_MESSAGE = 3; // 0x3
+ field @Deprecated public static final int STATUS_NOT_REQUESTED = 2; // 0x2
+ field @Deprecated public static final int STATUS_NO_ACCESS_CONTROL_PROFILES = 6; // 0x6
+ field @Deprecated public static final int STATUS_NO_SUCH_ENTRY = 1; // 0x1
+ field @Deprecated public static final int STATUS_OK = 0; // 0x0
+ field @Deprecated public static final int STATUS_READER_AUTHENTICATION_FAILED = 5; // 0x5
+ field @Deprecated public static final int STATUS_USER_AUTHENTICATION_FAILED = 4; // 0x4
}
public class SessionTranscriptMismatchException extends android.security.identity.IdentityCredentialException {
@@ -47166,6 +47259,15 @@
field public float ydpi;
}
+ public interface Dumpable {
+ method public void dump(@NonNull java.io.PrintWriter, @Nullable String[]);
+ method @NonNull public default String getDumpableName();
+ }
+
+ public interface DumpableContainer {
+ method public boolean addDumpable(@NonNull android.util.Dumpable);
+ }
+
public class EventLog {
method public static int getTagCode(String);
method public static String getTagName(int);
@@ -49165,6 +49267,7 @@
field public static final int EDGE_LEFT = 4; // 0x4
field public static final int EDGE_RIGHT = 8; // 0x8
field public static final int EDGE_TOP = 1; // 0x1
+ field public static final int FLAG_CANCELED = 32; // 0x20
field public static final int FLAG_WINDOW_IS_OBSCURED = 1; // 0x1
field public static final int FLAG_WINDOW_IS_PARTIALLY_OBSCURED = 2; // 0x2
field public static final int INVALID_POINTER_ID = -1; // 0xffffffff
@@ -52267,6 +52370,7 @@
method public final float getFontScale();
method @Nullable public final java.util.Locale getLocale();
method @NonNull public android.view.accessibility.CaptioningManager.CaptionStyle getUserStyle();
+ method public boolean isCallCaptioningEnabled();
method public final boolean isEnabled();
method public void removeCaptioningChangeListener(@NonNull android.view.accessibility.CaptioningManager.CaptioningChangeListener);
}
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 4d84537..c4aff2d0 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -9,6 +9,10 @@
package android.app {
+ @UiContext public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
+ method public final boolean addDumpable(@NonNull android.util.Dumpable);
+ }
+
public class ActivityManager {
method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void addHomeVisibilityListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.HomeVisibilityListener);
method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void removeHomeVisibilityListener(@NonNull android.app.HomeVisibilityListener);
@@ -160,6 +164,7 @@
public final class BtProfileConnectionInfo implements android.os.Parcelable {
method @NonNull public static android.media.BtProfileConnectionInfo a2dpInfo(boolean, int);
+ method @NonNull public static android.media.BtProfileConnectionInfo a2dpSinkInfo(int);
method public int describeContents();
method public boolean getIsLeOutput();
method public int getProfile();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 4b74e98..832478c 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -367,7 +367,6 @@
field public static final int config_showDefaultAssistant = 17891329; // 0x1110001
field public static final int config_showDefaultEmergency = 17891330; // 0x1110002
field public static final int config_showDefaultHome = 17891331; // 0x1110003
- field public static final int config_systemCaptionsServiceCallsEnabled;
}
public static final class R.color {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index d3c99b8..f1b4624 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1319,7 +1319,7 @@
method public void setAccumulatedDeltaRangeMeters(double);
method public void setAccumulatedDeltaRangeState(int);
method public void setAccumulatedDeltaRangeUncertaintyMeters(double);
- method public void setAutomaticGainControlLevelInDb(double);
+ method @Deprecated public void setAutomaticGainControlLevelInDb(double);
method public void setBasebandCn0DbHz(double);
method @Deprecated public void setCarrierCycles(long);
method public void setCarrierFrequencyHz(float);
@@ -1346,10 +1346,6 @@
field public static final int ADR_STATE_ALL = 31; // 0x1f
}
- public final class GnssMeasurementsEvent implements android.os.Parcelable {
- ctor public GnssMeasurementsEvent(android.location.GnssClock, android.location.GnssMeasurement[]);
- }
-
public final class GnssNavigationMessage implements android.os.Parcelable {
ctor public GnssNavigationMessage();
method public void reset();
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index cf2b7ac..283345f 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -90,6 +90,7 @@
import android.transition.TransitionManager;
import android.util.ArrayMap;
import android.util.AttributeSet;
+import android.util.Dumpable;
import android.util.EventLog;
import android.util.Log;
import android.util.PrintWriterPrinter;
@@ -145,6 +146,7 @@
import com.android.internal.app.ToolbarActionBar;
import com.android.internal.app.WindowDecorActionBar;
import com.android.internal.policy.PhoneWindow;
+import com.android.internal.util.dump.DumpableContainerImpl;
import dalvik.system.VMRuntime;
@@ -954,6 +956,9 @@
private SplashScreen mSplashScreen;
+ @Nullable
+ private DumpableContainerImpl mDumpableContainer;
+
private final WindowControllerCallback mWindowControllerCallback =
new WindowControllerCallback() {
/**
@@ -7081,8 +7086,23 @@
dumpInner(prefix, fd, writer, args);
}
+ /**
+ * See {@link android.util.DumpableContainer#addDumpable(Dumpable)}.
+ *
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public final boolean addDumpable(@NonNull Dumpable dumpable) {
+ if (mDumpableContainer == null) {
+ mDumpableContainer = new DumpableContainerImpl();
+ }
+ return mDumpableContainer.addDumpable(dumpable);
+ }
+
void dumpInner(@NonNull String prefix, @Nullable FileDescriptor fd,
@NonNull PrintWriter writer, @Nullable String[] args) {
+ String innerPrefix = prefix + " ";
+
if (args != null && args.length > 0) {
// Handle special cases
switch (args[0]) {
@@ -7095,12 +7115,33 @@
case "--translation":
dumpUiTranslation(prefix, writer);
return;
+ case "--list-dumpables":
+ if (mDumpableContainer == null) {
+ writer.print(prefix); writer.println("No dumpables");
+ return;
+ }
+ mDumpableContainer.listDumpables(prefix, writer);
+ return;
+ case "--dump-dumpable":
+ if (args.length == 1) {
+ writer.println("--dump-dumpable requires the dumpable name");
+ return;
+ }
+ if (mDumpableContainer == null) {
+ writer.println("no dumpables");
+ return;
+ }
+ // Strips --dump-dumpable NAME
+ String[] prunedArgs = new String[args.length - 2];
+ System.arraycopy(args, 2, prunedArgs, 0, prunedArgs.length);
+ mDumpableContainer.dumpOneDumpable(prefix, writer, args[1], prunedArgs);
+ return;
}
}
+
writer.print(prefix); writer.print("Local Activity ");
writer.print(Integer.toHexString(System.identityHashCode(this)));
writer.println(" State:");
- String innerPrefix = prefix + " ";
writer.print(innerPrefix); writer.print("mResumed=");
writer.print(mResumed); writer.print(" mStopped=");
writer.print(mStopped); writer.print(" mFinished=");
@@ -7138,6 +7179,10 @@
dumpUiTranslation(prefix, writer);
ResourcesManager.getInstance().dump(prefix, writer);
+
+ if (mDumpableContainer != null) {
+ mDumpableContainer.dumpAllDumpables(prefix, writer, args);
+ }
}
void dumpContentCaptureManager(String prefix, PrintWriter writer) {
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index fe512ff..fa48730 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -312,6 +312,14 @@
@ContextType
private int mContextType;
+ /**
+ * {@code true} to indicate that the {@link Context} owns the {@link #getWindowContextToken()}
+ * and is responsible for detaching the token when the Context is released.
+ *
+ * @see #finalize()
+ */
+ private boolean mOwnsToken = false;
+
@GuardedBy("mSync")
private File mDatabasesDir;
@GuardedBy("mSync")
@@ -3015,7 +3023,7 @@
// WindowContainer. We should detach from WindowContainer when the Context is finalized
// if this Context is not a WindowContext. WindowContext finalization is handled in
// WindowContext class.
- if (mToken instanceof WindowTokenClient && mContextType != CONTEXT_TYPE_WINDOW_CONTEXT) {
+ if (mToken instanceof WindowTokenClient && mOwnsToken) {
((WindowTokenClient) mToken).detachFromWindowContainerIfNeeded();
}
super.finalize();
@@ -3046,6 +3054,7 @@
token.attachContext(context);
token.attachToDisplayContent(displayId);
context.mContextType = CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI;
+ context.mOwnsToken = true;
return context;
}
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index fdcf99d..a82ecce 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -63,6 +63,7 @@
boolean isInInvalidMsgState(String pkg, int uid);
boolean hasUserDemotedInvalidMsgApp(String pkg, int uid);
void setInvalidMsgAppDemoted(String pkg, int uid, boolean isDemoted);
+ boolean hasSentValidBubble(String pkg, int uid);
void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled);
/**
* Updates the notification's enabled state. Additionally locks importance for all of the
diff --git a/core/java/android/bluetooth/OWNERS b/core/java/android/bluetooth/OWNERS
index fbee577..8e9d7b7 100644
--- a/core/java/android/bluetooth/OWNERS
+++ b/core/java/android/bluetooth/OWNERS
@@ -1,6 +1,4 @@
# Bug component: 27441
-rahulsabnis@google.com
sattiraju@google.com
-siyuanh@google.com
-zachoverflow@google.com
+baligh@google.com
diff --git a/core/java/android/companion/AssociationInfo.java b/core/java/android/companion/AssociationInfo.java
index f0566b8..373a8d9 100644
--- a/core/java/android/companion/AssociationInfo.java
+++ b/core/java/android/companion/AssociationInfo.java
@@ -54,13 +54,13 @@
private final @Nullable String mDeviceProfile;
private final boolean mSelfManaged;
- private boolean mNotifyOnDeviceNearby;
+ private final boolean mNotifyOnDeviceNearby;
private final long mTimeApprovedMs;
/**
* A long value indicates the last time connected reported by selfManaged devices
* Default value is Long.MAX_VALUE.
*/
- private long mLastTimeConnectedMs;
+ private final long mLastTimeConnectedMs;
/**
* Creates a new Association.
@@ -160,22 +160,6 @@
return mSelfManaged;
}
- /**
- * Should only be used by the CompanionDeviceManagerService.
- * @hide
- */
- public void setNotifyOnDeviceNearby(boolean notifyOnDeviceNearby) {
- mNotifyOnDeviceNearby = notifyOnDeviceNearby;
- }
-
- /**
- * Should only be used by the CompanionDeviceManagerService.
- * @hide
- */
- public void setLastTimeConnected(long lastTimeConnectedMs) {
- mLastTimeConnectedMs = lastTimeConnectedMs;
- }
-
/** @hide */
public boolean isNotifyOnDeviceNearby() {
return mNotifyOnDeviceNearby;
@@ -330,4 +314,112 @@
return new AssociationInfo(in);
}
};
+
+ /**
+ * Use this method to obtain a builder that you can use to create a copy of the
+ * given {@link AssociationInfo} with modified values of {@code mLastTimeConnected}
+ * or {@code mNotifyOnDeviceNearby}.
+ * <p>
+ * Note that you <b>must</b> call either {@link Builder#setLastTimeConnected(long)
+ * setLastTimeConnected} or {@link Builder#setNotifyOnDeviceNearby(boolean)
+ * setNotifyOnDeviceNearby} before you will be able to call {@link Builder#build() build}.
+ *
+ * This is ensured statically at compile time.
+ *
+ * @hide
+ */
+ @NonNull
+ public static NonActionableBuilder builder(@NonNull AssociationInfo info) {
+ return new Builder(info);
+ }
+
+ /**
+ * @hide
+ */
+ public static final class Builder implements NonActionableBuilder {
+ @NonNull
+ private final AssociationInfo mOriginalInfo;
+ private boolean mNotifyOnDeviceNearby;
+ private long mLastTimeConnectedMs;
+
+ private Builder(@NonNull AssociationInfo info) {
+ mOriginalInfo = info;
+ mNotifyOnDeviceNearby = info.mNotifyOnDeviceNearby;
+ mLastTimeConnectedMs = info.mLastTimeConnectedMs;
+ }
+
+ /**
+ * Should only be used by the CompanionDeviceManagerService.
+ * @hide
+ */
+ @Override
+ @NonNull
+ public Builder setLastTimeConnected(long lastTimeConnectedMs) {
+ if (lastTimeConnectedMs < 0) {
+ throw new IllegalArgumentException(
+ "lastTimeConnectedMs must not be negative! (Given " + lastTimeConnectedMs
+ + " )");
+ }
+ mLastTimeConnectedMs = lastTimeConnectedMs;
+ return this;
+ }
+
+ /**
+ * Should only be used by the CompanionDeviceManagerService.
+ * @hide
+ */
+ @Override
+ @NonNull
+ public Builder setNotifyOnDeviceNearby(boolean notifyOnDeviceNearby) {
+ mNotifyOnDeviceNearby = notifyOnDeviceNearby;
+ return this;
+ }
+
+ /**
+ * @hide
+ */
+ @NonNull
+ public AssociationInfo build() {
+ return new AssociationInfo(
+ mOriginalInfo.mId,
+ mOriginalInfo.mUserId,
+ mOriginalInfo.mPackageName,
+ mOriginalInfo.mDeviceMacAddress,
+ mOriginalInfo.mDisplayName,
+ mOriginalInfo.mDeviceProfile,
+ mOriginalInfo.mSelfManaged,
+ mNotifyOnDeviceNearby,
+ mOriginalInfo.mTimeApprovedMs,
+ mLastTimeConnectedMs
+ );
+ }
+ }
+
+ /**
+ * This interface is returned from the
+ * {@link AssociationInfo#builder(android.companion.AssociationInfo) builder} entry point
+ * to indicate that this builder is not yet in a state that can produce a meaningful
+ * {@link AssociationInfo} object that is different from the one originally passed in.
+ *
+ * <p>
+ * Only by calling one of the setter methods is this builder turned into one where calling
+ * {@link Builder#build() build()} makes sense.
+ *
+ * @hide
+ */
+ public interface NonActionableBuilder {
+ /**
+ * Should only be used by the CompanionDeviceManagerService.
+ * @hide
+ */
+ @NonNull
+ Builder setNotifyOnDeviceNearby(boolean notifyOnDeviceNearby);
+
+ /**
+ * Should only be used by the CompanionDeviceManagerService.
+ * @hide
+ */
+ @NonNull
+ Builder setLastTimeConnected(long lastTimeConnectedMs);
+ }
}
diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl
index 82ad150..85855be 100644
--- a/core/java/android/companion/virtual/IVirtualDevice.aidl
+++ b/core/java/android/companion/virtual/IVirtualDevice.aidl
@@ -16,12 +16,14 @@
package android.companion.virtual;
+import android.app.PendingIntent;
import android.graphics.Point;
import android.hardware.input.VirtualKeyEvent;
import android.hardware.input.VirtualMouseButtonEvent;
import android.hardware.input.VirtualMouseRelativeEvent;
import android.hardware.input.VirtualMouseScrollEvent;
import android.hardware.input.VirtualTouchEvent;
+import android.os.ResultReceiver;
/**
* Interface for a virtual device.
@@ -41,6 +43,7 @@
* Closes the virtual device and frees all associated resources.
*/
void close();
+
void createVirtualKeyboard(
int displayId,
String inputDeviceName,
@@ -66,4 +69,10 @@
boolean sendRelativeEvent(IBinder token, in VirtualMouseRelativeEvent event);
boolean sendScrollEvent(IBinder token, in VirtualMouseScrollEvent event);
boolean sendTouchEvent(IBinder token, in VirtualTouchEvent event);
+
+ /**
+ * Launches a pending intent on the given display that is owned by this virtual device.
+ */
+ void launchPendingIntent(
+ int displayId, in PendingIntent pendingIntent, in ResultReceiver resultReceiver);
}
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index 858e4daa1..8ab6688 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -23,6 +23,8 @@
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.app.Activity;
+import android.app.PendingIntent;
import android.companion.AssociationInfo;
import android.content.Context;
import android.graphics.Point;
@@ -33,15 +35,19 @@
import android.hardware.input.VirtualMouse;
import android.hardware.input.VirtualTouchscreen;
import android.os.Binder;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.view.Surface;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import java.util.concurrent.Executor;
/**
* System level service for managing virtual devices.
@@ -129,6 +135,49 @@
}
/**
+ * Launches a given pending intent on the give display ID.
+ *
+ * @param displayId The display to launch the pending intent on. This display must be
+ * created from this virtual device.
+ * @param pendingIntent The pending intent to be launched. If the intent is an activity
+ * intent, the activity will be started on the virtual display using
+ * {@link android.app.ActivityOptions#setLaunchDisplayId}. If the intent is a service or
+ * broadcast intent, an attempt will be made to catch activities started as a result of
+ * sending the pending intent and move them to the given display.
+ * @param executor The executor to run {@code launchCallback} on.
+ * @param launchCallback Callback that is called when the pending intent launching is
+ * complete.
+ *
+ * @hide
+ */
+ public void launchPendingIntent(
+ int displayId,
+ @NonNull PendingIntent pendingIntent,
+ @NonNull Executor executor,
+ @NonNull LaunchCallback launchCallback) {
+ try {
+ mVirtualDevice.launchPendingIntent(
+ displayId,
+ pendingIntent,
+ new ResultReceiver(new Handler(Looper.myLooper())) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ super.onReceiveResult(resultCode, resultData);
+ executor.execute(() -> {
+ if (resultCode == Activity.RESULT_OK) {
+ launchCallback.onLaunchSuccess();
+ } else {
+ launchCallback.onLaunchFailed();
+ }
+ });
+ }
+ });
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Creates a virtual display for this virtual device. All displays created on the same
* device belongs to the same display group.
*
@@ -299,4 +348,22 @@
}
}
}
+
+ /**
+ * Callback for launching pending intents on the virtual device.
+ *
+ * @hide
+ */
+ // TODO(b/194949534): Unhide this API
+ public interface LaunchCallback {
+ /**
+ * Called when the pending intent launched successfully.
+ */
+ void onLaunchSuccess();
+
+ /**
+ * Called when the pending intent failed to launch.
+ */
+ void onLaunchFailed();
+ }
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index a6d846b..d817f1e 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2734,6 +2734,8 @@
* API shipped in Android 11.
* <li><code>202101</code>: corresponds to the features included in the Identity Credential
* API shipped in Android 12.
+ * <li><code>202201</code>: corresponds to the features included in the Identity Credential
+ * API shipped in Android 13.
* </ul>
*/
@SdkConstant(SdkConstantType.FEATURE)
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index 08f5a8a..0e42b02 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -22,6 +22,8 @@
import android.hardware.input.InputSensorInfo;
import android.os.Build;
+import java.util.UUID;
+
/**
* Class representing a sensor. Use {@link SensorManager#getSensorList} to get
* the list of available sensors. For more information about Android sensors,
@@ -925,6 +927,7 @@
@UnsupportedAppUsage
private int mFlags;
private int mId;
+ private UUID mUuid;
Sensor() {
}
@@ -951,6 +954,8 @@
this.mMaxDelay = sensorInfo.getMaxDelay();
this.mFlags = sensorInfo.getFlags();
this.mId = sensorInfo.getId();
+ // The UUID is never specified when creating a sensor from Input manager
+ this.mUuid = new UUID((long) this.mId, 0);
}
/**
@@ -1040,11 +1045,9 @@
}
/**
- * Do not use.
- *
- * This method throws an UnsupportedOperationException.
- *
- * Use getId() if you want a unique ID.
+ * Reserved for system and audio servers.
+ * When called from an unauthorized context, the UUID will contain the
+ * sensor ID in the MSB and 0 in the LSB.
*
* @see getId
*
@@ -1052,7 +1055,7 @@
*/
@SystemApi
public java.util.UUID getUuid() {
- throw new UnsupportedOperationException();
+ return mUuid;
}
/**
@@ -1286,17 +1289,24 @@
}
/**
- * Sets the ID associated with the sensor.
+ * Sets the UUID associated with the sensor.
*
- * The method name is misleading; while this ID is based on the UUID,
- * we do not pass in the actual UUID.
+ * NOTE: to be used only by native bindings in SensorManager.
+ *
+ * @see #getUuid
+ */
+ private void setUuid(long msb, long lsb) {
+ mUuid = new UUID(msb, lsb);
+ }
+
+ /**
+ * Sets the ID associated with the sensor.
*
* NOTE: to be used only by native bindings in SensorManager.
*
* @see #getId
*/
- private void setUuid(long msb, long lsb) {
- // TODO(b/29547335): Rename this method to setId.
- mId = (int) msb;
+ private void setId(int id) {
+ mId = id;
}
}
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index 6b5bec9..dc65bef 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -38,6 +38,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.security.identity.IdentityCredential;
+import android.security.identity.PresentationSession;
import android.security.keystore.KeyProperties;
import android.text.TextUtils;
import android.util.Log;
@@ -666,8 +667,8 @@
/**
* A wrapper class for the cryptographic operations supported by BiometricPrompt.
*
- * <p>Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac}, and
- * {@link IdentityCredential}.
+ * <p>Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac},
+ * {@link IdentityCredential}, and {@link PresentationSession}.
*
* <p>Cryptographic operations in Android can be split into two categories: auth-per-use and
* time-based. This is specified during key creation via the timeout parameter of the
@@ -697,10 +698,21 @@
super(mac);
}
+ /**
+ * Create from a {@link IdentityCredential} object.
+ *
+ * @param credential a {@link IdentityCredential} object.
+ * @deprecated Use {@link PresentationSession} instead of {@link IdentityCredential}.
+ */
+ @Deprecated
public CryptoObject(@NonNull IdentityCredential credential) {
super(credential);
}
+ public CryptoObject(@NonNull PresentationSession session) {
+ super(session);
+ }
+
/**
* Get {@link Signature} object.
* @return {@link Signature} object or null if this doesn't contain one.
@@ -728,10 +740,20 @@
/**
* Get {@link IdentityCredential} object.
* @return {@link IdentityCredential} object or null if this doesn't contain one.
+ * @deprecated Use {@link PresentationSession} instead of {@link IdentityCredential}.
*/
+ @Deprecated
public @Nullable IdentityCredential getIdentityCredential() {
return super.getIdentityCredential();
}
+
+ /**
+ * Get {@link PresentationSession} object.
+ * @return {@link PresentationSession} object or null if this doesn't contain one.
+ */
+ public @Nullable PresentationSession getPresentationSession() {
+ return super.getPresentationSession();
+ }
}
/**
diff --git a/core/java/android/hardware/biometrics/CryptoObject.java b/core/java/android/hardware/biometrics/CryptoObject.java
index 7648cf2..d415706 100644
--- a/core/java/android/hardware/biometrics/CryptoObject.java
+++ b/core/java/android/hardware/biometrics/CryptoObject.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.security.identity.IdentityCredential;
+import android.security.identity.PresentationSession;
import android.security.keystore2.AndroidKeyStoreProvider;
import java.security.Signature;
@@ -27,8 +28,8 @@
/**
* A wrapper class for the crypto objects supported by BiometricPrompt and FingerprintManager.
- * Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac} and
- * {@link IdentityCredential} objects.
+ * Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac},
+ * {@link IdentityCredential}, and {@link PresentationSession} objects.
* @hide
*/
public class CryptoObject {
@@ -46,10 +47,21 @@
mCrypto = mac;
}
+ /**
+ * Create from a {@link IdentityCredential} object.
+ *
+ * @param credential a {@link IdentityCredential} object.
+ * @deprecated Use {@link PresentationSession} instead of {@link IdentityCredential}.
+ */
+ @Deprecated
public CryptoObject(@NonNull IdentityCredential credential) {
mCrypto = credential;
}
+ public CryptoObject(@NonNull PresentationSession session) {
+ mCrypto = session;
+ }
+
/**
* Get {@link Signature} object.
* @return {@link Signature} object or null if this doesn't contain one.
@@ -77,12 +89,22 @@
/**
* Get {@link IdentityCredential} object.
* @return {@link IdentityCredential} object or null if this doesn't contain one.
+ * @deprecated Use {@link PresentationSession} instead of {@link IdentityCredential}.
*/
+ @Deprecated
public IdentityCredential getIdentityCredential() {
return mCrypto instanceof IdentityCredential ? (IdentityCredential) mCrypto : null;
}
/**
+ * Get {@link PresentationSession} object.
+ * @return {@link PresentationSession} object or null if this doesn't contain one.
+ */
+ public PresentationSession getPresentationSession() {
+ return mCrypto instanceof PresentationSession ? (PresentationSession) mCrypto : null;
+ }
+
+ /**
* @hide
* @return the opId associated with this object or 0 if none
*/
@@ -91,6 +113,8 @@
return 0;
} else if (mCrypto instanceof IdentityCredential) {
return ((IdentityCredential) mCrypto).getCredstoreOperationHandle();
+ } else if (mCrypto instanceof PresentationSession) {
+ return ((PresentationSession) mCrypto).getCredstoreOperationHandle();
}
return AndroidKeyStoreProvider.getKeyStoreOperationHandle(mCrypto);
}
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 56f8142..b970559 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -306,22 +306,21 @@
throw new IllegalArgumentException("Must supply an enrollment callback");
}
- if (cancel != null) {
- if (cancel.isCanceled()) {
- Slog.w(TAG, "enrollment already canceled");
- return;
- } else {
- cancel.setOnCancelListener(new OnEnrollCancelListener());
- }
+ if (cancel != null && cancel.isCanceled()) {
+ Slog.w(TAG, "enrollment already canceled");
+ return;
}
if (mService != null) {
try {
mEnrollmentCallback = callback;
Trace.beginSection("FaceManager#enroll");
- mService.enroll(userId, mToken, hardwareAuthToken, mServiceReceiver,
- mContext.getOpPackageName(), disabledFeatures, previewSurface,
- debugConsent);
+ final long enrollId = mService.enroll(userId, mToken, hardwareAuthToken,
+ mServiceReceiver, mContext.getOpPackageName(), disabledFeatures,
+ previewSurface, debugConsent);
+ if (cancel != null) {
+ cancel.setOnCancelListener(new OnEnrollCancelListener(enrollId));
+ }
} catch (RemoteException e) {
Slog.w(TAG, "Remote exception in enroll: ", e);
// Though this may not be a hardware issue, it will cause apps to give up or
@@ -359,21 +358,20 @@
throw new IllegalArgumentException("Must supply an enrollment callback");
}
- if (cancel != null) {
- if (cancel.isCanceled()) {
- Slog.w(TAG, "enrollRemotely is already canceled.");
- return;
- } else {
- cancel.setOnCancelListener(new OnEnrollCancelListener());
- }
+ if (cancel != null && cancel.isCanceled()) {
+ Slog.w(TAG, "enrollRemotely is already canceled.");
+ return;
}
if (mService != null) {
try {
mEnrollmentCallback = callback;
Trace.beginSection("FaceManager#enrollRemotely");
- mService.enrollRemotely(userId, mToken, hardwareAuthToken, mServiceReceiver,
- mContext.getOpPackageName(), disabledFeatures);
+ final long enrolId = mService.enrollRemotely(userId, mToken, hardwareAuthToken,
+ mServiceReceiver, mContext.getOpPackageName(), disabledFeatures);
+ if (cancel != null) {
+ cancel.setOnCancelListener(new OnEnrollCancelListener(enrolId));
+ }
} catch (RemoteException e) {
Slog.w(TAG, "Remote exception in enrollRemotely: ", e);
// Though this may not be a hardware issue, it will cause apps to give up or
@@ -713,10 +711,10 @@
}
}
- private void cancelEnrollment() {
+ private void cancelEnrollment(long requestId) {
if (mService != null) {
try {
- mService.cancelEnrollment(mToken);
+ mService.cancelEnrollment(mToken, requestId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1100,9 +1098,16 @@
}
private class OnEnrollCancelListener implements OnCancelListener {
+ private final long mAuthRequestId;
+
+ private OnEnrollCancelListener(long id) {
+ mAuthRequestId = id;
+ }
+
@Override
public void onCancel() {
- cancelEnrollment();
+ Slog.d(TAG, "Cancel face enrollment requested for: " + mAuthRequestId);
+ cancelEnrollment(mAuthRequestId);
}
}
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index e919824..989b001 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -76,15 +76,16 @@
void cancelAuthenticationFromService(int sensorId, IBinder token, String opPackageName, long requestId);
// Start face enrollment
- void enroll(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver,
- String opPackageName, in int [] disabledFeatures, in Surface previewSurface, boolean debugConsent);
+ long enroll(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver,
+ String opPackageName, in int [] disabledFeatures,
+ in Surface previewSurface, boolean debugConsent);
// Start remote face enrollment
- void enrollRemotely(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver,
+ long enrollRemotely(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver,
String opPackageName, in int [] disabledFeatures);
// Cancel enrollment in progress
- void cancelEnrollment(IBinder token);
+ void cancelEnrollment(IBinder token, long requestId);
// Removes the specified face enrollment for the specified userId.
void remove(IBinder token, int faceId, int userId, IFaceServiceReceiver receiver,
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index fe04e5d..7e070bc 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -58,6 +58,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.security.identity.IdentityCredential;
+import android.security.identity.PresentationSession;
import android.util.Slog;
import android.view.Surface;
@@ -183,9 +184,16 @@
}
private class OnEnrollCancelListener implements OnCancelListener {
+ private final long mAuthRequestId;
+
+ private OnEnrollCancelListener(long id) {
+ mAuthRequestId = id;
+ }
+
@Override
public void onCancel() {
- cancelEnrollment();
+ Slog.d(TAG, "Cancel fingerprint enrollment requested for: " + mAuthRequestId);
+ cancelEnrollment(mAuthRequestId);
}
}
@@ -264,10 +272,21 @@
* Get {@link IdentityCredential} object.
* @return {@link IdentityCredential} object or null if this doesn't contain one.
* @hide
+ * @deprecated Use {@link PresentationSession} instead of {@link IdentityCredential}.
*/
+ @Deprecated
public IdentityCredential getIdentityCredential() {
return super.getIdentityCredential();
}
+
+ /**
+ * Get {@link PresentationSession} object.
+ * @return {@link PresentationSession} object or null if this doesn't contain one.
+ * @hide
+ */
+ public PresentationSession getPresentationSession() {
+ return super.getPresentationSession();
+ }
}
/**
@@ -646,20 +665,19 @@
throw new IllegalArgumentException("Must supply an enrollment callback");
}
- if (cancel != null) {
- if (cancel.isCanceled()) {
- Slog.w(TAG, "enrollment already canceled");
- return;
- } else {
- cancel.setOnCancelListener(new OnEnrollCancelListener());
- }
+ if (cancel != null && cancel.isCanceled()) {
+ Slog.w(TAG, "enrollment already canceled");
+ return;
}
if (mService != null) {
try {
mEnrollmentCallback = callback;
- mService.enroll(mToken, hardwareAuthToken, userId, mServiceReceiver,
- mContext.getOpPackageName(), enrollReason);
+ final long enrollId = mService.enroll(mToken, hardwareAuthToken, userId,
+ mServiceReceiver, mContext.getOpPackageName(), enrollReason);
+ if (cancel != null) {
+ cancel.setOnCancelListener(new OnEnrollCancelListener(enrollId));
+ }
} catch (RemoteException e) {
Slog.w(TAG, "Remote exception in enroll: ", e);
// Though this may not be a hardware issue, it will cause apps to give up or try
@@ -1302,9 +1320,9 @@
return allSensors.isEmpty() ? null : allSensors.get(0);
}
- private void cancelEnrollment() {
+ private void cancelEnrollment(long requestId) {
if (mService != null) try {
- mService.cancelEnrollment(mToken);
+ mService.cancelEnrollment(mToken, requestId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index ba1dc6d..cbff8b1 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -84,11 +84,11 @@
void cancelAuthenticationFromService(int sensorId, IBinder token, String opPackageName, long requestId);
// Start fingerprint enrollment
- void enroll(IBinder token, in byte [] hardwareAuthToken, int userId, IFingerprintServiceReceiver receiver,
+ long enroll(IBinder token, in byte [] hardwareAuthToken, int userId, IFingerprintServiceReceiver receiver,
String opPackageName, int enrollReason);
// Cancel enrollment in progress
- void cancelEnrollment(IBinder token);
+ void cancelEnrollment(IBinder token, long requestId);
// Any errors resulting from this call will be returned to the listener
void remove(IBinder token, int fingerId, int userId, IFingerprintServiceReceiver receiver,
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index f50aa99..147138e 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -69,6 +69,8 @@
int getMultipathPreference(in Network network);
+ SubscriptionPlan getSubscriptionPlan(in NetworkTemplate template);
+ void onStatsProviderWarningOrLimitReached();
SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage);
void setSubscriptionPlans(int subId, in SubscriptionPlan[] plans, String callingPackage);
String getSubscriptionPlansOwner(int subId);
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 7ebb646..426fc61 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -535,6 +535,46 @@
}
/**
+ * Get subscription plan for the given networkTemplate.
+ *
+ * @param template the networkTemplate to get the subscription plan for.
+ * @return the active {@link SubscriptionPlan} for the given template, or
+ * {@code null} if not found.
+ * @hide
+ */
+ @Nullable
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_STACK})
+ // @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public SubscriptionPlan getSubscriptionPlan(@NonNull NetworkTemplate template) {
+ try {
+ return mService.getSubscriptionPlan(template);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Notifies that the specified {@link NetworkStatsProvider} has reached its quota
+ * which was set through {@link NetworkStatsProvider#onSetLimit(String, long)} or
+ * {@link NetworkStatsProvider#onSetWarningAndLimit(String, long, long)}.
+ *
+ * @hide
+ */
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_STACK})
+ // @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public void onStatsProviderWarningOrLimitReached() {
+ try {
+ mService.onStatsProviderWarningOrLimitReached();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Resets network policy settings back to factory defaults.
*
* @hide
diff --git a/core/java/android/net/annotations/PolicyDirection.java b/core/java/android/net/annotations/PolicyDirection.java
index febd9b4..3f7521a 100644
--- a/core/java/android/net/annotations/PolicyDirection.java
+++ b/core/java/android/net/annotations/PolicyDirection.java
@@ -24,10 +24,6 @@
/**
* IPsec traffic direction.
- *
- * <p>Mainline modules cannot reference hidden @IntDef. Moving this annotation to a separate class
- * to allow others to statically include it.
- *
* @hide
*/
@IntDef(value = {IpSecManager.DIRECTION_IN, IpSecManager.DIRECTION_OUT})
diff --git a/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
index 125b573..69e6313 100644
--- a/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
+++ b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
@@ -63,13 +63,22 @@
private final int mOpportunisticMatchCriteria;
private VcnCellUnderlyingNetworkTemplate(
- int networkQuality,
int meteredMatchCriteria,
+ int minEntryUpstreamBandwidthKbps,
+ int minExitUpstreamBandwidthKbps,
+ int minEntryDownstreamBandwidthKbps,
+ int minExitDownstreamBandwidthKbps,
Set<String> allowedNetworkPlmnIds,
Set<Integer> allowedSpecificCarrierIds,
int roamingMatchCriteria,
int opportunisticMatchCriteria) {
- super(NETWORK_PRIORITY_TYPE_CELL, networkQuality, meteredMatchCriteria);
+ super(
+ NETWORK_PRIORITY_TYPE_CELL,
+ meteredMatchCriteria,
+ minEntryUpstreamBandwidthKbps,
+ minExitUpstreamBandwidthKbps,
+ minEntryDownstreamBandwidthKbps,
+ minExitDownstreamBandwidthKbps);
mAllowedNetworkPlmnIds = new ArraySet<>(allowedNetworkPlmnIds);
mAllowedSpecificCarrierIds = new ArraySet<>(allowedSpecificCarrierIds);
mRoamingMatchCriteria = roamingMatchCriteria;
@@ -109,9 +118,17 @@
@NonNull PersistableBundle in) {
Objects.requireNonNull(in, "PersistableBundle is null");
- final int networkQuality = in.getInt(NETWORK_QUALITY_KEY);
final int meteredMatchCriteria = in.getInt(METERED_MATCH_KEY);
+ final int minEntryUpstreamBandwidthKbps =
+ in.getInt(MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS_KEY, DEFAULT_MIN_BANDWIDTH_KBPS);
+ final int minExitUpstreamBandwidthKbps =
+ in.getInt(MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS_KEY, DEFAULT_MIN_BANDWIDTH_KBPS);
+ final int minEntryDownstreamBandwidthKbps =
+ in.getInt(MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS_KEY, DEFAULT_MIN_BANDWIDTH_KBPS);
+ final int minExitDownstreamBandwidthKbps =
+ in.getInt(MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS_KEY, DEFAULT_MIN_BANDWIDTH_KBPS);
+
final PersistableBundle plmnIdsBundle =
in.getPersistableBundle(ALLOWED_NETWORK_PLMN_IDS_KEY);
Objects.requireNonNull(plmnIdsBundle, "plmnIdsBundle is null");
@@ -131,8 +148,11 @@
final int opportunisticMatchCriteria = in.getInt(OPPORTUNISTIC_MATCH_KEY);
return new VcnCellUnderlyingNetworkTemplate(
- networkQuality,
meteredMatchCriteria,
+ minEntryUpstreamBandwidthKbps,
+ minExitUpstreamBandwidthKbps,
+ minEntryDownstreamBandwidthKbps,
+ minExitDownstreamBandwidthKbps,
allowedNetworkPlmnIds,
allowedSpecificCarrierIds,
roamingMatchCriteria,
@@ -243,7 +263,6 @@
/** This class is used to incrementally build VcnCellUnderlyingNetworkTemplate objects. */
public static final class Builder {
- private int mNetworkQuality = NETWORK_QUALITY_ANY;
private int mMeteredMatchCriteria = MATCH_ANY;
@NonNull private final Set<String> mAllowedNetworkPlmnIds = new ArraySet<>();
@@ -252,29 +271,15 @@
private int mRoamingMatchCriteria = MATCH_ANY;
private int mOpportunisticMatchCriteria = MATCH_ANY;
+ private int mMinEntryUpstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
+ private int mMinExitUpstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
+ private int mMinEntryDownstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
+ private int mMinExitDownstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
+
/** Construct a Builder object. */
public Builder() {}
/**
- * Set the required network quality to match this template.
- *
- * <p>Network quality is a aggregation of multiple signals that reflect the network link
- * metrics. For example, the network validation bit (see {@link
- * NetworkCapabilities#NET_CAPABILITY_VALIDATED}), estimated first hop transport bandwidth
- * and signal strength.
- *
- * @param networkQuality the required network quality. Defaults to NETWORK_QUALITY_ANY
- * @hide
- */
- @NonNull
- public Builder setNetworkQuality(@NetworkQuality int networkQuality) {
- validateNetworkQuality(networkQuality);
-
- mNetworkQuality = networkQuality;
- return this;
- }
-
- /**
* Set the matching criteria for metered networks.
*
* <p>A template where setMetered(MATCH_REQUIRED) will only match metered networks (one
@@ -369,12 +374,92 @@
return this;
}
+ /**
+ * Set the minimum upstream bandwidths that this template will match.
+ *
+ * <p>This template will not match a network that does not provide at least the bandwidth
+ * passed as the entry bandwidth, except in the case that the network is selected as the VCN
+ * Gateway Connection's underlying network, where it will continue to match until the
+ * bandwidth drops under the exit bandwidth.
+ *
+ * <p>The entry criteria MUST be greater than, or equal to the exit criteria to avoid the
+ * invalid case where a network fulfills the entry criteria, but at the same time fails the
+ * exit criteria.
+ *
+ * <p>Estimated bandwidth of a network is provided by the transport layer, and reported in
+ * {@link NetworkCapabilities}. The provided estimates will be used without modification.
+ *
+ * @param minEntryUpstreamBandwidthKbps the minimum accepted upstream bandwidth for networks
+ * that ARE NOT the already-selected underlying network, or {@code 0} to disable this
+ * requirement. Disabled by default.
+ * @param minExitUpstreamBandwidthKbps the minimum accepted upstream bandwidth for a network
+ * that IS the already-selected underlying network, or {@code 0} to disable this
+ * requirement. Disabled by default.
+ * @return this {@link Builder} instance, for chaining
+ */
+ @NonNull
+ // The getter for the two integers are separated, and in the superclass. Please see {@link
+ // VcnUnderlyingNetworkTemplate#getMinEntryUpstreamBandwidthKbps()} and {@link
+ // VcnUnderlyingNetworkTemplate#getMinExitUpstreamBandwidthKbps()}
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public Builder setMinUpstreamBandwidthKbps(
+ int minEntryUpstreamBandwidthKbps, int minExitUpstreamBandwidthKbps) {
+ validateMinBandwidthKbps(minEntryUpstreamBandwidthKbps, minExitUpstreamBandwidthKbps);
+
+ mMinEntryUpstreamBandwidthKbps = minEntryUpstreamBandwidthKbps;
+ mMinExitUpstreamBandwidthKbps = minExitUpstreamBandwidthKbps;
+
+ return this;
+ }
+
+ /**
+ * Set the minimum upstream bandwidths that this template will match.
+ *
+ * <p>This template will not match a network that does not provide at least the bandwidth
+ * passed as the entry bandwidth, except in the case that the network is selected as the VCN
+ * Gateway Connection's underlying network, where it will continue to match until the
+ * bandwidth drops under the exit bandwidth.
+ *
+ * <p>The entry criteria MUST be greater than, or equal to the exit criteria to avoid the
+ * invalid case where a network fulfills the entry criteria, but at the same time fails the
+ * exit criteria.
+ *
+ * <p>Estimated bandwidth of a network is provided by the transport layer, and reported in
+ * {@link NetworkCapabilities}. The provided estimates will be used without modification.
+ *
+ * @param minEntryDownstreamBandwidthKbps the minimum accepted downstream bandwidth for
+ * networks that ARE NOT the already-selected underlying network, or {@code 0} to
+ * disable this requirement. Disabled by default.
+ * @param minExitDownstreamBandwidthKbps the minimum accepted downstream bandwidth for a
+ * network that IS the already-selected underlying network, or {@code 0} to disable this
+ * requirement. Disabled by default.
+ * @return this {@link Builder} instance, for chaining
+ */
+ @NonNull
+ // The getter for the two integers are separated, and in the superclass. Please see {@link
+ // VcnUnderlyingNetworkTemplate#getMinEntryDownstreamBandwidthKbps()} and {@link
+ // VcnUnderlyingNetworkTemplate#getMinExitDownstreamBandwidthKbps()}
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public Builder setMinDownstreamBandwidthKbps(
+ int minEntryDownstreamBandwidthKbps, int minExitDownstreamBandwidthKbps) {
+ validateMinBandwidthKbps(
+ minEntryDownstreamBandwidthKbps, minExitDownstreamBandwidthKbps);
+
+ mMinEntryDownstreamBandwidthKbps = minEntryDownstreamBandwidthKbps;
+ mMinExitDownstreamBandwidthKbps = minExitDownstreamBandwidthKbps;
+
+ return this;
+ }
+
/** Build the VcnCellUnderlyingNetworkTemplate. */
@NonNull
public VcnCellUnderlyingNetworkTemplate build() {
return new VcnCellUnderlyingNetworkTemplate(
- mNetworkQuality,
mMeteredMatchCriteria,
+ mMinEntryUpstreamBandwidthKbps,
+ mMinExitUpstreamBandwidthKbps,
+ mMinEntryDownstreamBandwidthKbps,
+ mMinExitDownstreamBandwidthKbps,
mAllowedNetworkPlmnIds,
mAllowedSpecificCarrierIds,
mRoamingMatchCriteria,
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index 92956e8..a6830b7 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -17,7 +17,6 @@
import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
-import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK;
import static com.android.internal.annotations.VisibleForTesting.Visibility;
@@ -169,18 +168,15 @@
static {
DEFAULT_UNDERLYING_NETWORK_TEMPLATES.add(
new VcnCellUnderlyingNetworkTemplate.Builder()
- .setNetworkQuality(NETWORK_QUALITY_OK)
.setOpportunistic(MATCH_REQUIRED)
.build());
DEFAULT_UNDERLYING_NETWORK_TEMPLATES.add(
new VcnWifiUnderlyingNetworkTemplate.Builder()
- .setNetworkQuality(NETWORK_QUALITY_OK)
.build());
DEFAULT_UNDERLYING_NETWORK_TEMPLATES.add(
new VcnCellUnderlyingNetworkTemplate.Builder()
- .setNetworkQuality(NETWORK_QUALITY_OK)
.build());
}
diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java
index 60fc936..3a9ca3e 100644
--- a/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java
+++ b/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java
@@ -48,23 +48,6 @@
/** @hide */
static final int NETWORK_PRIORITY_TYPE_CELL = 2;
- /** Denotes that any network quality is acceptable. @hide */
- public static final int NETWORK_QUALITY_ANY = 0;
- /** Denotes that network quality needs to be OK. @hide */
- public static final int NETWORK_QUALITY_OK = 100000;
-
- private static final SparseArray<String> NETWORK_QUALITY_TO_STRING_MAP = new SparseArray<>();
-
- static {
- NETWORK_QUALITY_TO_STRING_MAP.put(NETWORK_QUALITY_ANY, "NETWORK_QUALITY_ANY");
- NETWORK_QUALITY_TO_STRING_MAP.put(NETWORK_QUALITY_OK, "NETWORK_QUALITY_OK");
- }
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({NETWORK_QUALITY_OK, NETWORK_QUALITY_ANY})
- public @interface NetworkQuality {}
-
/**
* Used to configure the matching criteria of a network characteristic. This may include network
* capabilities, or cellular subscription information. Denotes that networks with or without the
@@ -103,44 +86,73 @@
private final int mNetworkPriorityType;
/** @hide */
- static final String NETWORK_QUALITY_KEY = "mNetworkQuality";
-
- private final int mNetworkQuality;
-
- /** @hide */
static final String METERED_MATCH_KEY = "mMeteredMatchCriteria";
private final int mMeteredMatchCriteria;
/** @hide */
+ public static final int DEFAULT_MIN_BANDWIDTH_KBPS = 0;
+
+ /** @hide */
+ static final String MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS_KEY = "mMinEntryUpstreamBandwidthKbps";
+
+ private final int mMinEntryUpstreamBandwidthKbps;
+
+ /** @hide */
+ static final String MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS_KEY = "mMinExitUpstreamBandwidthKbps";
+
+ private final int mMinExitUpstreamBandwidthKbps;
+
+ /** @hide */
+ static final String MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS_KEY =
+ "mMinEntryDownstreamBandwidthKbps";
+
+ private final int mMinEntryDownstreamBandwidthKbps;
+
+ /** @hide */
+ static final String MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS_KEY = "mMinExitDownstreamBandwidthKbps";
+
+ private final int mMinExitDownstreamBandwidthKbps;
+
+ /** @hide */
VcnUnderlyingNetworkTemplate(
- int networkPriorityType, int networkQuality, int meteredMatchCriteria) {
+ int networkPriorityType,
+ int meteredMatchCriteria,
+ int minEntryUpstreamBandwidthKbps,
+ int minExitUpstreamBandwidthKbps,
+ int minEntryDownstreamBandwidthKbps,
+ int minExitDownstreamBandwidthKbps) {
mNetworkPriorityType = networkPriorityType;
- mNetworkQuality = networkQuality;
mMeteredMatchCriteria = meteredMatchCriteria;
+ mMinEntryUpstreamBandwidthKbps = minEntryUpstreamBandwidthKbps;
+ mMinExitUpstreamBandwidthKbps = minExitUpstreamBandwidthKbps;
+ mMinEntryDownstreamBandwidthKbps = minEntryDownstreamBandwidthKbps;
+ mMinExitDownstreamBandwidthKbps = minExitDownstreamBandwidthKbps;
}
/** @hide */
- static void validateNetworkQuality(int networkQuality) {
+ static void validateMatchCriteria(int matchCriteria, String matchingCapability) {
Preconditions.checkArgument(
- networkQuality == NETWORK_QUALITY_ANY || networkQuality == NETWORK_QUALITY_OK,
- "Invalid networkQuality:" + networkQuality);
+ MATCH_CRITERIA_TO_STRING_MAP.contains(matchCriteria),
+ "Invalid matching criteria: " + matchCriteria + " for " + matchingCapability);
}
/** @hide */
- static void validateMatchCriteria(int meteredMatchCriteria, String matchingCapability) {
+ static void validateMinBandwidthKbps(int minEntryBandwidth, int minExitBandwidth) {
Preconditions.checkArgument(
- MATCH_CRITERIA_TO_STRING_MAP.contains(meteredMatchCriteria),
- "Invalid matching criteria: "
- + meteredMatchCriteria
- + " for "
- + matchingCapability);
+ minEntryBandwidth >= 0, "Invalid minEntryBandwidth, must be >= 0");
+ Preconditions.checkArgument(
+ minExitBandwidth >= 0, "Invalid minExitBandwidth, must be >= 0");
+ Preconditions.checkArgument(
+ minEntryBandwidth >= minExitBandwidth,
+ "Minimum entry bandwidth must be >= exit bandwidth");
}
/** @hide */
protected void validate() {
- validateNetworkQuality(mNetworkQuality);
validateMatchCriteria(mMeteredMatchCriteria, "mMeteredMatchCriteria");
+ validateMinBandwidthKbps(mMinEntryUpstreamBandwidthKbps, mMinExitUpstreamBandwidthKbps);
+ validateMinBandwidthKbps(mMinEntryDownstreamBandwidthKbps, mMinExitDownstreamBandwidthKbps);
}
/** @hide */
@@ -168,15 +180,24 @@
final PersistableBundle result = new PersistableBundle();
result.putInt(NETWORK_PRIORITY_TYPE_KEY, mNetworkPriorityType);
- result.putInt(NETWORK_QUALITY_KEY, mNetworkQuality);
result.putInt(METERED_MATCH_KEY, mMeteredMatchCriteria);
+ result.putInt(MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS_KEY, mMinEntryUpstreamBandwidthKbps);
+ result.putInt(MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS_KEY, mMinExitUpstreamBandwidthKbps);
+ result.putInt(MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS_KEY, mMinEntryDownstreamBandwidthKbps);
+ result.putInt(MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS_KEY, mMinExitDownstreamBandwidthKbps);
return result;
}
@Override
public int hashCode() {
- return Objects.hash(mNetworkPriorityType, mNetworkQuality, mMeteredMatchCriteria);
+ return Objects.hash(
+ mNetworkPriorityType,
+ mMeteredMatchCriteria,
+ mMinEntryUpstreamBandwidthKbps,
+ mMinExitUpstreamBandwidthKbps,
+ mMinEntryDownstreamBandwidthKbps,
+ mMinExitDownstreamBandwidthKbps);
}
@Override
@@ -187,8 +208,11 @@
final VcnUnderlyingNetworkTemplate rhs = (VcnUnderlyingNetworkTemplate) other;
return mNetworkPriorityType == rhs.mNetworkPriorityType
- && mNetworkQuality == rhs.mNetworkQuality
- && mMeteredMatchCriteria == rhs.mMeteredMatchCriteria;
+ && mMeteredMatchCriteria == rhs.mMeteredMatchCriteria
+ && mMinEntryUpstreamBandwidthKbps == rhs.mMinEntryUpstreamBandwidthKbps
+ && mMinExitUpstreamBandwidthKbps == rhs.mMinExitUpstreamBandwidthKbps
+ && mMinEntryDownstreamBandwidthKbps == rhs.mMinEntryDownstreamBandwidthKbps
+ && mMinExitDownstreamBandwidthKbps == rhs.mMinExitDownstreamBandwidthKbps;
}
/** @hide */
@@ -197,8 +221,8 @@
}
/** @hide */
- static String getMatchCriteriaString(int meteredMatchCriteria) {
- return getNameString(MATCH_CRITERIA_TO_STRING_MAP, meteredMatchCriteria);
+ static String getMatchCriteriaString(int matchCriteria) {
+ return getNameString(MATCH_CRITERIA_TO_STRING_MAP, matchCriteria);
}
/** @hide */
@@ -213,34 +237,63 @@
pw.println(this.getClass().getSimpleName() + ":");
pw.increaseIndent();
- pw.println(
- "mNetworkQuality: "
- + getNameString(NETWORK_QUALITY_TO_STRING_MAP, mNetworkQuality));
pw.println("mMeteredMatchCriteria: " + getMatchCriteriaString(mMeteredMatchCriteria));
+ pw.println("mMinEntryUpstreamBandwidthKbps: " + mMinEntryUpstreamBandwidthKbps);
+ pw.println("mMinExitUpstreamBandwidthKbps: " + mMinExitUpstreamBandwidthKbps);
+ pw.println("mMinEntryDownstreamBandwidthKbps: " + mMinEntryDownstreamBandwidthKbps);
+ pw.println("mMinExitDownstreamBandwidthKbps: " + mMinExitDownstreamBandwidthKbps);
dumpTransportSpecificFields(pw);
pw.decreaseIndent();
}
/**
- * Retrieve the required network quality to match this template.
- *
- * @see Builder#setNetworkQuality(int)
- * @hide
- */
- @NetworkQuality
- public int getNetworkQuality() {
- return mNetworkQuality;
- }
-
- /**
* Return the matching criteria for metered networks.
*
* @see VcnWifiUnderlyingNetworkTemplate.Builder#setMetered(int)
* @see VcnCellUnderlyingNetworkTemplate.Builder#setMetered(int)
*/
- @MatchCriteria
public int getMetered() {
return mMeteredMatchCriteria;
}
+
+ /**
+ * Returns the minimum entry upstream bandwidth allowed by this template.
+ *
+ * @see VcnWifiUnderlyingNetworkTemplate.Builder#setMinUpstreamBandwidthKbps(int, int)
+ * @see VcnCellUnderlyingNetworkTemplate.Builder#setMinUpstreamBandwidthKbps(int, int)
+ */
+ public int getMinEntryUpstreamBandwidthKbps() {
+ return mMinEntryUpstreamBandwidthKbps;
+ }
+
+ /**
+ * Returns the minimum exit upstream bandwidth allowed by this template.
+ *
+ * @see VcnWifiUnderlyingNetworkTemplate.Builder#setMinUpstreamBandwidthKbps(int, int)
+ * @see VcnCellUnderlyingNetworkTemplate.Builder#setMinUpstreamBandwidthKbps(int, int)
+ */
+ public int getMinExitUpstreamBandwidthKbps() {
+ return mMinExitUpstreamBandwidthKbps;
+ }
+
+ /**
+ * Returns the minimum entry downstream bandwidth allowed by this template.
+ *
+ * @see VcnWifiUnderlyingNetworkTemplate.Builder#setMinDownstreamBandwidthKbps(int, int)
+ * @see VcnCellUnderlyingNetworkTemplate.Builder#setMinDownstreamBandwidthKbps(int, int)
+ */
+ public int getMinEntryDownstreamBandwidthKbps() {
+ return mMinEntryDownstreamBandwidthKbps;
+ }
+
+ /**
+ * Returns the minimum exit downstream bandwidth allowed by this template.
+ *
+ * @see VcnWifiUnderlyingNetworkTemplate.Builder#setMinDownstreamBandwidthKbps(int, int)
+ * @see VcnCellUnderlyingNetworkTemplate.Builder#setMinDownstreamBandwidthKbps(int, int)
+ */
+ public int getMinExitDownstreamBandwidthKbps() {
+ return mMinExitDownstreamBandwidthKbps;
+ }
}
diff --git a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
index 272ca9d..23a07ab 100644
--- a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
+++ b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
@@ -46,8 +46,19 @@
@Nullable private final Set<String> mSsids;
private VcnWifiUnderlyingNetworkTemplate(
- int networkQuality, int meteredMatchCriteria, Set<String> ssids) {
- super(NETWORK_PRIORITY_TYPE_WIFI, networkQuality, meteredMatchCriteria);
+ int meteredMatchCriteria,
+ int minEntryUpstreamBandwidthKbps,
+ int minExitUpstreamBandwidthKbps,
+ int minEntryDownstreamBandwidthKbps,
+ int minExitDownstreamBandwidthKbps,
+ Set<String> ssids) {
+ super(
+ NETWORK_PRIORITY_TYPE_WIFI,
+ meteredMatchCriteria,
+ minEntryUpstreamBandwidthKbps,
+ minExitUpstreamBandwidthKbps,
+ minEntryDownstreamBandwidthKbps,
+ minExitDownstreamBandwidthKbps);
mSsids = new ArraySet<>(ssids);
validate();
@@ -75,15 +86,29 @@
@NonNull PersistableBundle in) {
Objects.requireNonNull(in, "PersistableBundle is null");
- final int networkQuality = in.getInt(NETWORK_QUALITY_KEY);
final int meteredMatchCriteria = in.getInt(METERED_MATCH_KEY);
+ final int minEntryUpstreamBandwidthKbps =
+ in.getInt(MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS_KEY, DEFAULT_MIN_BANDWIDTH_KBPS);
+ final int minExitUpstreamBandwidthKbps =
+ in.getInt(MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS_KEY, DEFAULT_MIN_BANDWIDTH_KBPS);
+ final int minEntryDownstreamBandwidthKbps =
+ in.getInt(MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS_KEY, DEFAULT_MIN_BANDWIDTH_KBPS);
+ final int minExitDownstreamBandwidthKbps =
+ in.getInt(MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS_KEY, DEFAULT_MIN_BANDWIDTH_KBPS);
+
final PersistableBundle ssidsBundle = in.getPersistableBundle(SSIDS_KEY);
Objects.requireNonNull(ssidsBundle, "ssidsBundle is null");
final Set<String> ssids =
new ArraySet<String>(
PersistableBundleUtils.toList(ssidsBundle, STRING_DESERIALIZER));
- return new VcnWifiUnderlyingNetworkTemplate(networkQuality, meteredMatchCriteria, ssids);
+ return new VcnWifiUnderlyingNetworkTemplate(
+ meteredMatchCriteria,
+ minEntryUpstreamBandwidthKbps,
+ minExitUpstreamBandwidthKbps,
+ minEntryDownstreamBandwidthKbps,
+ minExitDownstreamBandwidthKbps,
+ ssids);
}
/** @hide */
@@ -137,33 +162,18 @@
/** This class is used to incrementally build VcnWifiUnderlyingNetworkTemplate objects. */
public static final class Builder {
- private int mNetworkQuality = NETWORK_QUALITY_ANY;
private int mMeteredMatchCriteria = MATCH_ANY;
@NonNull private final Set<String> mSsids = new ArraySet<>();
+ private int mMinEntryUpstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
+ private int mMinExitUpstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
+ private int mMinEntryDownstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
+ private int mMinExitDownstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
+
/** Construct a Builder object. */
public Builder() {}
/**
- * Set the required network quality to match this template.
- *
- * <p>Network quality is a aggregation of multiple signals that reflect the network link
- * metrics. For example, the network validation bit (see {@link
- * NetworkCapabilities#NET_CAPABILITY_VALIDATED}), estimated first hop transport bandwidth
- * and signal strength.
- *
- * @param networkQuality the required network quality. Defaults to NETWORK_QUALITY_ANY
- * @hide
- */
- @NonNull
- public Builder setNetworkQuality(@NetworkQuality int networkQuality) {
- validateNetworkQuality(networkQuality);
-
- mNetworkQuality = networkQuality;
- return this;
- }
-
- /**
* Set the matching criteria for metered networks.
*
* <p>A template where setMetered(MATCH_REQUIRED) will only match metered networks (one
@@ -200,11 +210,93 @@
return this;
}
+ /**
+ * Set the minimum upstream bandwidths that this template will match.
+ *
+ * <p>This template will not match a network that does not provide at least the bandwidth
+ * passed as the entry bandwidth, except in the case that the network is selected as the VCN
+ * Gateway Connection's underlying network, where it will continue to match until the
+ * bandwidth drops under the exit bandwidth.
+ *
+ * <p>The entry criteria MUST be greater than, or equal to the exit criteria to avoid the
+ * invalid case where a network fulfills the entry criteria, but at the same time fails the
+ * exit criteria.
+ *
+ * <p>Estimated bandwidth of a network is provided by the transport layer, and reported in
+ * {@link NetworkCapabilities}. The provided estimates will be used without modification.
+ *
+ * @param minEntryUpstreamBandwidthKbps the minimum accepted upstream bandwidth for networks
+ * that ARE NOT the already-selected underlying network, or {@code 0} to disable this
+ * requirement. Disabled by default.
+ * @param minExitUpstreamBandwidthKbps the minimum accepted upstream bandwidth for a network
+ * that IS the already-selected underlying network, or {@code 0} to disable this
+ * requirement. Disabled by default.
+ * @return this {@link Builder} instance, for chaining
+ */
+ @NonNull
+ // The getter for the two integers are separated, and in the superclass. Please see {@link
+ // VcnUnderlyingNetworkTemplate#getMinEntryUpstreamBandwidthKbps()} and {@link
+ // VcnUnderlyingNetworkTemplate#getMinExitUpstreamBandwidthKbps()}
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public Builder setMinUpstreamBandwidthKbps(
+ int minEntryUpstreamBandwidthKbps, int minExitUpstreamBandwidthKbps) {
+ validateMinBandwidthKbps(minEntryUpstreamBandwidthKbps, minExitUpstreamBandwidthKbps);
+
+ mMinEntryUpstreamBandwidthKbps = minEntryUpstreamBandwidthKbps;
+ mMinExitUpstreamBandwidthKbps = minExitUpstreamBandwidthKbps;
+
+ return this;
+ }
+
+ /**
+ * Set the minimum upstream bandwidths that this template will match.
+ *
+ * <p>This template will not match a network that does not provide at least the bandwidth
+ * passed as the entry bandwidth, except in the case that the network is selected as the VCN
+ * Gateway Connection's underlying network, where it will continue to match until the
+ * bandwidth drops under the exit bandwidth.
+ *
+ * <p>The entry criteria MUST be greater than, or equal to the exit criteria to avoid the
+ * invalid case where a network fulfills the entry criteria, but at the same time fails the
+ * exit criteria.
+ *
+ * <p>Estimated bandwidth of a network is provided by the transport layer, and reported in
+ * {@link NetworkCapabilities}. The provided estimates will be used without modification.
+ *
+ * @param minEntryDownstreamBandwidthKbps the minimum accepted downstream bandwidth for
+ * networks that ARE NOT the already-selected underlying network, or {@code 0} to
+ * disable this requirement. Disabled by default.
+ * @param minExitDownstreamBandwidthKbps the minimum accepted downstream bandwidth for a
+ * network that IS the already-selected underlying network, or {@code 0} to disable this
+ * requirement. Disabled by default.
+ * @return this {@link Builder} instance, for chaining
+ */
+ @NonNull
+ // The getter for the two integers are separated, and in the superclass. Please see {@link
+ // VcnUnderlyingNetworkTemplate#getMinEntryDownstreamBandwidthKbps()} and {@link
+ // VcnUnderlyingNetworkTemplate#getMinExitDownstreamBandwidthKbps()}
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public Builder setMinDownstreamBandwidthKbps(
+ int minEntryDownstreamBandwidthKbps, int minExitDownstreamBandwidthKbps) {
+ validateMinBandwidthKbps(
+ minEntryDownstreamBandwidthKbps, minExitDownstreamBandwidthKbps);
+
+ mMinEntryDownstreamBandwidthKbps = minEntryDownstreamBandwidthKbps;
+ mMinExitDownstreamBandwidthKbps = minExitDownstreamBandwidthKbps;
+
+ return this;
+ }
+
/** Build the VcnWifiUnderlyingNetworkTemplate. */
@NonNull
public VcnWifiUnderlyingNetworkTemplate build() {
return new VcnWifiUnderlyingNetworkTemplate(
- mNetworkQuality, mMeteredMatchCriteria, mSsids);
+ mMeteredMatchCriteria,
+ mMinEntryUpstreamBandwidthKbps,
+ mMinExitUpstreamBandwidthKbps,
+ mMinEntryDownstreamBandwidthKbps,
+ mMinExitDownstreamBandwidthKbps,
+ mSsids);
}
}
}
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 3f42164..fe86874 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -90,7 +90,7 @@
Bundle getApplicationRestrictionsForUser(in String packageName, int userId);
void setDefaultGuestRestrictions(in Bundle restrictions);
Bundle getDefaultGuestRestrictions();
- int removeUserOrSetEphemeral(int userId, boolean evenWhenDisallowed);
+ int removeUserWhenPossible(int userId, boolean overrideDevicePolicy);
boolean markGuestForDeletion(int userId);
UserInfo findCurrentGuestUser();
boolean isQuietModeEnabled(int userId);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index edf6280..190f5f1 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -4750,7 +4750,7 @@
public int removeUserWhenPossible(@NonNull UserHandle user,
boolean overrideDevicePolicy) {
try {
- return mService.removeUserOrSetEphemeral(user.getIdentifier(), overrideDevicePolicy);
+ return mService.removeUserWhenPossible(user.getIdentifier(), overrideDevicePolicy);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -4777,7 +4777,7 @@
public @RemoveResult int removeUserOrSetEphemeral(@UserIdInt int userId,
boolean evenWhenDisallowed) {
try {
- return mService.removeUserOrSetEphemeral(userId, evenWhenDisallowed);
+ return mService.removeUserWhenPossible(userId, evenWhenDisallowed);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 72e2863..5cdb1fd 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10525,7 +10525,7 @@
DEVICE_STATE_ROTATION_LOCK_UNLOCKED,
})
@Retention(RetentionPolicy.SOURCE)
- @interface DeviceStateRotationLockSetting {
+ public @interface DeviceStateRotationLockSetting {
}
/**
diff --git a/core/java/android/util/Dumpable.java b/core/java/android/util/Dumpable.java
new file mode 100644
index 0000000..79c576d
--- /dev/null
+++ b/core/java/android/util/Dumpable.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.util;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.io.PrintWriter;
+
+/**
+ * Represents an object whose state can be dumped into a {@link PrintWriter}.
+ */
+public interface Dumpable {
+
+ /**
+ * Gets the name of the {@link Dumpable}.
+ *
+ * @return class name, by default.
+ */
+ @NonNull
+ default String getDumpableName() {
+ return getClass().getName();
+ }
+
+ //TODO(b/149254050): decide whether it should take a ParcelFileDescription as well.
+
+ /**
+ * Dumps the internal state into the given {@code writer}.
+ *
+ * @param writer writer to be written to
+ * @param args optional list of arguments
+ */
+ void dump(@NonNull PrintWriter writer, @Nullable String[] args);
+}
diff --git a/core/java/android/util/DumpableContainer.java b/core/java/android/util/DumpableContainer.java
new file mode 100644
index 0000000..04d19dc
--- /dev/null
+++ b/core/java/android/util/DumpableContainer.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.util;
+
+import android.annotation.NonNull;
+
+/**
+ * Objects that contains a list of {@link Dumpable}, which will be dumped when the object itself
+ * is dumped.
+ */
+public interface DumpableContainer {
+
+ /**
+ * Adds the given {@link Dumpable dumpable} to the container.
+ *
+ * <p>If a dumpable with the same {@link Dumpable#getDumpableName() name} was added before, this
+ * call is ignored.
+ *
+ * @param dumpable dumpable to be added.
+ *
+ * @throws IllegalArgumentException if the {@link Dumpable#getDumpableName() dumpable name} is
+ * {@code null}.
+ *
+ * @return {@code true} if the dumpable was added, {@code false} if the call was ignored.
+ */
+ boolean addDumpable(@NonNull Dumpable dumpable);
+}
diff --git a/core/java/android/view/ISurfaceControlViewHost.aidl b/core/java/android/view/ISurfaceControlViewHost.aidl
new file mode 100644
index 0000000..fc9661a
--- /dev/null
+++ b/core/java/android/view/ISurfaceControlViewHost.aidl
@@ -0,0 +1,28 @@
+/*
+** Copyright 2021, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.view;
+
+import android.content.res.Configuration;
+
+/**
+ * API from content embedder back to embedded content in SurfaceControlViewHost
+ * {@hide}
+ */
+oneway interface ISurfaceControlViewHost {
+ void onConfigurationChanged(in Configuration newConfig);
+ void onDispatchDetachedFromWindow();
+}
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index adb8b86..0ef5854 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -478,10 +478,15 @@
public static final int FLAG_IS_GENERATED_GESTURE = 0x8;
/**
- * This flag associated with {@link #ACTION_POINTER_UP}, this indicates that the pointer
- * has been canceled. Typically this is used for palm event when the user has accidental
- * touches.
- * @hide
+ * This flag is only set for events with {@link #ACTION_POINTER_UP} and {@link #ACTION_CANCEL}.
+ * It indicates that the pointer going up was an unintentional user touch. When FLAG_CANCELED
+ * is set, the typical actions that occur in response for a pointer going up (such as click
+ * handlers, end of drawing) should be aborted. This flag is typically set when the user was
+ * accidentally touching the screen, such as by gripping the device, or placing the palm on the
+ * screen.
+ *
+ * @see #ACTION_POINTER_UP
+ * @see #ACTION_CANCEL
*/
public static final int FLAG_CANCELED = 0x20;
diff --git a/core/java/android/view/OWNERS b/core/java/android/view/OWNERS
index d160be5..43df294 100644
--- a/core/java/android/view/OWNERS
+++ b/core/java/android/view/OWNERS
@@ -80,6 +80,7 @@
per-file IPinnedStackListener.aidl = file:/services/core/java/com/android/server/wm/OWNERS
per-file IRecents*.aidl = file:/services/core/java/com/android/server/wm/OWNERS
per-file IRemote*.aidl = file:/services/core/java/com/android/server/wm/OWNERS
+per-file ISurfaceControlViewHost*.aidl = file:/services/core/java/com/android/server/wm/OWNERS
per-file IWindow*.aidl = file:/services/core/java/com/android/server/wm/OWNERS
per-file RemoteAnimation*.java = file:/services/core/java/com/android/server/wm/OWNERS
per-file RemoteAnimation*.aidl = file:/services/core/java/com/android/server/wm/OWNERS
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index a6c5042d..85a9dbd 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.content.Context;
+import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.os.IBinder;
import android.os.Parcel;
@@ -45,6 +46,35 @@
private SurfaceControl mSurfaceControl;
private IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection;
+ private final class ISurfaceControlViewHostImpl extends ISurfaceControlViewHost.Stub {
+ @Override
+ public void onConfigurationChanged(Configuration configuration) {
+ if (mViewRoot == null) {
+ return;
+ }
+ mViewRoot.mHandler.post(() -> {
+ if (mWm != null) {
+ mWm.setConfiguration(configuration);
+ }
+ if (mViewRoot != null) {
+ mViewRoot.forceWmRelayout();
+ }
+ });
+ }
+
+ @Override
+ public void onDispatchDetachedFromWindow() {
+ if (mViewRoot == null) {
+ return;
+ }
+ mViewRoot.mHandler.post(() -> {
+ release();
+ });
+ }
+ }
+
+ private ISurfaceControlViewHost mRemoteInterface = new ISurfaceControlViewHostImpl();
+
/**
* Package encapsulating a Surface hierarchy which contains interactive view
* elements. It's expected to get this object from
@@ -71,12 +101,14 @@
private SurfaceControl mSurfaceControl;
private final IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection;
private final IBinder mInputToken;
+ private final ISurfaceControlViewHost mRemoteInterface;
SurfacePackage(SurfaceControl sc, IAccessibilityEmbeddedConnection connection,
- IBinder inputToken) {
+ IBinder inputToken, ISurfaceControlViewHost ri) {
mSurfaceControl = sc;
mAccessibilityEmbeddedConnection = connection;
mInputToken = inputToken;
+ mRemoteInterface = ri;
}
/**
@@ -97,6 +129,7 @@
}
mAccessibilityEmbeddedConnection = other.mAccessibilityEmbeddedConnection;
mInputToken = other.mInputToken;
+ mRemoteInterface = other.mRemoteInterface;
}
private SurfacePackage(Parcel in) {
@@ -105,6 +138,8 @@
mAccessibilityEmbeddedConnection = IAccessibilityEmbeddedConnection.Stub.asInterface(
in.readStrongBinder());
mInputToken = in.readStrongBinder();
+ mRemoteInterface = ISurfaceControlViewHost.Stub.asInterface(
+ in.readStrongBinder());
}
/**
@@ -126,6 +161,13 @@
return mAccessibilityEmbeddedConnection;
}
+ /**
+ * @hide
+ */
+ public ISurfaceControlViewHost getRemoteInterface() {
+ return mRemoteInterface;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -136,6 +178,7 @@
mSurfaceControl.writeToParcel(out, flags);
out.writeStrongBinder(mAccessibilityEmbeddedConnection.asBinder());
out.writeStrongBinder(mInputToken);
+ out.writeStrongBinder(mRemoteInterface.asBinder());
}
/**
@@ -231,7 +274,7 @@
public @Nullable SurfacePackage getSurfacePackage() {
if (mSurfaceControl != null && mAccessibilityEmbeddedConnection != null) {
return new SurfacePackage(mSurfaceControl, mAccessibilityEmbeddedConnection,
- mViewRoot.getInputToken());
+ mViewRoot.getInputToken(), mRemoteInterface);
} else {
return null;
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 7718511..7412931 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -10639,4 +10639,9 @@
boolean wasRelayoutRequested() {
return mRelayoutRequested;
}
+
+ void forceWmRelayout() {
+ mForceNextWindowRelayout = true;
+ scheduleTraversals();
+ }
}
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index d699194d..5176f9b 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -87,7 +87,7 @@
mHostInputToken = hostInputToken;
}
- protected void setConfiguration(Configuration configuration) {
+ public void setConfiguration(Configuration configuration) {
mConfiguration.setTo(configuration);
}
@@ -330,6 +330,7 @@
public void setInsets(android.view.IWindow window, int touchableInsets,
android.graphics.Rect contentInsets, android.graphics.Rect visibleInsets,
android.graphics.Region touchableRegion) {
+ setTouchRegion(window.asBinder(), touchableRegion);
}
@Override
diff --git a/core/java/android/view/accessibility/CaptioningManager.java b/core/java/android/view/accessibility/CaptioningManager.java
index 3d68692..3f6a871 100644
--- a/core/java/android/view/accessibility/CaptioningManager.java
+++ b/core/java/android/view/accessibility/CaptioningManager.java
@@ -22,6 +22,7 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.Color;
import android.graphics.Typeface;
@@ -30,6 +31,8 @@
import android.provider.Settings.Secure;
import android.text.TextUtils;
+import com.android.internal.R;
+
import java.util.ArrayList;
import java.util.Locale;
@@ -51,6 +54,7 @@
private final ArrayList<CaptioningChangeListener> mListeners = new ArrayList<>();
private final ContentResolver mContentResolver;
private final ContentObserver mContentObserver;
+ private final Resources mResources;
/**
* Creates a new captioning manager for the specified context.
@@ -62,6 +66,7 @@
final Handler handler = new Handler(context.getMainLooper());
mContentObserver = new MyContentObserver(handler);
+ mResources = context.getResources();
}
/**
@@ -181,6 +186,13 @@
}
}
+ /**
+ * Returns true if system wide call captioning is enabled for this device.
+ */
+ public boolean isCallCaptioningEnabled() {
+ return mResources.getBoolean(R.bool.config_systemCaptionsServiceCallsEnabled);
+ }
+
private void notifyEnabledChanged() {
final boolean enabled = isEnabled();
synchronized (mListeners) {
diff --git a/core/java/android/window/DisplayWindowPolicyController.java b/core/java/android/window/DisplayWindowPolicyController.java
index c3ef881..3359a41 100644
--- a/core/java/android/window/DisplayWindowPolicyController.java
+++ b/core/java/android/window/DisplayWindowPolicyController.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
+import android.util.ArraySet;
import java.io.PrintWriter;
import java.util.List;
@@ -81,8 +82,10 @@
/**
* This is called when the apps that contains running activities on the display has changed.
+ * The running activities refer to the non-finishing activities regardless of they are running
+ * in a process.
*/
- public void onRunningAppsChanged(int[] runningUids) {}
+ public void onRunningAppsChanged(ArraySet<Integer> runningUids) {}
/** Dump debug data */
public void dump(String prefix, final PrintWriter pw) {
diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java
index 4ba7ef2..547535d 100644
--- a/core/java/android/window/WindowTokenClient.java
+++ b/core/java/android/window/WindowTokenClient.java
@@ -19,9 +19,10 @@
import static android.window.ConfigurationHelper.isDifferentDisplay;
import static android.window.ConfigurationHelper.shouldUpdateResources;
+import android.annotation.BinderThread;
+import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.ActivityThread;
import android.app.IWindowToken;
import android.app.ResourcesManager;
import android.content.Context;
@@ -30,7 +31,9 @@
import android.os.Build;
import android.os.Bundle;
import android.os.Debug;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
import android.view.IWindowManager;
@@ -71,6 +74,8 @@
private boolean mAttachToWindowContainer;
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
+
/**
* Attaches {@code context} to this {@link WindowTokenClient}. Each {@link WindowTokenClient}
* can only attach one {@link Context}.
@@ -132,7 +137,8 @@
if (configuration == null) {
return false;
}
- onConfigurationChanged(configuration, displayId, false /* shouldReportConfigChange */);
+ mHandler.post(() -> onConfigurationChanged(configuration, displayId,
+ false /* shouldReportConfigChange */));
mAttachToWindowContainer = true;
return true;
} catch (RemoteException e) {
@@ -179,9 +185,11 @@
* @param newConfig the updated {@link Configuration}
* @param newDisplayId the updated {@link android.view.Display} ID
*/
+ @BinderThread
@Override
public void onConfigurationChanged(Configuration newConfig, int newDisplayId) {
- onConfigurationChanged(newConfig, newDisplayId, true /* shouldReportConfigChange */);
+ mHandler.post(() -> onConfigurationChanged(newConfig, newDisplayId,
+ true /* shouldReportConfigChange */));
}
// TODO(b/192048581): rewrite this method based on WindowContext and WindowProviderService
@@ -192,6 +200,7 @@
* Similar to {@link #onConfigurationChanged(Configuration, int)}, but adds a flag to control
* whether to dispatch configuration update or not.
*/
+ @MainThread
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public void onConfigurationChanged(Configuration newConfig, int newDisplayId,
boolean shouldReportConfigChange) {
@@ -217,16 +226,14 @@
if (shouldReportConfigChange && context instanceof WindowContext) {
final WindowContext windowContext = (WindowContext) context;
- ActivityThread.currentActivityThread().getHandler().post(
- () -> windowContext.dispatchConfigurationChanged(newConfig));
+ windowContext.dispatchConfigurationChanged(newConfig);
}
final int diff = mConfiguration.diffPublicOnly(newConfig);
if (shouldReportConfigChange && diff != 0
&& context instanceof WindowProviderService) {
final WindowProviderService windowProviderService = (WindowProviderService) context;
- ActivityThread.currentActivityThread().getHandler().post(
- () -> windowProviderService.onConfigurationChanged(newConfig));
+ windowProviderService.onConfigurationChanged(newConfig);
}
freeTextLayoutCachesIfNeeded(diff);
if (mShouldDumpConfigForIme) {
@@ -248,12 +255,15 @@
}
}
+ @BinderThread
@Override
public void onWindowTokenRemoved() {
- final Context context = mContextRef.get();
- if (context != null) {
- context.destroy();
- mContextRef.clear();
- }
+ mHandler.post(() -> {
+ final Context context = mContextRef.get();
+ if (context != null) {
+ context.destroy();
+ mContextRef.clear();
+ }
+ });
}
}
diff --git a/core/java/com/android/internal/util/dump/DumpableContainerImpl.java b/core/java/com/android/internal/util/dump/DumpableContainerImpl.java
new file mode 100644
index 0000000..d48b4b1
--- /dev/null
+++ b/core/java/com/android/internal/util/dump/DumpableContainerImpl.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.util.dump;
+
+import android.annotation.Nullable;
+import android.util.ArrayMap;
+import android.util.Dumpable;
+import android.util.DumpableContainer;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
+
+import java.io.PrintWriter;
+import java.util.Objects;
+
+// TODO(b/149254050): add unit tests
+/**
+ * Helper class for {@link DumpableContainer} implementations - they can "implement it by
+ * association", i.e., by delegating the interface methods to a {@code DumpableContainerImpl}.
+ *
+ * @hide
+ */
+public final class DumpableContainerImpl implements DumpableContainer {
+
+ private static final String TAG = DumpableContainerImpl.class.getSimpleName();
+
+ private static final boolean DEBUG = false;
+
+ @Nullable
+ private final ArrayMap<String, Dumpable> mDumpables = new ArrayMap<>();
+
+ @Override
+ public boolean addDumpable(Dumpable dumpable) {
+ Objects.requireNonNull(dumpable, "dumpable");
+ String name = dumpable.getDumpableName();
+ Objects.requireNonNull(name, () -> "name of" + dumpable);
+
+ if (mDumpables.containsKey(name)) {
+ Log.e(TAG, "addDumpable(): ignoring " + dumpable + " as there is already a dumpable"
+ + " with that name (" + name + "): " + mDumpables.get(name));
+ return false;
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "Adding " + name + " -> " + dumpable);
+ }
+ mDumpables.put(name, dumpable);
+ return true;
+ }
+
+ /**
+ * Dumps the number of dumpable, without a newline.
+ */
+ private int dumpNumberDumpables(IndentingPrintWriter writer) {
+ int size = mDumpables == null ? 0 : mDumpables.size();
+ if (size == 0) {
+ writer.print("No dumpables");
+ } else {
+ writer.print(size); writer.print(" dumpables");
+ }
+ return size;
+ }
+
+ /**
+ * Lists the name of all dumpables to the given {@code writer}.
+ */
+ public void listDumpables(String prefix, PrintWriter writer) {
+ IndentingPrintWriter ipw = new IndentingPrintWriter(writer, prefix, prefix);
+
+ int size = dumpNumberDumpables(ipw);
+ if (size == 0) {
+ ipw.println();
+ return;
+ }
+ ipw.print(": ");
+ for (int i = 0; i < size; i++) {
+ ipw.print(mDumpables.keyAt(i));
+ if (i < size - 1) ipw.print(' ');
+ }
+ ipw.println();
+ }
+
+ /**
+ * Dumps the content of all dumpables to the given {@code writer}.
+ */
+ public void dumpAllDumpables(String prefix, PrintWriter writer, String[] args) {
+ IndentingPrintWriter ipw = new IndentingPrintWriter(writer, prefix, prefix);
+ int size = dumpNumberDumpables(ipw);
+ if (size == 0) {
+ ipw.println();
+ return;
+ }
+ ipw.println(": ");
+
+ for (int i = 0; i < size; i++) {
+ String dumpableName = mDumpables.keyAt(i);
+ ipw.print('#'); ipw.print(i); ipw.print(": "); ipw.println(dumpableName);
+ Dumpable dumpable = mDumpables.valueAt(i);
+ indentAndDump(ipw, dumpable, args);
+ }
+ }
+
+ private void indentAndDump(IndentingPrintWriter writer, Dumpable dumpable, String[] args) {
+ writer.increaseIndent();
+ try {
+ dumpable.dump(writer, args);
+ } finally {
+ writer.decreaseIndent();
+ }
+ }
+
+ /**
+ * Dumps the content of a specific dumpable to the given {@code writer}.
+ */
+ @SuppressWarnings("resource") // cannot close ipw as it would close writer
+ public void dumpOneDumpable(String prefix, PrintWriter writer, String dumpableName,
+ String[] args) {
+ IndentingPrintWriter ipw = new IndentingPrintWriter(writer, prefix, prefix);
+ Dumpable dumpable = mDumpables.get(dumpableName);
+ if (dumpable == null) {
+ ipw.print("No "); ipw.println(dumpableName);
+ return;
+ }
+ ipw.print(dumpableName); ipw.println(':');
+ indentAndDump(ipw, dumpable, args);
+ }
+}
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index d0504fb..d039bcf 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -66,6 +66,7 @@
jfieldID flags;
//methods
jmethodID setType;
+ jmethodID setId;
jmethodID setUuid;
jmethodID init;
} gSensorOffsets;
@@ -112,6 +113,7 @@
sensorOffsets.flags = GetFieldIDOrDie(_env,sensorClass, "mFlags", "I");
sensorOffsets.setType = GetMethodIDOrDie(_env,sensorClass, "setType", "(I)Z");
+ sensorOffsets.setId = GetMethodIDOrDie(_env,sensorClass, "setId", "(I)V");
sensorOffsets.setUuid = GetMethodIDOrDie(_env,sensorClass, "setUuid", "(JJ)V");
sensorOffsets.init = GetMethodIDOrDie(_env,sensorClass, "<init>", "()V");
@@ -188,9 +190,10 @@
env->SetObjectField(sensor, sensorOffsets.stringType, stringType);
}
- // TODO(b/29547335): Rename "setUuid" method to "setId".
- int64_t id = nativeSensor.getId();
- env->CallVoidMethod(sensor, sensorOffsets.setUuid, id, 0);
+ int32_t id = nativeSensor.getId();
+ env->CallVoidMethod(sensor, sensorOffsets.setId, id);
+ Sensor::uuid_t uuid = nativeSensor.getUuid();
+ env->CallVoidMethod(sensor, sensorOffsets.setUuid, id, uuid.i64[0], uuid.i64[1]);
}
return sensor;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 777ddaa..d6bbe71 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3595,6 +3595,18 @@
<permission android:name="android.permission.REQUEST_INCIDENT_REPORT_APPROVAL"
android:protectionLevel="signature|privileged" />
+ <!-- ========================================= -->
+ <!-- Permissions for SupplementalApi -->
+ <!-- ========================================= -->
+ <eat-comment />
+
+ <!-- TODO(b/213488783): Update with correct names. -->
+ <!-- Allows an application to access SupplementalApis. -->
+ <permission android:name="android.permission.ACCESS_SUPPLEMENTAL_APIS"
+ android:label="@string/permlab_accessSupplementalApi"
+ android:description="@string/permdesc_accessSupplementalApi"
+ android:protectionLevel="normal" />
+
<!-- ==================================== -->
<!-- Private permissions -->
<!-- ==================================== -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 5fa7409..574c2f7 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3317,8 +3317,6 @@
</staging-public-group>
<staging-public-group type="bool" first-id="0x01cf0000">
- <!-- @hide @SystemApi -->
- <public name="config_systemCaptionsServiceCallsEnabled" />
<!-- @hide @TestApi -->
<public name="config_preventImeStartupUnlessTextEditor" />
</staging-public-group>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 2879759..6577ebc 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4046,11 +4046,16 @@
<!-- Description of an application permission that lets it ask user to ignore battery optimizations for that app-->
<string name="permdesc_requestIgnoreBatteryOptimizations">Allows an app to ask for permission to ignore battery optimizations for that app.</string>
- <!-- Title of an application permission that lets query all other packages. [CHAR LIMIT=NONE] -->
+ <!-- Title of an application permission that lets it query all other packages. [CHAR LIMIT=NONE] -->
<string name="permlab_queryAllPackages">query all packages</string>
<!-- Description of an application permission that lets it query all other packages. [CHAR LIMIT=NONE] -->
<string name="permdesc_queryAllPackages">Allows an app to see all installed packages.</string>
+ <!-- Title of an application permission that lets it access SupplementalApis. [CHAR LIMIT=NONE] -->
+ <string name="permlab_accessSupplementalApi">access SupplementalApis</string>
+ <!-- Description of an application permission that lets it access SupplementalApis. [CHAR LIMIT=NONE]-->
+ <string name="permdesc_accessSupplementalApi">Allows an application to access SupplementalApis.</string>
+
<!-- Shown in the tutorial for tap twice for zoom control. -->
<string name="tutorial_double_tap_to_zoom_message_short">Tap twice for zoom control</string>
diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml
index dcfca84..3b2f244 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -42,7 +42,6 @@
<item name="outlineSpotShadowColor">@color/btn_colored_background_material</item>
<item name="textAppearance">?attr/textAppearanceButton</item>
<item name="textColor">@color/btn_colored_text_material</item>
- <item name="drawableTint">@color/btn_colored_text_material</item>
</style>
<style name="Widget.DeviceDefault.TextView" parent="Widget.Material.TextView" />
<style name="Widget.DeviceDefault.CheckedTextView" parent="Widget.Material.CheckedTextView"/>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 0acd4bf..4e77563 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3654,6 +3654,8 @@
<java-symbol type="string" name="config_retailDemoPackage" />
<java-symbol type="string" name="config_retailDemoPackageSignature" />
+ <java-symbol type="bool" name="config_systemCaptionsServiceCallsEnabled" />
+
<java-symbol type="string" name="notification_channel_foreground_service" />
<java-symbol type="string" name="foreground_service_app_in_background" />
<java-symbol type="string" name="foreground_service_apps_in_background" />
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index ee0fb44..6f5951b 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -392,6 +392,7 @@
<permission name="android.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS"/>
<permission name="android.permission.SET_WALLPAPER" />
<permission name="android.permission.SET_WALLPAPER_COMPONENT" />
+ <permission name="android.permission.SET_WALLPAPER_DIM_AMOUNT" />
<permission name="android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE" />
<!-- Permissions required for Incremental CTS tests -->
<permission name="com.android.permission.USE_INSTALLER_V2"/>
diff --git a/identity/java/android/security/identity/CredentialDataRequest.java b/identity/java/android/security/identity/CredentialDataRequest.java
new file mode 100644
index 0000000..2a47a02
--- /dev/null
+++ b/identity/java/android/security/identity/CredentialDataRequest.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * An object representing a request for credential data.
+ */
+public class CredentialDataRequest {
+ CredentialDataRequest() {}
+
+ /**
+ * Gets the device-signed entries to request.
+ *
+ * @return the device-signed entries to request.
+ */
+ public @NonNull Map<String, Collection<String>> getDeviceSignedEntriesToRequest() {
+ return mDeviceSignedEntriesToRequest;
+ }
+
+ /**
+ * Gets the issuer-signed entries to request.
+ *
+ * @return the issuer-signed entries to request.
+ */
+ public @NonNull Map<String, Collection<String>> getIssuerSignedEntriesToRequest() {
+ return mIssuerSignedEntriesToRequest;
+ }
+
+ /**
+ * Gets whether to allow using an authentication key which use count has been exceeded.
+ *
+ * <p>By default this is set to true.
+ *
+ * @return whether to allow using an authentication key which use
+ * count has been exceeded if no other key is available.
+ */
+ public boolean isAllowUsingExhaustedKeys() {
+ return mAllowUsingExhaustedKeys;
+ }
+
+ /**
+ * Gets whether to allow using an authentication key which is expired.
+ *
+ * <p>By default this is set to false.
+ *
+ * @return whether to allow using an authentication key which is
+ * expired if no other key is available.
+ */
+ public boolean isAllowUsingExpiredKeys() {
+ return mAllowUsingExpiredKeys;
+ }
+
+ /**
+ * Gets whether to increment the use-count for the authentication key used.
+ *
+ * <p>By default this is set to true.
+ *
+ * @return whether to increment the use count of the authentication key used.
+ */
+ public boolean isIncrementUseCount() {
+ return mIncrementUseCount;
+ }
+
+ /**
+ * Gets the request message CBOR.
+ *
+ * <p>This data structure is described in the documentation for the
+ * {@link PresentationSession#getCredentialData(String, CredentialDataRequest)} method.
+ *
+ * @return the request message CBOR as described above.
+ */
+ public @Nullable byte[] getRequestMessage() {
+ return mRequestMessage;
+ }
+
+ /**
+ * Gets the reader signature.
+ *
+ * <p>This data structure is described in the documentation for the
+ * {@link PresentationSession#getCredentialData(String, CredentialDataRequest)} method.
+ *
+ * @return a {@code COSE_Sign1} structure as described above.
+ */
+ public @Nullable byte[] getReaderSignature() {
+ return mReaderSignature;
+ }
+
+ Map<String, Collection<String>> mDeviceSignedEntriesToRequest = new LinkedHashMap<>();
+ Map<String, Collection<String>> mIssuerSignedEntriesToRequest = new LinkedHashMap<>();
+ boolean mAllowUsingExhaustedKeys = true;
+ boolean mAllowUsingExpiredKeys = false;
+ boolean mIncrementUseCount = true;
+ byte[] mRequestMessage = null;
+ byte[] mReaderSignature = null;
+
+ /**
+ * A builder for {@link CredentialDataRequest}.
+ */
+ public static final class Builder {
+ private CredentialDataRequest mData;
+
+ /**
+ * Creates a new builder.
+ */
+ public Builder() {
+ mData = new CredentialDataRequest();
+ }
+
+ /**
+ * Sets the device-signed entries to request.
+ *
+ * @param entriesToRequest the device-signed entries to request.
+ */
+ public @NonNull Builder setDeviceSignedEntriesToRequest(
+ @NonNull Map<String, Collection<String>> entriesToRequest) {
+ mData.mDeviceSignedEntriesToRequest = entriesToRequest;
+ return this;
+ }
+
+ /**
+ * Sets the issuer-signed entries to request.
+ *
+ * @param entriesToRequest the issuer-signed entries to request.
+ * @return the builder.
+ */
+ public @NonNull Builder setIssuerSignedEntriesToRequest(
+ @NonNull Map<String, Collection<String>> entriesToRequest) {
+ mData.mIssuerSignedEntriesToRequest = entriesToRequest;
+ return this;
+ }
+
+ /**
+ * Sets whether to allow using an authentication key which use count has been exceeded.
+ *
+ * By default this is set to true.
+ *
+ * @param allowUsingExhaustedKeys whether to allow using an authentication key which use
+ * count has been exceeded if no other key is available.
+ * @return the builder.
+ */
+ public @NonNull Builder setAllowUsingExhaustedKeys(boolean allowUsingExhaustedKeys) {
+ mData.mAllowUsingExhaustedKeys = allowUsingExhaustedKeys;
+ return this;
+ }
+
+ /**
+ * Sets whether to allow using an authentication key which is expired.
+ *
+ * By default this is set to false.
+ *
+ * @param allowUsingExpiredKeys whether to allow using an authentication key which is
+ * expired if no other key is available.
+ * @return the builder.
+ */
+ public @NonNull Builder setAllowUsingExpiredKeys(boolean allowUsingExpiredKeys) {
+ mData.mAllowUsingExpiredKeys = allowUsingExpiredKeys;
+ return this;
+ }
+
+ /**
+ * Sets whether to increment the use-count for the authentication key used.
+ *
+ * By default this is set to true.
+ *
+ * @param incrementUseCount whether to increment the use count of the authentication
+ * key used.
+ * @return the builder.
+ */
+ public @NonNull Builder setIncrementUseCount(boolean incrementUseCount) {
+ mData.mIncrementUseCount = incrementUseCount;
+ return this;
+ }
+
+ /**
+ * Sets the request message CBOR.
+ *
+ * <p>This data structure is described in the documentation for the
+ * {@link PresentationSession#getCredentialData(String, CredentialDataRequest)} method.
+ *
+ * @param requestMessage the request message CBOR as described above.
+ * @return the builder.
+ */
+ public @NonNull Builder setRequestMessage(@NonNull byte[] requestMessage) {
+ mData.mRequestMessage = requestMessage;
+ return this;
+ }
+
+ /**
+ * Sets the reader signature.
+ *
+ * <p>This data structure is described in the documentation for the
+ * {@link PresentationSession#getCredentialData(String, CredentialDataRequest)} method.
+ *
+ * @param readerSignature a {@code COSE_Sign1} structure as described above.
+ * @return the builder.
+ */
+ public @NonNull Builder setReaderSignature(@NonNull byte[] readerSignature) {
+ mData.mReaderSignature = readerSignature;
+ return this;
+ }
+
+ /**
+ * Finishes building a {@link CredentialDataRequest}.
+ *
+ * @return the {@link CredentialDataRequest} object.
+ */
+ public @NonNull CredentialDataRequest build() {
+ return mData;
+ }
+ }
+}
diff --git a/identity/java/android/security/identity/CredentialDataResult.java b/identity/java/android/security/identity/CredentialDataResult.java
new file mode 100644
index 0000000..beb03af
--- /dev/null
+++ b/identity/java/android/security/identity/CredentialDataResult.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.lang.annotation.Retention;
+import java.util.Collection;
+
+
+/**
+ * An object that contains the result of retrieving data from a credential. This is used to return
+ * data requested in a {@link PresentationSession}.
+ */
+public abstract class CredentialDataResult {
+ /**
+ * @hide
+ */
+ protected CredentialDataResult() {}
+
+ /**
+ * Returns a CBOR structure containing the retrieved device-signed data.
+ *
+ * <p>This structure - along with the session transcript - may be cryptographically
+ * authenticated to prove to the reader that the data is from a trusted credential and
+ * {@link #getDeviceMac()} can be used to get a MAC.
+ *
+ * <p>The CBOR structure which is cryptographically authenticated is the
+ * {@code DeviceAuthenticationBytes} structure according to the following
+ * <a href="https://tools.ietf.org/html/rfc8610">CDDL</a> schema:
+ *
+ * <pre>
+ * DeviceAuthentication = [
+ * "DeviceAuthentication",
+ * SessionTranscript,
+ * DocType,
+ * DeviceNameSpacesBytes
+ * ]
+ *
+ * DocType = tstr
+ * SessionTranscript = any
+ * DeviceNameSpacesBytes = #6.24(bstr .cbor DeviceNameSpaces)
+ * DeviceAuthenticationBytes = #6.24(bstr .cbor DeviceAuthentication)
+ * </pre>
+ *
+ * <p>where
+ *
+ * <pre>
+ * DeviceNameSpaces = {
+ * * NameSpace => DeviceSignedItems
+ * }
+ *
+ * DeviceSignedItems = {
+ * + DataItemName => DataItemValue
+ * }
+ *
+ * NameSpace = tstr
+ * DataItemName = tstr
+ * DataItemValue = any
+ * </pre>
+ *
+ * <p>The returned data is the binary encoding of the {@code DeviceNameSpaces} structure
+ * as defined above.
+ *
+ * @return The bytes of the {@code DeviceNameSpaces} CBOR structure.
+ */
+ public abstract @NonNull byte[] getDeviceNameSpaces();
+
+ /**
+ * Returns a message authentication code over the {@code DeviceAuthenticationBytes} CBOR
+ * specified in {@link #getDeviceNameSpaces()}, to prove to the reader that the data
+ * is from a trusted credential.
+ *
+ * <p>The MAC proves to the reader that the data is from a trusted credential. This code is
+ * produced by using the key agreement and key derivation function from the ciphersuite
+ * with the authentication private key and the reader ephemeral public key to compute a
+ * shared message authentication code (MAC) key, then using the MAC function from the
+ * ciphersuite to compute a MAC of the authenticated data. See section 9.2.3.5 of
+ * ISO/IEC 18013-5 for details of this operation.
+ *
+ * <p>If the session transcript or reader ephemeral key wasn't set on the {@link
+ * PresentationSession} used to obtain this data no message authencation code will be produced
+ * and this method will return {@code null}.
+ *
+ * @return A COSE_Mac0 structure with the message authentication code as described above
+ * or {@code null} if the conditions specified above are not met.
+ */
+ public abstract @Nullable byte[] getDeviceMac();
+
+ /**
+ * Returns the static authentication data associated with the dynamic authentication
+ * key used to MAC the data returned by {@link #getDeviceNameSpaces()}.
+ *
+ * @return The static authentication data associated with dynamic authentication key used to
+ * MAC the data.
+ */
+ public abstract @NonNull byte[] getStaticAuthenticationData();
+
+ /**
+ * Gets the device-signed entries that was returned.
+ *
+ * @return an object to examine the entries returned.
+ */
+ public abstract @NonNull Entries getDeviceSignedEntries();
+
+ /**
+ * Gets the issuer-signed entries that was returned.
+ *
+ * @return an object to examine the entries returned.
+ */
+ public abstract @NonNull Entries getIssuerSignedEntries();
+
+ /**
+ * A class for representing data elements returned.
+ */
+ public interface Entries {
+ /** Value was successfully retrieved. */
+ int STATUS_OK = 0;
+
+ /** The entry does not exist. */
+ int STATUS_NO_SUCH_ENTRY = 1;
+
+ /** The entry was not requested. */
+ int STATUS_NOT_REQUESTED = 2;
+
+ /** The entry wasn't in the request message. */
+ int STATUS_NOT_IN_REQUEST_MESSAGE = 3;
+
+ /** The entry was not retrieved because user authentication failed. */
+ int STATUS_USER_AUTHENTICATION_FAILED = 4;
+
+ /** The entry was not retrieved because reader authentication failed. */
+ int STATUS_READER_AUTHENTICATION_FAILED = 5;
+
+ /**
+ * The entry was not retrieved because it was configured without any access
+ * control profile.
+ */
+ int STATUS_NO_ACCESS_CONTROL_PROFILES = 6;
+
+ /**
+ * Gets the names of namespaces with retrieved entries.
+ *
+ * @return collection of name of namespaces containing retrieved entries. May be empty if no
+ * data was retrieved.
+ */
+ @NonNull Collection<String> getNamespaces();
+
+ /**
+ * Get the names of all requested entries in a name space.
+ *
+ * <p>This includes the name of entries that wasn't successfully retrieved.
+ *
+ * @param namespaceName the namespace name to get entries for.
+ * @return A collection of names for the given namespace or the empty collection if no
+ * entries was returned for the given name space.
+ */
+ @NonNull Collection<String> getEntryNames(@NonNull String namespaceName);
+
+ /**
+ * Get the names of all entries that was successfully retrieved from a name space.
+ *
+ * <p>This only return entries for which {@link #getStatus(String, String)} will return
+ * {@link #STATUS_OK}.
+ *
+ * @param namespaceName the namespace name to get entries for.
+ * @return The entries in the given namespace that were successfully rerieved or the
+ * empty collection if no entries was returned for the given name space.
+ */
+ @NonNull Collection<String> getRetrievedEntryNames(@NonNull String namespaceName);
+
+ /**
+ * Gets the status of an entry.
+ *
+ * <p>This returns {@link #STATUS_OK} if the value was retrieved, {@link
+ * #STATUS_NO_SUCH_ENTRY} if the given entry wasn't retrieved, {@link
+ * #STATUS_NOT_REQUESTED} if it wasn't requested, {@link #STATUS_NOT_IN_REQUEST_MESSAGE} if
+ * the request message was set but the entry wasn't present in the request message, {@link
+ * #STATUS_USER_AUTHENTICATION_FAILED} if the value wasn't retrieved because the necessary
+ * user authentication wasn't performed, {@link #STATUS_READER_AUTHENTICATION_FAILED} if
+ * the supplied reader certificate chain didn't match the set of certificates the entry was
+ * provisioned with, or {@link #STATUS_NO_ACCESS_CONTROL_PROFILES} if the entry was
+ * configured without any access control profiles.
+ *
+ * @param namespaceName the namespace name of the entry.
+ * @param name the name of the entry to get the value for.
+ * @return the status indicating whether the value was retrieved and if not, why.
+ */
+ @Status int getStatus(@NonNull String namespaceName, @NonNull String name);
+
+ /**
+ * Gets the raw CBOR data for the value of an entry.
+ *
+ * <p>This should only be called on an entry for which the {@link #getStatus(String,
+ * String)} method returns {@link #STATUS_OK}.
+ *
+ * @param namespaceName the namespace name of the entry.
+ * @param name the name of the entry to get the value for.
+ * @return the raw CBOR data or {@code null} if no entry with the given name exists.
+ */
+ @Nullable byte[] getEntry(@NonNull String namespaceName, @NonNull String name);
+
+ /**
+ * The type of the entry status.
+ * @hide
+ */
+ @Retention(SOURCE)
+ @IntDef({STATUS_OK, STATUS_NO_SUCH_ENTRY, STATUS_NOT_REQUESTED,
+ STATUS_NOT_IN_REQUEST_MESSAGE, STATUS_USER_AUTHENTICATION_FAILED,
+ STATUS_READER_AUTHENTICATION_FAILED, STATUS_NO_ACCESS_CONTROL_PROFILES})
+ @interface Status {}
+ }
+
+}
diff --git a/identity/java/android/security/identity/CredstoreCredentialDataResult.java b/identity/java/android/security/identity/CredstoreCredentialDataResult.java
new file mode 100644
index 0000000..7afe3d4
--- /dev/null
+++ b/identity/java/android/security/identity/CredstoreCredentialDataResult.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+class CredstoreCredentialDataResult extends CredentialDataResult {
+
+ ResultData mDeviceSignedResult;
+ ResultData mIssuerSignedResult;
+ CredstoreEntries mDeviceSignedEntries;
+ CredstoreEntries mIssuerSignedEntries;
+
+ CredstoreCredentialDataResult(ResultData deviceSignedResult, ResultData issuerSignedResult) {
+ mDeviceSignedResult = deviceSignedResult;
+ mIssuerSignedResult = issuerSignedResult;
+ mDeviceSignedEntries = new CredstoreEntries(deviceSignedResult);
+ mIssuerSignedEntries = new CredstoreEntries(issuerSignedResult);
+ }
+
+ @Override
+ public @NonNull byte[] getDeviceNameSpaces() {
+ return mDeviceSignedResult.getAuthenticatedData();
+ }
+
+ @Override
+ public @Nullable byte[] getDeviceMac() {
+ return mDeviceSignedResult.getMessageAuthenticationCode();
+ }
+
+ @Override
+ public @NonNull byte[] getStaticAuthenticationData() {
+ return mDeviceSignedResult.getStaticAuthenticationData();
+ }
+
+ @Override
+ public @NonNull CredentialDataResult.Entries getDeviceSignedEntries() {
+ return mDeviceSignedEntries;
+ }
+
+ @Override
+ public @NonNull CredentialDataResult.Entries getIssuerSignedEntries() {
+ return mIssuerSignedEntries;
+ }
+
+ static class CredstoreEntries implements CredentialDataResult.Entries {
+ ResultData mResultData;
+
+ CredstoreEntries(ResultData resultData) {
+ mResultData = resultData;
+ }
+
+ @Override
+ public @NonNull Collection<String> getNamespaces() {
+ return mResultData.getNamespaces();
+ }
+
+ @Override
+ public @NonNull Collection<String> getEntryNames(@NonNull String namespaceName) {
+ Collection<String> ret = mResultData.getEntryNames(namespaceName);
+ if (ret == null) {
+ ret = new LinkedList<String>();
+ }
+ return ret;
+ }
+
+ @Override
+ public @NonNull Collection<String> getRetrievedEntryNames(@NonNull String namespaceName) {
+ Collection<String> ret = mResultData.getRetrievedEntryNames(namespaceName);
+ if (ret == null) {
+ ret = new LinkedList<String>();
+ }
+ return ret;
+ }
+
+ @Override
+ @Status
+ public int getStatus(@NonNull String namespaceName, @NonNull String name) {
+ return mResultData.getStatus(namespaceName, name);
+ }
+
+ @Override
+ public @Nullable byte[] getEntry(@NonNull String namespaceName, @NonNull String name) {
+ return mResultData.getEntry(namespaceName, name);
+ }
+ }
+
+}
diff --git a/identity/java/android/security/identity/CredstoreIdentityCredential.java b/identity/java/android/security/identity/CredstoreIdentityCredential.java
index 6398cee..8e01105 100644
--- a/identity/java/android/security/identity/CredstoreIdentityCredential.java
+++ b/identity/java/android/security/identity/CredstoreIdentityCredential.java
@@ -58,14 +58,17 @@
private @IdentityCredentialStore.Ciphersuite int mCipherSuite;
private Context mContext;
private ICredential mBinder;
+ private CredstorePresentationSession mSession;
CredstoreIdentityCredential(Context context, String credentialName,
@IdentityCredentialStore.Ciphersuite int cipherSuite,
- ICredential binder) {
+ ICredential binder,
+ @Nullable CredstorePresentationSession session) {
mContext = context;
mCredentialName = credentialName;
mCipherSuite = cipherSuite;
mBinder = binder;
+ mSession = session;
}
private KeyPair mEphemeralKeyPair = null;
@@ -239,6 +242,7 @@
private boolean mAllowUsingExhaustedKeys = true;
private boolean mAllowUsingExpiredKeys = false;
+ private boolean mIncrementKeyUsageCount = true;
@Override
public void setAllowUsingExhaustedKeys(boolean allowUsingExhaustedKeys) {
@@ -250,6 +254,11 @@
mAllowUsingExpiredKeys = allowUsingExpiredKeys;
}
+ @Override
+ public void setIncrementKeyUsageCount(boolean incrementKeyUsageCount) {
+ mIncrementKeyUsageCount = incrementKeyUsageCount;
+ }
+
private boolean mOperationHandleSet = false;
private long mOperationHandle = 0;
@@ -264,7 +273,8 @@
if (!mOperationHandleSet) {
try {
mOperationHandle = mBinder.selectAuthKey(mAllowUsingExhaustedKeys,
- mAllowUsingExpiredKeys);
+ mAllowUsingExpiredKeys,
+ mIncrementKeyUsageCount);
mOperationHandleSet = true;
} catch (android.os.RemoteException e) {
throw new RuntimeException("Unexpected RemoteException ", e);
@@ -315,7 +325,8 @@
sessionTranscript != null ? sessionTranscript : new byte[0],
readerSignature != null ? readerSignature : new byte[0],
mAllowUsingExhaustedKeys,
- mAllowUsingExpiredKeys);
+ mAllowUsingExpiredKeys,
+ mIncrementKeyUsageCount);
} catch (android.os.RemoteException e) {
throw new RuntimeException("Unexpected RemoteException ", e);
} catch (android.os.ServiceSpecificException e) {
diff --git a/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java b/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
index d8d4742..fb0880c 100644
--- a/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
+++ b/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
@@ -126,7 +126,8 @@
ICredential credstoreCredential;
credstoreCredential = mStore.getCredentialByName(credentialName, cipherSuite);
return new CredstoreIdentityCredential(mContext, credentialName, cipherSuite,
- credstoreCredential);
+ credstoreCredential,
+ null);
} catch (android.os.RemoteException e) {
throw new RuntimeException("Unexpected RemoteException ", e);
} catch (android.os.ServiceSpecificException e) {
@@ -162,4 +163,23 @@
+ e.errorCode, e);
}
}
+
+ @Override
+ public @NonNull PresentationSession createPresentationSession(@Ciphersuite int cipherSuite)
+ throws CipherSuiteNotSupportedException {
+ try {
+ ISession credstoreSession = mStore.createPresentationSession(cipherSuite);
+ return new CredstorePresentationSession(mContext, cipherSuite, this, credstoreSession);
+ } catch (android.os.RemoteException e) {
+ throw new RuntimeException("Unexpected RemoteException ", e);
+ } catch (android.os.ServiceSpecificException e) {
+ if (e.errorCode == ICredentialStore.ERROR_CIPHER_SUITE_NOT_SUPPORTED) {
+ throw new CipherSuiteNotSupportedException(e.getMessage(), e);
+ } else {
+ throw new RuntimeException("Unexpected ServiceSpecificException with code "
+ + e.errorCode, e);
+ }
+ }
+ }
+
}
diff --git a/identity/java/android/security/identity/CredstorePresentationSession.java b/identity/java/android/security/identity/CredstorePresentationSession.java
new file mode 100644
index 0000000..e3c6689
--- /dev/null
+++ b/identity/java/android/security/identity/CredstorePresentationSession.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+class CredstorePresentationSession extends PresentationSession {
+ private static final String TAG = "CredstorePresentationSession";
+
+ private @IdentityCredentialStore.Ciphersuite int mCipherSuite;
+ private Context mContext;
+ private CredstoreIdentityCredentialStore mStore;
+ private ISession mBinder;
+ private Map<String, CredstoreIdentityCredential> mCredentialCache = new LinkedHashMap<>();
+ private KeyPair mEphemeralKeyPair = null;
+ private byte[] mSessionTranscript = null;
+ private boolean mOperationHandleSet = false;
+ private long mOperationHandle = 0;
+
+ CredstorePresentationSession(Context context,
+ @IdentityCredentialStore.Ciphersuite int cipherSuite,
+ CredstoreIdentityCredentialStore store,
+ ISession binder) {
+ mContext = context;
+ mCipherSuite = cipherSuite;
+ mStore = store;
+ mBinder = binder;
+ }
+
+ private void ensureEphemeralKeyPair() {
+ if (mEphemeralKeyPair != null) {
+ return;
+ }
+ try {
+ // This PKCS#12 blob is generated in credstore, using BoringSSL.
+ //
+ // The main reason for this convoluted approach and not just sending the decomposed
+ // key-pair is that this would require directly using (device-side) BouncyCastle which
+ // is tricky due to various API hiding efforts. So instead we have credstore generate
+ // this PKCS#12 blob. The blob is encrypted with no password (sadly, also, BoringSSL
+ // doesn't support not using encryption when building a PKCS#12 blob).
+ //
+ byte[] pkcs12 = mBinder.getEphemeralKeyPair();
+ String alias = "ephemeralKey";
+ char[] password = {};
+
+ KeyStore ks = KeyStore.getInstance("PKCS12");
+ ByteArrayInputStream bais = new ByteArrayInputStream(pkcs12);
+ ks.load(bais, password);
+ PrivateKey privKey = (PrivateKey) ks.getKey(alias, password);
+
+ Certificate cert = ks.getCertificate(alias);
+ PublicKey pubKey = cert.getPublicKey();
+
+ mEphemeralKeyPair = new KeyPair(pubKey, privKey);
+ } catch (android.os.ServiceSpecificException e) {
+ throw new RuntimeException("Unexpected ServiceSpecificException with code "
+ + e.errorCode, e);
+ } catch (android.os.RemoteException
+ | KeyStoreException
+ | CertificateException
+ | UnrecoverableKeyException
+ | NoSuchAlgorithmException
+ | IOException e) {
+ throw new RuntimeException("Unexpected exception ", e);
+ }
+ }
+
+ @Override
+ public @NonNull KeyPair getEphemeralKeyPair() {
+ ensureEphemeralKeyPair();
+ return mEphemeralKeyPair;
+ }
+
+ @Override
+ public void setReaderEphemeralPublicKey(@NonNull PublicKey readerEphemeralPublicKey)
+ throws InvalidKeyException {
+ try {
+ byte[] uncompressedForm =
+ Util.publicKeyEncodeUncompressedForm(readerEphemeralPublicKey);
+ mBinder.setReaderEphemeralPublicKey(uncompressedForm);
+ } catch (android.os.RemoteException e) {
+ throw new RuntimeException("Unexpected RemoteException ", e);
+ } catch (android.os.ServiceSpecificException e) {
+ throw new RuntimeException("Unexpected ServiceSpecificException with code "
+ + e.errorCode, e);
+ }
+ }
+
+ @Override
+ public void setSessionTranscript(@NonNull byte[] sessionTranscript) {
+ try {
+ mBinder.setSessionTranscript(sessionTranscript);
+ mSessionTranscript = sessionTranscript;
+ } catch (android.os.RemoteException e) {
+ throw new RuntimeException("Unexpected RemoteException ", e);
+ } catch (android.os.ServiceSpecificException e) {
+ throw new RuntimeException("Unexpected ServiceSpecificException with code "
+ + e.errorCode, e);
+ }
+ }
+
+ @Override
+ public @Nullable CredentialDataResult getCredentialData(@NonNull String credentialName,
+ @NonNull CredentialDataRequest request)
+ throws NoAuthenticationKeyAvailableException, InvalidReaderSignatureException,
+ InvalidRequestMessageException, EphemeralPublicKeyNotFoundException {
+ try {
+ // Cache the IdentityCredential to satisfy the property that AuthKey usage counts are
+ // incremented on only the _first_ getCredentialData() call.
+ //
+ CredstoreIdentityCredential credential = mCredentialCache.get(credentialName);
+ if (credential == null) {
+ ICredential credstoreCredential =
+ mBinder.getCredentialForPresentation(credentialName);
+ credential = new CredstoreIdentityCredential(mContext, credentialName,
+ mCipherSuite, credstoreCredential,
+ this);
+ mCredentialCache.put(credentialName, credential);
+
+ credential.setAllowUsingExhaustedKeys(request.isAllowUsingExhaustedKeys());
+ credential.setAllowUsingExpiredKeys(request.isAllowUsingExpiredKeys());
+ credential.setIncrementKeyUsageCount(request.isIncrementUseCount());
+ }
+
+ ResultData deviceSignedResult = credential.getEntries(
+ request.getRequestMessage(),
+ request.getDeviceSignedEntriesToRequest(),
+ mSessionTranscript,
+ request.getReaderSignature());
+
+ // By design this second getEntries() call consumes the same auth-key.
+
+ ResultData issuerSignedResult = credential.getEntries(
+ request.getRequestMessage(),
+ request.getIssuerSignedEntriesToRequest(),
+ mSessionTranscript,
+ request.getReaderSignature());
+
+ return new CredstoreCredentialDataResult(deviceSignedResult, issuerSignedResult);
+
+ } catch (SessionTranscriptMismatchException e) {
+ throw new RuntimeException("Unexpected ", e);
+ } catch (android.os.RemoteException e) {
+ throw new RuntimeException("Unexpected RemoteException ", e);
+ } catch (android.os.ServiceSpecificException e) {
+ if (e.errorCode == ICredentialStore.ERROR_NO_SUCH_CREDENTIAL) {
+ return null;
+ } else {
+ throw new RuntimeException("Unexpected ServiceSpecificException with code "
+ + e.errorCode, e);
+ }
+ }
+ }
+
+ /**
+ * Called by android.hardware.biometrics.CryptoObject#getOpId() to get an
+ * operation handle.
+ *
+ * @hide
+ */
+ @Override
+ public long getCredstoreOperationHandle() {
+ if (!mOperationHandleSet) {
+ try {
+ mOperationHandle = mBinder.getAuthChallenge();
+ mOperationHandleSet = true;
+ } catch (android.os.RemoteException e) {
+ throw new RuntimeException("Unexpected RemoteException ", e);
+ } catch (android.os.ServiceSpecificException e) {
+ if (e.errorCode == ICredentialStore.ERROR_NO_AUTHENTICATION_KEY_AVAILABLE) {
+ // The NoAuthenticationKeyAvailableException will be thrown when
+ // the caller proceeds to call getEntries().
+ }
+ throw new RuntimeException("Unexpected ServiceSpecificException with code "
+ + e.errorCode, e);
+ }
+ }
+ return mOperationHandle;
+ }
+
+}
diff --git a/identity/java/android/security/identity/IdentityCredential.java b/identity/java/android/security/identity/IdentityCredential.java
index 1e68585..cdf746f 100644
--- a/identity/java/android/security/identity/IdentityCredential.java
+++ b/identity/java/android/security/identity/IdentityCredential.java
@@ -48,7 +48,9 @@
* encryption".
*
* @return ephemeral key pair to use to establish a secure channel with a reader.
+ * @deprecated Use {@link PresentationSession} instead.
*/
+ @Deprecated
public @NonNull abstract KeyPair createEphemeralKeyPair();
/**
@@ -58,7 +60,9 @@
* @param readerEphemeralPublicKey The ephemeral public key provided by the reader to
* establish a secure session.
* @throws InvalidKeyException if the given key is invalid.
+ * @deprecated Use {@link PresentationSession} instead.
*/
+ @Deprecated
public abstract void setReaderEphemeralPublicKey(@NonNull PublicKey readerEphemeralPublicKey)
throws InvalidKeyException;
@@ -72,7 +76,10 @@
*
* @param messagePlaintext unencrypted message to encrypt.
* @return encrypted message.
+ * @deprecated Applications should use {@link PresentationSession} and
+ * implement encryption/decryption themselves.
*/
+ @Deprecated
public @NonNull abstract byte[] encryptMessageToReader(@NonNull byte[] messagePlaintext);
/**
@@ -86,7 +93,10 @@
* @param messageCiphertext encrypted message to decrypt.
* @return decrypted message.
* @throws MessageDecryptionException if the ciphertext couldn't be decrypted.
+ * @deprecated Applications should use {@link PresentationSession} and
+ * implement encryption/decryption themselves.
*/
+ @Deprecated
public @NonNull abstract byte[] decryptMessageFromReader(@NonNull byte[] messageCiphertext)
throws MessageDecryptionException;
@@ -111,7 +121,9 @@
*
* @param allowUsingExhaustedKeys whether to allow using an authentication key which use count
* has been exceeded if no other key is available.
+ * @deprecated Use {@link PresentationSession} instead.
*/
+ @Deprecated
public abstract void setAllowUsingExhaustedKeys(boolean allowUsingExhaustedKeys);
/**
@@ -128,12 +140,36 @@
*
* @param allowUsingExpiredKeys whether to allow using an authentication key which use count
* has been exceeded if no other key is available.
+ * @deprecated Use {@link PresentationSession} instead.
*/
+ @Deprecated
public void setAllowUsingExpiredKeys(boolean allowUsingExpiredKeys) {
throw new UnsupportedOperationException();
}
/**
+ * @hide
+ *
+ * Sets whether the usage count of an authentication key should be increased. This must be
+ * called prior to calling
+ * {@link #getEntries(byte[], Map, byte[], byte[])} or using a
+ * {@link android.hardware.biometrics.BiometricPrompt.CryptoObject} which references this object.
+ *
+ * <p>By default this is set to true.
+ *
+ * <p>This is only implemented in feature version 202201 or later. If not implemented, the call
+ * fails with {@link UnsupportedOperationException}. See
+ * {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known
+ * feature versions.
+ *
+ * @param incrementKeyUsageCount whether the usage count of the key should be increased.
+ * @deprecated Use {@link PresentationSession} instead.
+ */
+ public void setIncrementKeyUsageCount(boolean incrementKeyUsageCount) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
* Called by android.hardware.biometrics.CryptoObject#getOpId() to get an
* operation handle.
*
@@ -149,15 +185,19 @@
* by using the {@link ResultData#getStatus(String, String)} method on each of the requested
* entries.
*
- * <p>It is the responsibility of the calling application to know if authentication is needed
- * and use e.g. {@link android.hardware.biometrics.BiometricPrompt} to make the user
- * authenticate using a {@link android.hardware.biometrics.BiometricPrompt.CryptoObject} which
- * references this object. If needed, this must be done before calling
- * {@link #getEntries(byte[], Map, byte[], byte[])}.
- *
* <p>It is permissible to call this method multiple times using the same instance but if this
* is done, the {@code sessionTranscript} parameter must be identical for each call. If this is
* not the case, the {@link SessionTranscriptMismatchException} exception is thrown.
+ * Additionally, if this is done the same auth-key will be used.
+ *
+ * <p>The application should not make any assumptions on whether user authentication is needed.
+ * Instead, the application should request the data elements values first and then examine
+ * the returned {@link ResultData}. If {@link ResultData#STATUS_USER_AUTHENTICATION_FAILED}
+ * is returned the application should get a
+ * {@link android.hardware.biometrics.BiometricPrompt.CryptoObject} which references this
+ * object and use it with a {@link android.hardware.biometrics.BiometricPrompt}. Upon successful
+ * authentication the application may call {@link #getEntries(byte[], Map, byte[], byte[])}
+ * again.
*
* <p>If not {@code null} the {@code requestMessage} parameter must contain data for the request
* from the verifier. The content can be defined in the way appropriate for the credential, but
@@ -269,7 +309,9 @@
* @throws InvalidRequestMessageException if the requestMessage is malformed.
* @throws EphemeralPublicKeyNotFoundException if the ephemeral public key was not found in
* the session transcript.
+ * @deprecated Use {@link PresentationSession} instead.
*/
+ @Deprecated
public abstract @NonNull ResultData getEntries(
@Nullable byte[] requestMessage,
@NonNull Map<String, Collection<String>> entriesToRequest,
diff --git a/identity/java/android/security/identity/IdentityCredentialStore.java b/identity/java/android/security/identity/IdentityCredentialStore.java
index 6ccd0e8..dbb8aaa 100644
--- a/identity/java/android/security/identity/IdentityCredentialStore.java
+++ b/identity/java/android/security/identity/IdentityCredentialStore.java
@@ -209,6 +209,25 @@
@Deprecated
public abstract @Nullable byte[] deleteCredentialByName(@NonNull String credentialName);
+ /**
+ * Creates a new presentation session.
+ *
+ * <p>This method gets an object to be used for interaction with a remote verifier for
+ * presentation of one or more credentials.
+ *
+ * <p>This is only implemented in feature version 202201 or later. If not implemented, the call
+ * fails with {@link UnsupportedOperationException}. See
+ * {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known
+ * feature versions.
+ *
+ * @param cipherSuite the cipher suite to use for communicating with the verifier.
+ * @return The presentation session.
+ */
+ public @NonNull PresentationSession createPresentationSession(@Ciphersuite int cipherSuite)
+ throws CipherSuiteNotSupportedException {
+ throw new UnsupportedOperationException();
+ }
+
/** @hide */
@IntDef(value = {CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256})
@Retention(RetentionPolicy.SOURCE)
diff --git a/identity/java/android/security/identity/PresentationSession.java b/identity/java/android/security/identity/PresentationSession.java
new file mode 100644
index 0000000..afaafce
--- /dev/null
+++ b/identity/java/android/security/identity/PresentationSession.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.PublicKey;
+
+/**
+ * Class for presenting multiple documents to a remote verifier.
+ *
+ * Use {@link IdentityCredentialStore#createPresentationSession(int)} to create a {@link
+ * PresentationSession} instance.
+ */
+public abstract class PresentationSession {
+ /**
+ * @hide
+ */
+ protected PresentationSession() {}
+
+ /**
+ * Gets the ephemeral key pair to use to establish a secure channel with the verifier.
+ *
+ * <p>Applications should use this key-pair for the communications channel with the verifier
+ * using a protocol / cipher-suite appropriate for the application. One example of such a
+ * protocol is the one used for Mobile Driving Licenses, see ISO 18013-5.
+ *
+ * <p>The ephemeral key pair is tied to the {@link PresentationSession} instance so subsequent
+ * calls to this method will return the same key-pair.
+ *
+ * @return ephemeral key pair to use to establish a secure channel with a reader.
+ */
+ public @NonNull abstract KeyPair getEphemeralKeyPair();
+
+ /**
+ * Set the ephemeral public key provided by the verifier.
+ *
+ * <p>If called, this must be called before any calls to
+ * {@link #getCredentialData(String, CredentialDataRequest)}.
+ *
+ * <p>This method can only be called once per {@link PresentationSession} instance.
+ *
+ * @param readerEphemeralPublicKey The ephemeral public key provided by the reader to
+ * establish a secure session.
+ * @throws InvalidKeyException if the given key is invalid.
+ */
+ public abstract void setReaderEphemeralPublicKey(@NonNull PublicKey readerEphemeralPublicKey)
+ throws InvalidKeyException;
+
+ /**
+ * Set the session transcript.
+ *
+ * <p>If called, this must be called before any calls to
+ * {@link #getCredentialData(String, CredentialDataRequest)}.
+ *
+ * <p>The X and Y coordinates of the public part of the key-pair returned by {@link
+ * #getEphemeralKeyPair()} must appear somewhere in the bytes of the passed in CBOR. Each of
+ * these coordinates must appear encoded with the most significant bits first and use the exact
+ * amount of bits indicated by the key size of the ephemeral keys. For example, if the
+ * ephemeral key is using the P-256 curve then the 32 bytes for the X coordinate encoded with
+ * the most significant bits first must appear somewhere and ditto for the 32 bytes for the Y
+ * coordinate.
+ *
+ * <p>This method can only be called once per {@link PresentationSession} instance.
+ *
+ * @param sessionTranscript the session transcript.
+ */
+ public abstract void setSessionTranscript(@NonNull byte[] sessionTranscript);
+
+ /**
+ * Retrieves data from a named credential in the current presentation session.
+ *
+ * <p>If an access control check fails for one of the requested entries or if the entry
+ * doesn't exist, the entry is simply not returned. The application can detect this
+ * by using the {@link CredentialDataResult.Entries#getStatus(String, String)} method on
+ * each of the requested entries.
+ *
+ * <p>The application should not make any assumptions on whether user authentication is needed.
+ * Instead, the application should request the data elements values first and then examine
+ * the returned {@link CredentialDataResult.Entries}. If
+ * {@link CredentialDataResult.Entries#STATUS_USER_AUTHENTICATION_FAILED} is returned the
+ * application should get a
+ * {@link android.hardware.biometrics.BiometricPrompt.CryptoObject} which references this
+ * object and use it with a {@link android.hardware.biometrics.BiometricPrompt}. Upon successful
+ * authentication the application may call
+ * {@link #getCredentialData(String, CredentialDataRequest)} again.
+ *
+ * <p>It is permissible to call this method multiple times using the same credential name.
+ * If this is done the same auth-key will be used.
+ *
+ * <p>If the reader signature is set in the request parameter (via the
+ * {@link CredentialDataRequest.Builder#setReaderSignature(byte[])} method) it must contain
+ * the bytes of a {@code COSE_Sign1} structure as defined in RFC 8152. For the payload
+ * {@code nil} shall be used and the detached payload is the {@code ReaderAuthenticationBytes}
+ * CBOR described below.
+ * <pre>
+ * ReaderAuthentication = [
+ * "ReaderAuthentication",
+ * SessionTranscript,
+ * ItemsRequestBytes
+ * ]
+ *
+ * ItemsRequestBytes = #6.24(bstr .cbor ItemsRequest)
+ *
+ * ReaderAuthenticationBytes = #6.24(bstr .cbor ReaderAuthentication)
+ * </pre>
+ *
+ * <p>where {@code ItemsRequestBytes} are the bytes of the request message set in
+ * the request parameter (via the
+ * {@link CredentialDataRequest.Builder#setRequestMessage(byte[])} method).
+ *
+ * <p>The public key corresponding to the key used to make the signature, can be found in the
+ * {@code x5chain} unprotected header element of the {@code COSE_Sign1} structure (as as
+ * described in
+ * <a href="https://tools.ietf.org/html/draft-ietf-cose-x509-08">draft-ietf-cose-x509-08</a>).
+ * There will be at least one certificate in said element and there may be more (and if so,
+ * each certificate must be signed by its successor).
+ *
+ * <p>Data elements protected by reader authentication are returned if, and only if,
+ * {@code requestMessage} is signed by the top-most certificate in the reader's certificate
+ * chain, and the data element is configured with an {@link AccessControlProfile} configured
+ * with an X.509 certificate for a key which appear in the certificate chain.
+ *
+ * <p>Note that the request message CBOR is used only for enforcing reader authentication, it's
+ * not used for determining which entries this API will return. The application is expected to
+ * have parsed the request message and filtered it according to user preference and/or consent.
+ *
+ * @param credentialName the name of the credential to retrieve.
+ * @param request the data to retrieve from the credential
+ * @return If the credential wasn't found, returns null. Otherwise a
+ * {@link CredentialDataResult} object containing entry data organized by namespace and
+ * a cryptographically authenticated representation of the same data, bound to the
+ * current session.
+ * @throws NoAuthenticationKeyAvailableException if authentication keys were never
+ * provisioned for the credential or if they
+ * are expired or exhausted their use-count.
+ * @throws InvalidRequestMessageException if the requestMessage is malformed.
+ * @throws InvalidReaderSignatureException if the reader signature is invalid, or it
+ * doesn't contain a certificate chain, or if
+ * the signature failed to validate.
+ * @throws EphemeralPublicKeyNotFoundException if the ephemeral public key was not found in
+ * the session transcript.
+ */
+ public abstract @Nullable CredentialDataResult getCredentialData(
+ @NonNull String credentialName, @NonNull CredentialDataRequest request)
+ throws NoAuthenticationKeyAvailableException, InvalidReaderSignatureException,
+ InvalidRequestMessageException, EphemeralPublicKeyNotFoundException;
+
+ /**
+ * Called by android.hardware.biometrics.CryptoObject#getOpId() to get an
+ * operation handle.
+ *
+ * @hide
+ */
+ public abstract long getCredstoreOperationHandle();
+}
diff --git a/identity/java/android/security/identity/ResultData.java b/identity/java/android/security/identity/ResultData.java
index 71860d2..d46f985 100644
--- a/identity/java/android/security/identity/ResultData.java
+++ b/identity/java/android/security/identity/ResultData.java
@@ -28,7 +28,10 @@
/**
* An object that contains the result of retrieving data from a credential. This is used to return
* data requested from a {@link IdentityCredential}.
+ *
+ * @deprecated Use {@link PresentationSession} instead.
*/
+@Deprecated
public abstract class ResultData {
/** Value was successfully retrieved. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
index 8e6c05d..eda09e3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
@@ -62,6 +62,7 @@
import androidx.annotation.VisibleForTesting;
import com.android.internal.logging.InstanceId;
+import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -132,6 +133,8 @@
final Rect fullscreenHitRegion = new Rect(displayRegion);
final boolean inLandscape = mSession.displayLayout.isLandscape();
final boolean inSplitScreen = mSplitScreen != null && mSplitScreen.isSplitScreenVisible();
+ final float dividerWidth = mContext.getResources().getDimensionPixelSize(
+ R.dimen.split_divider_bar_width);
// We allow splitting if we are already in split-screen or the running task is a standard
// task in fullscreen mode.
final boolean allowSplit = inSplitScreen
@@ -153,8 +156,11 @@
// If we have existing split regions use those bounds, otherwise split it 50/50
if (inSplitScreen) {
+ // Add the divider bounds to each side since that counts for the hit region.
leftHitRegion.set(topOrLeftBounds);
+ leftHitRegion.right += dividerWidth / 2;
rightHitRegion.set(bottomOrRightBounds);
+ rightHitRegion.left -= dividerWidth / 2;
} else {
displayRegion.splitVertically(leftHitRegion, rightHitRegion);
}
@@ -170,8 +176,11 @@
// If we have existing split regions use those bounds, otherwise split it 50/50
if (inSplitScreen) {
+ // Add the divider bounds to each side since that counts for the hit region.
topHitRegion.set(topOrLeftBounds);
+ topHitRegion.bottom += dividerWidth / 2;
bottomHitRegion.set(bottomOrRightBounds);
+ bottomHitRegion.top -= dividerWidth / 2;
} else {
displayRegion.splitHorizontally(topHitRegion, bottomHitRegion);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index 73f65b0..f8902c6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -269,6 +269,8 @@
// touchable or focusable by the user. We also add in the ALT_FOCUSABLE_IM
// flag because we do know that the next window will take input
// focus, so we want to get the IME window up on top of us right away.
+ // Touches will only pass through to the host activity window and will be blocked from
+ // passing to any other windows.
windowFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
@@ -276,9 +278,6 @@
params.token = appToken;
params.packageName = activityInfo.packageName;
params.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
- // Setting as trusted overlay to let touches pass through. This is safe because this
- // window is controlled by the system.
- params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
if (!context.getResources().getCompatibilityInfo().supportsScreen()) {
params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
index 2c02d2c..fb1004b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
@@ -27,7 +27,6 @@
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.exitSplitScreen
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview
import com.android.server.wm.flicker.helpers.setRotation
@@ -41,7 +40,6 @@
import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.dockedStackDividerBecomesInvisible
import com.android.wm.shell.flicker.helpers.SimpleAppHelper
-import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -112,11 +110,7 @@
@FlakyTest(bugId = 206753786)
@Test
- fun statusBarLayerRotatesScales() {
- // This test doesn't work in shell transitions because of b/206753786
- assumeFalse(isShellTransitionsEnabled)
- testSpec.statusBarLayerRotatesScales()
- }
+ fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
index 7d7add4..264d482 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
@@ -29,7 +29,6 @@
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.ImeAppHelper
import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.resizeSplitScreen
import com.android.server.wm.flicker.helpers.setRotation
@@ -45,7 +44,6 @@
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.helpers.SimpleAppHelper
import com.android.wm.shell.flicker.testapp.Components
-import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -137,11 +135,7 @@
@FlakyTest(bugId = 206753786)
@Test
- fun statusBarLayerRotatesScales() {
- // This test doesn't work in shell transitions because of b/206753786
- assumeFalse(isShellTransitionsEnabled)
- testSpec.statusBarLayerRotatesScales()
- }
+ fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
@Test
fun topAppLayerIsAlwaysVisible() {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
index 5678899..d703ea0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
@@ -25,7 +25,6 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
@@ -35,7 +34,6 @@
import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -79,11 +77,7 @@
@FlakyTest(bugId = 206753786)
@Test
- fun statusBarLayerRotatesScales() {
- // This test doesn't work in shell transitions because of b/206753786
- assumeFalse(isShellTransitionsEnabled)
- testSpec.statusBarLayerRotatesScales()
- }
+ fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
index c2edf9d..6b18839 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
@@ -25,7 +25,6 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
@@ -35,7 +34,6 @@
import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -78,11 +76,7 @@
@FlakyTest(bugId = 206753786)
@Test
- fun statusBarLayerRotatesScales() {
- // This test doesn't work in shell transitions because of b/206753786
- assumeFalse(isShellTransitionsEnabled)
- testSpec.statusBarLayerRotatesScales()
- }
+ fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
index 777998c..acd658b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
@@ -25,7 +25,6 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
import com.android.server.wm.flicker.helpers.setRotation
@@ -37,7 +36,6 @@
import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -88,11 +86,7 @@
@FlakyTest(bugId = 206753786)
@Test
- fun statusBarLayerRotatesScales() {
- // This test doesn't work in shell transitions because of b/206753786
- assumeFalse(isShellTransitionsEnabled)
- testSpec.statusBarLayerRotatesScales()
- }
+ fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
index 914b11d..b40be8b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
@@ -25,7 +25,6 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
import com.android.server.wm.flicker.helpers.setRotation
@@ -37,7 +36,6 @@
import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -93,11 +91,7 @@
@FlakyTest(bugId = 206753786)
@Test
- fun statusBarLayerRotatesScales() {
- // This test doesn't work in shell transitions because of b/206753786
- assumeFalse(isShellTransitionsEnabled)
- testSpec.statusBarLayerRotatesScales()
- }
+ fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
@FlakyTest
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
index d3bb008..2deff7b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -26,9 +26,7 @@
import com.android.server.wm.flicker.LAUNCHER_COMPONENT
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.rules.WMFlickerServiceRuleForTestSpec
-import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Rule
import org.junit.Test
@@ -93,11 +91,7 @@
/** {@inheritDoc} */
@FlakyTest(bugId = 206753786)
@Test
- override fun statusBarLayerRotatesScales() {
- // This test doesn't work in shell transitions because of b/206753786
- assumeFalse(isShellTransitionsEnabled)
- super.statusBarLayerRotatesScales()
- }
+ override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
/**
* Checks [pipApp] window remains visible throughout the animation
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
index fa9fbcd..0e73463 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
@@ -27,7 +27,6 @@
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.traces.common.FlickerComponentName
@@ -36,7 +35,6 @@
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_PORTRAIT
import com.android.wm.shell.flicker.testapp.Components.PipActivity.ACTION_ENTER_PIP
import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION
-import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -121,11 +119,7 @@
*/
@FlakyTest(bugId = 206753786)
@Test
- override fun statusBarLayerRotatesScales() {
- // This test doesn't work in shell transitions because of b/206753786
- assumeFalse(isShellTransitionsEnabled)
- testSpec.statusBarLayerRotatesScales()
- }
+ override fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
/**
* Checks that all parts of the screen are covered at the start and end of the transition
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
index f8a3aff..990872f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
@@ -18,9 +18,7 @@
import android.platform.test.annotations.Presubmit
import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.wm.shell.flicker.helpers.FixedAppHelper
-import org.junit.Assume.assumeFalse
import org.junit.Test
/**
@@ -29,15 +27,6 @@
abstract class ExitPipToAppTransition(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
protected val testApp = FixedAppHelper(instrumentation)
- /** {@inheritDoc} */
- @Presubmit
- @Test
- override fun statusBarLayerRotatesScales() {
- // This test doesn't work in shell transitions because of b/206753786
- assumeFalse(isShellTransitionsEnabled)
- super.statusBarLayerRotatesScales()
- }
-
/**
* Checks that the pip app window remains inside the display bounds throughout the whole
* animation
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
index 2231d88..2c08b7f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
@@ -24,8 +24,6 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -82,11 +80,7 @@
/** {@inheritDoc} */
@FlakyTest(bugId = 206753786)
@Test
- override fun statusBarLayerRotatesScales() {
- // This test doesn't work in shell transitions because of b/206753786
- assumeFalse(isShellTransitionsEnabled)
- super.statusBarLayerRotatesScales()
- }
+ override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
companion object {
/**
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
index fcac2c7..e340f4c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
@@ -24,9 +24,7 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.rules.WMFlickerServiceRuleForTestSpec
-import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Rule
import org.junit.Test
@@ -101,11 +99,7 @@
/** {@inheritDoc} */
@FlakyTest(bugId = 206753786)
@Test
- override fun statusBarLayerRotatesScales() {
- // This test doesn't work in shell transitions because of b/206753786
- assumeFalse(isShellTransitionsEnabled)
- super.statusBarLayerRotatesScales()
- }
+ override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
companion object {
/**
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
index 8e6603b..c036515 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
@@ -24,9 +24,7 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -81,11 +79,7 @@
@FlakyTest(bugId = 206753786)
@Test
- override fun statusBarLayerRotatesScales() {
- // This test doesn't work in shell transitions because of b/206753786
- assumeFalse(isShellTransitionsEnabled)
- testSpec.statusBarLayerRotatesScales()
- }
+ override fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
companion object {
/**
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt
index f9e180e..e415024 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt
@@ -24,8 +24,6 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import org.junit.Assume.assumeFalse
import com.android.server.wm.flicker.traces.region.RegionSubject
import org.junit.FixMethodOrder
import org.junit.Test
@@ -85,11 +83,7 @@
/** {@inheritDoc} */
@FlakyTest(bugId = 206753786)
@Test
- override fun statusBarLayerRotatesScales() {
- // This test doesn't work in shell transitions because of b/206753786
- assumeFalse(isShellTransitionsEnabled)
- super.statusBarLayerRotatesScales()
- }
+ override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
companion object {
/**
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
index b7bfa1b..4a4c46c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
@@ -25,8 +25,6 @@
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.traces.region.RegionSubject
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -85,11 +83,7 @@
/** {@inheritDoc} */
@FlakyTest(bugId = 206753786)
@Test
- override fun statusBarLayerRotatesScales() {
- // This test doesn't work in shell transitions because of b/206753786
- assumeFalse(isShellTransitionsEnabled)
- super.statusBarLayerRotatesScales()
- }
+ override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
companion object {
/**
diff --git a/media/java/android/media/tv/interactive/TvIAppInfo.aidl b/location/java/android/location/GnssAutomaticGainControl.aidl
similarity index 82%
copy from media/java/android/media/tv/interactive/TvIAppInfo.aidl
copy to location/java/android/location/GnssAutomaticGainControl.aidl
index 6041460..8298cb71 100644
--- a/media/java/android/media/tv/interactive/TvIAppInfo.aidl
+++ b/location/java/android/location/GnssAutomaticGainControl.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * 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.
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.media.tv.interactive;
+package android.location;
-parcelable TvIAppInfo;
\ No newline at end of file
+parcelable GnssAutomaticGainControl;
diff --git a/location/java/android/location/GnssAutomaticGainControl.java b/location/java/android/location/GnssAutomaticGainControl.java
new file mode 100644
index 0000000..e4f7304
--- /dev/null
+++ b/location/java/android/location/GnssAutomaticGainControl.java
@@ -0,0 +1,215 @@
+/*
+ * 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 android.location;
+
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
+/**
+ * A class that contains GNSS Automatic Gain Control (AGC) information.
+ *
+ * <p> AGC acts as a variable gain amplifier adjusting the power of the incoming signal. The AGC
+ * level may be used to indicate potential interference. Higher gain (and/or lower input power)
+ * shall be output as a positive number. Hence in cases of strong jamming, in the band of this
+ * signal, this value will go more negative. This value must be consistent given the same level
+ * of the incoming signal power.
+ *
+ * <p> Note: Different hardware designs (e.g. antenna, pre-amplification, or other RF HW
+ * components) may also affect the typical output of this value on any given hardware design
+ * in an open sky test - the important aspect of this output is that changes in this value are
+ * indicative of changes on input signal power in the frequency band for this measurement.
+ */
+public final class GnssAutomaticGainControl implements Parcelable {
+ private final double mLevelDb;
+ private final int mConstellationType;
+ private final long mCarrierFrequencyHz;
+
+ /**
+ * Creates a {@link GnssAutomaticGainControl} with a full list of parameters.
+ */
+ private GnssAutomaticGainControl(double levelDb, int constellationType,
+ long carrierFrequencyHz) {
+ mLevelDb = levelDb;
+ mConstellationType = constellationType;
+ mCarrierFrequencyHz = carrierFrequencyHz;
+ }
+
+ /**
+ * Gets the Automatic Gain Control level in dB.
+ */
+ @FloatRange(from = -10000, to = 10000)
+ public double getLevelDb() {
+ return mLevelDb;
+ }
+
+ /**
+ * Gets the constellation type.
+ *
+ * <p>The return value is one of those constants with {@code CONSTELLATION_} prefix in
+ * {@link GnssStatus}.
+ */
+ @GnssStatus.ConstellationType
+ public int getConstellationType() {
+ return mConstellationType;
+ }
+
+ /**
+ * Gets the carrier frequency of the tracked signal.
+ *
+ * <p>For example it can be the GPS central frequency for L1 = 1575.45 MHz, or L2 = 1227.60 MHz,
+ * L5 = 1176.45 MHz, varying GLO channels, etc.
+ *
+ * @return the carrier frequency of the signal tracked in Hz.
+ */
+ @IntRange(from = 0)
+ public long getCarrierFrequencyHz() {
+ return mCarrierFrequencyHz;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flag) {
+ parcel.writeDouble(mLevelDb);
+ parcel.writeInt(mConstellationType);
+ parcel.writeLong(mCarrierFrequencyHz);
+ }
+
+ @NonNull
+ public static final Creator<GnssAutomaticGainControl> CREATOR =
+ new Creator<GnssAutomaticGainControl>() {
+ @Override
+ @NonNull
+ public GnssAutomaticGainControl createFromParcel(@NonNull Parcel parcel) {
+ return new GnssAutomaticGainControl(parcel.readDouble(), parcel.readInt(),
+ parcel.readLong());
+ }
+
+ @Override
+ public GnssAutomaticGainControl[] newArray(int i) {
+ return new GnssAutomaticGainControl[i];
+ }
+ };
+
+ @NonNull
+ @Override
+ public String toString() {
+ StringBuilder s = new StringBuilder();
+ s.append("GnssAutomaticGainControl[");
+ s.append("Level=").append(mLevelDb).append(" dB");
+ s.append(" Constellation=").append(
+ GnssStatus.constellationTypeToString(mConstellationType));
+ s.append(" CarrierFrequency=").append(mCarrierFrequencyHz).append(" Hz");
+ s.append(']');
+ return s.toString();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof GnssAutomaticGainControl)) {
+ return false;
+ }
+
+ GnssAutomaticGainControl other = (GnssAutomaticGainControl) obj;
+ if (Double.compare(mLevelDb, other.mLevelDb)
+ != 0) {
+ return false;
+ }
+ if (mConstellationType != other.mConstellationType) {
+ return false;
+ }
+ if (mCarrierFrequencyHz != other.mCarrierFrequencyHz) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mLevelDb, mConstellationType, mCarrierFrequencyHz);
+ }
+
+ /** Builder for {@link GnssAutomaticGainControl} */
+ public static final class Builder {
+ private double mLevelDb;
+ private int mConstellationType;
+ private long mCarrierFrequencyHz;
+
+ /**
+ * Constructs a {@link GnssAutomaticGainControl.Builder} instance.
+ */
+ public Builder() {
+ }
+
+ /**
+ * Constructs a {@link GnssAutomaticGainControl.Builder} instance by copying a
+ * {@link GnssAutomaticGainControl}.
+ */
+ public Builder(@NonNull GnssAutomaticGainControl agc) {
+ mLevelDb = agc.getLevelDb();
+ mConstellationType = agc.getConstellationType();
+ mCarrierFrequencyHz = agc.getCarrierFrequencyHz();
+ }
+
+ /**
+ * Sets the Automatic Gain Control level in dB.
+ */
+ @NonNull
+ public Builder setLevelDb(@FloatRange(from = -10000, to = 10000) double levelDb) {
+ Preconditions.checkArgument(levelDb >= -10000 && levelDb <= 10000);
+ mLevelDb = levelDb;
+ return this;
+ }
+
+ /**
+ * Sets the constellation type.
+ */
+ @NonNull
+ public Builder setConstellationType(@GnssStatus.ConstellationType int constellationType) {
+ mConstellationType = constellationType;
+ return this;
+ }
+
+ /**
+ * Sets the Carrier frequency in Hz.
+ */
+ @NonNull public Builder setCarrierFrequencyHz(@IntRange(from = 0) long carrierFrequencyHz) {
+ Preconditions.checkArgumentNonnegative(carrierFrequencyHz);
+ mCarrierFrequencyHz = carrierFrequencyHz;
+ return this;
+ }
+
+ /** Builds a {@link GnssAutomaticGainControl} instance as specified by this builder. */
+ @NonNull
+ public GnssAutomaticGainControl build() {
+ return new GnssAutomaticGainControl(mLevelDb, mConstellationType, mCarrierFrequencyHz);
+ }
+ }
+}
diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java
index ecdd4b6..cdfa02c 100644
--- a/location/java/android/location/GnssMeasurement.java
+++ b/location/java/android/location/GnssMeasurement.java
@@ -1381,7 +1381,10 @@
/**
* Returns {@code true} if {@link #getAutomaticGainControlLevelDb()} is available,
* {@code false} otherwise.
+ *
+ * @deprecated Use {@link GnssMeasurementsEvent#getGnssAutomaticGainControls()} instead.
*/
+ @Deprecated
public boolean hasAutomaticGainControlLevelDb() {
return isFlagSet(HAS_AUTOMATIC_GAIN_CONTROL);
}
@@ -1401,7 +1404,10 @@
* indicative of changes on input signal power in the frequency band for this measurement.
*
* <p> The value is only available if {@link #hasAutomaticGainControlLevelDb()} is {@code true}
+ *
+ * @deprecated Use {@link GnssMeasurementsEvent#getGnssAutomaticGainControls()} instead.
*/
+ @Deprecated
public double getAutomaticGainControlLevelDb() {
return mAutomaticGainControlLevelInDb;
}
@@ -1409,7 +1415,9 @@
/**
* Sets the Automatic Gain Control level in dB.
* @hide
+ * @deprecated Use {@link GnssMeasurementsEvent.Builder#setGnssAutomaticGainControls()} instead.
*/
+ @Deprecated
@TestApi
public void setAutomaticGainControlLevelInDb(double agcLevelDb) {
setFlag(HAS_AUTOMATIC_GAIN_CONTROL);
diff --git a/location/java/android/location/GnssMeasurementsEvent.java b/location/java/android/location/GnssMeasurementsEvent.java
index a07a64a..075ddeb 100644
--- a/location/java/android/location/GnssMeasurementsEvent.java
+++ b/location/java/android/location/GnssMeasurementsEvent.java
@@ -18,16 +18,19 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.TestApi;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.util.Preconditions;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.security.InvalidParameterException;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.List;
/**
* A class implementing a container for data associated with a measurement event.
@@ -35,7 +38,8 @@
*/
public final class GnssMeasurementsEvent implements Parcelable {
private final GnssClock mClock;
- private final Collection<GnssMeasurement> mReadOnlyMeasurements;
+ private final List<GnssMeasurement> mMeasurements;
+ private final List<GnssAutomaticGainControl> mGnssAgcs;
/**
* Used for receiving GNSS satellite measurements from the GNSS engine.
@@ -116,20 +120,13 @@
}
/**
- * @hide
+ * Create a {@link GnssMeasurementsEvent} instance with a full list of parameters.
*/
- @TestApi
- public GnssMeasurementsEvent(GnssClock clock, GnssMeasurement[] measurements) {
- if (clock == null) {
- throw new InvalidParameterException("Parameter 'clock' must not be null.");
- }
- if (measurements == null || measurements.length == 0) {
- mReadOnlyMeasurements = Collections.emptyList();
- } else {
- Collection<GnssMeasurement> measurementCollection = Arrays.asList(measurements);
- mReadOnlyMeasurements = Collections.unmodifiableCollection(measurementCollection);
- }
-
+ private GnssMeasurementsEvent(@NonNull GnssClock clock,
+ @NonNull List<GnssMeasurement> measurements,
+ @NonNull List<GnssAutomaticGainControl> agcs) {
+ mMeasurements = measurements;
+ mGnssAgcs = agcs;
mClock = clock;
}
@@ -143,26 +140,31 @@
}
/**
- * Gets a read-only collection of measurements associated with the current event.
+ * Gets the collection of measurements associated with the current event.
*/
@NonNull
public Collection<GnssMeasurement> getMeasurements() {
- return mReadOnlyMeasurements;
+ return mMeasurements;
+ }
+
+ /**
+ * Gets the collection of {@link GnssAutomaticGainControl} associated with the
+ * current event.
+ */
+ @NonNull
+ public Collection<GnssAutomaticGainControl> getGnssAutomaticGainControls() {
+ return mGnssAgcs;
}
public static final @android.annotation.NonNull Creator<GnssMeasurementsEvent> CREATOR =
new Creator<GnssMeasurementsEvent>() {
@Override
public GnssMeasurementsEvent createFromParcel(Parcel in) {
- ClassLoader classLoader = getClass().getClassLoader();
-
- GnssClock clock = in.readParcelable(classLoader);
-
- int measurementsLength = in.readInt();
- GnssMeasurement[] measurementsArray = new GnssMeasurement[measurementsLength];
- in.readTypedArray(measurementsArray, GnssMeasurement.CREATOR);
-
- return new GnssMeasurementsEvent(clock, measurementsArray);
+ GnssClock clock = in.readParcelable(getClass().getClassLoader());
+ List<GnssMeasurement> measurements = in.createTypedArrayList(GnssMeasurement.CREATOR);
+ List<GnssAutomaticGainControl> agcs = in.createTypedArrayList(
+ GnssAutomaticGainControl.CREATOR);
+ return new GnssMeasurementsEvent(clock, measurements, agcs);
}
@Override
@@ -179,28 +181,105 @@
@Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeParcelable(mClock, flags);
-
- int measurementsCount = mReadOnlyMeasurements.size();
- GnssMeasurement[] measurementsArray =
- mReadOnlyMeasurements.toArray(new GnssMeasurement[measurementsCount]);
- parcel.writeInt(measurementsArray.length);
- parcel.writeTypedArray(measurementsArray, flags);
+ parcel.writeTypedList(mMeasurements);
+ parcel.writeTypedList(mGnssAgcs);
}
@Override
public String toString() {
- StringBuilder builder = new StringBuilder("[ GnssMeasurementsEvent:\n\n");
+ StringBuilder builder = new StringBuilder("GnssMeasurementsEvent[");
+ builder.append(mClock);
+ builder.append(' ').append(mMeasurements.toString());
+ builder.append(' ').append(mGnssAgcs.toString());
+ builder.append("]");
+ return builder.toString();
+ }
- builder.append(mClock.toString());
- builder.append("\n");
+ /** Builder for {@link GnssMeasurementsEvent} */
+ public static final class Builder {
+ private GnssClock mClock;
+ private List<GnssMeasurement> mMeasurements;
+ private List<GnssAutomaticGainControl> mGnssAgcs;
- for (GnssMeasurement measurement : mReadOnlyMeasurements) {
- builder.append(measurement.toString());
- builder.append("\n");
+ /**
+ * Constructs a {@link GnssMeasurementsEvent.Builder} instance.
+ */
+ public Builder() {
+ mClock = new GnssClock();
+ mMeasurements = new ArrayList<>();
+ mGnssAgcs = new ArrayList<>();
}
- builder.append("]");
+ /**
+ * Constructs a {@link GnssMeasurementsEvent.Builder} instance by copying a
+ * {@link GnssMeasurementsEvent}.
+ */
+ public Builder(@NonNull GnssMeasurementsEvent event) {
+ mClock = event.getClock();
+ mMeasurements = (List<GnssMeasurement>) event.getMeasurements();
+ mGnssAgcs = (List<GnssAutomaticGainControl>) event.getGnssAutomaticGainControls();
+ }
- return builder.toString();
+ /**
+ * Sets the {@link GnssClock}.
+ */
+ @NonNull
+ public Builder setClock(@NonNull GnssClock clock) {
+ Preconditions.checkNotNull(clock);
+ mClock = clock;
+ return this;
+ }
+
+ /**
+ * Sets the collection of {@link GnssMeasurement}.
+ *
+ * This API exists for JNI since it is easier for JNI to work with an array than a
+ * collection.
+ * @hide
+ */
+ @NonNull
+ public Builder setMeasurements(@Nullable GnssMeasurement... measurements) {
+ mMeasurements = measurements == null ? Collections.emptyList() : Arrays.asList(
+ measurements);
+ return this;
+ }
+
+ /**
+ * Sets the collection of {@link GnssMeasurement}.
+ */
+ @NonNull
+ public Builder setMeasurements(@NonNull Collection<GnssMeasurement> measurements) {
+ mMeasurements = new ArrayList<>(measurements);
+ return this;
+ }
+
+ /**
+ * Sets the collection of {@link GnssAutomaticGainControl}.
+ *
+ * This API exists for JNI since it is easier for JNI to work with an array than a
+ * collection.
+ * @hide
+ */
+ @NonNull
+ public Builder setGnssAutomaticGainControls(@Nullable GnssAutomaticGainControl... agcs) {
+ mGnssAgcs = agcs == null ? Collections.emptyList() : Arrays.asList(agcs);
+ return this;
+ }
+
+ /**
+ * Sets the collection of {@link GnssAutomaticGainControl}.
+ */
+ @NonNull
+ public Builder setGnssAutomaticGainControls(
+ @NonNull Collection<GnssAutomaticGainControl> agcs) {
+ mGnssAgcs = new ArrayList<>(agcs);
+ return this;
+ }
+
+ /** Builds a {@link GnssMeasurementsEvent} instance as specified by this builder. */
+ @NonNull
+ public GnssMeasurementsEvent build() {
+ return new GnssMeasurementsEvent(mClock, mMeasurements, mGnssAgcs);
+ }
}
}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index ffd5eaa..68e5d94 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -79,6 +79,7 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -5648,6 +5649,43 @@
}
/**
+ * Get the audio devices that would be used for the routing of the given audio attributes.
+ * These are the devices anticipated to play sound from an {@link AudioTrack} created with
+ * the specified {@link AudioAttributes}.
+ * The audio routing can change if audio devices are physically connected or disconnected or
+ * concurrently through {@link AudioRouting} or {@link MediaRouter}.
+ * @param attributes the {@link AudioAttributes} for which the routing is being queried
+ * @return an empty list if there was an issue with the request, a list of
+ * {@link AudioDeviceInfo} otherwise (typically one device, except for duplicated paths).
+ */
+ public @NonNull List<AudioDeviceInfo> getAudioDevicesForAttributes(
+ @NonNull AudioAttributes attributes) {
+ final List<AudioDeviceAttributes> devicesForAttributes;
+ try {
+ Objects.requireNonNull(attributes);
+ final IAudioService service = getService();
+ devicesForAttributes = service.getDevicesForAttributesUnprotected(attributes);
+ } catch (Exception e) {
+ Log.i(TAG, "No audio devices available for specified attributes.");
+ return Collections.emptyList();
+ }
+
+ // Map from AudioDeviceAttributes to AudioDeviceInfo
+ AudioDeviceInfo[] outputDeviceInfos = getDevicesStatic(GET_DEVICES_OUTPUTS);
+ List<AudioDeviceInfo> deviceInfosForAttributes = new ArrayList<>();
+ for (AudioDeviceAttributes deviceForAttributes : devicesForAttributes) {
+ for (AudioDeviceInfo deviceInfo : outputDeviceInfos) {
+ if (deviceForAttributes.getType() == deviceInfo.getType()
+ && TextUtils.equals(deviceForAttributes.getAddress(),
+ deviceInfo.getAddress())) {
+ deviceInfosForAttributes.add(deviceInfo);
+ }
+ }
+ }
+ return Collections.unmodifiableList(deviceInfosForAttributes);
+ }
+
+ /**
* @hide
* Volume behavior for an audio device that has no particular volume behavior set. Invalid as
* an argument to {@link #setDeviceVolumeBehavior(AudioDeviceAttributes, int)} and should not
diff --git a/media/java/android/media/BtProfileConnectionInfo.java b/media/java/android/media/BtProfileConnectionInfo.java
index 19ea2de..d1bb41e 100644
--- a/media/java/android/media/BtProfileConnectionInfo.java
+++ b/media/java/android/media/BtProfileConnectionInfo.java
@@ -34,7 +34,7 @@
/** @hide */
@IntDef({
BluetoothProfile.A2DP,
- BluetoothProfile.A2DP_SINK, // Can only be set by BtHelper
+ BluetoothProfile.A2DP_SINK,
BluetoothProfile.HEADSET, // Can only be set by BtHelper
BluetoothProfile.HEARING_AID,
BluetoothProfile.LE_AUDIO,
@@ -105,6 +105,16 @@
}
/**
+ * Constructor for A2dp sink info
+ * The {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be sent.
+ *
+ * @param volume of device -1 to ignore value
+ */
+ public static @NonNull BtProfileConnectionInfo a2dpSinkInfo(int volume) {
+ return new BtProfileConnectionInfo(BluetoothProfile.A2DP_SINK, true, volume, false);
+ }
+
+ /**
* Constructor for hearing aid info
*
* @param suppressNoisyIntent if true the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 7f6fb90..96199a9 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -308,6 +308,8 @@
List<AudioDeviceAttributes> getDevicesForAttributes(in AudioAttributes attributes);
+ List<AudioDeviceAttributes> getDevicesForAttributesUnprotected(in AudioAttributes attributes);
+
int setAllowedCapturePolicy(in int capturePolicy);
int getAllowedCapturePolicy();
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index 69ce8d2..09d7fbd 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -1124,6 +1124,8 @@
private class SurfaceImage extends android.media.Image {
public SurfaceImage(int format) {
mFormat = format;
+ mHardwareBufferFormat = ImageReader.this.mHardwareBufferFormat;
+ mDataSpace = ImageReader.this.mDataSpace;
}
SurfaceImage(int hardwareBufferFormat, long dataSpace) {
@@ -1229,6 +1231,12 @@
}
@Override
+ public long getDataSpace() {
+ throwISEIfImageIsInvalid();
+ return mDataSpace;
+ }
+
+ @Override
public void setTimestamp(long timestampNs) {
throwISEIfImageIsInvalid();
mTimestamp = timestampNs;
diff --git a/media/java/android/media/tv/BroadcastInfoRequest.java b/media/java/android/media/tv/BroadcastInfoRequest.java
index 85ad3cd..6ba9133 100644
--- a/media/java/android/media/tv/BroadcastInfoRequest.java
+++ b/media/java/android/media/tv/BroadcastInfoRequest.java
@@ -49,8 +49,10 @@
return StreamEventRequest.createFromParcelBody(source);
case TvInputManager.BROADCAST_INFO_TYPE_DSMCC:
return DsmccRequest.createFromParcelBody(source);
- case TvInputManager.BROADCAST_INFO_TYPE_TV_PROPRIETARY_FUNCTION:
+ case TvInputManager.BROADCAST_INFO_TYPE_COMMAND:
return CommandRequest.createFromParcelBody(source);
+ case TvInputManager.BROADCAST_INFO_TYPE_TIMELINE:
+ return TimelineRequest.createFromParcelBody(source);
default:
throw new IllegalStateException(
"Unexpected broadcast info request type (value "
diff --git a/media/java/android/media/tv/BroadcastInfoResponse.java b/media/java/android/media/tv/BroadcastInfoResponse.java
index e423aba..67bdedc 100644
--- a/media/java/android/media/tv/BroadcastInfoResponse.java
+++ b/media/java/android/media/tv/BroadcastInfoResponse.java
@@ -50,8 +50,10 @@
return StreamEventResponse.createFromParcelBody(source);
case TvInputManager.BROADCAST_INFO_TYPE_DSMCC:
return DsmccResponse.createFromParcelBody(source);
- case TvInputManager.BROADCAST_INFO_TYPE_TV_PROPRIETARY_FUNCTION:
+ case TvInputManager.BROADCAST_INFO_TYPE_COMMAND:
return CommandResponse.createFromParcelBody(source);
+ case TvInputManager.BROADCAST_INFO_TYPE_TIMELINE:
+ return TimelineResponse.createFromParcelBody(source);
default:
throw new IllegalStateException(
"Unexpected broadcast info response type (value "
diff --git a/media/java/android/media/tv/CommandRequest.java b/media/java/android/media/tv/CommandRequest.java
index 2391fa3..d61c858 100644
--- a/media/java/android/media/tv/CommandRequest.java
+++ b/media/java/android/media/tv/CommandRequest.java
@@ -23,7 +23,7 @@
/** @hide */
public final class CommandRequest extends BroadcastInfoRequest implements Parcelable {
public static final @TvInputManager.BroadcastInfoType int requestType =
- TvInputManager.BROADCAST_INFO_TYPE_TV_PROPRIETARY_FUNCTION;
+ TvInputManager.BROADCAST_INFO_TYPE_COMMAND;
public static final @NonNull Parcelable.Creator<CommandRequest> CREATOR =
new Parcelable.Creator<CommandRequest>() {
diff --git a/media/java/android/media/tv/CommandResponse.java b/media/java/android/media/tv/CommandResponse.java
index d34681f..af3d00c 100644
--- a/media/java/android/media/tv/CommandResponse.java
+++ b/media/java/android/media/tv/CommandResponse.java
@@ -23,7 +23,7 @@
/** @hide */
public final class CommandResponse extends BroadcastInfoResponse implements Parcelable {
public static final @TvInputManager.BroadcastInfoType int responseType =
- TvInputManager.BROADCAST_INFO_TYPE_TV_PROPRIETARY_FUNCTION;
+ TvInputManager.BROADCAST_INFO_TYPE_COMMAND;
public static final @NonNull Parcelable.Creator<CommandResponse> CREATOR =
new Parcelable.Creator<CommandResponse>() {
diff --git a/media/java/android/media/tv/DsmccResponse.java b/media/java/android/media/tv/DsmccResponse.java
index e43d31a..4d49620 100644
--- a/media/java/android/media/tv/DsmccResponse.java
+++ b/media/java/android/media/tv/DsmccResponse.java
@@ -21,6 +21,9 @@
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
+import java.util.ArrayList;
+import java.util.List;
+
/** @hide */
public final class DsmccResponse extends BroadcastInfoResponse implements Parcelable {
public static final @TvInputManager.BroadcastInfoType int responseType =
@@ -41,20 +44,27 @@
};
private final ParcelFileDescriptor mFileDescriptor;
+ private final boolean mIsDirectory;
+ private final List<String> mChildren;
public static DsmccResponse createFromParcelBody(Parcel in) {
return new DsmccResponse(in);
}
public DsmccResponse(int requestId, int sequence, @ResponseResult int responseResult,
- ParcelFileDescriptor file) {
+ ParcelFileDescriptor file, boolean isDirectory, List<String> children) {
super(responseType, requestId, sequence, responseResult);
mFileDescriptor = file;
+ mIsDirectory = isDirectory;
+ mChildren = children;
}
protected DsmccResponse(Parcel source) {
super(responseType, source);
mFileDescriptor = source.readFileDescriptor();
+ mIsDirectory = (source.readInt() == 1);
+ mChildren = new ArrayList<>();
+ source.readStringList(mChildren);
}
public ParcelFileDescriptor getFile() {
@@ -65,5 +75,7 @@
public void writeToParcel(@NonNull Parcel dest, int flags) {
super.writeToParcel(dest, flags);
mFileDescriptor.writeToParcel(dest, flags);
+ dest.writeInt(mIsDirectory ? 1 : 0);
+ dest.writeStringList(mChildren);
}
}
diff --git a/media/java/android/media/tv/ITvInputClient.aidl b/media/java/android/media/tv/ITvInputClient.aidl
index f4f55e4..49148ce 100644
--- a/media/java/android/media/tv/ITvInputClient.aidl
+++ b/media/java/android/media/tv/ITvInputClient.aidl
@@ -47,6 +47,7 @@
void onTimeShiftStartPositionChanged(long timeMs, int seq);
void onTimeShiftCurrentPositionChanged(long timeMs, int seq);
void onAitInfoUpdated(in AitInfo aitInfo, int seq);
+ void onSignalStrength(int stength, int seq);
void onTuned(in Uri channelUri, int seq);
// For the recording session
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index 770b8aa..2a33ee6 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -77,7 +77,7 @@
void setCaptionEnabled(in IBinder sessionToken, boolean enabled, int userId);
void selectTrack(in IBinder sessionToken, int type, in String trackId, int userId);
- void setIAppNotificationEnabled(in IBinder sessionToken, boolean enabled, int userId);
+ void setInteractiveAppNotificationEnabled(in IBinder sessionToken, boolean enabled, int userId);
void sendAppPrivateCommand(in IBinder sessionToken, in String action, in Bundle data,
int userId);
diff --git a/media/java/android/media/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl
index f427501..9820034 100644
--- a/media/java/android/media/tv/ITvInputSession.aidl
+++ b/media/java/android/media/tv/ITvInputSession.aidl
@@ -42,7 +42,7 @@
void setCaptionEnabled(boolean enabled);
void selectTrack(int type, in String trackId);
- void setIAppNotificationEnabled(boolean enable);
+ void setInteractiveAppNotificationEnabled(boolean enable);
void appPrivateCommand(in String action, in Bundle data);
diff --git a/media/java/android/media/tv/ITvInputSessionCallback.aidl b/media/java/android/media/tv/ITvInputSessionCallback.aidl
index 9830e78..9dfdb78 100644
--- a/media/java/android/media/tv/ITvInputSessionCallback.aidl
+++ b/media/java/android/media/tv/ITvInputSessionCallback.aidl
@@ -44,6 +44,7 @@
void onTimeShiftStartPositionChanged(long timeMs);
void onTimeShiftCurrentPositionChanged(long timeMs);
void onAitInfoUpdated(in AitInfo aitInfo);
+ void onSignalStrength(int strength);
// For the recording session
void onTuned(in Uri channelUri);
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index 418ab2c..8911f6c 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -247,7 +247,7 @@
break;
}
case DO_SET_IAPP_NOTIFICATION_ENABLED: {
- mTvInputSessionImpl.setIAppNotificationEnabled((Boolean) msg.obj);
+ mTvInputSessionImpl.setInteractiveAppNotificationEnabled((Boolean) msg.obj);
break;
}
case DO_REQUEST_AD: {
@@ -322,7 +322,7 @@
}
@Override
- public void setIAppNotificationEnabled(boolean enabled) {
+ public void setInteractiveAppNotificationEnabled(boolean enabled) {
mCaller.executeOrSendMessage(
mCaller.obtainMessageO(DO_SET_IAPP_NOTIFICATION_ENABLED, enabled));
}
diff --git a/media/java/android/media/tv/OWNERS b/media/java/android/media/tv/OWNERS
index 33acd0d..fa04293 100644
--- a/media/java/android/media/tv/OWNERS
+++ b/media/java/android/media/tv/OWNERS
@@ -1,6 +1,6 @@
-nchalko@google.com
quxiangfang@google.com
shubang@google.com
+hgchen@google.com
# For android remote service
per-file ITvRemoteServiceInput.aidl = file:/media/lib/tvremote/OWNERS
diff --git a/media/java/android/media/tv/TableResponse.java b/media/java/android/media/tv/TableResponse.java
index 912cbce..68d5f8a 100644
--- a/media/java/android/media/tv/TableResponse.java
+++ b/media/java/android/media/tv/TableResponse.java
@@ -17,9 +17,9 @@
package android.media.tv;
import android.annotation.NonNull;
+import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
-import android.net.Uri;
/** @hide */
public final class TableResponse extends BroadcastInfoResponse implements Parcelable {
diff --git a/media/java/android/media/tv/TimelineRequest.java b/media/java/android/media/tv/TimelineRequest.java
new file mode 100644
index 0000000..0714972
--- /dev/null
+++ b/media/java/android/media/tv/TimelineRequest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/** @hide */
+public final class TimelineRequest extends BroadcastInfoRequest implements Parcelable {
+ private static final @TvInputManager.BroadcastInfoType int REQUEST_TYPE =
+ TvInputManager.BROADCAST_INFO_TYPE_TIMELINE;
+
+ public static final @NonNull Parcelable.Creator<TimelineRequest> CREATOR =
+ new Parcelable.Creator<TimelineRequest>() {
+ @Override
+ public TimelineRequest createFromParcel(Parcel source) {
+ source.readInt();
+ return createFromParcelBody(source);
+ }
+
+ @Override
+ public TimelineRequest[] newArray(int size) {
+ return new TimelineRequest[size];
+ }
+ };
+
+ private final int mIntervalMs;
+
+ static TimelineRequest createFromParcelBody(Parcel in) {
+ return new TimelineRequest(in);
+ }
+
+ public TimelineRequest(int requestId, @RequestOption int option, int intervalMs) {
+ super(REQUEST_TYPE, requestId, option);
+ mIntervalMs = intervalMs;
+ }
+
+ protected TimelineRequest(Parcel source) {
+ super(REQUEST_TYPE, source);
+ mIntervalMs = source.readInt();
+ }
+
+ public int getIntervalMs() {
+ return mIntervalMs;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeInt(mIntervalMs);
+ }
+}
diff --git a/media/java/android/media/tv/TimelineResponse.java b/media/java/android/media/tv/TimelineResponse.java
new file mode 100644
index 0000000..fee10b4
--- /dev/null
+++ b/media/java/android/media/tv/TimelineResponse.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/** @hide */
+public final class TimelineResponse extends BroadcastInfoResponse implements Parcelable {
+ private static final @TvInputManager.BroadcastInfoType int RESPONSE_TYPE =
+ TvInputManager.BROADCAST_INFO_TYPE_TIMELINE;
+
+ public static final @NonNull Parcelable.Creator<TimelineResponse> CREATOR =
+ new Parcelable.Creator<TimelineResponse>() {
+ @Override
+ public TimelineResponse createFromParcel(Parcel source) {
+ source.readInt();
+ return createFromParcelBody(source);
+ }
+
+ @Override
+ public TimelineResponse[] newArray(int size) {
+ return new TimelineResponse[size];
+ }
+ };
+
+ private final String mSelector;
+ private final int mUnitsPerTick;
+ private final int mUnitsPerSecond;
+ private final long mWallClock;
+ private final long mTicks;
+
+ static TimelineResponse createFromParcelBody(Parcel in) {
+ return new TimelineResponse(in);
+ }
+
+ public TimelineResponse(int requestId, int sequence,
+ @ResponseResult int responseResult, String selector, int unitsPerTick,
+ int unitsPerSecond, long wallClock, long ticks) {
+ super(RESPONSE_TYPE, requestId, sequence, responseResult);
+ mSelector = selector;
+ mUnitsPerTick = unitsPerTick;
+ mUnitsPerSecond = unitsPerSecond;
+ mWallClock = wallClock;
+ mTicks = ticks;
+ }
+
+ protected TimelineResponse(Parcel source) {
+ super(RESPONSE_TYPE, source);
+ mSelector = source.readString();
+ mUnitsPerTick = source.readInt();
+ mUnitsPerSecond = source.readInt();
+ mWallClock = source.readLong();
+ mTicks = source.readLong();
+ }
+
+ public String getSelector() {
+ return mSelector;
+ }
+
+ public int getUnitsPerTick() {
+ return mUnitsPerTick;
+ }
+
+ public int getUnitsPerSecond() {
+ return mUnitsPerSecond;
+ }
+
+ public long getWallClock() {
+ return mWallClock;
+ }
+
+ public long getTicks() {
+ return mTicks;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeString(mSelector);
+ dest.writeInt(mUnitsPerTick);
+ dest.writeInt(mUnitsPerSecond);
+ dest.writeLong(mWallClock);
+ dest.writeLong(mTicks);
+ }
+}
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 6420b4a..98d1599 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -363,9 +363,10 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef({BROADCAST_INFO_TYPE_TS, BROADCAST_INFO_TYPE_TABLE, BROADCAST_INFO_TYPE_SECTION,
+ @IntDef(prefix = "BROADCAST_INFO_TYPE_", value =
+ {BROADCAST_INFO_TYPE_TS, BROADCAST_INFO_TYPE_TABLE, BROADCAST_INFO_TYPE_SECTION,
BROADCAST_INFO_TYPE_PES, BROADCAST_INFO_STREAM_EVENT, BROADCAST_INFO_TYPE_DSMCC,
- BROADCAST_INFO_TYPE_TV_PROPRIETARY_FUNCTION})
+ BROADCAST_INFO_TYPE_COMMAND, BROADCAST_INFO_TYPE_TIMELINE})
public @interface BroadcastInfoType {}
/** @hide */
@@ -381,7 +382,31 @@
/** @hide */
public static final int BROADCAST_INFO_TYPE_DSMCC = 6;
/** @hide */
- public static final int BROADCAST_INFO_TYPE_TV_PROPRIETARY_FUNCTION = 7;
+ public static final int BROADCAST_INFO_TYPE_COMMAND = 7;
+ /** @hide */
+ public static final int BROADCAST_INFO_TYPE_TIMELINE = 8;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "SIGNAL_STRENGTH_",
+ value = {SIGNAL_STRENGTH_LOST, SIGNAL_STRENGTH_WEAK, SIGNAL_STRENGTH_STRONG})
+ public @interface SignalStrength {}
+
+ /**
+ * Signal lost.
+ * @hide
+ */
+ public static final int SIGNAL_STRENGTH_LOST = 1;
+ /**
+ * Weak signal.
+ * @hide
+ */
+ public static final int SIGNAL_STRENGTH_WEAK = 2;
+ /**
+ * Strong signal.
+ * @hide
+ */
+ public static final int SIGNAL_STRENGTH_STRONG = 3;
/**
* An unknown state of the client pid gets from the TvInputManager. Client gets this value when
@@ -662,6 +687,14 @@
}
/**
+ * This is called when signal strength is updated.
+ * @param session A {@link TvInputManager.Session} associated with this callback.
+ * @param strength The current signal strength.
+ */
+ public void onSignalStrength(Session session, @SignalStrength int strength) {
+ }
+
+ /**
* This is called when the session has been tuned to the given channel.
*
* @param channelUri The URI of a channel.
@@ -735,8 +768,9 @@
@Override
public void run() {
mSessionCallback.onTracksChanged(mSession, tracks);
- if (mSession.mIAppNotificationEnabled && mSession.getIAppSession() != null) {
- mSession.getIAppSession().notifyTracksChanged(tracks);
+ if (mSession.mIAppNotificationEnabled
+ && mSession.getInteractiveAppSession() != null) {
+ mSession.getInteractiveAppSession().notifyTracksChanged(tracks);
}
}
});
@@ -747,8 +781,9 @@
@Override
public void run() {
mSessionCallback.onTrackSelected(mSession, type, trackId);
- if (mSession.mIAppNotificationEnabled && mSession.getIAppSession() != null) {
- mSession.getIAppSession().notifyTrackSelected(type, trackId);
+ if (mSession.mIAppNotificationEnabled
+ && mSession.getInteractiveAppSession() != null) {
+ mSession.getInteractiveAppSession().notifyTrackSelected(type, trackId);
}
}
});
@@ -768,8 +803,9 @@
@Override
public void run() {
mSessionCallback.onVideoAvailable(mSession);
- if (mSession.mIAppNotificationEnabled && mSession.getIAppSession() != null) {
- mSession.getIAppSession().notifyVideoAvailable();
+ if (mSession.mIAppNotificationEnabled
+ && mSession.getInteractiveAppSession() != null) {
+ mSession.getInteractiveAppSession().notifyVideoAvailable();
}
}
});
@@ -780,8 +816,9 @@
@Override
public void run() {
mSessionCallback.onVideoUnavailable(mSession, reason);
- if (mSession.mIAppNotificationEnabled && mSession.getIAppSession() != null) {
- mSession.getIAppSession().notifyVideoUnavailable(reason);
+ if (mSession.mIAppNotificationEnabled
+ && mSession.getInteractiveAppSession() != null) {
+ mSession.getInteractiveAppSession().notifyVideoUnavailable(reason);
}
}
});
@@ -792,8 +829,9 @@
@Override
public void run() {
mSessionCallback.onContentAllowed(mSession);
- if (mSession.mIAppNotificationEnabled && mSession.getIAppSession() != null) {
- mSession.getIAppSession().notifyContentAllowed();
+ if (mSession.mIAppNotificationEnabled
+ && mSession.getInteractiveAppSession() != null) {
+ mSession.getInteractiveAppSession().notifyContentAllowed();
}
}
});
@@ -804,8 +842,9 @@
@Override
public void run() {
mSessionCallback.onContentBlocked(mSession, rating);
- if (mSession.mIAppNotificationEnabled && mSession.getIAppSession() != null) {
- mSession.getIAppSession().notifyContentBlocked(rating);
+ if (mSession.mIAppNotificationEnabled
+ && mSession.getInteractiveAppSession() != null) {
+ mSession.getInteractiveAppSession().notifyContentBlocked(rating);
}
}
});
@@ -866,13 +905,27 @@
});
}
+ void postSignalStrength(final int strength) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSessionCallback.onSignalStrength(mSession, strength);
+ if (mSession.mIAppNotificationEnabled
+ && mSession.getInteractiveAppSession() != null) {
+ mSession.getInteractiveAppSession().notifySignalStrength(strength);
+ }
+ }
+ });
+ }
+
void postTuned(final Uri channelUri) {
mHandler.post(new Runnable() {
@Override
public void run() {
mSessionCallback.onTuned(mSession, channelUri);
- if (mSession.mIAppNotificationEnabled && mSession.getIAppSession() != null) {
- mSession.getIAppSession().notifyTuned(channelUri);
+ if (mSession.mIAppNotificationEnabled
+ && mSession.getInteractiveAppSession() != null) {
+ mSession.getInteractiveAppSession().notifyTuned(channelUri);
}
}
});
@@ -903,8 +956,9 @@
mHandler.post(new Runnable() {
@Override
public void run() {
- if (mSession.getIAppSession() != null) {
- mSession.getIAppSession().notifyBroadcastInfoResponse(response);
+ if (mSession.getInteractiveAppSession() != null) {
+ mSession.getInteractiveAppSession()
+ .notifyBroadcastInfoResponse(response);
}
}
});
@@ -916,8 +970,8 @@
mHandler.post(new Runnable() {
@Override
public void run() {
- if (mSession.getIAppSession() != null) {
- mSession.getIAppSession().notifyAdResponse(response);
+ if (mSession.getInteractiveAppSession() != null) {
+ mSession.getInteractiveAppSession().notifyAdResponse(response);
}
}
});
@@ -1299,6 +1353,18 @@
}
@Override
+ public void onSignalStrength(int strength, int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq " + seq);
+ return;
+ }
+ record.postSignalStrength(strength);
+ }
+ }
+
+ @Override
public void onTuned(Uri channelUri, int seq) {
synchronized (mSessionCallbackRecordMap) {
SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
@@ -2265,12 +2331,12 @@
mSessionCallbackRecordMap = sessionCallbackRecordMap;
}
- public TvIAppManager.Session getIAppSession() {
+ public TvIAppManager.Session getInteractiveAppSession() {
return mIAppSession;
}
- public void setIAppSession(TvIAppManager.Session IAppSession) {
- this.mIAppSession = IAppSession;
+ public void setInteractiveAppSession(TvIAppManager.Session iAppSession) {
+ this.mIAppSession = iAppSession;
}
/**
@@ -2531,13 +2597,13 @@
* {@code false} otherwise.
* @hide
*/
- public void setIAppNotificationEnabled(boolean enabled) {
+ public void setInteractiveAppNotificationEnabled(boolean enabled) {
if (mToken == null) {
Log.w(TAG, "The session has been already released");
return;
}
try {
- mService.setIAppNotificationEnabled(mToken, enabled, mUserId);
+ mService.setInteractiveAppNotificationEnabled(mToken, enabled, mUserId);
mIAppNotificationEnabled = enabled;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 3a40d6f..524ba34 100755
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -966,6 +966,27 @@
}
/**
+ * Notifies signal strength.
+ * @hide
+ */
+ public void notifySignalStrength(@TvInputManager.SignalStrength final int strength) {
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) Log.d(TAG, "notifySignalStrength");
+ if (mSessionCallback != null) {
+ mSessionCallback.onSignalStrength(strength);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "error in notifySignalStrength", e);
+ }
+ }
+ });
+ }
+
+ /**
* Assigns a size and position to the surface passed in {@link #onSetSurface}. The position
* is relative to the overlay view that sits on top of this surface.
*
@@ -1180,7 +1201,7 @@
* @param enabled {@code true} to enable, {@code false} to disable.
* @hide
*/
- public void onSetIAppNotificationEnabled(boolean enabled) {
+ public void onSetInteractiveAppNotificationEnabled(boolean enabled) {
}
/**
@@ -1532,10 +1553,10 @@
}
/**
- * Calls {@link #onSetIAppNotificationEnabled}.
+ * Calls {@link #onSetInteractiveAppNotificationEnabled}.
*/
- void setIAppNotificationEnabled(boolean enabled) {
- onSetIAppNotificationEnabled(enabled);
+ void setInteractiveAppNotificationEnabled(boolean enabled) {
+ onSetInteractiveAppNotificationEnabled(enabled);
}
/**
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index 4a12cd7..71f6ad6 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -486,9 +486,9 @@
* {@code false} otherwise.
* @hide
*/
- public void setIAppNotificationEnabled(boolean enabled) {
+ public void setInteractiveAppNotificationEnabled(boolean enabled) {
if (mSession != null) {
- mSession.setIAppNotificationEnabled(enabled);
+ mSession.setInteractiveAppNotificationEnabled(enabled);
}
}
@@ -1071,6 +1071,16 @@
}
/**
+ * This is called when signal strength is updated.
+ * @param inputId The ID of the TV input bound to this view.
+ * @param strength The current signal strength.
+ *
+ * @hide
+ */
+ public void onSignalStrength(String inputId, @TvInputManager.SignalStrength int strength) {
+ }
+
+ /**
* This is called when the session has been tuned to the given channel.
*
* @param channelUri The URI of a channel.
@@ -1390,6 +1400,20 @@
}
@Override
+ public void onSignalStrength(Session session, int strength) {
+ if (DEBUG) {
+ Log.d(TAG, "onSignalStrength(strength=" + strength + ")");
+ }
+ if (this != mSessionCallback) {
+ Log.w(TAG, "onSignalStrength - session not created");
+ return;
+ }
+ if (mCallback != null) {
+ mCallback.onSignalStrength(mInputId, strength);
+ }
+ }
+
+ @Override
public void onTuned(Session session, Uri channelUri) {
if (DEBUG) {
Log.d(TAG, "onTuned(channelUri=" + channelUri + ")");
diff --git a/media/java/android/media/tv/interactive/ITvIAppManager.aidl b/media/java/android/media/tv/interactive/ITvIAppManager.aidl
index 2e04359..a19a2d2 100644
--- a/media/java/android/media/tv/interactive/ITvIAppManager.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppManager.aidl
@@ -20,9 +20,9 @@
import android.media.tv.AdResponse;
import android.media.tv.BroadcastInfoResponse;
import android.media.tv.TvTrackInfo;
-import android.media.tv.interactive.ITvIAppClient;
-import android.media.tv.interactive.ITvIAppManagerCallback;
-import android.media.tv.interactive.TvIAppInfo;
+import android.media.tv.interactive.ITvInteractiveAppClient;
+import android.media.tv.interactive.ITvInteractiveAppManagerCallback;
+import android.media.tv.interactive.TvInteractiveAppInfo;
import android.net.Uri;
import android.os.Bundle;
import android.view.Surface;
@@ -32,21 +32,25 @@
* @hide
*/
interface ITvIAppManager {
- List<TvIAppInfo> getTvIAppServiceList(int userId);
+ List<TvInteractiveAppInfo> getTvInteractiveAppServiceList(int userId);
void prepare(String tiasId, int type, int userId);
- void notifyAppLinkInfo(String tiasId, in Bundle info, int userId);
+ void registerAppLinkInfo(String tiasId, in Bundle info, int userId);
+ void unregisterAppLinkInfo(String tiasId, in Bundle info, int userId);
void sendAppLinkCommand(String tiasId, in Bundle command, int userId);
- void startIApp(in IBinder sessionToken, int userId);
- void stopIApp(in IBinder sessionToken, int userId);
+ void startInteractiveApp(in IBinder sessionToken, int userId);
+ void stopInteractiveApp(in IBinder sessionToken, int userId);
+ void resetInteractiveApp(in IBinder sessionToken, int userId);
void createBiInteractiveApp(
in IBinder sessionToken, in Uri biIAppUri, in Bundle params, int userId);
void destroyBiInteractiveApp(in IBinder sessionToken, in String biIAppId, int userId);
+ void setTeletextAppEnabled(in IBinder sessionToken, boolean enable, int userId);
void sendCurrentChannelUri(in IBinder sessionToken, in Uri channelUri, int userId);
void sendCurrentChannelLcn(in IBinder sessionToken, int lcn, int userId);
void sendStreamVolume(in IBinder sessionToken, float volume, int userId);
void sendTrackInfoList(in IBinder sessionToken, in List<TvTrackInfo> tracks, int userId);
- void createSession(
- in ITvIAppClient client, in String iAppServiceId, int type, int seq, int userId);
+ void sendCurrentTvInputId(in IBinder sessionToken, in String inputId, int userId);
+ void createSession(in ITvInteractiveAppClient client, in String iAppServiceId, int type,
+ int seq, int userId);
void releaseSession(in IBinder sessionToken, int userId);
void notifyTuned(in IBinder sessionToken, in Uri channelUri, int userId);
void notifyTrackSelected(in IBinder sessionToken, int type, in String trackId, int userId);
@@ -55,6 +59,7 @@
void notifyVideoUnavailable(in IBinder sessionToken, int reason, int userId);
void notifyContentAllowed(in IBinder sessionToken, int userId);
void notifyContentBlocked(in IBinder sessionToken, in String rating, int userId);
+ void notifySignalStrength(in IBinder sessionToken, int stength, int userId);
void setSurface(in IBinder sessionToken, in Surface surface, int userId);
void dispatchSurfaceChanged(in IBinder sessionToken, int format, int width, int height,
int userId);
@@ -67,6 +72,6 @@
void relayoutMediaView(in IBinder sessionToken, in Rect frame, int userId);
void removeMediaView(in IBinder sessionToken, int userId);
- void registerCallback(in ITvIAppManagerCallback callback, int userId);
- void unregisterCallback(in ITvIAppManagerCallback callback, int userId);
+ void registerCallback(in ITvInteractiveAppManagerCallback callback, int userId);
+ void unregisterCallback(in ITvInteractiveAppManagerCallback callback, int userId);
}
diff --git a/media/java/android/media/tv/interactive/ITvIAppService.aidl b/media/java/android/media/tv/interactive/ITvIAppService.aidl
deleted file mode 100644
index 8acb75f..0000000
--- a/media/java/android/media/tv/interactive/ITvIAppService.aidl
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.tv.interactive;
-
-import android.media.tv.interactive.ITvIAppServiceCallback;
-import android.media.tv.interactive.ITvIAppSessionCallback;
-import android.os.Bundle;
-import android.view.InputChannel;
-
-/**
- * Top-level interface to a TV IApp component (implemented in a Service). It's used for
- * TvIAppManagerService to communicate with TvIAppService.
- * @hide
- */
-oneway interface ITvIAppService {
- void registerCallback(in ITvIAppServiceCallback callback);
- void unregisterCallback(in ITvIAppServiceCallback callback);
- void createSession(in InputChannel channel, in ITvIAppSessionCallback callback,
- in String iAppServiceId, int type);
- void prepare(int type);
- void notifyAppLinkInfo(in Bundle info);
- void sendAppLinkCommand(in Bundle command);
-}
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/ITvIAppClient.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
similarity index 83%
rename from media/java/android/media/tv/interactive/ITvIAppClient.aidl
rename to media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
index 892a800..1a8fc46 100644
--- a/media/java/android/media/tv/interactive/ITvIAppClient.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
@@ -24,11 +24,11 @@
import android.view.InputChannel;
/**
- * Interface a client of the ITvIAppManager implements, to identify itself and receive information
- * about changes to the state of each TV interactive application service.
+ * Interface a client of the ITvInteractiveAppManager implements, to identify itself and receive
+ * information about changes to the state of each TV interactive application service.
* @hide
*/
-oneway interface ITvIAppClient {
+oneway interface ITvInteractiveAppClient {
void onSessionCreated(in String iAppServiceId, IBinder token, in InputChannel channel, int seq);
void onSessionReleased(int seq);
void onLayoutSurface(int left, int top, int right, int bottom, int seq);
@@ -36,11 +36,13 @@
void onRemoveBroadcastInfo(int id, int seq);
void onSessionStateChanged(int state, int seq);
void onBiInteractiveAppCreated(in Uri biIAppUri, in String biIAppId, int seq);
+ void onTeletextAppStateChanged(int state, int seq);
void onCommandRequest(in String cmdType, in Bundle parameters, int seq);
void onSetVideoBounds(in Rect rect, int seq);
void onRequestCurrentChannelUri(int seq);
void onRequestCurrentChannelLcn(int seq);
void onRequestStreamVolume(int seq);
void onRequestTrackInfoList(int seq);
+ void onRequestCurrentTvInputId(int seq);
void onAdRequest(in AdRequest request, int Seq);
}
diff --git a/media/java/android/media/tv/interactive/ITvIAppManagerCallback.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppManagerCallback.aidl
similarity index 62%
rename from media/java/android/media/tv/interactive/ITvIAppManagerCallback.aidl
rename to media/java/android/media/tv/interactive/ITvInteractiveAppManagerCallback.aidl
index d5e0c63..f4510f6 100644
--- a/media/java/android/media/tv/interactive/ITvIAppManagerCallback.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppManagerCallback.aidl
@@ -16,16 +16,16 @@
package android.media.tv.interactive;
-import android.media.tv.interactive.TvIAppInfo;
+import android.media.tv.interactive.TvInteractiveAppInfo;
/**
- * Interface to receive callbacks from ITvIAppManager regardless of sessions.
+ * Interface to receive callbacks from ITvInteractiveAppManager regardless of sessions.
* @hide
*/
-interface ITvIAppManagerCallback {
- void onIAppServiceAdded(in String iAppServiceId);
- void onIAppServiceRemoved(in String iAppServiceId);
- void onIAppServiceUpdated(in String iAppServiceId);
- void onTvIAppInfoUpdated(in TvIAppInfo tvIAppInfo);
+interface ITvInteractiveAppManagerCallback {
+ void onInteractiveAppServiceAdded(in String iAppServiceId);
+ void onInteractiveAppServiceRemoved(in String iAppServiceId);
+ void onInteractiveAppServiceUpdated(in String iAppServiceId);
+ void onTvInteractiveAppInfoUpdated(in TvInteractiveAppInfo tvIAppInfo);
void onStateChanged(in String iAppServiceId, int type, int state);
}
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppService.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppService.aidl
new file mode 100644
index 0000000..c1e6622
--- /dev/null
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppService.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.interactive;
+
+import android.media.tv.interactive.ITvInteractiveAppServiceCallback;
+import android.media.tv.interactive.ITvInteractiveAppSessionCallback;
+import android.os.Bundle;
+import android.view.InputChannel;
+
+/**
+ * Top-level interface to a TV Interactive App component (implemented in a Service). It's used for
+ * TvIAppManagerService to communicate with TvIAppService.
+ * @hide
+ */
+oneway interface ITvInteractiveAppService {
+ void registerCallback(in ITvInteractiveAppServiceCallback callback);
+ void unregisterCallback(in ITvInteractiveAppServiceCallback callback);
+ void createSession(in InputChannel channel, in ITvInteractiveAppSessionCallback callback,
+ in String iAppServiceId, int type);
+ void prepare(int type);
+ void registerAppLinkInfo(in Bundle info);
+ void unregisterAppLinkInfo(in Bundle info);
+ void sendAppLinkCommand(in Bundle command);
+}
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/ITvIAppServiceCallback.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppServiceCallback.aidl
similarity index 84%
rename from media/java/android/media/tv/interactive/ITvIAppServiceCallback.aidl
rename to media/java/android/media/tv/interactive/ITvInteractiveAppServiceCallback.aidl
index fec7d78..f56d3bd 100644
--- a/media/java/android/media/tv/interactive/ITvIAppServiceCallback.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppServiceCallback.aidl
@@ -17,10 +17,10 @@
package android.media.tv.interactive;
/**
- * Helper interface for ITvIAppService to allow the TvIAppService to notify the
+ * Helper interface for ITvInteractiveAppService to allow the TvIAppService to notify the
* TvIAppManagerService.
* @hide
*/
-oneway interface ITvIAppServiceCallback {
+oneway interface ITvInteractiveAppServiceCallback {
void onStateChanged(int type, int state);
}
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/ITvIAppSession.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl
similarity index 83%
rename from media/java/android/media/tv/interactive/ITvIAppSession.aidl
rename to media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl
index 2788ff6..c449d2475 100644
--- a/media/java/android/media/tv/interactive/ITvIAppSession.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl
@@ -26,18 +26,22 @@
import android.view.Surface;
/**
- * Sub-interface of ITvIAppService.aidl which is created per session and has its own context.
+ * Sub-interface of ITvInteractiveAppService.aidl which is created per session and has its own
+ * context.
* @hide
*/
-oneway interface ITvIAppSession {
- void startIApp();
- void stopIApp();
+oneway interface ITvInteractiveAppSession {
+ void startInteractiveApp();
+ void stopInteractiveApp();
+ void resetInteractiveApp();
void createBiInteractiveApp(in Uri biIAppUri, in Bundle params);
void destroyBiInteractiveApp(in String biIAppId);
+ void setTeletextAppEnabled(boolean enable);
void sendCurrentChannelUri(in Uri channelUri);
void sendCurrentChannelLcn(int lcn);
void sendStreamVolume(float volume);
void sendTrackInfoList(in List<TvTrackInfo> tracks);
+ void sendCurrentTvInputId(in String inputId);
void release();
void notifyTuned(in Uri channelUri);
void notifyTrackSelected(int type, in String trackId);
@@ -46,6 +50,7 @@
void notifyVideoUnavailable(int reason);
void notifyContentAllowed();
void notifyContentBlocked(in String rating);
+ void notifySignalStrength(int strength);
void setSurface(in Surface surface);
void dispatchSurfaceChanged(int format, int width, int height);
void notifyBroadcastInfoResponse(in BroadcastInfoResponse response);
diff --git a/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
similarity index 78%
rename from media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl
rename to media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
index 9b9e6af..c270424 100644
--- a/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
@@ -19,27 +19,29 @@
import android.graphics.Rect;
import android.media.tv.AdRequest;
import android.media.tv.BroadcastInfoRequest;
-import android.media.tv.interactive.ITvIAppSession;
+import android.media.tv.interactive.ITvInteractiveAppSession;
import android.net.Uri;
import android.os.Bundle;
/**
- * Helper interface for ITvIAppSession to allow TvIAppService to notify the system service when
- * there is a related event.
+ * Helper interface for ITvInteractiveAppSession to allow TvIAppService to notify the
+ * system service when there is a related event.
* @hide
*/
-oneway interface ITvIAppSessionCallback {
- void onSessionCreated(in ITvIAppSession session);
+oneway interface ITvInteractiveAppSessionCallback {
+ void onSessionCreated(in ITvInteractiveAppSession session);
void onLayoutSurface(int left, int top, int right, int bottom);
void onBroadcastInfoRequest(in BroadcastInfoRequest request);
void onRemoveBroadcastInfo(int id);
void onSessionStateChanged(int state);
void onBiInteractiveAppCreated(in Uri biIAppUri, in String biIAppId);
+ void onTeletextAppStateChanged(int state);
void onCommandRequest(in String cmdType, in Bundle parameters);
void onSetVideoBounds(in Rect rect);
void onRequestCurrentChannelUri();
void onRequestCurrentChannelLcn();
void onRequestStreamVolume();
void onRequestTrackInfoList();
+ void onRequestCurrentTvInputId();
void onAdRequest(in AdRequest request);
}
diff --git a/media/java/android/media/tv/interactive/TvIAppInfo.java b/media/java/android/media/tv/interactive/TvIAppInfo.java
deleted file mode 100644
index b5245fc..0000000
--- a/media/java/android/media/tv/interactive/TvIAppInfo.java
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.tv.interactive;
-
-import android.annotation.NonNull;
-import android.annotation.StringDef;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.AttributeSet;
-import android.util.Xml;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * This class is used to specify meta information of a TV interactive app.
- * @hide
- */
-public final class TvIAppInfo implements Parcelable {
- private static final boolean DEBUG = false;
- private static final String TAG = "TvIAppInfo";
-
- @Retention(RetentionPolicy.SOURCE)
- @StringDef(prefix = { "INTERACTIVE_APP_TYPE_" }, value = {
- INTERACTIVE_APP_TYPE_HBBTV,
- INTERACTIVE_APP_TYPE_ATSC,
- INTERACTIVE_APP_TYPE_GINGA,
- })
- @interface InteractiveAppType {}
-
- /** HbbTV interactive app type */
- public static final String INTERACTIVE_APP_TYPE_HBBTV = "hbbtv";
- /** ATSC interactive app type */
- public static final String INTERACTIVE_APP_TYPE_ATSC = "atsc";
- /** Ginga interactive app type */
- public static final String INTERACTIVE_APP_TYPE_GINGA = "ginga";
-
- private final ResolveInfo mService;
- private final String mId;
- private List<String> mTypes = new ArrayList<>();
-
- private TvIAppInfo(ResolveInfo service, String id, List<String> types) {
- mService = service;
- mId = id;
- mTypes = types;
- }
-
- private TvIAppInfo(@NonNull Parcel in) {
- mService = ResolveInfo.CREATOR.createFromParcel(in);
- mId = in.readString();
- in.readStringList(mTypes);
- }
-
- public static final @NonNull Creator<TvIAppInfo> CREATOR = new Creator<TvIAppInfo>() {
- @Override
- public TvIAppInfo createFromParcel(Parcel in) {
- return new TvIAppInfo(in);
- }
-
- @Override
- public TvIAppInfo[] newArray(int size) {
- return new TvIAppInfo[size];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- mService.writeToParcel(dest, flags);
- dest.writeString(mId);
- dest.writeStringList(mTypes);
- }
-
- @NonNull
- public String getId() {
- return mId;
- }
-
- /**
- * Returns the component of the TV IApp service.
- * @hide
- */
- public ComponentName getComponent() {
- return new ComponentName(mService.serviceInfo.packageName, mService.serviceInfo.name);
- }
-
- /**
- * Returns the information of the service that implements this TV IApp service.
- */
- public ServiceInfo getServiceInfo() {
- return mService.serviceInfo;
- }
-
- /**
- * Gets supported interactive app types
- */
- @NonNull
- public List<String> getSupportedTypes() {
- return new ArrayList<>(mTypes);
- }
-
- /**
- * A convenience builder for creating {@link TvIAppInfo} objects.
- */
- public static final class Builder {
- private static final String XML_START_TAG_NAME = "tv-iapp";
- private final Context mContext;
- private final ResolveInfo mResolveInfo;
- private final List<String> mTypes = new ArrayList<>();
-
- /**
- * Constructs a new builder for {@link TvIAppInfo}.
- *
- * @param context A Context of the application package implementing this class.
- * @param component The name of the application component to be used for the
- * {@link TvIAppService}.
- */
- public Builder(@NonNull Context context, @NonNull ComponentName component) {
- if (context == null) {
- throw new IllegalArgumentException("context cannot be null.");
- }
- Intent intent = new Intent(TvIAppService.SERVICE_INTERFACE).setComponent(component);
- mResolveInfo = context.getPackageManager().resolveService(intent,
- PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
- if (mResolveInfo == null) {
- throw new IllegalArgumentException("Invalid component. Can't find the service.");
- }
- mContext = context;
- }
-
- /**
- * Creates a {@link TvIAppInfo} instance with the specified fields. Most of the information
- * is obtained by parsing the AndroidManifest and {@link TvIAppService#SERVICE_META_DATA}
- * for the {@link TvIAppService} this TV IApp implements.
- *
- * @return TvIAppInfo containing information about this TV IApp service.
- */
- @NonNull
- public TvIAppInfo build() {
- ComponentName componentName = new ComponentName(mResolveInfo.serviceInfo.packageName,
- mResolveInfo.serviceInfo.name);
- String id;
- id = generateIAppServiceId(componentName);
- parseServiceMetadata();
- return new TvIAppInfo(mResolveInfo, id, mTypes);
- }
-
- private static String generateIAppServiceId(ComponentName name) {
- return name.flattenToShortString();
- }
-
- private void parseServiceMetadata() {
- ServiceInfo si = mResolveInfo.serviceInfo;
- PackageManager pm = mContext.getPackageManager();
- try (XmlResourceParser parser =
- si.loadXmlMetaData(pm, TvIAppService.SERVICE_META_DATA)) {
- if (parser == null) {
- throw new IllegalStateException("No " + TvIAppService.SERVICE_META_DATA
- + " meta-data found for " + si.name);
- }
-
- Resources res = pm.getResourcesForApplication(si.applicationInfo);
- AttributeSet attrs = Xml.asAttributeSet(parser);
-
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && type != XmlPullParser.START_TAG) {
- // move to the START_TAG
- }
-
- String nodeName = parser.getName();
- if (!XML_START_TAG_NAME.equals(nodeName)) {
- throw new IllegalStateException("Meta-data does not start with "
- + XML_START_TAG_NAME + " tag for " + si.name);
- }
-
- TypedArray sa = res.obtainAttributes(attrs,
- com.android.internal.R.styleable.TvIAppService);
- CharSequence[] types = sa.getTextArray(
- com.android.internal.R.styleable.TvIAppService_supportedTypes);
- for (CharSequence cs : types) {
- mTypes.add(cs.toString().toLowerCase());
- }
-
- sa.recycle();
- } catch (IOException | XmlPullParserException e) {
- throw new IllegalStateException(
- "Failed reading meta-data for " + si.packageName, e);
- } catch (PackageManager.NameNotFoundException e) {
- throw new IllegalStateException("No resources found for " + si.packageName, e);
- }
- }
- }
-}
diff --git a/media/java/android/media/tv/interactive/TvIAppManager.java b/media/java/android/media/tv/interactive/TvIAppManager.java
old mode 100644
new mode 100755
index 9685e3a..f819438
--- a/media/java/android/media/tv/interactive/TvIAppManager.java
+++ b/media/java/android/media/tv/interactive/TvIAppManager.java
@@ -64,39 +64,63 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = false, prefix = "TV_IAPP_RTE_STATE_", value = {
- TV_IAPP_RTE_STATE_UNREALIZED,
- TV_IAPP_RTE_STATE_PREPARING,
- TV_IAPP_RTE_STATE_READY,
- TV_IAPP_RTE_STATE_ERROR})
- public @interface TvIAppRteState {}
+ @IntDef(flag = false, prefix = "TV_INTERACTIVE_APP_RTE_STATE_", value = {
+ TV_INTERACTIVE_APP_RTE_STATE_UNREALIZED,
+ TV_INTERACTIVE_APP_RTE_STATE_PREPARING,
+ TV_INTERACTIVE_APP_RTE_STATE_READY,
+ TV_INTERACTIVE_APP_RTE_STATE_ERROR})
+ public @interface TvInteractiveAppRteState {}
/**
* Unrealized state of interactive app RTE.
* @hide
*/
- public static final int TV_IAPP_RTE_STATE_UNREALIZED = 1;
+ public static final int TV_INTERACTIVE_APP_RTE_STATE_UNREALIZED = 1;
/**
* Preparing state of interactive app RTE.
* @hide
*/
- public static final int TV_IAPP_RTE_STATE_PREPARING = 2;
+ public static final int TV_INTERACTIVE_APP_RTE_STATE_PREPARING = 2;
/**
* Ready state of interactive app RTE.
* @hide
*/
- public static final int TV_IAPP_RTE_STATE_READY = 3;
+ public static final int TV_INTERACTIVE_APP_RTE_STATE_READY = 3;
/**
* Error state of interactive app RTE.
* @hide
*/
- public static final int TV_IAPP_RTE_STATE_ERROR = 4;
+ public static final int TV_INTERACTIVE_APP_RTE_STATE_ERROR = 4;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = false, prefix = "TELETEXT_APP_STATE_", value = {
+ TELETEXT_APP_STATE_SHOW,
+ TELETEXT_APP_STATE_HIDE,
+ TELETEXT_APP_STATE_ERROR})
+ public @interface TeletextAppState {}
+
+ /**
+ * Show state of Teletext app.
+ * @hide
+ */
+ public static final int TELETEXT_APP_STATE_SHOW = 1;
+ /**
+ * Hide state of Teletext app.
+ * @hide
+ */
+ public static final int TELETEXT_APP_STATE_HIDE = 2;
+ /**
+ * Error state of Teletext app.
+ * @hide
+ */
+ public static final int TELETEXT_APP_STATE_ERROR = 3;
/**
* Key for package name in app link.
* <p>Type: String
*
- * @see #notifyAppLinkInfo(String, Bundle)
+ * @see #registerAppLinkInfo(String, Bundle)
* @see #sendAppLinkCommand(String, Bundle)
* @hide
*/
@@ -106,7 +130,7 @@
* Key for class name in app link.
* <p>Type: String
*
- * @see #notifyAppLinkInfo(String, Bundle)
+ * @see #registerAppLinkInfo(String, Bundle)
* @see #sendAppLinkCommand(String, Bundle)
* @hide
*/
@@ -116,7 +140,7 @@
* Key for URI scheme in app link.
* <p>Type: String
*
- * @see #notifyAppLinkInfo(String, Bundle)
+ * @see #registerAppLinkInfo(String, Bundle)
* @hide
*/
public static final String KEY_URI_SCHEME = "uri_scheme";
@@ -125,7 +149,7 @@
* Key for URI host in app link.
* <p>Type: String
*
- * @see #notifyAppLinkInfo(String, Bundle)
+ * @see #registerAppLinkInfo(String, Bundle)
* @hide
*/
public static final String KEY_URI_HOST = "uri_host";
@@ -134,7 +158,7 @@
* Key for URI prefix in app link.
* <p>Type: String
*
- * @see #notifyAppLinkInfo(String, Bundle)
+ * @see #registerAppLinkInfo(String, Bundle)
* @hide
*/
public static final String KEY_URI_PREFIX = "uri_prefix";
@@ -174,7 +198,7 @@
new SparseArray<>();
// @GuardedBy("mLock")
- private final List<TvIAppCallbackRecord> mCallbackRecords = new LinkedList<>();
+ private final List<TvInteractiveAppCallbackRecord> mCallbackRecords = new LinkedList<>();
// A sequence number for the next session to be created. Should be protected by a lock
// {@code mSessionCallbackRecordMap}.
@@ -182,13 +206,13 @@
private final Object mLock = new Object();
- private final ITvIAppClient mClient;
+ private final ITvInteractiveAppClient mClient;
/** @hide */
public TvIAppManager(ITvIAppManager service, int userId) {
mService = service;
mUserId = userId;
- mClient = new ITvIAppClient.Stub() {
+ mClient = new ITvInteractiveAppClient.Stub() {
@Override
public void onSessionCreated(String iAppServiceId, IBinder token, InputChannel channel,
int seq) {
@@ -260,8 +284,10 @@
}
@Override
- public void onCommandRequest(@TvIAppService.IAppServiceCommandType String cmdType,
- Bundle parameters, int seq) {
+ public void onCommandRequest(
+ @TvIAppService.InteractiveAppServiceCommandType String cmdType,
+ Bundle parameters,
+ int seq) {
synchronized (mSessionCallbackRecordMap) {
SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
if (record == null) {
@@ -345,6 +371,18 @@
}
@Override
+ public void onRequestCurrentTvInputId(int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq " + seq);
+ return;
+ }
+ record.postRequestCurrentTvInputId();
+ }
+ }
+
+ @Override
public void onSessionStateChanged(int state, int seq) {
synchronized (mSessionCallbackRecordMap) {
SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
@@ -367,41 +405,54 @@
record.postBiInteractiveAppCreated(biIAppUri, biIAppId);
}
}
+
+ @Override
+ public void onTeletextAppStateChanged(int state, int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq " + seq);
+ return;
+ }
+ record.postTeletextAppStateChanged(state);
+ }
+ }
};
- ITvIAppManagerCallback managerCallback = new ITvIAppManagerCallback.Stub() {
+ ITvInteractiveAppManagerCallback managerCallback =
+ new ITvInteractiveAppManagerCallback.Stub() {
@Override
- public void onIAppServiceAdded(String iAppServiceId) {
+ public void onInteractiveAppServiceAdded(String iAppServiceId) {
synchronized (mLock) {
- for (TvIAppCallbackRecord record : mCallbackRecords) {
- record.postIAppServiceAdded(iAppServiceId);
+ for (TvInteractiveAppCallbackRecord record : mCallbackRecords) {
+ record.postInteractiveAppServiceAdded(iAppServiceId);
}
}
}
@Override
- public void onIAppServiceRemoved(String iAppServiceId) {
+ public void onInteractiveAppServiceRemoved(String iAppServiceId) {
synchronized (mLock) {
- for (TvIAppCallbackRecord record : mCallbackRecords) {
- record.postIAppServiceRemoved(iAppServiceId);
+ for (TvInteractiveAppCallbackRecord record : mCallbackRecords) {
+ record.postInteractiveAppServiceRemoved(iAppServiceId);
}
}
}
@Override
- public void onIAppServiceUpdated(String iAppServiceId) {
+ public void onInteractiveAppServiceUpdated(String iAppServiceId) {
synchronized (mLock) {
- for (TvIAppCallbackRecord record : mCallbackRecords) {
- record.postIAppServiceUpdated(iAppServiceId);
+ for (TvInteractiveAppCallbackRecord record : mCallbackRecords) {
+ record.postInteractiveAppServiceUpdated(iAppServiceId);
}
}
}
@Override
- public void onTvIAppInfoUpdated(TvIAppInfo iAppInfo) {
- // TODO: add public API updateIAppInfo()
+ public void onTvInteractiveAppInfoUpdated(TvInteractiveAppInfo iAppInfo) {
+ // TODO: add public API updateInteractiveAppInfo()
synchronized (mLock) {
- for (TvIAppCallbackRecord record : mCallbackRecords) {
- record.postTvIAppInfoUpdated(iAppInfo);
+ for (TvInteractiveAppCallbackRecord record : mCallbackRecords) {
+ record.postTvInteractiveAppInfoUpdated(iAppInfo);
}
}
}
@@ -409,7 +460,7 @@
@Override
public void onStateChanged(String iAppServiceId, int type, int state) {
synchronized (mLock) {
- for (TvIAppCallbackRecord record : mCallbackRecords) {
+ for (TvInteractiveAppCallbackRecord record : mCallbackRecords) {
record.postStateChanged(iAppServiceId, type, state);
}
}
@@ -425,110 +476,112 @@
}
/**
- * Callback used to monitor status of the TV IApp.
+ * Callback used to monitor status of the TV Interactive App.
* @hide
*/
- public abstract static class TvIAppCallback {
+ public abstract static class TvInteractiveAppCallback {
/**
- * This is called when a TV IApp service is added to the system.
+ * This is called when a TV Interactive App service is added to the system.
*
- * <p>Normally it happens when the user installs a new TV IApp service package that
- * implements {@link TvIAppService} interface.
+ * <p>Normally it happens when the user installs a new TV Interactive App service package
+ * that implements {@link TvIAppService} interface.
*
- * @param iAppServiceId The ID of the TV IApp service.
+ * @param iAppServiceId The ID of the TV Interactive App service.
*/
- public void onIAppServiceAdded(@NonNull String iAppServiceId) {
+ public void onInteractiveAppServiceAdded(@NonNull String iAppServiceId) {
}
/**
- * This is called when a TV IApp service is removed from the system.
+ * This is called when a TV Interactive App service is removed from the system.
*
- * <p>Normally it happens when the user uninstalls the previously installed TV IApp service
- * package.
+ * <p>Normally it happens when the user uninstalls the previously installed TV Interactive
+ * App service package.
*
- * @param iAppServiceId The ID of the TV IApp service.
+ * @param iAppServiceId The ID of the TV Interactive App service.
*/
- public void onIAppServiceRemoved(@NonNull String iAppServiceId) {
+ public void onInteractiveAppServiceRemoved(@NonNull String iAppServiceId) {
}
/**
- * This is called when a TV IApp service is updated on the system.
+ * This is called when a TV Interactive App service is updated on the system.
*
- * <p>Normally it happens when a previously installed TV IApp service package is
+ * <p>Normally it happens when a previously installed TV Interactive App service package is
* re-installed or a newer version of the package exists becomes available/unavailable.
*
- * @param iAppServiceId The ID of the TV IApp service.
+ * @param iAppServiceId The ID of the TV Interactive App service.
*/
- public void onIAppServiceUpdated(@NonNull String iAppServiceId) {
+ public void onInteractiveAppServiceUpdated(@NonNull String iAppServiceId) {
}
/**
- * This is called when the information about an existing TV IApp service has been updated.
+ * This is called when the information about an existing TV Interactive App service has been
+ * updated.
*
- * <p>Because the system automatically creates a <code>TvIAppInfo</code> object for each TV
- * IApp service based on the information collected from the
+ * <p>Because the system automatically creates a <code>TvInteractiveAppInfo</code> object
+ * for each TV Interactive App service based on the information collected from the
* <code>AndroidManifest.xml</code>, this method is only called back when such information
* has changed dynamically.
*
- * @param iAppInfo The <code>TvIAppInfo</code> object that contains new information.
+ * @param iAppInfo The <code>TvInteractiveAppInfo</code> object that contains new
+ * information.
*/
- public void onTvIAppInfoUpdated(@NonNull TvIAppInfo iAppInfo) {
+ public void onTvInteractiveAppInfoUpdated(@NonNull TvInteractiveAppInfo iAppInfo) {
}
/**
* This is called when the state of the interactive app service is changed.
* @hide
*/
- public void onTvIAppServiceStateChanged(
- @NonNull String iAppServiceId, int type, @TvIAppRteState int state) {
+ public void onTvInteractiveAppServiceStateChanged(
+ @NonNull String iAppServiceId, int type, @TvInteractiveAppRteState int state) {
}
}
- private static final class TvIAppCallbackRecord {
- private final TvIAppCallback mCallback;
+ private static final class TvInteractiveAppCallbackRecord {
+ private final TvInteractiveAppCallback mCallback;
private final Handler mHandler;
- TvIAppCallbackRecord(TvIAppCallback callback, Handler handler) {
+ TvInteractiveAppCallbackRecord(TvInteractiveAppCallback callback, Handler handler) {
mCallback = callback;
mHandler = handler;
}
- public TvIAppCallback getCallback() {
+ public TvInteractiveAppCallback getCallback() {
return mCallback;
}
- public void postIAppServiceAdded(final String iAppServiceId) {
+ public void postInteractiveAppServiceAdded(final String iAppServiceId) {
mHandler.post(new Runnable() {
@Override
public void run() {
- mCallback.onIAppServiceAdded(iAppServiceId);
+ mCallback.onInteractiveAppServiceAdded(iAppServiceId);
}
});
}
- public void postIAppServiceRemoved(final String iAppServiceId) {
+ public void postInteractiveAppServiceRemoved(final String iAppServiceId) {
mHandler.post(new Runnable() {
@Override
public void run() {
- mCallback.onIAppServiceRemoved(iAppServiceId);
+ mCallback.onInteractiveAppServiceRemoved(iAppServiceId);
}
});
}
- public void postIAppServiceUpdated(final String iAppServiceId) {
+ public void postInteractiveAppServiceUpdated(final String iAppServiceId) {
mHandler.post(new Runnable() {
@Override
public void run() {
- mCallback.onIAppServiceUpdated(iAppServiceId);
+ mCallback.onInteractiveAppServiceUpdated(iAppServiceId);
}
});
}
- public void postTvIAppInfoUpdated(final TvIAppInfo iAppInfo) {
+ public void postTvInteractiveAppInfoUpdated(final TvInteractiveAppInfo iAppInfo) {
mHandler.post(new Runnable() {
@Override
public void run() {
- mCallback.onTvIAppInfoUpdated(iAppInfo);
+ mCallback.onTvInteractiveAppInfoUpdated(iAppInfo);
}
});
}
@@ -537,7 +590,7 @@
mHandler.post(new Runnable() {
@Override
public void run() {
- mCallback.onTvIAppServiceStateChanged(iAppServiceId, type, state);
+ mCallback.onTvInteractiveAppServiceStateChanged(iAppServiceId, type, state);
}
});
}
@@ -578,23 +631,23 @@
}
/**
- * Returns the complete list of TV IApp service on the system.
+ * Returns the complete list of TV Interactive App service on the system.
*
- * @return List of {@link TvIAppInfo} for each TV IApp service that describes its meta
- * information.
+ * @return List of {@link TvInteractiveAppInfo} for each TV Interactive App service that
+ * describes its meta information.
* @hide
*/
@NonNull
- public List<TvIAppInfo> getTvIAppServiceList() {
+ public List<TvInteractiveAppInfo> getTvInteractiveAppServiceList() {
try {
- return mService.getTvIAppServiceList(mUserId);
+ return mService.getTvInteractiveAppServiceList(mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Prepares TV IApp service for the given type.
+ * Prepares TV Interactive App service for the given type.
* @hide
*/
public void prepare(@NonNull String tvIAppServiceId, int type) {
@@ -606,12 +659,25 @@
}
/**
- * Notifies app link info.
+ * Registers app link info.
* @hide
*/
- public void notifyAppLinkInfo(@NonNull String tvIAppServiceId, @NonNull Bundle appLinkInfo) {
+ public void registerAppLinkInfo(@NonNull String tvIAppServiceId, @NonNull Bundle appLinkInfo) {
try {
- mService.notifyAppLinkInfo(tvIAppServiceId, appLinkInfo, mUserId);
+ mService.registerAppLinkInfo(tvIAppServiceId, appLinkInfo, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Unregisters app link info.
+ * @hide
+ */
+ public void unregisterAppLinkInfo(
+ @NonNull String tvIAppServiceId, @NonNull Bundle appLinkInfo) {
+ try {
+ mService.unregisterAppLinkInfo(tvIAppServiceId, appLinkInfo, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -630,32 +696,33 @@
}
/**
- * Registers a {@link TvIAppManager.TvIAppCallback}.
+ * Registers a {@link TvInteractiveAppCallback}.
*
- * @param callback A callback used to monitor status of the TV IApp services.
+ * @param callback A callback used to monitor status of the TV Interactive App services.
* @param handler A {@link Handler} that the status change will be delivered to.
* @hide
*/
- public void registerCallback(@NonNull TvIAppCallback callback, @NonNull Handler handler) {
+ public void registerCallback(
+ @NonNull TvInteractiveAppCallback callback, @NonNull Handler handler) {
Preconditions.checkNotNull(callback);
Preconditions.checkNotNull(handler);
synchronized (mLock) {
- mCallbackRecords.add(new TvIAppCallbackRecord(callback, handler));
+ mCallbackRecords.add(new TvInteractiveAppCallbackRecord(callback, handler));
}
}
/**
- * Unregisters the existing {@link TvIAppManager.TvIAppCallback}.
+ * Unregisters the existing {@link TvInteractiveAppCallback}.
*
* @param callback The existing callback to remove.
* @hide
*/
- public void unregisterCallback(@NonNull final TvIAppCallback callback) {
+ public void unregisterCallback(@NonNull final TvInteractiveAppCallback callback) {
Preconditions.checkNotNull(callback);
synchronized (mLock) {
- for (Iterator<TvIAppCallbackRecord> it = mCallbackRecords.iterator();
+ for (Iterator<TvInteractiveAppCallbackRecord> it = mCallbackRecords.iterator();
it.hasNext(); ) {
- TvIAppCallbackRecord record = it.next();
+ TvInteractiveAppCallbackRecord record = it.next();
if (record.getCallback() == callback) {
it.remove();
break;
@@ -692,8 +759,8 @@
private TvInputEventSender mSender;
private InputChannel mInputChannel;
- private Session(IBinder token, InputChannel channel, ITvIAppManager service, int userId,
- int seq, SparseArray<SessionCallbackRecord> sessionCallbackRecordMap) {
+ private Session(IBinder token, InputChannel channel, ITvIAppManager service,
+ int userId, int seq, SparseArray<SessionCallbackRecord> sessionCallbackRecordMap) {
mToken = token;
mInputChannel = channel;
mService = service;
@@ -710,25 +777,37 @@
mInputSession = inputSession;
}
- void startIApp() {
+ void startInteractiveApp() {
if (mToken == null) {
Log.w(TAG, "The session has been already released");
return;
}
try {
- mService.startIApp(mToken, mUserId);
+ mService.startInteractiveApp(mToken, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
- void stopIApp() {
+ void stopInteractiveApp() {
if (mToken == null) {
Log.w(TAG, "The session has been already released");
return;
}
try {
- mService.stopIApp(mToken, mUserId);
+ mService.stopInteractiveApp(mToken, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ void resetInteractiveApp() {
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.resetInteractiveApp(mToken, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -758,6 +837,18 @@
}
}
+ void setTeletextAppEnabled(boolean enable) {
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.setTeletextAppEnabled(mToken, enable, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
void sendCurrentChannelUri(@Nullable Uri channelUri) {
if (mToken == null) {
Log.w(TAG, "The session has been already released");
@@ -806,6 +897,18 @@
}
}
+ void sendCurrentTvInputId(@Nullable String inputId) {
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.sendCurrentTvInputId(mToken, inputId, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/**
* Sets the {@link android.view.Surface} for this session.
*
@@ -994,7 +1097,7 @@
}
/**
- * Notifies IAPP session when a channel is tuned.
+ * Notifies Interactive APP session when a channel is tuned.
*/
public void notifyTuned(Uri channelUri) {
if (mToken == null) {
@@ -1009,7 +1112,7 @@
}
/**
- * Notifies IAPP session when a track is selected.
+ * Notifies Interactive APP session when a track is selected.
*/
public void notifyTrackSelected(int type, String trackId) {
if (mToken == null) {
@@ -1024,7 +1127,7 @@
}
/**
- * Notifies IAPP session when tracks are changed.
+ * Notifies Interactive APP session when tracks are changed.
*/
public void notifyTracksChanged(List<TvTrackInfo> tracks) {
if (mToken == null) {
@@ -1098,6 +1201,21 @@
}
}
+ /**
+ * Notifies Interactive APP session when signal strength is changed.
+ */
+ public void notifySignalStrength(int strength) {
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.notifySignalStrength(mToken, strength, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
private void flushPendingEventsLocked() {
mHandler.removeMessages(InputEventHandler.MSG_FLUSH_INPUT_EVENT);
@@ -1359,7 +1477,8 @@
});
}
- void postCommandRequest(final @TvIAppService.IAppServiceCommandType String cmdType,
+ void postCommandRequest(
+ final @TvIAppService.InteractiveAppServiceCommandType String cmdType,
final Bundle parameters) {
mHandler.post(new Runnable() {
@Override
@@ -1414,6 +1533,15 @@
});
}
+ void postRequestCurrentTvInputId() {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSessionCallback.onRequestCurrentTvInputId(mSession);
+ }
+ });
+ }
+
void postAdRequest(final AdRequest request) {
mHandler.post(new Runnable() {
@Override
@@ -1442,6 +1570,15 @@
}
});
}
+
+ void postTeletextAppStateChanged(int state) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSessionCallback.onTeletextAppStateChanged(mSession, state);
+ }
+ });
+ }
}
/**
@@ -1452,8 +1589,8 @@
/**
* This is called after {@link TvIAppManager#createSession} has been processed.
*
- * @param session A {@link TvIAppManager.Session} instance created. This can be {@code null}
- * if the creation request failed.
+ * @param session A {@link TvIAppManager.Session} instance created. This can be
+ * {@code null} if the creation request failed.
*/
public void onSessionCreated(@Nullable Session session) {
}
@@ -1468,8 +1605,8 @@
}
/**
- * This is called when {@link TvIAppService.Session#layoutSurface} is called to change the
- * layout of surface.
+ * This is called when {@link TvIAppService.Session#layoutSurface} is called to
+ * change the layout of surface.
*
* @param session A {@link TvIAppManager.Session} associated with this callback.
* @param left Left position.
@@ -1487,8 +1624,10 @@
* @param cmdType type of the command.
* @param parameters parameters of the command.
*/
- public void onCommandRequest(Session session,
- @TvIAppService.IAppServiceCommandType String cmdType, Bundle parameters) {
+ public void onCommandRequest(
+ Session session,
+ @TvIAppService.InteractiveAppServiceCommandType String cmdType,
+ Bundle parameters) {
}
/**
@@ -1500,7 +1639,8 @@
}
/**
- * This is called when {@link TvIAppService.Session#RequestCurrentChannelUri} is called.
+ * This is called when {@link TvIAppService.Session#RequestCurrentChannelUri} is
+ * called.
*
* @param session A {@link TvIAppManager.Session} associated with this callback.
*/
@@ -1508,7 +1648,8 @@
}
/**
- * This is called when {@link TvIAppService.Session#RequestCurrentChannelLcn} is called.
+ * This is called when {@link TvIAppService.Session#RequestCurrentChannelLcn} is
+ * called.
*
* @param session A {@link TvIAppManager.Session} associated with this callback.
*/
@@ -1516,7 +1657,8 @@
}
/**
- * This is called when {@link TvIAppService.Session#RequestStreamVolume} is called.
+ * This is called when {@link TvIAppService.Session#RequestStreamVolume} is
+ * called.
*
* @param session A {@link TvIAppManager.Session} associated with this callback.
*/
@@ -1524,7 +1666,8 @@
}
/**
- * This is called when {@link TvIAppService.Session#RequestTrackInfoList} is called.
+ * This is called when {@link TvIAppService.Session#RequestTrackInfoList} is
+ * called.
*
* @param session A {@link TvIAppManager.Session} associated with this callback.
*/
@@ -1532,6 +1675,15 @@
}
/**
+ * This is called when {@link TvIAppService.Session#RequestCurrentTvInputId} is called.
+ *
+ * @param session A {@link TvIAppManager.Session} associated with this callback.
+ * @hide
+ */
+ public void onRequestCurrentTvInputId(Session session) {
+ }
+
+ /**
* This is called when {@link TvIAppService.Session#notifySessionStateChanged} is called.
*
* @param session A {@link TvIAppManager.Session} associated with this callback.
@@ -1541,8 +1693,8 @@
}
/**
- * This is called when {@link TvIAppService.Session#notifyBiInteractiveAppCreated} is
- * called.
+ * This is called when {@link TvIAppService.Session#notifyBiInteractiveAppCreated}
+ * is called.
*
* @param session A {@link TvIAppManager.Session} associated with this callback.
* @param biIAppUri URI associated this BI interactive app. This is the same URI in
@@ -1552,5 +1704,16 @@
*/
public void onBiInteractiveAppCreated(Session session, Uri biIAppUri, String biIAppId) {
}
+
+ /**
+ * This is called when {@link TvIAppService.Session#notifyTeletextAppStateChanged} is
+ * called.
+ *
+ * @param session A {@link TvIAppManager.Session} associated with this callback.
+ * @param state the current state.
+ */
+ public void onTeletextAppStateChanged(
+ Session session, @TvIAppManager.TeletextAppState int state) {
+ }
}
}
diff --git a/media/java/android/media/tv/interactive/TvIAppService.java b/media/java/android/media/tv/interactive/TvIAppService.java
old mode 100644
new mode 100755
index 4993bc3..c0ec76b
--- a/media/java/android/media/tv/interactive/TvIAppService.java
+++ b/media/java/android/media/tv/interactive/TvIAppService.java
@@ -32,6 +32,7 @@
import android.media.tv.BroadcastInfoRequest;
import android.media.tv.BroadcastInfoResponse;
import android.media.tv.TvContentRating;
+import android.media.tv.TvInputManager;
import android.media.tv.TvTrackInfo;
import android.net.Uri;
import android.os.AsyncTask;
@@ -75,45 +76,48 @@
// TODO: cleanup and unhide APIs.
/**
- * This is the interface name that a service implementing a TV IApp service should say that it
- * supports -- that is, this is the action it uses for its intent filter. To be supported, the
- * service must also require the android.Manifest.permission#BIND_TV_IAPP permission so
- * that other applications cannot abuse it.
+ * This is the interface name that a service implementing a TV Interactive App service should
+ * say that it supports -- that is, this is the action it uses for its intent filter. To be
+ * supported, the service must also require the
+ * android.Manifest.permission#BIND_TV_INTERACTIVE_APP permission so that other applications
+ * cannot abuse it.
*/
- public static final String SERVICE_INTERFACE = "android.media.tv.interactive.TvIAppService";
+ public static final String SERVICE_INTERFACE =
+ "android.media.tv.interactive.TvIAppService";
/**
- * Name under which a TvIAppService component publishes information about itself. This meta-data
- * must reference an XML resource containing an
- * <code><{@link android.R.styleable#TvIAppService tv-iapp}></code>
+ * Name under which a TvIAppService component publishes information about itself. This
+ * meta-data must reference an XML resource containing an
+ * <code><{@link android.R.styleable#TvIAppService tv-interactive-app}></code>
* tag.
*/
public static final String SERVICE_META_DATA = "android.media.tv.interactive.app";
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @StringDef(prefix = "IAPP_SERVICE_COMMAND_TYPE_", value = {
- IAPP_SERVICE_COMMAND_TYPE_TUNE,
- IAPP_SERVICE_COMMAND_TYPE_TUNE_NEXT,
- IAPP_SERVICE_COMMAND_TYPE_TUNE_PREV,
- IAPP_SERVICE_COMMAND_TYPE_STOP,
- IAPP_SERVICE_COMMAND_TYPE_SET_STREAM_VOLUME,
- IAPP_SERVICE_COMMAND_TYPE_SELECT_TRACK
+ @StringDef(prefix = "INTERACTIVE_APP_SERVICE_COMMAND_TYPE_", value = {
+ INTERACTIVE_APP_SERVICE_COMMAND_TYPE_TUNE,
+ INTERACTIVE_APP_SERVICE_COMMAND_TYPE_TUNE_NEXT,
+ INTERACTIVE_APP_SERVICE_COMMAND_TYPE_TUNE_PREV,
+ INTERACTIVE_APP_SERVICE_COMMAND_TYPE_STOP,
+ INTERACTIVE_APP_SERVICE_COMMAND_TYPE_SET_STREAM_VOLUME,
+ INTERACTIVE_APP_SERVICE_COMMAND_TYPE_SELECT_TRACK
})
- public @interface IAppServiceCommandType {}
+ public @interface InteractiveAppServiceCommandType {}
/** @hide */
- public static final String IAPP_SERVICE_COMMAND_TYPE_TUNE = "tune";
+ public static final String INTERACTIVE_APP_SERVICE_COMMAND_TYPE_TUNE = "tune";
/** @hide */
- public static final String IAPP_SERVICE_COMMAND_TYPE_TUNE_NEXT = "tune_next";
+ public static final String INTERACTIVE_APP_SERVICE_COMMAND_TYPE_TUNE_NEXT = "tune_next";
/** @hide */
- public static final String IAPP_SERVICE_COMMAND_TYPE_TUNE_PREV = "tune_previous";
+ public static final String INTERACTIVE_APP_SERVICE_COMMAND_TYPE_TUNE_PREV = "tune_previous";
/** @hide */
- public static final String IAPP_SERVICE_COMMAND_TYPE_STOP = "stop";
+ public static final String INTERACTIVE_APP_SERVICE_COMMAND_TYPE_STOP = "stop";
/** @hide */
- public static final String IAPP_SERVICE_COMMAND_TYPE_SET_STREAM_VOLUME = "set_stream_volume";
+ public static final String INTERACTIVE_APP_SERVICE_COMMAND_TYPE_SET_STREAM_VOLUME =
+ "set_stream_volume";
/** @hide */
- public static final String IAPP_SERVICE_COMMAND_TYPE_SELECT_TRACK = "select_track";
+ public static final String INTERACTIVE_APP_SERVICE_COMMAND_TYPE_SELECT_TRACK = "select_track";
/** @hide */
public static final String COMMAND_PARAMETER_KEY_CHANNEL_URI = "command_channel_uri";
/** @hide */
@@ -129,29 +133,29 @@
"command_track_select_mode";
private final Handler mServiceHandler = new ServiceHandler();
- private final RemoteCallbackList<ITvIAppServiceCallback> mCallbacks =
+ private final RemoteCallbackList<ITvInteractiveAppServiceCallback> mCallbacks =
new RemoteCallbackList<>();
/** @hide */
@Override
public final IBinder onBind(Intent intent) {
- ITvIAppService.Stub tvIAppServiceBinder = new ITvIAppService.Stub() {
+ ITvInteractiveAppService.Stub tvIAppServiceBinder = new ITvInteractiveAppService.Stub() {
@Override
- public void registerCallback(ITvIAppServiceCallback cb) {
+ public void registerCallback(ITvInteractiveAppServiceCallback cb) {
if (cb != null) {
mCallbacks.register(cb);
}
}
@Override
- public void unregisterCallback(ITvIAppServiceCallback cb) {
+ public void unregisterCallback(ITvInteractiveAppServiceCallback cb) {
if (cb != null) {
mCallbacks.unregister(cb);
}
}
@Override
- public void createSession(InputChannel channel, ITvIAppSessionCallback cb,
+ public void createSession(InputChannel channel, ITvInteractiveAppSessionCallback cb,
String iAppServiceId, int type) {
if (cb == null) {
return;
@@ -171,8 +175,13 @@
}
@Override
- public void notifyAppLinkInfo(Bundle appLinkInfo) {
- onAppLinkInfo(appLinkInfo);
+ public void registerAppLinkInfo(Bundle appLinkInfo) {
+ onRegisterAppLinkInfo(appLinkInfo);
+ }
+
+ @Override
+ public void unregisterAppLinkInfo(Bundle appLinkInfo) {
+ onUnregisterAppLinkInfo(appLinkInfo);
}
@Override
@@ -184,7 +193,7 @@
}
/**
- * Prepares TV IApp service for the given type.
+ * Prepares TV Interactive App service for the given type.
* @hide
*/
public void onPrepare(int type) {
@@ -195,7 +204,15 @@
* Registers App link info.
* @hide
*/
- public void onAppLinkInfo(Bundle appLinkInfo) {
+ public void onRegisterAppLinkInfo(Bundle appLinkInfo) {
+ // TODO: make it abstract when unhide
+ }
+
+ /**
+ * Unregisters App link info.
+ * @hide
+ */
+ public void onUnregisterAppLinkInfo(Bundle appLinkInfo) {
// TODO: make it abstract when unhide
}
@@ -211,11 +228,11 @@
/**
* Returns a concrete implementation of {@link Session}.
*
- * <p>May return {@code null} if this TV IApp service fails to create a session for some
- * reason.
+ * <p>May return {@code null} if this TV Interactive App service fails to create a session for
+ * some reason.
*
- * @param iAppServiceId The ID of the TV IApp associated with the session.
- * @param type The type of the TV IApp associated with the session.
+ * @param iAppServiceId The ID of the TV Interactive App associated with the session.
+ * @param type The type of the TV Interactive App associated with the session.
* @hide
*/
@Nullable
@@ -229,7 +246,8 @@
* @param state the current state
* @hide
*/
- public final void notifyStateChanged(int type, @TvIAppManager.TvIAppRteState int state) {
+ public final void notifyStateChanged(
+ int type, @TvIAppManager.TvInteractiveAppRteState int state) {
mServiceHandler.obtainMessage(ServiceHandler.DO_NOTIFY_RTE_STATE_CHANGED,
type, state).sendToTarget();
}
@@ -243,7 +261,7 @@
private final Object mLock = new Object();
// @GuardedBy("mLock")
- private ITvIAppSessionCallback mSessionCallback;
+ private ITvInteractiveAppSessionCallback mSessionCallback;
// @GuardedBy("mLock")
private final List<Runnable> mPendingActions = new ArrayList<>();
@@ -276,7 +294,7 @@
* <p>By default, the media view is disabled. Must be called explicitly after the
* session is created to enable the media view.
*
- * <p>The TV IApp service can disable its media view when needed.
+ * <p>The TV Interactive App service can disable its media view when needed.
*
* @param enable {@code true} if you want to enable the media view. {@code false}
* otherwise.
@@ -304,14 +322,21 @@
* Starts TvIAppService session.
* @hide
*/
- public void onStartIApp() {
+ public void onStartInteractiveApp() {
}
/**
* Stops TvIAppService session.
* @hide
*/
- public void onStopIApp() {
+ public void onStopInteractiveApp() {
+ }
+
+ /**
+ * Resets TvIAppService session.
+ * @hide
+ */
+ public void onResetInteractiveApp() {
}
/**
@@ -337,6 +362,13 @@
}
/**
+ * To toggle Digital Teletext Application if there is one in AIT app list.
+ * @param enable
+ */
+ public void onSetTeletextAppEnabled(boolean enable) {
+ }
+
+ /**
* Receives current channel URI.
* @hide
*/
@@ -365,11 +397,18 @@
}
/**
+ * Receives current TV input ID.
+ * @hide
+ */
+ public void onCurrentTvInputId(@Nullable String inputId) {
+ }
+
+ /**
* Called when the application sets the surface.
*
- * <p>The TV IApp service should render interactive app UI onto the given surface. When
- * called with {@code null}, the IApp service should immediately free any references to the
- * currently set surface and stop using it.
+ * <p>The TV Interactive App service should render interactive app UI onto the given
+ * surface. When called with {@code null}, the Interactive App service should immediately
+ * free any references to the currently set surface and stop using it.
*
* @param surface The surface to be used for interactive app UI rendering. Can be
* {@code null}.
@@ -394,8 +433,8 @@
*
* <p>This is always called at least once when the session is created regardless of whether
* the media view is enabled or not. The media view container size is the same as the
- * containing {@link TvIAppView}. Note that the size of the underlying surface can be
- * different if the surface was changed by calling {@link #layoutSurface}.
+ * containing {@link TvInteractiveAppView}. Note that the size of the underlying surface can
+ * be different if the surface was changed by calling {@link #layoutSurface}.
*
* @param width The width of the media view.
* @param height The height of the media view.
@@ -471,6 +510,13 @@
}
/**
+ * Called when signal strength is changed.
+ * @hide
+ */
+ public void onSignalStrength(@TvInputManager.SignalStrength int strength) {
+ }
+
+ /**
* Called when a broadcast info response is received.
* @hide
*/
@@ -624,7 +670,8 @@
* @param cmdType type of the specific command
* @param parameters parameters of the specific command
*/
- public void requestCommand(@IAppServiceCommandType String cmdType, Bundle parameters) {
+ public void requestCommand(
+ @InteractiveAppServiceCommandType String cmdType, Bundle parameters) {
executeOrPostRunnableOnMainThread(new Runnable() {
@MainThread
@Override
@@ -755,6 +802,31 @@
}
/**
+ * Requests current TV input ID.
+ *
+ * @see android.media.tv.TvInputInfo
+ * @hide
+ */
+ public void requestCurrentTvInputId() {
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) {
+ Log.d(TAG, "requestCurrentTvInputId");
+ }
+ if (mSessionCallback != null) {
+ mSessionCallback.onRequestCurrentTvInputId();
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "error in requestCurrentTvInputId", e);
+ }
+ }
+ });
+ }
+
+ /**
* requests an advertisement request to be processed by the related TV input.
* @param request advertisement request
*/
@@ -777,12 +849,16 @@
});
}
- void startIApp() {
- onStartIApp();
+ void startInteractiveApp() {
+ onStartInteractiveApp();
}
- void stopIApp() {
- onStopIApp();
+ void stopInteractiveApp() {
+ onStopInteractiveApp();
+ }
+
+ void resetInteractiveApp() {
+ onResetInteractiveApp();
}
void createBiInteractiveApp(@NonNull Uri biIAppUri, @Nullable Bundle params) {
@@ -793,6 +869,10 @@
onDestroyBiInteractiveApp(biIAppId);
}
+ void setTeletextAppEnabled(boolean enable) {
+ onSetTeletextAppEnabled(enable);
+ }
+
void sendCurrentChannelUri(@Nullable Uri channelUri) {
onCurrentChannelUri(channelUri);
}
@@ -809,6 +889,10 @@
onTrackInfoList(tracks);
}
+ void sendCurrentTvInputId(@Nullable String inputId) {
+ onCurrentTvInputId(inputId);
+ }
+
void release() {
onRelease();
if (mSurface != null) {
@@ -873,6 +957,13 @@
onContentBlocked(rating);
}
+ void notifySignalStrength(int strength) {
+ if (DEBUG) {
+ Log.d(TAG, "notifySignalStrength (strength=" + strength + ")");
+ }
+ onSignalStrength(strength);
+ }
+
/**
* Calls {@link #onBroadcastInfoResponse}.
*/
@@ -898,7 +989,8 @@
* Notifies when the session state is changed.
* @param state the current state.
*/
- public void notifySessionStateChanged(@TvIAppManager.TvIAppRteState int state) {
+ public void notifySessionStateChanged(
+ @TvIAppManager.TvInteractiveAppRteState int state) {
executeOrPostRunnableOnMainThread(new Runnable() {
@MainThread
@Override
@@ -945,6 +1037,30 @@
}
/**
+ * Notifies when the digital teletext app state is changed.
+ * @param state the current state.
+ */
+ public final void notifyTeletextAppStateChanged(@TvIAppManager.TeletextAppState int state) {
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) {
+ Log.d(TAG, "notifyTeletextAppState (state="
+ + state + ")");
+ }
+ if (mSessionCallback != null) {
+ mSessionCallback.onTeletextAppStateChanged(state);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "error in notifyTeletextAppState", e);
+ }
+ }
+ });
+ }
+
+ /**
* Takes care of dispatching incoming input events and tells whether the event was handled.
*/
int dispatchInputEvent(InputEvent event, InputEventReceiver receiver) {
@@ -977,7 +1093,7 @@
return TvIAppManager.Session.DISPATCH_NOT_HANDLED;
}
- private void initialize(ITvIAppSessionCallback callback) {
+ private void initialize(ITvInteractiveAppSessionCallback callback) {
synchronized (mLock) {
mSessionCallback = callback;
for (Runnable runnable : mPendingActions) {
@@ -1087,8 +1203,8 @@
if (DEBUG) Log.d(TAG, "relayoutMediaView(" + frame + ")");
if (mMediaFrame == null || mMediaFrame.width() != frame.width()
|| mMediaFrame.height() != frame.height()) {
- // Note: relayoutMediaView is called whenever TvIAppView's layout is changed
- // regardless of setMediaViewEnabled.
+ // Note: relayoutMediaView is called whenever TvInteractiveAppView's layout is
+ // changed regardless of setMediaViewEnabled.
onMediaViewSizeChanged(frame.right - frame.left, frame.bottom - frame.top);
}
mMediaFrame = frame;
@@ -1159,31 +1275,37 @@
}
/**
- * Implements the internal ITvIAppSession interface.
+ * Implements the internal ITvInteractiveAppSession interface.
* @hide
*/
- public static class ITvIAppSessionWrapper extends ITvIAppSession.Stub {
- // TODO: put ITvIAppSessionWrapper in a separate Java file
+ public static class ITvInteractiveAppSessionWrapper extends ITvInteractiveAppSession.Stub {
+ // TODO: put ITvInteractiveAppSessionWrapper in a separate Java file
private final Session mSessionImpl;
private InputChannel mChannel;
- private TvIAppEventReceiver mReceiver;
+ private TvInteractiveAppEventReceiver mReceiver;
- public ITvIAppSessionWrapper(Context context, Session mSessionImpl, InputChannel channel) {
+ public ITvInteractiveAppSessionWrapper(
+ Context context, Session mSessionImpl, InputChannel channel) {
this.mSessionImpl = mSessionImpl;
mChannel = channel;
if (channel != null) {
- mReceiver = new TvIAppEventReceiver(channel, context.getMainLooper());
+ mReceiver = new TvInteractiveAppEventReceiver(channel, context.getMainLooper());
}
}
@Override
- public void startIApp() {
- mSessionImpl.startIApp();
+ public void startInteractiveApp() {
+ mSessionImpl.startInteractiveApp();
}
@Override
- public void stopIApp() {
- mSessionImpl.stopIApp();
+ public void stopInteractiveApp() {
+ mSessionImpl.stopInteractiveApp();
+ }
+
+ @Override
+ public void resetInteractiveApp() {
+ mSessionImpl.resetInteractiveApp();
}
@Override
@@ -1192,6 +1314,11 @@
}
@Override
+ public void setTeletextAppEnabled(boolean enable) {
+ mSessionImpl.setTeletextAppEnabled(enable);
+ }
+
+ @Override
public void destroyBiInteractiveApp(@NonNull String biIAppId) {
mSessionImpl.destroyBiInteractiveApp(biIAppId);
}
@@ -1217,6 +1344,11 @@
}
@Override
+ public void sendCurrentTvInputId(@Nullable String inputId) {
+ mSessionImpl.sendCurrentTvInputId(inputId);
+ }
+
+ @Override
public void release() {
mSessionImpl.scheduleMediaViewCleanup();
mSessionImpl.release();
@@ -1258,6 +1390,11 @@
}
@Override
+ public void notifySignalStrength(int strength) {
+ mSessionImpl.notifySignalStrength(strength);
+ }
+
+ @Override
public void setSurface(Surface surface) {
mSessionImpl.setSurface(surface);
}
@@ -1292,8 +1429,8 @@
mSessionImpl.removeMediaView(true);
}
- private final class TvIAppEventReceiver extends InputEventReceiver {
- TvIAppEventReceiver(InputChannel inputChannel, Looper looper) {
+ private final class TvInteractiveAppEventReceiver extends InputEventReceiver {
+ TvInteractiveAppEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
}
@@ -1307,7 +1444,8 @@
int handled = mSessionImpl.dispatchInputEvent(event, this);
if (handled != TvIAppManager.Session.DISPATCH_IN_PROGRESS) {
- finishInputEvent(event, handled == TvIAppManager.Session.DISPATCH_HANDLED);
+ finishInputEvent(
+ event, handled == TvIAppManager.Session.DISPATCH_HANDLED);
}
}
}
@@ -1337,7 +1475,8 @@
case DO_CREATE_SESSION: {
SomeArgs args = (SomeArgs) msg.obj;
InputChannel channel = (InputChannel) args.arg1;
- ITvIAppSessionCallback cb = (ITvIAppSessionCallback) args.arg2;
+ ITvInteractiveAppSessionCallback cb =
+ (ITvInteractiveAppSessionCallback) args.arg2;
String iAppServiceId = (String) args.arg3;
int type = (int) args.arg4;
args.recycle();
@@ -1351,8 +1490,8 @@
}
return;
}
- ITvIAppSession stub = new ITvIAppSessionWrapper(
- TvIAppService.this, sessionImpl, channel);
+ ITvInteractiveAppSession stub = new ITvInteractiveAppSessionWrapper(
+ android.media.tv.interactive.TvIAppService.this, sessionImpl, channel);
SomeArgs someArgs = SomeArgs.obtain();
someArgs.arg1 = sessionImpl;
@@ -1365,8 +1504,9 @@
case DO_NOTIFY_SESSION_CREATED: {
SomeArgs args = (SomeArgs) msg.obj;
Session sessionImpl = (Session) args.arg1;
- ITvIAppSession stub = (ITvIAppSession) args.arg2;
- ITvIAppSessionCallback cb = (ITvIAppSessionCallback) args.arg3;
+ ITvInteractiveAppSession stub = (ITvInteractiveAppSession) args.arg2;
+ ITvInteractiveAppSessionCallback cb =
+ (ITvInteractiveAppSessionCallback) args.arg3;
try {
cb.onSessionCreated(stub);
} catch (RemoteException e) {
diff --git a/media/java/android/media/tv/interactive/TvIAppInfo.aidl b/media/java/android/media/tv/interactive/TvInteractiveAppInfo.aidl
similarity index 95%
rename from media/java/android/media/tv/interactive/TvIAppInfo.aidl
rename to media/java/android/media/tv/interactive/TvInteractiveAppInfo.aidl
index 6041460..5e15016 100644
--- a/media/java/android/media/tv/interactive/TvIAppInfo.aidl
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppInfo.aidl
@@ -16,4 +16,4 @@
package android.media.tv.interactive;
-parcelable TvIAppInfo;
\ No newline at end of file
+parcelable TvInteractiveAppInfo;
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppInfo.java b/media/java/android/media/tv/interactive/TvInteractiveAppInfo.java
new file mode 100644
index 0000000..2f96552
--- /dev/null
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppInfo.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.interactive;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.util.Xml;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class is used to specify meta information of a TV interactive app.
+ * @hide
+ */
+public final class TvInteractiveAppInfo implements Parcelable {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "TvInteractiveAppInfo";
+
+ private static final String XML_START_TAG_NAME = "tv-interactive-app";
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "INTERACTIVE_APP_TYPE_" }, value = {
+ INTERACTIVE_APP_TYPE_HBBTV,
+ INTERACTIVE_APP_TYPE_ATSC,
+ INTERACTIVE_APP_TYPE_GINGA,
+ })
+ @interface InteractiveAppType {}
+
+ /** HbbTV interactive app type */
+ public static final int INTERACTIVE_APP_TYPE_HBBTV = 0x1;
+ /** ATSC interactive app type */
+ public static final int INTERACTIVE_APP_TYPE_ATSC = 0x2;
+ /** Ginga interactive app type */
+ public static final int INTERACTIVE_APP_TYPE_GINGA = 0x4;
+
+ private final ResolveInfo mService;
+ private final String mId;
+ private int mTypes;
+
+ public TvInteractiveAppInfo(@NonNull Context context, @NonNull ComponentName component) {
+ if (context == null) {
+ throw new IllegalArgumentException("context cannot be null.");
+ }
+ Intent intent =
+ new Intent(TvIAppService.SERVICE_INTERFACE).setComponent(component);
+ ResolveInfo resolveInfo = context.getPackageManager().resolveService(intent,
+ PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
+ if (resolveInfo == null) {
+ throw new IllegalArgumentException("Invalid component. Can't find the service.");
+ }
+
+ ComponentName componentName = new ComponentName(resolveInfo.serviceInfo.packageName,
+ resolveInfo.serviceInfo.name);
+ String id;
+ id = generateInteractiveAppServiceId(componentName);
+ List<String> types = new ArrayList<>();
+ parseServiceMetadata(resolveInfo, context, types);
+
+ mService = resolveInfo;
+ mId = id;
+ mTypes = toTypesFlag(types);
+ }
+ private TvInteractiveAppInfo(ResolveInfo service, String id, int types) {
+ mService = service;
+ mId = id;
+ mTypes = types;
+ }
+
+ private TvInteractiveAppInfo(@NonNull Parcel in) {
+ mService = ResolveInfo.CREATOR.createFromParcel(in);
+ mId = in.readString();
+ mTypes = in.readInt();
+ }
+
+ public static final @NonNull Creator<TvInteractiveAppInfo> CREATOR =
+ new Creator<TvInteractiveAppInfo>() {
+ @Override
+ public TvInteractiveAppInfo createFromParcel(Parcel in) {
+ return new TvInteractiveAppInfo(in);
+ }
+
+ @Override
+ public TvInteractiveAppInfo[] newArray(int size) {
+ return new TvInteractiveAppInfo[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ mService.writeToParcel(dest, flags);
+ dest.writeString(mId);
+ dest.writeInt(mTypes);
+ }
+
+ @NonNull
+ public String getId() {
+ return mId;
+ }
+
+ /**
+ * Returns the component of the TV Interactive App service.
+ * @hide
+ */
+ public ComponentName getComponent() {
+ return new ComponentName(mService.serviceInfo.packageName, mService.serviceInfo.name);
+ }
+
+ /**
+ * Returns the information of the service that implements this TV Interactive App service.
+ */
+ @Nullable
+ public ServiceInfo getServiceInfo() {
+ return mService.serviceInfo;
+ }
+
+ /**
+ * Gets supported interactive app types
+ */
+ @InteractiveAppType
+ @NonNull
+ public int getSupportedTypes() {
+ return mTypes;
+ }
+
+ private static String generateInteractiveAppServiceId(ComponentName name) {
+ return name.flattenToShortString();
+ }
+
+ private static void parseServiceMetadata(
+ ResolveInfo resolveInfo, Context context, List<String> types) {
+ ServiceInfo si = resolveInfo.serviceInfo;
+ PackageManager pm = context.getPackageManager();
+ try (XmlResourceParser parser =
+ si.loadXmlMetaData(pm, TvIAppService.SERVICE_META_DATA)) {
+ if (parser == null) {
+ throw new IllegalStateException(
+ "No " + TvIAppService.SERVICE_META_DATA
+ + " meta-data found for " + si.name);
+ }
+
+ Resources res = pm.getResourcesForApplication(si.applicationInfo);
+ AttributeSet attrs = Xml.asAttributeSet(parser);
+
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && type != XmlPullParser.START_TAG) {
+ // move to the START_TAG
+ }
+
+ String nodeName = parser.getName();
+ if (!XML_START_TAG_NAME.equals(nodeName)) {
+ throw new IllegalStateException("Meta-data does not start with "
+ + XML_START_TAG_NAME + " tag for " + si.name);
+ }
+
+ TypedArray sa = res.obtainAttributes(attrs,
+ com.android.internal.R.styleable.TvIAppService);
+ CharSequence[] textArr = sa.getTextArray(
+ com.android.internal.R.styleable.TvIAppService_supportedTypes);
+ for (CharSequence cs : textArr) {
+ types.add(cs.toString().toLowerCase());
+ }
+
+ sa.recycle();
+ } catch (IOException | XmlPullParserException e) {
+ throw new IllegalStateException(
+ "Failed reading meta-data for " + si.packageName, e);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new IllegalStateException("No resources found for " + si.packageName, e);
+ }
+ }
+
+ private static int toTypesFlag(List<String> types) {
+ int flag = 0;
+ for (String type : types) {
+ switch (type) {
+ case "hbbtv":
+ flag |= INTERACTIVE_APP_TYPE_HBBTV;
+ break;
+ case "atsc":
+ flag |= INTERACTIVE_APP_TYPE_ATSC;
+ break;
+ case "ginga":
+ flag |= INTERACTIVE_APP_TYPE_GINGA;
+ break;
+ default:
+ break;
+ }
+ }
+ return flag;
+ }
+}
diff --git a/media/java/android/media/tv/interactive/TvIAppView.java b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
old mode 100644
new mode 100755
similarity index 72%
rename from media/java/android/media/tv/interactive/TvIAppView.java
rename to media/java/android/media/tv/interactive/TvInteractiveAppView.java
index b295055..6f99d51
--- a/media/java/android/media/tv/interactive/TvIAppView.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
@@ -16,6 +16,7 @@
package android.media.tv.interactive;
+import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -45,13 +46,14 @@
import android.view.ViewRootImpl;
import java.util.List;
+import java.util.concurrent.Executor;
/**
* Displays contents of interactive TV applications.
* @hide
*/
-public class TvIAppView extends ViewGroup {
- private static final String TAG = "TvIAppView";
+public class TvInteractiveAppView extends ViewGroup {
+ private static final String TAG = "TvInteractiveAppView";
private static final boolean DEBUG = false;
private static final int SET_TVVIEW_SUCCESS = 1;
@@ -59,11 +61,13 @@
private static final int UNSET_TVVIEW_SUCCESS = 3;
private static final int UNSET_TVVIEW_FAIL = 4;
- private final TvIAppManager mTvIAppManager;
+ private final TvIAppManager mTvInteractiveAppManager;
private final Handler mHandler = new Handler();
+ private final Object mCallbackLock = new Object();
private Session mSession;
private MySessionCallback mSessionCallback;
- private TvIAppCallback mCallback;
+ private TvInteractiveAppCallback mCallback;
+ private Executor mCallbackExecutor;
private SurfaceView mSurfaceView;
private Surface mSurface;
@@ -114,15 +118,16 @@
}
};
- public TvIAppView(@NonNull Context context) {
+ public TvInteractiveAppView(@NonNull Context context) {
this(context, null, 0);
}
- public TvIAppView(@NonNull Context context, @Nullable AttributeSet attrs) {
+ public TvInteractiveAppView(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
- public TvIAppView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ public TvInteractiveAppView(@NonNull Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr) {
super(context, attrs, defStyleAttr);
int sourceResId = Resources.getAttributeSetSourceResId(attrs);
if (sourceResId != Resources.ID_NULL) {
@@ -136,31 +141,50 @@
}
mDefStyleAttr = defStyleAttr;
resetSurfaceView();
- mTvIAppManager = (TvIAppManager) getContext().getSystemService(Context.TV_IAPP_SERVICE);
+ mTvInteractiveAppManager = (TvIAppManager) getContext().getSystemService(
+ Context.TV_IAPP_SERVICE);
}
/**
- * Sets the callback to be invoked when an event is dispatched to this TvIAppView.
+ * Sets the callback to be invoked when an event is dispatched to this TvInteractiveAppView.
*
* @param callback The callback to receive events. A value of {@code null} removes the existing
- * callback.
+ * callback.
*/
- public void setCallback(@Nullable TvIAppCallback callback) {
- mCallback = callback;
+ public void setCallback(
+ @NonNull TvInteractiveAppCallback callback,
+ @NonNull @CallbackExecutor Executor executor) {
+ synchronized (mCallbackLock) {
+ mCallbackExecutor = executor;
+ mCallback = callback;
+ }
}
+ /**
+ * Clears the callback.
+ */
+ public void clearCallback() {
+ synchronized (mCallbackLock) {
+ mCallback = null;
+ mCallbackExecutor = null;
+ }
+ }
+
+ /** @hide */
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
createSessionMediaView();
}
+ /** @hide */
@Override
protected void onDetachedFromWindow() {
removeSessionMediaView();
super.onDetachedFromWindow();
}
+ /** @hide */
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
if (DEBUG) {
@@ -175,6 +199,7 @@
}
}
+ /** @hide */
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
mSurfaceView.measure(widthMeasureSpec, heightMeasureSpec);
@@ -186,6 +211,7 @@
childState << MEASURED_HEIGHT_STATE_SHIFT));
}
+ /** @hide */
@Override
protected void onVisibilityChanged(View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
@@ -216,7 +242,8 @@
}
/**
- * Resets this TvIAppView.
+ * Resets this TvInteractiveAppView.
+ * @hide
*/
public void reset() {
if (DEBUG) Log.d(TAG, "reset()");
@@ -302,6 +329,7 @@
/**
* Dispatches an unhandled input event to the next receiver.
+ * @hide
*/
public boolean dispatchUnhandledInputEvent(@NonNull InputEvent event) {
if (mOnUnhandledInputEventListener != null) {
@@ -314,11 +342,13 @@
/**
* Called when an unhandled input event also has not been handled by the user provided
- * callback. This is the last chance to handle the unhandled input event in the TvIAppView.
+ * callback. This is the last chance to handle the unhandled input event in the
+ * TvInteractiveAppView.
*
* @param event The input event.
* @return If you handled the event, return {@code true}. If you want to allow the event to be
* handled by the next receiver, return {@code false}.
+ * @hide
*/
public boolean onUnhandledInputEvent(@NonNull InputEvent event) {
return false;
@@ -329,6 +359,7 @@
* by the TV Interactive App.
*
* @param listener The callback to be invoked when the unhandled input event is received.
+ * @hide
*/
public void setOnUnhandledInputEventListener(@NonNull OnUnhandledInputEventListener listener) {
mOnUnhandledInputEventListener = listener;
@@ -350,44 +381,60 @@
/**
* Prepares the interactive application.
+ * @hide
*/
- public void prepareIApp(@NonNull String iAppServiceId, int type) {
+ public void prepareInteractiveApp(@NonNull String iAppServiceId, int type) {
// TODO: document and handle the cases that this method is called multiple times.
if (DEBUG) {
- Log.d(TAG, "prepareIApp");
+ Log.d(TAG, "prepareInteractiveApp");
}
mSessionCallback = new MySessionCallback(iAppServiceId, type);
- if (mTvIAppManager != null) {
- mTvIAppManager.createSession(iAppServiceId, type, mSessionCallback, mHandler);
+ if (mTvInteractiveAppManager != null) {
+ mTvInteractiveAppManager.createSession(iAppServiceId, type, mSessionCallback, mHandler);
}
}
/**
* Starts the interactive application.
*/
- public void startIApp() {
+ public void startInteractiveApp() {
if (DEBUG) {
- Log.d(TAG, "startIApp");
+ Log.d(TAG, "startInteractiveApp");
}
if (mSession != null) {
- mSession.startIApp();
+ mSession.startInteractiveApp();
}
}
/**
* Stops the interactive application.
+ * @hide
*/
- public void stopIApp() {
+ public void stopInteractiveApp() {
if (DEBUG) {
- Log.d(TAG, "stopIApp");
+ Log.d(TAG, "stopInteractiveApp");
}
if (mSession != null) {
- mSession.stopIApp();
+ mSession.stopInteractiveApp();
+ }
+ }
+
+ /**
+ * Resets the interactive application.
+ * @hide
+ */
+ public void resetInteractiveApp() {
+ if (DEBUG) {
+ Log.d(TAG, "resetInteractiveApp");
+ }
+ if (mSession != null) {
+ mSession.resetInteractiveApp();
}
}
/**
* Sends current channel URI to related TV interactive app.
+ * @hide
*/
public void sendCurrentChannelUri(Uri channelUri) {
if (DEBUG) {
@@ -400,6 +447,7 @@
/**
* Sends current channel logical channel number (LCN) to related TV interactive app.
+ * @hide
*/
public void sendCurrentChannelLcn(int lcn) {
if (DEBUG) {
@@ -412,6 +460,7 @@
/**
* Sends stream volume to related TV interactive app.
+ * @hide
*/
public void sendStreamVolume(float volume) {
if (DEBUG) {
@@ -424,6 +473,7 @@
/**
* Sends track info list to related TV interactive app.
+ * @hide
*/
public void sendTrackInfoList(List<TvTrackInfo> tracks) {
if (DEBUG) {
@@ -434,6 +484,23 @@
}
}
+ /**
+ * Sends current TV input ID to related TV interactive app.
+ *
+ * @param inputId The current TV input ID whose channel is tuned. {@code null} if no channel is
+ * tuned.
+ * @see android.media.tv.TvInputInfo
+ * @hide
+ */
+ public void sendCurrentTvInputId(@Nullable String inputId) {
+ if (DEBUG) {
+ Log.d(TAG, "sendCurrentTvInputId");
+ }
+ if (mSession != null) {
+ mSession.sendCurrentTvInputId(inputId);
+ }
+ }
+
private void resetInternal() {
mSessionCallback = null;
if (mSession != null) {
@@ -478,16 +545,18 @@
}
}
- public Session getIAppSession() {
+ /** @hide */
+ public Session getInteractiveAppSession() {
return mSession;
}
/**
- * Sets the TvIAppView to receive events from TIS. This method links the session of
+ * Sets the TvInteractiveAppView to receive events from TIS. This method links the session of
* TvIAppManager to TvInputManager session, so the TIAS can get the TIS events.
*
- * @param tvView the TvView to be linked to this TvIAppView via linking of Sessions.
+ * @param tvView the TvView to be linked to this TvInteractiveAppView via linking of Sessions.
* @return to be added
+ * @hide
*/
public int setTvView(@Nullable TvView tvView) {
if (tvView == null) {
@@ -498,7 +567,7 @@
return SET_TVVIEW_FAIL;
}
mSession.setInputSession(inputSession);
- inputSession.setIAppSession(mSession);
+ inputSession.setInteractiveAppSession(mSession);
return SET_TVVIEW_SUCCESS;
}
@@ -506,15 +575,29 @@
if (mSession == null || mSession.getInputSession() == null) {
return UNSET_TVVIEW_FAIL;
}
- mSession.getInputSession().setIAppSession(null);
+ mSession.getInputSession().setInteractiveAppSession(null);
mSession.setInputSession(null);
return UNSET_TVVIEW_SUCCESS;
}
/**
- * Callback used to receive various status updates on the {@link TvIAppView}.
+ * To toggle Digital Teletext Application if there is one in AIT app list.
+ * @param enable
*/
- public abstract static class TvIAppCallback {
+ public void setTeletextAppEnabled(boolean enable) {
+ if (DEBUG) {
+ Log.d(TAG, "setTeletextAppEnabled enable=" + enable);
+ }
+ if (mSession != null) {
+ mSession.setTeletextAppEnabled(enable);
+ }
+ }
+
+ /**
+ * Callback used to receive various status updates on the {@link TvInteractiveAppView}.
+ */
+ public abstract static class TvInteractiveAppCallback {
+ // TODO: unhide the following public APIs
/**
* This is called when a command is requested to be processed by the related TV input.
@@ -522,10 +605,11 @@
* @param iAppServiceId The ID of the TV interactive app service bound to this view.
* @param cmdType type of the command
* @param parameters parameters of the command
+ * @hide
*/
public void onCommandRequest(
@NonNull String iAppServiceId,
- @NonNull @TvIAppService.IAppServiceCommandType String cmdType,
+ @NonNull @TvIAppService.InteractiveAppServiceCommandType String cmdType,
@Nullable Bundle parameters) {
}
@@ -534,6 +618,7 @@
*
* @param iAppServiceId The ID of the TV interactive app service bound to this view.
* @param state current session state.
+ * @hide
*/
public void onSessionStateChanged(@NonNull String iAppServiceId, int state) {
}
@@ -546,55 +631,84 @@
* {@link Session#createBiInteractiveApp(Uri, Bundle)}
* @param biIAppId BI interactive app ID, which can be used to destroy the BI interactive
* app.
+ * @hide
*/
public void onBiInteractiveAppCreated(@NonNull String iAppServiceId, @NonNull Uri biIAppUri,
@Nullable String biIAppId) {
}
/**
+ * This is called when the digital teletext app state is changed.
+ *
+ * @param iAppServiceId The ID of the TV interactive app service bound to this view.
+ * @param state digital teletext app current state.
+ */
+ public void onTeletextAppStateChanged(
+ @NonNull String iAppServiceId, @TvIAppManager.TeletextAppState int state) {
+ }
+
+ /**
* This is called when {@link TvIAppService.Session#SetVideoBounds} is called.
*
* @param iAppServiceId The ID of the TV interactive app service bound to this view.
+ * @hide
*/
public void onSetVideoBounds(@NonNull String iAppServiceId, @NonNull Rect rect) {
}
/**
- * This is called when {@link TvIAppService.Session#RequestCurrentChannelUri} is called.
+ * This is called when {@link TvIAppService.Session#RequestCurrentChannelUri} is
+ * called.
*
* @param iAppServiceId The ID of the TV interactive app service bound to this view.
+ * @hide
*/
public void onRequestCurrentChannelUri(@NonNull String iAppServiceId) {
}
/**
- * This is called when {@link TvIAppService.Session#RequestCurrentChannelLcn} is called.
+ * This is called when {@link TvIAppService.Session#RequestCurrentChannelLcn} is
+ * called.
*
* @param iAppServiceId The ID of the TV interactive app service bound to this view.
+ * @hide
*/
public void onRequestCurrentChannelLcn(@NonNull String iAppServiceId) {
}
/**
- * This is called when {@link TvIAppService.Session#RequestStreamVolume} is called.
+ * This is called when {@link TvIAppService.Session#RequestStreamVolume} is
+ * called.
*
* @param iAppServiceId The ID of the TV interactive app service bound to this view.
+ * @hide
*/
public void onRequestStreamVolume(@NonNull String iAppServiceId) {
}
/**
- * This is called when {@link TvIAppService.Session#RequestTrackInfoList} is called.
+ * This is called when {@link TvIAppService.Session#RequestTrackInfoList} is
+ * called.
+ *
+ * @param iAppServiceId The ID of the TV interactive app service bound to this view.
+ * @hide
+ */
+ public void onRequestTrackInfoList(@NonNull String iAppServiceId) {
+ }
+
+ /**
+ * This is called when {@link TvIAppService.Session#RequestCurrentTvInputId} is called.
*
* @param iAppServiceId The ID of the TV interactive app service bound to this view.
*/
- public void onRequestTrackInfoList(@NonNull String iAppServiceId) {
+ public void onRequestCurrentTvInputId(@NonNull String iAppServiceId) {
}
}
/**
* Interface definition for a callback to be invoked when the unhandled input event is received.
+ * @hide
*/
public interface OnUnhandledInputEventListener {
/**
@@ -685,8 +799,10 @@
}
@Override
- public void onCommandRequest(Session session,
- @TvIAppService.IAppServiceCommandType String cmdType, Bundle parameters) {
+ public void onCommandRequest(
+ Session session,
+ @TvIAppService.InteractiveAppServiceCommandType String cmdType,
+ Bundle parameters) {
if (DEBUG) {
Log.d(TAG, "onCommandRequest (cmdType=" + cmdType + ", parameters="
+ parameters.toString() + ")");
@@ -695,8 +811,16 @@
Log.w(TAG, "onCommandRequest - session not created");
return;
}
- if (mCallback != null) {
- mCallback.onCommandRequest(mIAppServiceId, cmdType, parameters);
+ synchronized (mCallbackLock) {
+ if (mCallbackExecutor != null) {
+ mCallbackExecutor.execute(() -> {
+ synchronized (mCallbackLock) {
+ if (mCallback != null) {
+ mCallback.onCommandRequest(mIAppServiceId, cmdType, parameters);
+ }
+ }
+ });
+ }
}
}
@@ -709,8 +833,16 @@
Log.w(TAG, "onSessionStateChanged - session not created");
return;
}
- if (mCallback != null) {
- mCallback.onSessionStateChanged(mIAppServiceId, state);
+ synchronized (mCallbackLock) {
+ if (mCallbackExecutor != null) {
+ mCallbackExecutor.execute(() -> {
+ synchronized (mCallbackLock) {
+ if (mCallback != null) {
+ mCallback.onSessionStateChanged(mIAppServiceId, state);
+ }
+ }
+ });
+ }
}
}
@@ -724,8 +856,31 @@
Log.w(TAG, "onBiInteractiveAppCreated - session not created");
return;
}
+ synchronized (mCallbackLock) {
+ if (mCallbackExecutor != null) {
+ mCallbackExecutor.execute(() -> {
+ synchronized (mCallbackLock) {
+ if (mCallback != null) {
+ mCallback.onBiInteractiveAppCreated(
+ mIAppServiceId, biIAppUri, biIAppId);
+ }
+ }
+ });
+ }
+ }
+ }
+
+ @Override
+ public void onTeletextAppStateChanged(Session session, int state) {
+ if (DEBUG) {
+ Log.d(TAG, "onTeletextAppStateChanged (state=" + state + ")");
+ }
+ if (this != mSessionCallback) {
+ Log.w(TAG, "onTeletextAppStateChanged - session not created");
+ return;
+ }
if (mCallback != null) {
- mCallback.onBiInteractiveAppCreated(mIAppServiceId, biIAppUri, biIAppId);
+ mCallback.onTeletextAppStateChanged(mIAppServiceId, state);
}
}
@@ -738,8 +893,16 @@
Log.w(TAG, "onSetVideoBounds - session not created");
return;
}
- if (mCallback != null) {
- mCallback.onSetVideoBounds(mIAppServiceId, rect);
+ synchronized (mCallbackLock) {
+ if (mCallbackExecutor != null) {
+ mCallbackExecutor.execute(() -> {
+ synchronized (mCallbackLock) {
+ if (mCallback != null) {
+ mCallback.onSetVideoBounds(mIAppServiceId, rect);
+ }
+ }
+ });
+ }
}
}
@@ -752,8 +915,16 @@
Log.w(TAG, "onRequestCurrentChannelUri - session not created");
return;
}
- if (mCallback != null) {
- mCallback.onRequestCurrentChannelUri(mIAppServiceId);
+ synchronized (mCallbackLock) {
+ if (mCallbackExecutor != null) {
+ mCallbackExecutor.execute(() -> {
+ synchronized (mCallbackLock) {
+ if (mCallback != null) {
+ mCallback.onRequestCurrentChannelUri(mIAppServiceId);
+ }
+ }
+ });
+ }
}
}
@@ -766,8 +937,16 @@
Log.w(TAG, "onRequestCurrentChannelLcn - session not created");
return;
}
- if (mCallback != null) {
- mCallback.onRequestCurrentChannelLcn(mIAppServiceId);
+ synchronized (mCallbackLock) {
+ if (mCallbackExecutor != null) {
+ mCallbackExecutor.execute(() -> {
+ synchronized (mCallbackLock) {
+ if (mCallback != null) {
+ mCallback.onRequestCurrentChannelLcn(mIAppServiceId);
+ }
+ }
+ });
+ }
}
}
@@ -780,8 +959,16 @@
Log.w(TAG, "onRequestStreamVolume - session not created");
return;
}
- if (mCallback != null) {
- mCallback.onRequestStreamVolume(mIAppServiceId);
+ synchronized (mCallbackLock) {
+ if (mCallbackExecutor != null) {
+ mCallbackExecutor.execute(() -> {
+ synchronized (mCallbackLock) {
+ if (mCallback != null) {
+ mCallback.onRequestStreamVolume(mIAppServiceId);
+ }
+ }
+ });
+ }
}
}
@@ -794,8 +981,30 @@
Log.w(TAG, "onRequestTrackInfoList - session not created");
return;
}
+ synchronized (mCallbackLock) {
+ if (mCallbackExecutor != null) {
+ mCallbackExecutor.execute(() -> {
+ synchronized (mCallbackLock) {
+ if (mCallback != null) {
+ mCallback.onRequestTrackInfoList(mIAppServiceId);
+ }
+ }
+ });
+ }
+ }
+ }
+
+ @Override
+ public void onRequestCurrentTvInputId(Session session) {
+ if (DEBUG) {
+ Log.d(TAG, "onRequestCurrentTvInputId");
+ }
+ if (this != mSessionCallback) {
+ Log.w(TAG, "onRequestCurrentTvInputId - session not created");
+ return;
+ }
if (mCallback != null) {
- mCallback.onRequestTrackInfoList(mIAppServiceId);
+ mCallback.onRequestCurrentTvInputId(mIAppServiceId);
}
}
}
diff --git a/media/java/android/media/tv/tunerresourcemanager/OWNER b/media/java/android/media/tv/tunerresourcemanager/OWNER
index 76b84d9..0eb1c31 100644
--- a/media/java/android/media/tv/tunerresourcemanager/OWNER
+++ b/media/java/android/media/tv/tunerresourcemanager/OWNER
@@ -1,4 +1,3 @@
-amyjojo@google.com
-nchalko@google.com
quxiangfang@google.com
-shubang@google.com
\ No newline at end of file
+shubang@google.com
+kemiyagi@google.com
\ No newline at end of file
diff --git a/media/tests/TunerTest/OWNERS b/media/tests/TunerTest/OWNERS
index 73ea663..7554889 100644
--- a/media/tests/TunerTest/OWNERS
+++ b/media/tests/TunerTest/OWNERS
@@ -1,4 +1,4 @@
-amyjojo@google.com
-nchalko@google.com
quxiangfang@google.com
shubang@google.com
+hgchen@google.com
+kemiyagi@google.com
\ No newline at end of file
diff --git a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
index cc7b2a5..f74edb1 100644
--- a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
@@ -142,7 +142,15 @@
setAugmentWithSubscriptionPlan(true);
}
- /** @hide */
+ /**
+ * Set poll on open flag to indicate the poll is needed before service gets statistics
+ * result. This is default enabled. However, for any non-privileged caller, the poll might
+ * be omitted in case of rate limiting.
+ *
+ * @param pollOnOpen true if poll is needed.
+ * @hide
+ */
+ // @SystemApi(client = MODULE_LIBRARIES)
public void setPollOnOpen(boolean pollOnOpen) {
if (pollOnOpen) {
mFlags |= FLAG_POLL_ON_OPEN;
@@ -863,4 +871,74 @@
return msg.getData().getParcelable(key);
}
}
+
+ /**
+ * Mark given UID as being in foreground for stats purposes.
+ *
+ * @hide
+ */
+ // @SystemApi
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_STACK})
+ public void setUidForeground(int uid, boolean uidForeground) {
+ try {
+ mService.setUidForeground(uid, uidForeground);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Advise persistence threshold; may be overridden internally.
+ *
+ * @hide
+ */
+ // @SystemApi
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_STACK})
+ public void advisePersistThreshold(long thresholdBytes) {
+ try {
+ mService.advisePersistThreshold(thresholdBytes);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Force update of statistics.
+ *
+ * @hide
+ */
+ // @SystemApi
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_STACK})
+ public void forceUpdate() {
+ try {
+ mService.forceUpdate();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set the warning and limit to all registered custom network stats providers.
+ * Note that invocation of any interface will be sent to all providers.
+ *
+ * @hide
+ */
+ // @SystemApi
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_STACK})
+ public void setStatsProviderWarningAndLimitAsync(@NonNull String iface, long warning,
+ long limit) {
+ try {
+ mService.setStatsProviderWarningAndLimitAsync(iface, warning, limit);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsService.aidl b/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsService.aidl
index 12937b5..a4babb5 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsService.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsService.aidl
@@ -94,4 +94,16 @@
/** Registers a network stats provider */
INetworkStatsProviderCallback registerNetworkStatsProvider(String tag,
in INetworkStatsProvider provider);
+
+ /** Mark given UID as being in foreground for stats purposes. */
+ void setUidForeground(int uid, boolean uidForeground);
+
+ /** Advise persistence threshold; may be overridden internally. */
+ void advisePersistThreshold(long thresholdBytes);
+
+ /**
+ * Set the warning and limit to all registered custom network stats providers.
+ * Note that invocation of any interface will be sent to all providers.
+ */
+ void setStatsProviderWarningAndLimitAsync(String iface, long warning, long limit);
}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java
index 0d15dff..49aa99b 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java
@@ -17,6 +17,7 @@
import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
@@ -25,7 +26,6 @@
import android.annotation.TestApi;
import android.content.Context;
import android.content.pm.PackageManager;
-import android.net.annotations.PolicyDirection;
import android.os.Binder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
@@ -41,6 +41,8 @@
import java.io.FileDescriptor;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.Socket;
@@ -88,6 +90,11 @@
@SystemApi(client = MODULE_LIBRARIES)
public static final int DIRECTION_FWD = 2;
+ /** @hide */
+ @IntDef(value = {DIRECTION_IN, DIRECTION_OUT})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PolicyDirection {}
+
/**
* The Security Parameter Index (SPI) 0 indicates an unknown or invalid index.
*
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
index 7935d28..9f9d73f 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
@@ -46,8 +46,6 @@
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.FastDataInput;
-import com.android.internal.util.FastDataOutput;
import com.android.internal.util.FileRotator;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.NetworkStatsUtils;
@@ -58,6 +56,7 @@
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
+import java.io.DataOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -83,9 +82,6 @@
/** File header magic number: "ANET" */
private static final int FILE_MAGIC = 0x414E4554;
- /** Default buffer size from BufferedInputStream */
- private static final int BUFFER_SIZE = 8192;
-
private static final int VERSION_NETWORK_INIT = 1;
private static final int VERSION_UID_INIT = 1;
@@ -439,8 +435,7 @@
@Override
public void read(InputStream in) throws IOException {
- final FastDataInput dataIn = new FastDataInput(in, BUFFER_SIZE);
- read(dataIn);
+ read((DataInput) new DataInputStream(in));
}
private void read(DataInput in) throws IOException {
@@ -479,9 +474,8 @@
@Override
public void write(OutputStream out) throws IOException {
- final FastDataOutput dataOut = new FastDataOutput(out, BUFFER_SIZE);
- write(dataOut);
- dataOut.flush();
+ write((DataOutput) new DataOutputStream(out));
+ out.flush();
}
private void write(DataOutput out) throws IOException {
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsManagerInternal.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsManagerInternal.java
deleted file mode 100644
index 0e9a9da..0000000
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsManagerInternal.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.net;
-
-import android.annotation.NonNull;
-import android.net.NetworkStats;
-import android.net.NetworkTemplate;
-
-public abstract class NetworkStatsManagerInternal {
- /** Return network layer usage total for traffic that matches template. */
- public abstract long getNetworkTotalBytes(NetworkTemplate template, long start, long end);
-
- /** Return network layer usage per-UID for traffic that matches template. */
- public abstract NetworkStats getNetworkUidBytes(NetworkTemplate template, long start, long end);
-
- /** Mark given UID as being in foreground for stats purposes. */
- public abstract void setUidForeground(int uid, boolean uidForeground);
-
- /** Advise persistance threshold; may be overridden internally. */
- public abstract void advisePersistThreshold(long thresholdBytes);
-
- /** Force update of statistics. */
- public abstract void forceUpdate();
-
- /**
- * Set the warning and limit to all registered custom network stats providers.
- * Note that invocation of any interface will be sent to all providers.
- */
- public abstract void setStatsProviderWarningAndLimitAsync(@NonNull String iface, long warning,
- long limit);
-}
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
index 3273e88..e15acf3 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
@@ -96,6 +96,7 @@
import android.net.NetworkCapabilities;
import android.net.NetworkIdentity;
import android.net.NetworkIdentitySet;
+import android.net.NetworkPolicyManager;
import android.net.NetworkSpecifier;
import android.net.NetworkStack;
import android.net.NetworkStateSnapshot;
@@ -155,7 +156,6 @@
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.NetworkStatsUtils;
import com.android.net.module.util.PermissionUtils;
-import com.android.server.EventLogTags;
import com.android.server.LocalServices;
import java.io.File;
@@ -209,6 +209,14 @@
private static final String TAG_NETSTATS_ERROR = "netstats_error";
+ /**
+ * EventLog tags used when logging into the event log. Note the values must be sync with
+ * frameworks/base/services/core/java/com/android/server/EventLogTags.logtags to get correct
+ * name translation.
+ */
+ private static final int LOG_TAG_NETSTATS_MOBILE_SAMPLE = 51100;
+ private static final int LOG_TAG_NETSTATS_WIFI_SAMPLE = 51101;
+
private final Context mContext;
private final NetworkStatsFactory mStatsFactory;
private final AlarmManager mAlarmManager;
@@ -423,7 +431,6 @@
new DefaultNetworkStatsSettings(context), new NetworkStatsFactory(netd),
new NetworkStatsObservers(), getDefaultSystemDir(), getDefaultBaseDir(),
new Dependencies());
- service.registerLocalService();
return service;
}
@@ -504,11 +511,6 @@
}
}
- private void registerLocalService() {
- LocalServices.addService(NetworkStatsManagerInternal.class,
- new NetworkStatsManagerInternalImpl());
- }
-
/**
* Observer that watches for {@link INetdUnsolicitedEventListener} alerts.
*/
@@ -860,7 +862,7 @@
if (LOGD) Log.d(TAG, "Resolving plan for " + template);
final long token = Binder.clearCallingIdentity();
try {
- plan = LocalServices.getService(NetworkPolicyManagerInternal.class)
+ plan = mContext.getSystemService(NetworkPolicyManager.class)
.getSubscriptionPlan(template);
} finally {
Binder.restoreCallingIdentity(token);
@@ -999,7 +1001,8 @@
}
@VisibleForTesting
- void setUidForeground(int uid, boolean uidForeground) {
+ public void setUidForeground(int uid, boolean uidForeground) {
+ PermissionUtils.enforceNetworkStackPermission(mContext);
synchronized (mStatsLock) {
final int set = uidForeground ? SET_FOREGROUND : SET_DEFAULT;
final int oldSet = mActiveUidCounterSet.get(uid, SET_DEFAULT);
@@ -1035,7 +1038,7 @@
@Override
public void forceUpdate() {
- mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
+ PermissionUtils.enforceNetworkStackPermission(mContext);
final long token = Binder.clearCallingIdentity();
try {
@@ -1045,7 +1048,9 @@
}
}
- private void advisePersistThreshold(long thresholdBytes) {
+ /** Advise persistence threshold; may be overridden internally. */
+ public void advisePersistThreshold(long thresholdBytes) {
+ PermissionUtils.enforceNetworkStackPermission(mContext);
// clamp threshold into safe range
mPersistThreshold = NetworkStatsUtils.constrain(thresholdBytes,
128 * KB_IN_BYTES, 2 * MB_IN_BYTES);
@@ -1624,7 +1629,7 @@
xtTotal = mXtRecorder.getTotalSinceBootLocked(template);
uidTotal = mUidRecorder.getTotalSinceBootLocked(template);
- EventLogTags.writeNetstatsMobileSample(
+ EventLog.writeEvent(LOG_TAG_NETSTATS_MOBILE_SAMPLE,
devTotal.rxBytes, devTotal.rxPackets, devTotal.txBytes, devTotal.txPackets,
xtTotal.rxBytes, xtTotal.rxPackets, xtTotal.txBytes, xtTotal.txPackets,
uidTotal.rxBytes, uidTotal.rxPackets, uidTotal.txBytes, uidTotal.txPackets,
@@ -1636,7 +1641,7 @@
xtTotal = mXtRecorder.getTotalSinceBootLocked(template);
uidTotal = mUidRecorder.getTotalSinceBootLocked(template);
- EventLogTags.writeNetstatsWifiSample(
+ EventLog.writeEvent(LOG_TAG_NETSTATS_WIFI_SAMPLE,
devTotal.rxBytes, devTotal.rxPackets, devTotal.txBytes, devTotal.txPackets,
xtTotal.rxBytes, xtTotal.rxPackets, xtTotal.txBytes, xtTotal.txPackets,
uidTotal.rxBytes, uidTotal.rxPackets, uidTotal.txBytes, uidTotal.txPackets,
@@ -1682,52 +1687,19 @@
removeUidsLocked(CollectionUtils.toIntArray(uids));
}
- private class NetworkStatsManagerInternalImpl extends NetworkStatsManagerInternal {
- @Override
- public long getNetworkTotalBytes(NetworkTemplate template, long start, long end) {
- Trace.traceBegin(TRACE_TAG_NETWORK, "getNetworkTotalBytes");
- try {
- return NetworkStatsService.this.getNetworkTotalBytes(template, start, end);
- } finally {
- Trace.traceEnd(TRACE_TAG_NETWORK);
- }
+ /**
+ * Set the warning and limit to all registered custom network stats providers.
+ * Note that invocation of any interface will be sent to all providers.
+ */
+ public void setStatsProviderWarningAndLimitAsync(
+ @NonNull String iface, long warning, long limit) {
+ PermissionUtils.enforceNetworkStackPermission(mContext);
+ if (LOGV) {
+ Log.v(TAG, "setStatsProviderWarningAndLimitAsync("
+ + iface + "," + warning + "," + limit + ")");
}
-
- @Override
- public NetworkStats getNetworkUidBytes(NetworkTemplate template, long start, long end) {
- Trace.traceBegin(TRACE_TAG_NETWORK, "getNetworkUidBytes");
- try {
- return NetworkStatsService.this.getNetworkUidBytes(template, start, end);
- } finally {
- Trace.traceEnd(TRACE_TAG_NETWORK);
- }
- }
-
- @Override
- public void setUidForeground(int uid, boolean uidForeground) {
- NetworkStatsService.this.setUidForeground(uid, uidForeground);
- }
-
- @Override
- public void advisePersistThreshold(long thresholdBytes) {
- NetworkStatsService.this.advisePersistThreshold(thresholdBytes);
- }
-
- @Override
- public void forceUpdate() {
- NetworkStatsService.this.forceUpdate();
- }
-
- @Override
- public void setStatsProviderWarningAndLimitAsync(
- @NonNull String iface, long warning, long limit) {
- if (LOGV) {
- Log.v(TAG, "setStatsProviderWarningAndLimitAsync("
- + iface + "," + warning + "," + limit + ")");
- }
- invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.onSetWarningAndLimit(iface,
- warning, limit));
- }
+ invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.onSetWarningAndLimit(iface,
+ warning, limit));
}
@Override
@@ -2031,10 +2003,12 @@
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
Objects.requireNonNull(provider, "provider is null");
Objects.requireNonNull(tag, "tag is null");
+ final NetworkPolicyManager netPolicyManager = mContext
+ .getSystemService(NetworkPolicyManager.class);
try {
NetworkStatsProviderCallbackImpl callback = new NetworkStatsProviderCallbackImpl(
tag, provider, mStatsProviderSem, mAlertObserver,
- mStatsProviderCbList);
+ mStatsProviderCbList, netPolicyManager);
mStatsProviderCbList.add(callback);
Log.d(TAG, "registerNetworkStatsProvider from " + callback.mTag + " uid/pid="
+ getCallingUid() + "/" + getCallingPid());
@@ -2076,6 +2050,7 @@
@NonNull private final Semaphore mSemaphore;
@NonNull final AlertObserver mAlertObserver;
@NonNull final CopyOnWriteArrayList<NetworkStatsProviderCallbackImpl> mStatsProviderCbList;
+ @NonNull final NetworkPolicyManager mNetworkPolicyManager;
@NonNull private final Object mProviderStatsLock = new Object();
@@ -2089,7 +2064,8 @@
@NonNull String tag, @NonNull INetworkStatsProvider provider,
@NonNull Semaphore semaphore,
@NonNull AlertObserver alertObserver,
- @NonNull CopyOnWriteArrayList<NetworkStatsProviderCallbackImpl> cbList)
+ @NonNull CopyOnWriteArrayList<NetworkStatsProviderCallbackImpl> cbList,
+ @NonNull NetworkPolicyManager networkPolicyManager)
throws RemoteException {
mTag = tag;
mProvider = provider;
@@ -2097,6 +2073,7 @@
mSemaphore = semaphore;
mAlertObserver = alertObserver;
mStatsProviderCbList = cbList;
+ mNetworkPolicyManager = networkPolicyManager;
}
@NonNull
@@ -2143,8 +2120,7 @@
public void notifyWarningOrLimitReached() {
Log.d(TAG, mTag + ": notifyWarningOrLimitReached");
BinderUtils.withCleanCallingIdentity(() ->
- LocalServices.getService(NetworkPolicyManagerInternal.class)
- .onStatsProviderWarningOrLimitReached(mTag));
+ mNetworkPolicyManager.onStatsProviderWarningOrLimitReached());
}
@Override
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 4cdcca7..5222d8a 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -250,7 +250,7 @@
<string name="adb_wireless_device_connected_summary" msgid="3039660790249148713">"Konektatuta daudenak"</string>
<string name="adb_wireless_device_details_title" msgid="7129369670526565786">"Gailuaren xehetasunak"</string>
<string name="adb_device_forget" msgid="193072400783068417">"Ahaztu"</string>
- <string name="adb_device_fingerprint_title_format" msgid="291504822917843701">"Gailuaren erreferentzia-gako digitala: <xliff:g id="FINGERPRINT_PARAM">%1$s</xliff:g>"</string>
+ <string name="adb_device_fingerprint_title_format" msgid="291504822917843701">"Gailuaren aztarna digitala: <xliff:g id="FINGERPRINT_PARAM">%1$s</xliff:g>"</string>
<string name="adb_wireless_connection_failed_title" msgid="664211177427438438">"Ezin izan da konektatu"</string>
<string name="adb_wireless_connection_failed_message" msgid="9213896700171602073">"Ziurtatu <xliff:g id="DEVICE_NAME">%1$s</xliff:g> sare berera konektatuta dagoela"</string>
<string name="adb_pairing_device_dialog_title" msgid="7141739231018530210">"Parekatu gailuarekin"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 9490ede..4103a9f 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -209,7 +209,7 @@
<string name="tts_status_checking" msgid="8026559918948285013">"Tarkistetaan…"</string>
<string name="tts_engine_settings_title" msgid="7849477533103566291">"Asetukset: <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
<string name="tts_engine_settings_button" msgid="477155276199968948">"Käynnistä moottorin asetukset"</string>
- <string name="tts_engine_preference_section_title" msgid="3861562305498624904">"Ensisijainen kone"</string>
+ <string name="tts_engine_preference_section_title" msgid="3861562305498624904">"Ensisijainen moottori"</string>
<string name="tts_general_section_title" msgid="8919671529502364567">"Yleiset"</string>
<string name="tts_reset_speech_pitch_title" msgid="7149398585468413246">"Palauta äänenkorkeus"</string>
<string name="tts_reset_speech_pitch_summary" msgid="6822904157021406449">"Palauta tekstin lukemisen oletusäänenkorkeus"</string>
@@ -220,8 +220,8 @@
<item msgid="1158955023692670059">"Nopea"</item>
<item msgid="5664310435707146591">"Nopeampi"</item>
<item msgid="5491266922147715962">"Hyvin nopea"</item>
- <item msgid="7659240015901486196">"Nopea"</item>
- <item msgid="7147051179282410945">"Erittäin nopea"</item>
+ <item msgid="7659240015901486196">"Vauhdikas"</item>
+ <item msgid="7147051179282410945">"Erittäin vauhdikas"</item>
<item msgid="581904787661470707">"Nopein"</item>
</string-array>
<string name="choose_profile" msgid="343803890897657450">"Valitse profiili"</string>
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 1303a62..8546b16 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -341,6 +341,9 @@
<uses-permission android:name="android.permission.SET_WALLPAPER" />
<uses-permission android:name="android.permission.SET_WALLPAPER_COMPONENT" />
+ <!-- Permission needed to test wallpaper dimming -->
+ <uses-permission android:name="android.permission.SET_WALLPAPER_DIM_AMOUNT" />
+
<!-- Permission required to test ContentResolver caching. -->
<uses-permission android:name="android.permission.CACHE_CONTENT" />
diff --git a/media/java/android/media/tv/interactive/TvIAppInfo.aidl b/packages/SystemUI/src-debug/com/android/systemui/util/Compile.java
similarity index 70%
copy from media/java/android/media/tv/interactive/TvIAppInfo.aidl
copy to packages/SystemUI/src-debug/com/android/systemui/util/Compile.java
index 6041460..dc804ca 100644
--- a/media/java/android/media/tv/interactive/TvIAppInfo.aidl
+++ b/packages/SystemUI/src-debug/com/android/systemui/util/Compile.java
@@ -14,6 +14,10 @@
* limitations under the License.
*/
-package android.media.tv.interactive;
+package com.android.systemui.util;
-parcelable TvIAppInfo;
\ No newline at end of file
+/** Constants that vary by compilation configuration. */
+public class Compile {
+ /** Whether SystemUI was compiled in debug mode, and supports debug features */
+ public static final boolean IS_DEBUG = true;
+}
diff --git a/media/java/android/media/tv/interactive/TvIAppInfo.aidl b/packages/SystemUI/src-release/com/android/systemui/util/Compile.java
similarity index 70%
copy from media/java/android/media/tv/interactive/TvIAppInfo.aidl
copy to packages/SystemUI/src-release/com/android/systemui/util/Compile.java
index 6041460..8a63763 100644
--- a/media/java/android/media/tv/interactive/TvIAppInfo.aidl
+++ b/packages/SystemUI/src-release/com/android/systemui/util/Compile.java
@@ -14,6 +14,10 @@
* limitations under the License.
*/
-package android.media.tv.interactive;
+package com.android.systemui.util;
-parcelable TvIAppInfo;
\ No newline at end of file
+/** Constants that vary by compilation configuration. */
+public class Compile {
+ /** Whether SystemUI was compiled in debug mode, and supports debug features */
+ public static final boolean IS_DEBUG = false;
+}
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index a100cb8..9c2971c 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -96,6 +96,8 @@
import com.android.systemui.util.concurrency.ThreadFactory;
import com.android.systemui.util.settings.SecureSettings;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
@@ -107,7 +109,7 @@
* for antialiasing and emulation purposes.
*/
@SysUISingleton
-public class ScreenDecorations extends CoreStartable implements Tunable {
+public class ScreenDecorations extends CoreStartable implements Tunable , Dumpable{
private static final boolean DEBUG = false;
private static final String TAG = "ScreenDecorations";
@@ -677,6 +679,20 @@
});
}
+ @Override
+ public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+ pw.println("ScreenDecorations state:");
+ pw.println(" DEBUG_DISABLE_SCREEN_DECORATIONS:" + DEBUG_DISABLE_SCREEN_DECORATIONS);
+ pw.println(" mIsRoundedCornerMultipleRadius:" + mIsRoundedCornerMultipleRadius);
+ pw.println(" mIsPrivacyDotEnabled:" + mIsPrivacyDotEnabled);
+ pw.println(" mPendingRotationChange:" + mPendingRotationChange);
+ pw.println(" mRoundedDefault(x,y)=(" + mRoundedDefault.x + "," + mRoundedDefault.y + ")");
+ pw.println(" mRoundedDefaultTop(x,y)=(" + mRoundedDefaultTop.x + "," + mRoundedDefaultTop.y
+ + ")");
+ pw.println(" mRoundedDefaultBottom(x,y)=(" + mRoundedDefaultBottom.x + ","
+ + mRoundedDefaultBottom.y + ")");
+ }
+
private void updateOrientation() {
Preconditions.checkState(mHandler.getLooper().getThread() == Thread.currentThread(),
"must call on " + mHandler.getLooper().getThread()
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 63962fa..daca918 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -32,6 +32,9 @@
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.Dumpable;
+import android.util.DumpableContainer;
import android.util.Log;
import android.util.TimingsTraceLog;
import android.view.SurfaceControl;
@@ -53,13 +56,19 @@
* Application class for SystemUI.
*/
public class SystemUIApplication extends Application implements
- SystemUIAppComponentFactory.ContextInitializer {
+ SystemUIAppComponentFactory.ContextInitializer, DumpableContainer {
public static final String TAG = "SystemUIService";
private static final boolean DEBUG = false;
private ContextComponentHelper mComponentHelper;
private BootCompleteCacheImpl mBootCompleteCache;
+ private DumpManager mDumpManager;
+
+ /**
+ * Map of dumpables added externally.
+ */
+ private final ArrayMap<String, Dumpable> mDumpables = new ArrayMap<>();
/**
* Hold a reference on the stuff we start.
@@ -214,7 +223,7 @@
}
}
- final DumpManager dumpManager = mSysUIComponent.createDumpManager();
+ mDumpManager = mSysUIComponent.createDumpManager();
Log.v(TAG, "Starting SystemUI services for user " +
Process.myUserHandle().getIdentifier() + ".");
@@ -255,7 +264,7 @@
mServices[i].onBootCompleted();
}
- dumpManager.registerDumpable(mServices[i].getClass().getName(), mServices[i]);
+ mDumpManager.registerDumpable(mServices[i].getClass().getName(), mServices[i]);
}
mSysUIComponent.getInitController().executePostInitTasks();
log.traceEnd();
@@ -263,6 +272,29 @@
mServicesStarted = true;
}
+ // TODO(b/149254050): add unit tests? There doesn't seem to be a SystemUiApplicationTest...
+ @Override
+ public boolean addDumpable(Dumpable dumpable) {
+ String name = dumpable.getDumpableName();
+ if (mDumpables.containsKey(name)) {
+ // This is normal because SystemUIApplication is an application context that is shared
+ // among multiple components
+ if (DEBUG) {
+ Log.d(TAG, "addDumpable(): ignoring " + dumpable + " as there is already a dumpable"
+ + " with that name (" + name + "): " + mDumpables.get(name));
+ }
+ return false;
+ }
+ if (DEBUG) Log.d(TAG, "addDumpable(): adding '" + name + "' = " + dumpable);
+ mDumpables.put(name, dumpable);
+
+ // TODO(b/149254050): replace com.android.systemui.dump.Dumpable by
+ // com.android.util.Dumpable and get rid of the intermediate lambda
+ mDumpManager.registerDumpable(dumpable.getDumpableName(),
+ (fd, pw, args) -> dumpable.dump(pw, args));
+ return true;
+ }
+
@Override
public void onConfigurationChanged(Configuration newConfig) {
if (mServicesStarted) {
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
index c46ffa0..b24d08d 100644
--- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
+++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
@@ -28,6 +28,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Parcelable;
+import android.os.Trace;
import android.util.ArrayMap;
import android.view.LayoutInflater;
import android.view.View;
@@ -224,10 +225,12 @@
}
public void reloadFragments() {
+ Trace.beginSection("FrargmentHostManager#reloadFragments");
// Save the old state.
Parcelable p = destroyFragmentHost();
// Generate a new fragment host and restore its state.
createFragmentHost(p);
+ Trace.endSection();
}
class HostCallbacks extends FragmentHostCallback<FragmentHostManager> {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
index 2e1c9fa..474a81b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
@@ -138,6 +138,7 @@
}
setWakefulness(WAKEFULNESS_AWAKE);
dispatch(Observer::onFinishedWakingUp);
+ dispatch(Observer::onPostFinishedWakingUp);
}
public void dispatchStartedGoingToSleep(@PowerManager.GoToSleepReason int pmSleepReason) {
@@ -236,6 +237,12 @@
public interface Observer {
default void onStartedWakingUp() {}
default void onFinishedWakingUp() {}
+
+ /**
+ * Called after the finished waking up call, ensuring it's after all the other listeners,
+ * reacting to {@link #onFinishedWakingUp()}
+ */
+ default void onPostFinishedWakingUp() {}
default void onStartedGoingToSleep() {}
default void onFinishedGoingToSleep() {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 41dced6..259b786 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -25,6 +25,7 @@
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
+import android.os.Trace;
import android.util.Log;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
@@ -172,9 +173,14 @@
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
Bundle savedInstanceState) {
- inflater = inflater.cloneInContext(new ContextThemeWrapper(getContext(),
- R.style.Theme_SystemUI_QuickSettings));
- return inflater.inflate(R.layout.qs_panel, container, false);
+ try {
+ Trace.beginSection("QSFragment#onCreateView");
+ inflater = inflater.cloneInContext(new ContextThemeWrapper(getContext(),
+ R.style.Theme_SystemUI_QuickSettings));
+ return inflater.inflate(R.layout.qs_panel, container, false);
+ } finally {
+ Trace.endSection();
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 648e14c..c136d9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -7,7 +7,6 @@
import android.content.Context
import android.content.res.Configuration
import android.os.SystemClock
-import android.util.DisplayMetrics
import android.util.IndentingPrintWriter
import android.util.MathUtils
import android.view.MotionEvent
@@ -24,6 +23,7 @@
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
+import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.media.MediaHierarchyManager
import com.android.systemui.plugins.ActivityStarter.OnDismissAction
import com.android.systemui.plugins.FalsingManager
@@ -64,6 +64,7 @@
private val scrimController: ScrimController,
private val depthController: NotificationShadeDepthController,
private val context: Context,
+ wakefulnessLifecycle: WakefulnessLifecycle,
configurationController: ConfigurationController,
falsingManager: FalsingManager,
dumpManager: DumpManager,
@@ -120,6 +121,12 @@
private var nextHideKeyguardNeedsNoAnimation = false
/**
+ * Are we currently waking up to the shade locked
+ */
+ var isWakingToShadeLocked: Boolean = false
+ private set
+
+ /**
* The distance until we're showing the notifications when pulsing
*/
val distanceUntilShowingPulsingNotifications
@@ -160,6 +167,13 @@
}
}
})
+ wakefulnessLifecycle.addObserver(object : WakefulnessLifecycle.Observer {
+ override fun onPostFinishedWakingUp() {
+ // when finishing waking up, the UnlockedScreenOffAnimation has another attempt
+ // to reset keyguard. Let's do it in post
+ isWakingToShadeLocked = false
+ }
+ })
}
private fun updateResources() {
@@ -494,6 +508,10 @@
draggedDownEntry = entry
} else {
logger.logGoingToLockedShade(animationHandler != null)
+ if (statusBarStateController.isDozing) {
+ // Make sure we don't go back to keyguard immediately again after waking up
+ isWakingToShadeLocked = true
+ }
statusBarStateController.setState(StatusBarState.SHADE_LOCKED)
// This call needs to be after updating the shade state since otherwise
// the scrimstate resets too early
@@ -605,6 +623,7 @@
it.println("qSDragProgress: $qSDragProgress")
it.println("isDragDownAnywhereEnabled: $isDragDownAnywhereEnabled")
it.println("isFalsingCheckNeeded: $isFalsingCheckNeeded")
+ it.println("isWakingToShadeLocked: $isWakingToShadeLocked")
it.println("hasPendingHandlerOnKeyguardDismiss: " +
"${animationHandlerOnKeyguardDismiss != null}")
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
index ea51bd8..3fe108f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
@@ -107,8 +107,6 @@
private var mDraggedFarEnough: Boolean = false
private var mStartingChild: ExpandableView? = null
private var mPulsing: Boolean = false
- var isWakingToShadeLocked: Boolean = false
- private set
private var velocityTracker: VelocityTracker? = null
@@ -235,7 +233,6 @@
mStartingChild = null
}
if (statusBarStateController.isDozing) {
- isWakingToShadeLocked = true
wakeUpCoordinator.willWakeUp = true
mPowerManager!!.wakeUp(SystemClock.uptimeMillis(), WAKE_REASON_GESTURE,
"com.android.systemui:PULSEDRAG")
@@ -333,10 +330,6 @@
mPulsing = pulsing
}
- fun onStartedWakingUp() {
- isWakingToShadeLocked = false
- }
-
override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
IndentingPrintWriter(pw, " ").let {
it.println("PulseExpansionHandler:")
@@ -344,7 +337,6 @@
it.println("isExpanding: $isExpanding")
it.println("leavingLockscreen: $leavingLockscreen")
it.println("mPulsing: $mPulsing")
- it.println("isWakingToShadeLocked: $isWakingToShadeLocked")
it.println("qsExpanded: $qsExpanded")
it.println("bouncerShowing: $bouncerShowing")
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 09c608d..062e239 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -59,6 +59,7 @@
import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.util.Assert;
+import com.android.systemui.util.Compile;
import com.android.systemui.util.leak.LeakDetector;
import java.io.FileDescriptor;
@@ -1011,7 +1012,7 @@
}
private static final String TAG = "NotificationEntryMgr";
- private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
/**
* Used when a notification is removed and it doesn't have a reason that maps to one of the
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
index 5993f1d..3b93020 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
@@ -34,9 +34,9 @@
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+import com.android.systemui.util.Compile;
import com.android.wm.shell.bubbles.Bubbles;
import java.io.FileDescriptor;
@@ -69,8 +69,8 @@
Dumpable {
private static final String TAG = "NotifGroupManager";
- private static final boolean DEBUG = StatusBar.DEBUG;
- private static final boolean SPEW = StatusBar.SPEW;
+ private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
+ private static final boolean SPEW = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE);
/**
* The maximum amount of time (in ms) between the posting of notifications that can be
* considered part of the same update batch.
@@ -384,9 +384,9 @@
// * Only necessary when all notifications in the group use GROUP_ALERT_SUMMARY
// * Only necessary when at least one notification in the group is on a priority channel
if (group.summary.getSbn().getNotification().getGroupAlertBehavior()
- != Notification.GROUP_ALERT_SUMMARY) {
+ == Notification.GROUP_ALERT_CHILDREN) {
if (SPEW) {
- Log.d(TAG, "getPriorityConversationAlertOverride: summary != GROUP_ALERT_SUMMARY");
+ Log.d(TAG, "getPriorityConversationAlertOverride: summary == GROUP_ALERT_CHILDREN");
}
return null;
}
@@ -529,8 +529,10 @@
mIsolatedEntries.put(entry.getKey(), entry.getSbn());
if (groupKeysChanged) {
updateSuppression(mGroupMap.get(oldGroupKey));
- updateSuppression(mGroupMap.get(newGroupKey));
}
+ // Always update the suppression of the group from which you're isolated, in case
+ // this entry was or now is the alertOverride for that group.
+ updateSuppression(mGroupMap.get(newGroupKey));
} else if (!wasGroupChild && isGroupChild) {
onEntryBecomingChild(entry);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
index 433d5e1..2b4bc91 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
@@ -40,6 +40,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.util.Compile;
import java.util.ArrayList;
import java.util.List;
@@ -52,8 +53,8 @@
@SysUISingleton
public class NotificationInterruptStateProviderImpl implements NotificationInterruptStateProvider {
private static final String TAG = "InterruptionStateProvider";
- private static final boolean DEBUG = true; //false;
- private static final boolean DEBUG_HEADS_UP = true;
+ private static final boolean DEBUG = Compile.IS_DEBUG;
+ private static final boolean DEBUG_HEADS_UP = Compile.IS_DEBUG;
private static final boolean ENABLE_HEADS_UP = true;
private static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index 9e8200b..dc39413 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -50,6 +50,7 @@
import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.util.Compile;
import java.util.Collection;
import java.util.Collections;
@@ -65,7 +66,7 @@
*/
public class NotificationLogger implements StateListener {
private static final String TAG = "NotificationLogger";
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
/** The minimum delay in ms between reports of notification visibility. */
private static final int VISIBILITY_REPORT_MIN_DELAY_MS = 500;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 08a230b..dbd22db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -115,6 +115,7 @@
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent;
+import com.android.systemui.util.Compile;
import com.android.systemui.util.DumpUtilsKt;
import com.android.systemui.wmshell.BubblesManager;
@@ -136,11 +137,11 @@
implements PluginListener<NotificationMenuRowPlugin>, SwipeableView,
NotificationFadeAware.FadeOptimizedNotification {
- private static final boolean DEBUG = false;
+ private static final String TAG = "ExpandableNotifRow";
+ private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
private static final int DEFAULT_DIVIDER_ALPHA = 0x29;
private static final int COLORED_DIVIDER_ALPHA = 0x7B;
private static final int MENU_VIEW_INDEX = 0;
- private static final String TAG = "ExpandableNotifRow";
public static final float DEFAULT_HEADER_VISIBLE_AMOUNT = 1.0f;
private static final long RECENTLY_ALERTED_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(30);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java
index c0bafb7..4893490 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java
@@ -48,11 +48,12 @@
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.util.Compile;
public class FeedbackInfo extends LinearLayout implements NotificationGuts.GutsContent {
private static final String TAG = "FeedbackInfo";
- private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
private NotificationGuts mGutsContainer;
private NotificationListenerService.Ranking mRanking;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
index ab78d19..6abfee9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
@@ -32,6 +32,7 @@
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
import com.android.systemui.statusbar.notification.logging.NotificationCounters;
+import com.android.systemui.util.Compile;
import java.util.Collections;
import java.util.HashSet;
@@ -43,8 +44,9 @@
*/
public class NotificationBlockingHelperManager {
/** Enables debug logging and always makes the blocking helper show up after a dismiss. */
- private static final boolean DEBUG = false;
private static final String TAG = "BlockingHelper";
+ private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
+ private static final boolean DEBUG_ALWAYS_SHOW = false;
private final Context mContext;
private final NotificationGutsManager mNotificationGutsManager;
@@ -98,7 +100,7 @@
// - The dismissed row is a valid group (>1 or 0 children from the same channel)
// or the only child in the group
final NotificationEntry entry = row.getEntry();
- if ((entry.getUserSentiment() == USER_SENTIMENT_NEGATIVE || DEBUG)
+ if ((entry.getUserSentiment() == USER_SENTIMENT_NEGATIVE || DEBUG_ALWAYS_SHOW)
&& mIsShadeExpanded
&& !row.getIsNonblockable()
&& ((!row.isChildInGroup() || mGroupMembershipManager.isOnlyChildInGroup(entry))
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 4dec1f1..9cb5dc5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -61,6 +61,7 @@
import com.android.systemui.statusbar.policy.SmartReplyStateInflaterKt;
import com.android.systemui.statusbar.policy.SmartReplyView;
import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent;
+import com.android.systemui.util.Compile;
import com.android.systemui.wmshell.BubblesManager;
import java.io.FileDescriptor;
@@ -77,7 +78,7 @@
public class NotificationContentView extends FrameLayout implements NotificationFadeAware {
private static final String TAG = "NotificationContentView";
- private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
public static final int VISIBLE_TYPE_CONTRACTED = 0;
public static final int VISIBLE_TYPE_EXPANDED = 1;
public static final int VISIBLE_TYPE_HEADSUP = 2;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index 9d599cb..d0fb416 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -57,9 +57,6 @@
public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnClickListener,
ExpandableNotificationRow.LayoutListener {
- private static final boolean DEBUG = false;
- private static final String TAG = "swipe";
-
// Notification must be swiped at least this fraction of a single menu item to show menu
private static final float SWIPED_FAR_ENOUGH_MENU_FRACTION = 0.25f;
private static final float SWIPED_FAR_ENOUGH_MENU_UNCLEARABLE_FRACTION = 0.15f;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 915a85d..90f5179 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -132,6 +132,7 @@
public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
private static final String TAG = "StackScroller";
+ private static final boolean SPEW = Log.isLoggable(TAG, Log.VERBOSE);
// Delay in milli-seconds before shade closes for clear all.
private final int DELAY_BEFORE_SHADE_CLOSE = 200;
@@ -3143,6 +3144,13 @@
AnimationEvent event = new AnimationEvent(row, type);
event.headsUpFromBottom = onBottom;
mAnimationEvents.add(event);
+ if (SPEW) {
+ Log.v(TAG, "Generating HUN animation event: "
+ + " isHeadsUp=" + isHeadsUp
+ + " type=" + type
+ + " onBottom=" + onBottom
+ + " row=" + row.getEntry().getKey());
+ }
}
mHeadsUpChangeAnimations.clear();
mAddedHeadsUpChildren.clear();
@@ -4677,7 +4685,22 @@
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
- if (mAnimationsEnabled && (isHeadsUp || mHeadsUpGoingAwayAnimationsAllowed)) {
+ final boolean add = mAnimationsEnabled && (isHeadsUp || mHeadsUpGoingAwayAnimationsAllowed);
+ if (SPEW) {
+ Log.v(TAG, "generateHeadsUpAnimation:"
+ + " willAdd=" + add
+ + " isHeadsUp=" + isHeadsUp
+ + " row=" + row.getEntry().getKey());
+ }
+ if (add) {
+ // If we're hiding a HUN we just started showing THIS FRAME, then remove that event,
+ // and do not add the disappear event either.
+ if (!isHeadsUp && mHeadsUpChangeAnimations.remove(new Pair<>(row, true))) {
+ if (SPEW) {
+ Log.v(TAG, "generateHeadsUpAnimation: previous hun appear animation cancelled");
+ }
+ return;
+ }
mHeadsUpChangeAnimations.add(new Pair<>(row, isHeadsUp));
mNeedsAnimation = true;
if (!mIsExpanded && !mWillExpand && !isHeadsUp) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index ff75eef..7c3399d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -127,6 +127,7 @@
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.tuner.TunerService;
+import com.android.systemui.util.Compile;
import java.util.ArrayList;
import java.util.List;
@@ -144,7 +145,7 @@
@StatusBarComponent.StatusBarScope
public class NotificationStackScrollLayoutController {
private static final String TAG = "StackScrollerController";
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
private final boolean mAllowLongPress;
private final NotificationGutsManager mNotificationGutsManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
index 9787a944..6632c9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -39,6 +39,7 @@
import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneModule;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+import com.android.systemui.util.Compile;
import java.util.ArrayList;
import java.util.List;
@@ -54,8 +55,8 @@
private static final long ALERT_TRANSFER_TIMEOUT = 300;
private static final String TAG = "NotifGroupAlertTransfer";
- private static final boolean DEBUG = StatusBar.DEBUG;
- private static final boolean SPEW = StatusBar.SPEW;
+ private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
+ private static final boolean SPEW = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE);
/**
* The list of entries containing group alert metadata for each group. Keyed by group key.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 4d625cf..23ea45b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -69,6 +69,7 @@
import android.os.Handler;
import android.os.PowerManager;
import android.os.SystemClock;
+import android.os.Trace;
import android.os.UserManager;
import android.os.VibrationEffect;
import android.provider.Settings;
@@ -4590,11 +4591,13 @@
@Override
public void onSmallestScreenWidthChanged() {
+ Trace.beginSection("onSmallestScreenWidthChanged");
if (DEBUG) Log.d(TAG, "onSmallestScreenWidthChanged");
// Can affect multi-user switcher visibility as it depends on screen size by default:
// it is enabled only for devices with large screens (see config_keyguardUserSwitcher)
reInflateViews();
+ Trace.endSection();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 981c843..137c519 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -2963,7 +2963,7 @@
mMessageRouter.cancelMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
if (mUserSwitcherController != null && mUserSwitcherController.useFullscreenUserSwitcher()) {
mStatusBarStateController.setState(StatusBarState.FULLSCREEN_USER_SWITCHER);
- } else if (!mPulseExpansionHandler.isWakingToShadeLocked()) {
+ } else if (!mLockscreenShadeTransitionController.isWakingToShadeLocked()) {
mStatusBarStateController.setState(StatusBarState.KEYGUARD);
}
updatePanelExpansionForKeyguard();
@@ -3569,7 +3569,6 @@
// once we fully woke up.
updateRevealEffect(true /* wakingUp */);
updateNotificationPanelTouchState();
- mPulseExpansionHandler.onStartedWakingUp();
// If we are waking up during the screen off animation, we should undo making the
// expanded visible (we did that so the LightRevealScrim would be visible).
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
index 41cacf5..1030bfd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
@@ -16,110 +16,53 @@
package com.android.systemui.statusbar.policy;
-
import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED;
import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED;
-import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCKED;
-
-import static com.android.systemui.statusbar.policy.dagger.StatusBarPolicyModule.DEVICE_STATE_ROTATION_LOCK_DEFAULTS;
import android.annotation.Nullable;
import android.hardware.devicestate.DeviceStateManager;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.text.TextUtils;
import android.util.Log;
-import android.util.SparseIntArray;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.wrapper.RotationPolicyWrapper;
import java.util.concurrent.Executor;
import javax.inject.Inject;
-import javax.inject.Named;
/**
- * Handles reading and writing of rotation lock settings per device state, as well as setting
- * the rotation lock when device state changes.
- **/
+ * Handles reading and writing of rotation lock settings per device state, as well as setting the
+ * rotation lock when device state changes.
+ */
@SysUISingleton
-public final class DeviceStateRotationLockSettingController implements Listenable,
- RotationLockController.RotationLockControllerCallback {
+public final class DeviceStateRotationLockSettingController
+ implements Listenable, RotationLockController.RotationLockControllerCallback {
private static final String TAG = "DSRotateLockSettingCon";
- private static final String SEPARATOR_REGEX = ":";
-
- private final SecureSettings mSecureSettings;
private final RotationPolicyWrapper mRotationPolicyWrapper;
private final DeviceStateManager mDeviceStateManager;
private final Executor mMainExecutor;
- private final String[] mDeviceStateRotationLockDefaults;
+ private final DeviceStateRotationLockSettingsManager mDeviceStateRotationLockSettingsManager;
- private SparseIntArray mDeviceStateRotationLockSettings;
- // TODO(b/183001527): Add API to query current device state and initialize this.
+ // On registration for DeviceStateCallback, we will receive a callback with the current state
+ // and this will be initialized.
private int mDeviceState = -1;
- @Nullable
- private DeviceStateManager.DeviceStateCallback mDeviceStateCallback;
-
+ @Nullable private DeviceStateManager.DeviceStateCallback mDeviceStateCallback;
+ private DeviceStateRotationLockSettingsManager.DeviceStateRotationLockSettingsListener
+ mDeviceStateRotationLockSettingsListener;
@Inject
public DeviceStateRotationLockSettingController(
- SecureSettings secureSettings,
RotationPolicyWrapper rotationPolicyWrapper,
DeviceStateManager deviceStateManager,
@Main Executor executor,
- @Named(DEVICE_STATE_ROTATION_LOCK_DEFAULTS) String[] deviceStateRotationLockDefaults
- ) {
- mSecureSettings = secureSettings;
+ DeviceStateRotationLockSettingsManager deviceStateRotationLockSettingsManager) {
mRotationPolicyWrapper = rotationPolicyWrapper;
mDeviceStateManager = deviceStateManager;
mMainExecutor = executor;
- mDeviceStateRotationLockDefaults = deviceStateRotationLockDefaults;
- }
-
- /**
- * Loads the settings from storage.
- */
- public void initialize() {
- String serializedSetting =
- mSecureSettings.getStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
- UserHandle.USER_CURRENT);
- if (TextUtils.isEmpty(serializedSetting)) {
- // No settings saved, we should load the defaults and persist them.
- fallbackOnDefaults();
- return;
- }
- String[] values = serializedSetting.split(SEPARATOR_REGEX);
- if (values.length % 2 != 0) {
- // Each entry should be a key/value pair, so this is corrupt.
- Log.wtf(TAG, "Can't deserialize saved settings, falling back on defaults");
- fallbackOnDefaults();
- return;
- }
- mDeviceStateRotationLockSettings = new SparseIntArray(values.length / 2);
- int key;
- int value;
-
- for (int i = 0; i < values.length - 1; ) {
- try {
- key = Integer.parseInt(values[i++]);
- value = Integer.parseInt(values[i++]);
- mDeviceStateRotationLockSettings.put(key, value);
- } catch (NumberFormatException e) {
- Log.wtf(TAG, "Error deserializing one of the saved settings", e);
- fallbackOnDefaults();
- return;
- }
- }
- }
-
- private void fallbackOnDefaults() {
- loadDefaults();
- persistSettings();
+ mDeviceStateRotationLockSettingsManager = deviceStateRotationLockSettingsManager;
}
@Override
@@ -129,10 +72,17 @@
// is no user action.
mDeviceStateCallback = this::updateDeviceState;
mDeviceStateManager.registerCallback(mMainExecutor, mDeviceStateCallback);
+ mDeviceStateRotationLockSettingsListener = () -> readPersistedSetting(mDeviceState);
+ mDeviceStateRotationLockSettingsManager.registerListener(
+ mDeviceStateRotationLockSettingsListener);
} else {
if (mDeviceStateCallback != null) {
mDeviceStateManager.unregisterCallback(mDeviceStateCallback);
}
+ if (mDeviceStateRotationLockSettingsListener != null) {
+ mDeviceStateRotationLockSettingsManager.unregisterListener(
+ mDeviceStateRotationLockSettingsListener);
+ }
}
}
@@ -143,7 +93,8 @@
return;
}
- if (rotationLocked == isRotationLockedForCurrentState()) {
+ if (rotationLocked
+ == mDeviceStateRotationLockSettingsManager.isRotationLocked(mDeviceState)) {
Log.v(TAG, "Rotation lock same as the current setting, no need to update.");
return;
}
@@ -152,19 +103,15 @@
}
private void saveNewRotationLockSetting(boolean isRotationLocked) {
- Log.v(TAG, "saveNewRotationLockSetting [state=" + mDeviceState + "] [isRotationLocked="
- + isRotationLocked + "]");
+ Log.v(
+ TAG,
+ "saveNewRotationLockSetting [state="
+ + mDeviceState
+ + "] [isRotationLocked="
+ + isRotationLocked
+ + "]");
- mDeviceStateRotationLockSettings.put(mDeviceState,
- isRotationLocked
- ? DEVICE_STATE_ROTATION_LOCK_LOCKED
- : DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
- persistSettings();
- }
-
- private boolean isRotationLockedForCurrentState() {
- return mDeviceStateRotationLockSettings.get(mDeviceState,
- DEVICE_STATE_ROTATION_LOCK_IGNORED) == DEVICE_STATE_ROTATION_LOCK_LOCKED;
+ mDeviceStateRotationLockSettingsManager.updateSetting(mDeviceState, isRotationLocked);
}
private void updateDeviceState(int state) {
@@ -173,8 +120,12 @@
return;
}
+ readPersistedSetting(state);
+ }
+
+ private void readPersistedSetting(int state) {
int rotationLockSetting =
- mDeviceStateRotationLockSettings.get(state, DEVICE_STATE_ROTATION_LOCK_IGNORED);
+ mDeviceStateRotationLockSettingsManager.getRotationLockSetting(state);
if (rotationLockSetting == DEVICE_STATE_ROTATION_LOCK_IGNORED) {
// We won't handle this device state. The same rotation lock setting as before should
// apply and any changes to the rotation lock setting will be written for the previous
@@ -186,54 +137,10 @@
// Accept the new state
mDeviceState = state;
- // Update the rotation lock setting if needed for this new device state
+ // Update the rotation policy, if needed, for this new device state
boolean newRotationLockSetting = rotationLockSetting == DEVICE_STATE_ROTATION_LOCK_LOCKED;
if (newRotationLockSetting != mRotationPolicyWrapper.isRotationLocked()) {
mRotationPolicyWrapper.setRotationLock(newRotationLockSetting);
}
}
-
- private void persistSettings() {
- if (mDeviceStateRotationLockSettings.size() == 0) {
- mSecureSettings.putStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
- /* value= */"", UserHandle.USER_CURRENT);
- return;
- }
-
- StringBuilder stringBuilder = new StringBuilder();
- stringBuilder.append(mDeviceStateRotationLockSettings.keyAt(0))
- .append(SEPARATOR_REGEX)
- .append(mDeviceStateRotationLockSettings.valueAt(0));
-
- for (int i = 1; i < mDeviceStateRotationLockSettings.size(); i++) {
- stringBuilder
- .append(SEPARATOR_REGEX)
- .append(mDeviceStateRotationLockSettings.keyAt(i))
- .append(SEPARATOR_REGEX)
- .append(mDeviceStateRotationLockSettings.valueAt(i));
- }
- mSecureSettings.putStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
- stringBuilder.toString(), UserHandle.USER_CURRENT);
- }
-
- private void loadDefaults() {
- if (mDeviceStateRotationLockDefaults.length == 0) {
- Log.w(TAG, "Empty default settings");
- mDeviceStateRotationLockSettings = new SparseIntArray(/* initialCapacity= */0);
- return;
- }
- mDeviceStateRotationLockSettings =
- new SparseIntArray(mDeviceStateRotationLockDefaults.length);
- for (String serializedDefault : mDeviceStateRotationLockDefaults) {
- String[] entry = serializedDefault.split(SEPARATOR_REGEX);
- try {
- int key = Integer.parseInt(entry[0]);
- int value = Integer.parseInt(entry[1]);
- mDeviceStateRotationLockSettings.put(key, value);
- } catch (NumberFormatException e) {
- Log.wtf(TAG, "Error deserializing default settings", e);
- }
- }
- }
-
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingsManager.java
new file mode 100644
index 0000000..a418c74
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingsManager.java
@@ -0,0 +1,266 @@
+/*
+ * 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.systemui.statusbar.policy;
+
+import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED;
+import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED;
+import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCKED;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.SparseIntArray;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Manages device-state based rotation lock settings. Handles reading, writing, and listening for
+ * changes.
+ */
+public final class DeviceStateRotationLockSettingsManager {
+
+ private static final String TAG = "DSRotLockSettingsMngr";
+ private static final String SEPARATOR_REGEX = ":";
+
+ private static DeviceStateRotationLockSettingsManager sSingleton;
+
+ private final ContentResolver mContentResolver;
+ private final String[] mDeviceStateRotationLockDefaults;
+ private final Handler mMainHandler = Handler.getMain();
+ private final Set<DeviceStateRotationLockSettingsListener> mListeners = new HashSet<>();
+ private SparseIntArray mDeviceStateRotationLockSettings;
+
+ private DeviceStateRotationLockSettingsManager(Context context) {
+ mContentResolver = context.getContentResolver();
+ mDeviceStateRotationLockDefaults =
+ context.getResources()
+ .getStringArray(R.array.config_perDeviceStateRotationLockDefaults);
+ initializeInMemoryMap();
+ listenForSettingsChange(context);
+ }
+
+ /** Returns a singleton instance of this class */
+ public static synchronized DeviceStateRotationLockSettingsManager getInstance(Context context) {
+ if (sSingleton == null) {
+ sSingleton =
+ new DeviceStateRotationLockSettingsManager(context.getApplicationContext());
+ }
+ return sSingleton;
+ }
+
+ /** Returns true if device-state based rotation lock settings are enabled. */
+ public static boolean isDeviceStateRotationLockEnabled(Context context) {
+ return context.getResources()
+ .getStringArray(R.array.config_perDeviceStateRotationLockDefaults)
+ .length
+ > 0;
+ }
+
+ private void listenForSettingsChange(Context context) {
+ context.getContentResolver()
+ .registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.DEVICE_STATE_ROTATION_LOCK),
+ /* notifyForDescendents= */ false, //NOTYPO
+ new ContentObserver(mMainHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ onPersistedSettingsChanged();
+ }
+ },
+ UserHandle.USER_CURRENT);
+ }
+
+ /**
+ * Registers a {@link DeviceStateRotationLockSettingsListener} to be notified when the settings
+ * change. Can be called multiple times with different listeners.
+ */
+ public void registerListener(DeviceStateRotationLockSettingsListener runnable) {
+ mListeners.add(runnable);
+ }
+
+ /**
+ * Unregisters a {@link DeviceStateRotationLockSettingsListener}. No-op if the given instance
+ * was never registered.
+ */
+ public void unregisterListener(
+ DeviceStateRotationLockSettingsListener deviceStateRotationLockSettingsListener) {
+ if (!mListeners.remove(deviceStateRotationLockSettingsListener)) {
+ Log.w(TAG, "Attempting to unregister a listener hadn't been registered");
+ }
+ }
+
+ /** Updates the rotation lock setting for a specified device state. */
+ public void updateSetting(int deviceState, boolean rotationLocked) {
+ mDeviceStateRotationLockSettings.put(
+ deviceState,
+ rotationLocked
+ ? DEVICE_STATE_ROTATION_LOCK_LOCKED
+ : DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
+ persistSettings();
+ }
+
+ /**
+ * Returns the {@link DeviceStateRotationLockSetting} for the given device state. If no setting
+ * is specified for this device state, it will return {@link
+ * DEVICE_STATE_ROTATION_LOCK_IGNORED}.
+ */
+ @Settings.Secure.DeviceStateRotationLockSetting
+ public int getRotationLockSetting(int deviceState) {
+ return mDeviceStateRotationLockSettings.get(
+ deviceState, DEVICE_STATE_ROTATION_LOCK_IGNORED);
+ }
+
+ /** Returns true if the rotation is locked for the current device state */
+ public boolean isRotationLocked(int deviceState) {
+ return getRotationLockSetting(deviceState) == DEVICE_STATE_ROTATION_LOCK_LOCKED;
+ }
+
+ /**
+ * Returns true if there is no device state for which the current setting is {@link
+ * DEVICE_STATE_ROTATION_LOCK_UNLOCKED}.
+ */
+ public boolean isRotationLockedForAllStates() {
+ for (int i = 0; i < mDeviceStateRotationLockSettings.size(); i++) {
+ if (mDeviceStateRotationLockSettings.valueAt(i)
+ == DEVICE_STATE_ROTATION_LOCK_UNLOCKED) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void initializeInMemoryMap() {
+ String serializedSetting =
+ Settings.Secure.getStringForUser(
+ mContentResolver,
+ Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+ UserHandle.USER_CURRENT);
+ if (TextUtils.isEmpty(serializedSetting)) {
+ // No settings saved, we should load the defaults and persist them.
+ fallbackOnDefaults();
+ return;
+ }
+ String[] values = serializedSetting.split(SEPARATOR_REGEX);
+ if (values.length % 2 != 0) {
+ // Each entry should be a key/value pair, so this is corrupt.
+ Log.wtf(TAG, "Can't deserialize saved settings, falling back on defaults");
+ fallbackOnDefaults();
+ return;
+ }
+ mDeviceStateRotationLockSettings = new SparseIntArray(values.length / 2);
+ int key;
+ int value;
+
+ for (int i = 0; i < values.length - 1; ) {
+ try {
+ key = Integer.parseInt(values[i++]);
+ value = Integer.parseInt(values[i++]);
+ mDeviceStateRotationLockSettings.put(key, value);
+ } catch (NumberFormatException e) {
+ Log.wtf(TAG, "Error deserializing one of the saved settings", e);
+ fallbackOnDefaults();
+ return;
+ }
+ }
+ }
+
+ private void fallbackOnDefaults() {
+ loadDefaults();
+ persistSettings();
+ }
+
+ private void persistSettings() {
+ if (mDeviceStateRotationLockSettings.size() == 0) {
+ Settings.Secure.putStringForUser(
+ mContentResolver,
+ Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+ /* value= */ "",
+ UserHandle.USER_CURRENT);
+ return;
+ }
+
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder
+ .append(mDeviceStateRotationLockSettings.keyAt(0))
+ .append(SEPARATOR_REGEX)
+ .append(mDeviceStateRotationLockSettings.valueAt(0));
+
+ for (int i = 1; i < mDeviceStateRotationLockSettings.size(); i++) {
+ stringBuilder
+ .append(SEPARATOR_REGEX)
+ .append(mDeviceStateRotationLockSettings.keyAt(i))
+ .append(SEPARATOR_REGEX)
+ .append(mDeviceStateRotationLockSettings.valueAt(i));
+ }
+ Settings.Secure.putStringForUser(
+ mContentResolver,
+ Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+ stringBuilder.toString(),
+ UserHandle.USER_CURRENT);
+ }
+
+ private void loadDefaults() {
+ if (mDeviceStateRotationLockDefaults.length == 0) {
+ Log.w(TAG, "Empty default settings");
+ mDeviceStateRotationLockSettings = new SparseIntArray(/* initialCapacity= */ 0);
+ return;
+ }
+ mDeviceStateRotationLockSettings =
+ new SparseIntArray(mDeviceStateRotationLockDefaults.length);
+ for (String serializedDefault : mDeviceStateRotationLockDefaults) {
+ String[] entry = serializedDefault.split(SEPARATOR_REGEX);
+ try {
+ int key = Integer.parseInt(entry[0]);
+ int value = Integer.parseInt(entry[1]);
+ mDeviceStateRotationLockSettings.put(key, value);
+ } catch (NumberFormatException e) {
+ Log.wtf(TAG, "Error deserializing default settings", e);
+ }
+ }
+ }
+
+ /**
+ * Called when the persisted settings have changed, requiring a reinitialization of the
+ * in-memory map.
+ */
+ @VisibleForTesting
+ public void onPersistedSettingsChanged() {
+ initializeInMemoryMap();
+ notifyListeners();
+ }
+
+ private void notifyListeners() {
+ for (DeviceStateRotationLockSettingsListener r : mListeners) {
+ r.onSettingsChanged();
+ }
+ }
+
+ /** Listener for changes in device-state based rotation lock settings */
+ public interface DeviceStateRotationLockSettingsListener {
+ /** Called whenever the settings have changed. */
+ void onSettingsChanged();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
index 3143a47..1eeb0ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
@@ -64,7 +64,6 @@
mDeviceStateRotationLockSettingController = deviceStateRotationLockSettingController;
mIsPerDeviceStateRotationLockEnabled = deviceStateRotationLockDefaults.length > 0;
if (mIsPerDeviceStateRotationLockEnabled) {
- deviceStateRotationLockSettingController.initialize();
mCallbacks.add(mDeviceStateRotationLockSettingController);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
index b6a96a7..60938fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.policy.dagger;
+import android.content.Context;
import android.content.res.Resources;
import android.os.UserManager;
@@ -35,6 +36,7 @@
import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl;
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.statusbar.policy.DevicePostureControllerImpl;
+import com.android.systemui.statusbar.policy.DeviceStateRotationLockSettingsManager;
import com.android.systemui.statusbar.policy.ExtensionController;
import com.android.systemui.statusbar.policy.ExtensionControllerImpl;
import com.android.systemui.statusbar.policy.FlashlightController;
@@ -163,6 +165,14 @@
return controller;
}
+ /** Returns a singleton instance of DeviceStateRotationLockSettingsManager */
+ @SysUISingleton
+ @Provides
+ static DeviceStateRotationLockSettingsManager provideAutoRotateSettingsManager(
+ Context context) {
+ return DeviceStateRotationLockSettingsManager.getInstance(context);
+ }
+
/**
* Default values for per-device state rotation lock settings.
*/
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java
index e453ff2d..fd282cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java
@@ -80,6 +80,7 @@
assertEquals(WakefulnessLifecycle.WAKEFULNESS_AWAKE, mWakefulness.getWakefulness());
verify(mWakefulnessObserver).onFinishedWakingUp();
+ verify(mWakefulnessObserver).onPostFinishedWakingUp();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index 13b8e81..42647f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -4,13 +4,12 @@
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
-import android.util.DisplayMetrics
import com.android.systemui.ExpandHelper
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.dump.DumpManager
-import com.android.systemui.log.LogBuffer
+import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.media.MediaHierarchyManager
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.qs.QS
@@ -64,12 +63,11 @@
@Mock lateinit var lockScreenUserManager: NotificationLockscreenUserManager
@Mock lateinit var falsingCollector: FalsingCollector
@Mock lateinit var ambientState: AmbientState
- @Mock lateinit var displayMetrics: DisplayMetrics
+ @Mock lateinit var wakefulnessLifecycle: WakefulnessLifecycle
@Mock lateinit var mediaHierarchyManager: MediaHierarchyManager
@Mock lateinit var scrimController: ScrimController
@Mock lateinit var configurationController: ConfigurationController
@Mock lateinit var falsingManager: FalsingManager
- @Mock lateinit var buffer: LogBuffer
@Mock lateinit var notificationPanelController: NotificationPanelViewController
@Mock lateinit var nsslController: NotificationStackScrollLayoutController
@Mock lateinit var depthController: NotificationShadeDepthController
@@ -98,6 +96,7 @@
mediaHierarchyManager = mediaHierarchyManager,
scrimController = scrimController,
depthController = depthController,
+ wakefulnessLifecycle = wakefulnessLifecycle,
context = context,
configurationController = configurationController,
falsingManager = falsingManager,
@@ -148,6 +147,23 @@
}
@Test
+ fun testWakingToShadeLockedWhenDozing() {
+ whenever(statusbarStateController.isDozing).thenReturn(true)
+ transitionController.goToLockedShade(null)
+ verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
+ assertTrue("Not waking to shade locked", transitionController.isWakingToShadeLocked)
+ }
+
+ @Test
+ fun testNotWakingToShadeLockedWhenNotDozing() {
+ whenever(statusbarStateController.isDozing).thenReturn(false)
+ transitionController.goToLockedShade(null)
+ verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
+ assertFalse("Waking to shade locked when not dozing",
+ transitionController.isWakingToShadeLocked)
+ }
+
+ @Test
fun testGoToLockedShadeOnlyOnKeyguard() {
whenever(statusbarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
transitionController.goToLockedShade(null)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
index 1be27da..6d170b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
@@ -178,20 +178,68 @@
}
/**
+ * Helper for testing various sibling counts
+ */
+ private void helpTestAlertOverrideWithSiblings(int numSiblings) {
+ helpTestAlertOverride(
+ /* numSiblings */ numSiblings,
+ /* summaryAlert */ Notification.GROUP_ALERT_SUMMARY,
+ /* childAlert */ Notification.GROUP_ALERT_SUMMARY,
+ /* siblingAlert */ Notification.GROUP_ALERT_SUMMARY,
+ /* expectAlertOverride */ true);
+ }
+
+ @Test
+ public void testAlertOverrideWithParentAlertAll() {
+ // tests that summary can have GROUP_ALERT_ALL and this still works
+ helpTestAlertOverride(
+ /* numSiblings */ 1,
+ /* summaryAlert */ Notification.GROUP_ALERT_ALL,
+ /* childAlert */ Notification.GROUP_ALERT_SUMMARY,
+ /* siblingAlert */ Notification.GROUP_ALERT_SUMMARY,
+ /* expectAlertOverride */ true);
+ }
+
+ @Test
+ public void testAlertOverrideWithParentAlertChild() {
+ // Tests that if the summary alerts CHILDREN, there's no alertOverride
+ helpTestAlertOverride(
+ /* numSiblings */ 1,
+ /* summaryAlert */ Notification.GROUP_ALERT_CHILDREN,
+ /* childAlert */ Notification.GROUP_ALERT_SUMMARY,
+ /* siblingAlert */ Notification.GROUP_ALERT_SUMMARY,
+ /* expectAlertOverride */ false);
+ }
+
+ @Test
+ public void testAlertOverrideWithChildrenAlertAll() {
+ // Tests that if the children alert ALL, there's no alertOverride
+ helpTestAlertOverride(
+ /* numSiblings */ 1,
+ /* summaryAlert */ Notification.GROUP_ALERT_SUMMARY,
+ /* childAlert */ Notification.GROUP_ALERT_ALL,
+ /* siblingAlert */ Notification.GROUP_ALERT_ALL,
+ /* expectAlertOverride */ false);
+ }
+
+ /**
* This tests, for a group with a priority entry and the given number of siblings, that:
* 1) the priority entry is identified as the alertOverride for the group
* 2) the onAlertOverrideChanged method is called at that time
* 3) when the priority entry is removed, these are reversed
*/
- private void helpTestAlertOverrideWithSiblings(int numSiblings) {
- int groupAlert = Notification.GROUP_ALERT_SUMMARY;
+ private void helpTestAlertOverride(int numSiblings,
+ @Notification.GroupAlertBehavior int summaryAlert,
+ @Notification.GroupAlertBehavior int childAlert,
+ @Notification.GroupAlertBehavior int siblingAlert,
+ boolean expectAlertOverride) {
// Create entries in an order so that the priority entry can be deemed the newest child.
NotificationEntry[] siblings = new NotificationEntry[numSiblings];
for (int i = 0; i < numSiblings; i++) {
- siblings[i] = mGroupTestHelper.createChildNotification(groupAlert);
+ siblings[i] = mGroupTestHelper.createChildNotification(siblingAlert);
}
- NotificationEntry priorityEntry = mGroupTestHelper.createChildNotification(groupAlert);
- NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(groupAlert);
+ NotificationEntry priorityEntry = mGroupTestHelper.createChildNotification(childAlert);
+ NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(summaryAlert);
// The priority entry is an important conversation.
when(mPeopleNotificationIdentifier.getPeopleNotificationType(eq(priorityEntry)))
@@ -208,6 +256,14 @@
}
mGroupManager.onEntryAdded(priorityEntry);
+ if (!expectAlertOverride) {
+ // Test expectation is that there will NOT be an alert, so verify that!
+ NotificationGroup summaryGroup =
+ mGroupManager.getGroupForSummary(summaryEntry.getSbn());
+ assertNull(summaryGroup.alertOverride);
+ return;
+ }
+
// Verify that the summary group has the priority child as its alertOverride
NotificationGroup summaryGroup = mGroupManager.getGroupForSummary(summaryEntry.getSbn());
assertEquals(priorityEntry, summaryGroup.alertOverride);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
index db7b2f2..a8522c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
@@ -16,6 +16,10 @@
package com.android.systemui.statusbar.policy;
+import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED;
+import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED;
+import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCKED;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -25,14 +29,15 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableContentResolver;
import android.testing.TestableResources;
import androidx.test.filters.SmallTest;
+import com.android.internal.R;
import com.android.internal.view.RotationPolicy;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.settings.FakeSettings;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.util.wrapper.RotationPolicyWrapper;
@@ -47,63 +52,55 @@
@SmallTest
public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase {
- private static final String[] DEFAULT_SETTINGS = new String[]{
- "0:0",
- "1:2"
- };
+ private static final String[] DEFAULT_SETTINGS = new String[] {"0:0", "1:2"};
- private final FakeSettings mFakeSettings = new FakeSettings();
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
private final FakeExecutor mFakeExecutor = new FakeExecutor(mFakeSystemClock);
@Mock DeviceStateManager mDeviceStateManager;
RotationPolicyWrapper mFakeRotationPolicy = new FakeRotationPolicy();
DeviceStateRotationLockSettingController mDeviceStateRotationLockSettingController;
private DeviceStateManager.DeviceStateCallback mDeviceStateCallback;
+ private DeviceStateRotationLockSettingsManager mSettingsManager;
+ private TestableContentResolver mContentResolver;
@Before
public void setUp() {
MockitoAnnotations.initMocks(/* testClass= */ this);
TestableResources resources = mContext.getOrCreateTestableResources();
+ resources.addOverride(R.array.config_perDeviceStateRotationLockDefaults, DEFAULT_SETTINGS);
ArgumentCaptor<DeviceStateManager.DeviceStateCallback> deviceStateCallbackArgumentCaptor =
- ArgumentCaptor.forClass(
- DeviceStateManager.DeviceStateCallback.class);
+ ArgumentCaptor.forClass(DeviceStateManager.DeviceStateCallback.class);
- mDeviceStateRotationLockSettingController = new DeviceStateRotationLockSettingController(
- mFakeSettings,
- mFakeRotationPolicy,
- mDeviceStateManager,
- mFakeExecutor,
- DEFAULT_SETTINGS
- );
+ mContentResolver = mContext.getContentResolver();
+ mSettingsManager = DeviceStateRotationLockSettingsManager.getInstance(mContext);
+ mDeviceStateRotationLockSettingController =
+ new DeviceStateRotationLockSettingController(
+ mFakeRotationPolicy, mDeviceStateManager, mFakeExecutor, mSettingsManager);
mDeviceStateRotationLockSettingController.setListening(true);
- verify(mDeviceStateManager).registerCallback(any(),
- deviceStateCallbackArgumentCaptor.capture());
+ verify(mDeviceStateManager)
+ .registerCallback(any(), deviceStateCallbackArgumentCaptor.capture());
mDeviceStateCallback = deviceStateCallbackArgumentCaptor.getValue();
}
@Test
public void whenSavedSettingsEmpty_defaultsLoadedAndSaved() {
- mFakeSettings.putStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK, "",
- UserHandle.USER_CURRENT);
+ initializeSettingsWith();
- mDeviceStateRotationLockSettingController.initialize();
-
- assertThat(mFakeSettings
- .getStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
- UserHandle.USER_CURRENT))
+ assertThat(
+ Settings.Secure.getStringForUser(
+ mContentResolver,
+ Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+ UserHandle.USER_CURRENT))
.isEqualTo("0:0:1:2");
}
@Test
public void whenNoSavedValueForDeviceState_assumeIgnored() {
- mFakeSettings.putStringForUser(
- Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
- /* value= */"0:2:1:2",
- UserHandle.USER_CURRENT);
+ initializeSettingsWith(
+ 0, DEVICE_STATE_ROTATION_LOCK_UNLOCKED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
mFakeRotationPolicy.setRotationLock(true);
- mDeviceStateRotationLockSettingController.initialize();
mDeviceStateCallback.onStateChanged(1);
assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
@@ -116,52 +113,43 @@
@Test
public void whenDeviceStateSwitched_loadCorrectSetting() {
- mFakeSettings.putStringForUser(
- Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
- /* value= */"0:2:1:1",
- UserHandle.USER_CURRENT);
+ initializeSettingsWith(
+ 0, DEVICE_STATE_ROTATION_LOCK_UNLOCKED, 1, DEVICE_STATE_ROTATION_LOCK_LOCKED);
mFakeRotationPolicy.setRotationLock(true);
- mDeviceStateRotationLockSettingController.initialize();
mDeviceStateCallback.onStateChanged(0);
assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
mDeviceStateCallback.onStateChanged(1);
assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue();
-
}
@Test
public void whenUserChangesSetting_saveSettingForCurrentState() {
- mFakeSettings.putStringForUser(
- Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
- /* value= */"0:1:1:2",
- UserHandle.USER_CURRENT);
+ initializeSettingsWith(
+ 0, DEVICE_STATE_ROTATION_LOCK_LOCKED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
+ mSettingsManager.onPersistedSettingsChanged();
mFakeRotationPolicy.setRotationLock(true);
- mDeviceStateRotationLockSettingController.initialize();
mDeviceStateCallback.onStateChanged(0);
assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue();
- mDeviceStateRotationLockSettingController
- .onRotationLockStateChanged(/* rotationLocked= */false,
- /* affordanceVisible= */ true);
+ mDeviceStateRotationLockSettingController.onRotationLockStateChanged(
+ /* rotationLocked= */ false, /* affordanceVisible= */ true);
- assertThat(mFakeSettings
- .getStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
- UserHandle.USER_CURRENT))
+ assertThat(
+ Settings.Secure.getStringForUser(
+ mContentResolver,
+ Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+ UserHandle.USER_CURRENT))
.isEqualTo("0:2:1:2");
}
-
@Test
public void whenDeviceStateSwitchedToIgnoredState_usePreviousSetting() {
- mFakeSettings.putStringForUser(
- Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
- /* value= */"0:0:1:2",
- UserHandle.USER_CURRENT);
+ initializeSettingsWith(
+ 0, DEVICE_STATE_ROTATION_LOCK_IGNORED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
mFakeRotationPolicy.setRotationLock(true);
- mDeviceStateRotationLockSettingController.initialize();
mDeviceStateCallback.onStateChanged(1);
assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
@@ -172,12 +160,9 @@
@Test
public void whenDeviceStateSwitchedToIgnoredState_newSettingsSaveForPreviousState() {
- mFakeSettings.putStringForUser(
- Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
- /* value= */"0:0:1:2",
- UserHandle.USER_CURRENT);
+ initializeSettingsWith(
+ 0, DEVICE_STATE_ROTATION_LOCK_IGNORED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
mFakeRotationPolicy.setRotationLock(true);
- mDeviceStateRotationLockSettingController.initialize();
mDeviceStateCallback.onStateChanged(1);
assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
@@ -185,16 +170,52 @@
mDeviceStateCallback.onStateChanged(0);
assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
- mDeviceStateRotationLockSettingController
- .onRotationLockStateChanged(/* rotationLocked= */true,
- /* affordanceVisible= */ true);
+ mDeviceStateRotationLockSettingController.onRotationLockStateChanged(
+ /* rotationLocked= */ true, /* affordanceVisible= */ true);
- assertThat(mFakeSettings
- .getStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
- UserHandle.USER_CURRENT))
+ assertThat(
+ Settings.Secure.getStringForUser(
+ mContentResolver,
+ Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+ UserHandle.USER_CURRENT))
.isEqualTo("0:0:1:1");
}
+ @Test
+ public void whenSettingsChangedExternally_updateRotationPolicy() throws InterruptedException {
+ initializeSettingsWith(
+ 0, DEVICE_STATE_ROTATION_LOCK_UNLOCKED,
+ 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
+ mFakeRotationPolicy.setRotationLock(false);
+ mDeviceStateCallback.onStateChanged(0);
+
+ assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
+
+ // Changing device state 0 to LOCKED
+ initializeSettingsWith(
+ 0, DEVICE_STATE_ROTATION_LOCK_LOCKED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
+
+ assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue();
+ }
+
+ private void initializeSettingsWith(int... values) {
+ if (values.length % 2 != 0) {
+ throw new IllegalArgumentException("Expecting key-value pairs");
+ }
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < values.length; sb.append(":")) {
+ sb.append(values[i++]).append(":").append(values[i++]);
+ }
+
+ Settings.Secure.putStringForUser(
+ mContentResolver,
+ Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+ sb.toString(),
+ UserHandle.USER_CURRENT);
+
+ mSettingsManager.onPersistedSettingsChanged();
+ }
+
private static class FakeRotationPolicy implements RotationPolicyWrapper {
private boolean mRotationLock;
@@ -230,8 +251,8 @@
}
@Override
- public void registerRotationPolicyListener(RotationPolicy.RotationPolicyListener listener,
- int userHandle) {
+ public void registerRotationPolicyListener(
+ RotationPolicy.RotationPolicyListener listener, int userHandle) {
throw new AssertionError("Not implemented");
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java
index 0581264..ea620a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java
@@ -23,7 +23,6 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.testing.TestableResources;
import androidx.test.filters.SmallTest;
@@ -43,25 +42,19 @@
@SmallTest
public class RotationLockControllerImplTest extends SysuiTestCase {
- private static final String[] DEFAULT_SETTINGS = new String[]{
- "0:0",
- "1:2"
- };
+ private static final String[] DEFAULT_SETTINGS = new String[] {"0:0", "1:2"};
@Mock RotationPolicyWrapper mRotationPolicyWrapper;
@Mock DeviceStateRotationLockSettingController mDeviceStateRotationLockSettingController;
- private TestableResources mResources;
- private ArgumentCaptor<RotationPolicy.RotationPolicyListener>
- mRotationPolicyListenerCaptor;
+ private ArgumentCaptor<RotationPolicy.RotationPolicyListener> mRotationPolicyListenerCaptor;
@Before
public void setUp() {
MockitoAnnotations.initMocks(/* testClass= */ this);
- mResources = mContext.getOrCreateTestableResources();
- mRotationPolicyListenerCaptor = ArgumentCaptor.forClass(
- RotationPolicy.RotationPolicyListener.class);
+ mRotationPolicyListenerCaptor =
+ ArgumentCaptor.forClass(RotationPolicy.RotationPolicyListener.class);
}
@Test
@@ -79,14 +72,7 @@
}
@Test
- public void whenFlagOn_initializesDeviceStateRotationController() {
- createRotationLockController();
-
- verify(mDeviceStateRotationLockSettingController).initialize();
- }
-
- @Test
- public void whenFlagOn_dviceStateRotationControllerAddedToCallbacks() {
+ public void whenFlagOn_deviceStateRotationControllerAddedToCallbacks() {
createRotationLockController();
captureRotationPolicyListener().onChange();
@@ -103,11 +89,11 @@
private void createRotationLockController() {
createRotationLockController(DEFAULT_SETTINGS);
}
+
private void createRotationLockController(String[] deviceStateRotationLockDefaults) {
new RotationLockControllerImpl(
mRotationPolicyWrapper,
mDeviceStateRotationLockSettingController,
- deviceStateRotationLockDefaults
- );
+ deviceStateRotationLockDefaults);
}
}
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index 6d0eadb..196c6aa 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -279,6 +279,9 @@
// Notify the user that a CA certificate is pending for the wifi connection.
NOTE_SERVER_CA_CERTIFICATE = 67;
+ // Notify the user to set up dream
+ NOTE_SETUP_DREAM = 68;
+
// ADD_NEW_IDS_ABOVE_THIS_LINE
// Legacy IDs with arbitrary values appear below
// Legacy IDs existed as stable non-conflicting constants prior to the O release
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index aba32ec..59c1461 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -667,10 +667,6 @@
return null;
}
- // Don't need to add the embedded hierarchy windows into the accessibility windows list.
- if (mHostEmbeddedMap.size() > 0 && isEmbeddedHierarchyWindowsLocked(windowId)) {
- return null;
- }
final AccessibilityWindowInfo reportedWindow = AccessibilityWindowInfo.obtain();
reportedWindow.setId(windowId);
@@ -703,21 +699,6 @@
return reportedWindow;
}
- private boolean isEmbeddedHierarchyWindowsLocked(int windowId) {
- final IBinder leashToken = mWindowIdMap.get(windowId);
- if (leashToken == null) {
- return false;
- }
-
- for (int i = 0; i < mHostEmbeddedMap.size(); i++) {
- if (mHostEmbeddedMap.keyAt(i).equals(leashToken)) {
- return true;
- }
- }
-
- return false;
- }
-
private int getTypeForWindowManagerWindowType(int windowType) {
switch (windowType) {
case WindowManager.LayoutParams.TYPE_APPLICATION:
diff --git a/services/companion/java/com/android/server/companion/AssociationStoreImpl.java b/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
index 3f0200e..5b318d3 100644
--- a/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
+++ b/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
@@ -136,6 +136,8 @@
// Update the ID-to-Association map.
mIdMap.put(id, updated);
+ // Invalidate the corresponding user cache entry.
+ invalidateCacheForUserLocked(current.getUserId());
// Update the MacAddress-to-List<Association> map if needed.
final MacAddress updatedAddress = updated.getDeviceMacAddress();
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 94a97d8..1c98347 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -600,11 +600,13 @@
return;
}
- association.setLastTimeConnected(System.currentTimeMillis());
- mAssociationStore.updateAssociation(association);
+ AssociationInfo updatedAssociationInfo = AssociationInfo.builder(association)
+ .setLastTimeConnected(System.currentTimeMillis())
+ .build();
+ mAssociationStore.updateAssociation(updatedAssociationInfo);
mCompanionDevicePresenceController.onDeviceNotifyAppeared(
- association, getContext(), mMainHandler);
+ updatedAssociationInfo, getContext(), mMainHandler);
}
@Override
@@ -651,8 +653,10 @@
+ " for user " + userId));
}
- association.setNotifyOnDeviceNearby(active);
- mAssociationStore.updateAssociation(association);
+ AssociationInfo updatedAssociationInfo = AssociationInfo.builder(association)
+ .setNotifyOnDeviceNearby(active)
+ .build();
+ mAssociationStore.updateAssociation(updatedAssociationInfo);
}
@Override
diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
index 8e71dd3..734e5c3 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -84,11 +84,9 @@
}
@Override
- public void onRunningAppsChanged(int[] runningUids) {
+ public void onRunningAppsChanged(ArraySet<Integer> runningUids) {
mRunningUids.clear();
- for (int i = 0; i < runningUids.length; i++) {
- mRunningUids.add(runningUids[i]);
- }
+ mRunningUids.addAll(runningUids);
}
/**
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 1bb95f8..0c0ee52 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -23,6 +23,9 @@
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import android.annotation.NonNull;
+import android.app.Activity;
+import android.app.ActivityOptions;
+import android.app.PendingIntent;
import android.app.admin.DevicePolicyManager;
import android.companion.AssociationInfo;
import android.companion.virtual.IVirtualDevice;
@@ -38,9 +41,11 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.ArraySet;
+import android.util.Slog;
import android.util.SparseArray;
import android.window.DisplayWindowPolicyController;
@@ -55,10 +60,12 @@
final class VirtualDeviceImpl extends IVirtualDevice.Stub
implements IBinder.DeathRecipient {
+ private static final String TAG = "VirtualDeviceImpl";
private final Object mVirtualDeviceLock = new Object();
private final Context mContext;
private final AssociationInfo mAssociationInfo;
+ private final PendingTrampolineCallback mPendingTrampolineCallback;
private final int mOwnerUid;
private final InputController mInputController;
@VisibleForTesting
@@ -76,17 +83,18 @@
VirtualDeviceImpl(Context context, AssociationInfo associationInfo,
IBinder token, int ownerUid, OnDeviceCloseListener listener,
- VirtualDeviceParams params) {
+ PendingTrampolineCallback pendingTrampolineCallback, VirtualDeviceParams params) {
this(context, associationInfo, token, ownerUid, /* inputController= */ null, listener,
- params);
+ pendingTrampolineCallback, params);
}
@VisibleForTesting
VirtualDeviceImpl(Context context, AssociationInfo associationInfo, IBinder token,
int ownerUid, InputController inputController, OnDeviceCloseListener listener,
- VirtualDeviceParams params) {
+ PendingTrampolineCallback pendingTrampolineCallback, VirtualDeviceParams params) {
mContext = context;
mAssociationInfo = associationInfo;
+ mPendingTrampolineCallback = pendingTrampolineCallback;
mOwnerUid = ownerUid;
mAppToken = token;
mParams = params;
@@ -121,6 +129,49 @@
}
@Override // Binder call
+ public void launchPendingIntent(int displayId, PendingIntent pendingIntent,
+ ResultReceiver resultReceiver) {
+ if (!mVirtualDisplayIds.contains(displayId)) {
+ throw new SecurityException("Display ID " + displayId
+ + " not found for this virtual device");
+ }
+ if (pendingIntent.isActivity()) {
+ try {
+ sendPendingIntent(displayId, pendingIntent);
+ resultReceiver.send(Activity.RESULT_OK, null);
+ } catch (PendingIntent.CanceledException e) {
+ Slog.w(TAG, "Pending intent canceled", e);
+ resultReceiver.send(Activity.RESULT_CANCELED, null);
+ }
+ } else {
+ PendingTrampoline pendingTrampoline = new PendingTrampoline(pendingIntent,
+ resultReceiver, displayId);
+ mPendingTrampolineCallback.startWaitingForPendingTrampoline(pendingTrampoline);
+ try {
+ sendPendingIntent(displayId, pendingIntent);
+ } catch (PendingIntent.CanceledException e) {
+ Slog.w(TAG, "Pending intent canceled", e);
+ resultReceiver.send(Activity.RESULT_CANCELED, null);
+ mPendingTrampolineCallback.stopWaitingForPendingTrampoline(pendingTrampoline);
+ }
+ }
+ }
+
+ private void sendPendingIntent(int displayId, PendingIntent pendingIntent)
+ throws PendingIntent.CanceledException {
+ pendingIntent.send(
+ mContext,
+ /* code= */ 0,
+ /* intent= */ null,
+ /* onFinished= */ null,
+ /* handler= */ null,
+ /* requiredPermission= */ null,
+ ActivityOptions.makeBasic()
+ .setLaunchDisplayId(displayId)
+ .toBundle());
+ }
+
+ @Override // Binder call
public void close() {
mListener.onClose(mAssociationInfo.getId());
mAppToken.unlinkToDeath(this, 0);
@@ -276,6 +327,7 @@
@Override
protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
fout.println(" VirtualDevice: ");
+ fout.println(" mAssociationId: " + mAssociationInfo.getId());
fout.println(" mVirtualDisplayIds: ");
synchronized (mVirtualDeviceLock) {
for (int id : mVirtualDisplayIds) {
@@ -346,4 +398,58 @@
interface OnDeviceCloseListener {
void onClose(int associationId);
}
+
+ interface PendingTrampolineCallback {
+ /**
+ * Called when the callback should start waiting for the given pending trampoline.
+ * Implementations should try to listen for activity starts associated with the given
+ * {@code pendingTrampoline}, and launch the activity on the display with
+ * {@link PendingTrampoline#mDisplayId}.
+ */
+ void startWaitingForPendingTrampoline(PendingTrampoline pendingTrampoline);
+
+ /**
+ * Called when the callback should stop waiting for the given pending trampoline. This can
+ * happen, for example, when the pending intent failed to send.
+ */
+ void stopWaitingForPendingTrampoline(PendingTrampoline pendingTrampoline);
+ }
+
+ /**
+ * A data class storing a pending trampoline this device is expecting.
+ */
+ static class PendingTrampoline {
+
+ /**
+ * The original pending intent sent, for which a trampoline activity launch is expected.
+ */
+ final PendingIntent mPendingIntent;
+
+ /**
+ * The result receiver associated with this pending call. {@link Activity#RESULT_OK} will
+ * be sent to the receiver if the trampoline activity was captured successfully.
+ * {@link Activity#RESULT_CANCELED} is sent otherwise.
+ */
+ final ResultReceiver mResultReceiver;
+
+ /**
+ * The display ID to send the captured trampoline activity launch to.
+ */
+ final int mDisplayId;
+
+ private PendingTrampoline(PendingIntent pendingIntent, ResultReceiver resultReceiver,
+ int displayId) {
+ mPendingIntent = pendingIntent;
+ mResultReceiver = resultReceiver;
+ mDisplayId = displayId;
+ }
+
+ @Override
+ public String toString() {
+ return "PendingTrampoline{"
+ + "pendingIntent=" + mPendingIntent
+ + ", resultReceiver=" + mResultReceiver
+ + ", displayId=" + mDisplayId + "}";
+ }
+ }
}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 12df79d..7e0c2fc 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -16,9 +16,13 @@
package com.android.server.companion.virtual;
+import static com.android.server.wm.ActivityInterceptorCallback.VIRTUAL_DEVICE_SERVICE_ORDERED_ID;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.ActivityOptions;
import android.companion.AssociationInfo;
import android.companion.CompanionDeviceManager;
import android.companion.CompanionDeviceManager.OnAssociationsChangedListener;
@@ -26,7 +30,9 @@
import android.companion.virtual.IVirtualDeviceManager;
import android.companion.virtual.VirtualDeviceParams;
import android.content.Context;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.Parcel;
import android.os.RemoteException;
import android.util.ExceptionUtils;
@@ -37,6 +43,9 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.DumpUtils;
import com.android.server.SystemService;
+import com.android.server.companion.virtual.VirtualDeviceImpl.PendingTrampoline;
+import com.android.server.wm.ActivityInterceptorCallback;
+import com.android.server.wm.ActivityTaskManagerInternal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -48,10 +57,12 @@
public class VirtualDeviceManagerService extends SystemService {
private static final boolean DEBUG = false;
- private static final String LOG_TAG = "VirtualDeviceManagerService";
+ private static final String TAG = "VirtualDeviceManagerService";
private final Object mVirtualDeviceManagerLock = new Object();
private final VirtualDeviceManagerImpl mImpl;
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
+ private final PendingTrampolineMap mPendingTrampolines = new PendingTrampolineMap(mHandler);
/**
* Mapping from CDM association IDs to virtual devices. Only one virtual device is allowed for
@@ -80,10 +91,39 @@
mImpl = new VirtualDeviceManagerImpl();
}
+ private final ActivityInterceptorCallback mActivityInterceptorCallback =
+ new ActivityInterceptorCallback() {
+
+ @Nullable
+ @Override
+ public ActivityInterceptResult intercept(ActivityInterceptorInfo info) {
+ if (info.callingPackage == null) {
+ return null;
+ }
+ PendingTrampoline pt = mPendingTrampolines.remove(info.callingPackage);
+ if (pt == null) {
+ return null;
+ }
+ pt.mResultReceiver.send(Activity.RESULT_OK, null);
+ ActivityOptions options = info.checkedOptions;
+ if (options == null) {
+ options = ActivityOptions.makeBasic();
+ }
+ return new ActivityInterceptResult(
+ info.intent, options.setLaunchDisplayId(pt.mDisplayId));
+ }
+ };
+
@Override
public void onStart() {
publishBinderService(Context.VIRTUAL_DEVICE_SERVICE, mImpl);
publishLocalService(VirtualDeviceManagerInternal.class, new LocalService());
+
+ ActivityTaskManagerInternal activityTaskManagerInternal = getLocalService(
+ ActivityTaskManagerInternal.class);
+ activityTaskManagerInternal.registerActivityStartInterceptor(
+ VIRTUAL_DEVICE_SERVICE_ORDERED_ID,
+ mActivityInterceptorCallback);
}
@GuardedBy("mVirtualDeviceManagerLock")
@@ -128,7 +168,8 @@
}
}
- class VirtualDeviceManagerImpl extends IVirtualDeviceManager.Stub {
+ class VirtualDeviceManagerImpl extends IVirtualDeviceManager.Stub implements
+ VirtualDeviceImpl.PendingTrampolineCallback {
@Override // Binder call
public IVirtualDevice createVirtualDevice(
@@ -164,7 +205,8 @@
mVirtualDevices.remove(associationId);
}
}
- }, params);
+ },
+ this, params);
mVirtualDevices.put(associationInfo.getId(), virtualDevice);
return virtualDevice;
}
@@ -185,7 +227,7 @@
}
}
} else {
- Slog.w(LOG_TAG, "No associations for user " + callingUserId);
+ Slog.w(TAG, "No associations for user " + callingUserId);
}
return null;
}
@@ -196,7 +238,7 @@
try {
return super.onTransact(code, data, reply, flags);
} catch (Throwable e) {
- Slog.e(LOG_TAG, "Error during IPC", e);
+ Slog.e(TAG, "Error during IPC", e);
throw ExceptionUtils.propagate(e, RemoteException.class);
}
}
@@ -205,7 +247,7 @@
public void dump(@NonNull FileDescriptor fd,
@NonNull PrintWriter fout,
@Nullable String[] args) {
- if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), LOG_TAG, fout)) {
+ if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, fout)) {
return;
}
fout.println("Created virtual devices: ");
@@ -215,10 +257,24 @@
}
}
}
+
+ @Override
+ public void startWaitingForPendingTrampoline(PendingTrampoline pendingTrampoline) {
+ PendingTrampoline existing = mPendingTrampolines.put(
+ pendingTrampoline.mPendingIntent.getCreatorPackage(),
+ pendingTrampoline);
+ if (existing != null) {
+ existing.mResultReceiver.send(Activity.RESULT_CANCELED, null);
+ }
+ }
+
+ @Override
+ public void stopWaitingForPendingTrampoline(PendingTrampoline pendingTrampoline) {
+ mPendingTrampolines.remove(pendingTrampoline.mPendingIntent.getCreatorPackage());
+ }
}
private final class LocalService extends VirtualDeviceManagerInternal {
-
@Override
public boolean isValidVirtualDevice(IVirtualDevice virtualDevice) {
synchronized (mVirtualDeviceManagerLock) {
@@ -272,4 +328,42 @@
return false;
}
}
+
+ private static final class PendingTrampolineMap {
+ /**
+ * The maximum duration, in milliseconds, to wait for a trampoline activity launch after
+ * invoking a pending intent.
+ */
+ private static final int TRAMPOLINE_WAIT_MS = 5000;
+
+ private final ConcurrentHashMap<String, PendingTrampoline> mMap = new ConcurrentHashMap<>();
+ private final Handler mHandler;
+
+ PendingTrampolineMap(Handler handler) {
+ mHandler = handler;
+ }
+
+ PendingTrampoline put(
+ @NonNull String packageName, @NonNull PendingTrampoline pendingTrampoline) {
+ PendingTrampoline existing = mMap.put(packageName, pendingTrampoline);
+ mHandler.removeCallbacksAndMessages(existing);
+ mHandler.postDelayed(
+ () -> {
+ final String creatorPackage =
+ pendingTrampoline.mPendingIntent.getCreatorPackage();
+ if (creatorPackage != null) {
+ remove(creatorPackage);
+ }
+ },
+ pendingTrampoline,
+ TRAMPOLINE_WAIT_MS);
+ return existing;
+ }
+
+ PendingTrampoline remove(@NonNull String packageName) {
+ PendingTrampoline pendingTrampoline = mMap.remove(packageName);
+ mHandler.removeCallbacksAndMessages(pendingTrampoline);
+ return pendingTrampoline;
+ }
+ }
}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 7b17162..95ec9e9 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -100,7 +100,7 @@
name: "services.core.unboosted",
defaults: ["platform_service_defaults"],
srcs: [
- ":android.hardware.biometrics.face-V1-java-source",
+ ":android.hardware.biometrics.face-V2-java-source",
":statslog-art-java-gen",
":statslog-contexthub-java-gen",
":services.core-sources",
diff --git a/services/core/java/com/android/server/Dumpable.java b/services/core/java/com/android/server/Dumpable.java
index 866f81c..004f923 100644
--- a/services/core/java/com/android/server/Dumpable.java
+++ b/services/core/java/com/android/server/Dumpable.java
@@ -24,6 +24,8 @@
*
* <p>See {@link SystemServer.SystemServerDumper} for usage example.
*/
+// TODO(b/149254050): replace / merge with package android.util.Dumpable (it would require
+// exporting IndentingPrintWriter as @SystemApi) and/or changing the method to use a prefix
public interface Dumpable {
/**
diff --git a/services/core/java/com/android/server/MasterClearReceiver.java b/services/core/java/com/android/server/MasterClearReceiver.java
index 513d86e7..62dc2733 100644
--- a/services/core/java/com/android/server/MasterClearReceiver.java
+++ b/services/core/java/com/android/server/MasterClearReceiver.java
@@ -126,8 +126,8 @@
private boolean wipeUser(Context context, @UserIdInt int userId, String wipeReason) {
final UserManager userManager = context.getSystemService(UserManager.class);
- final int result = userManager.removeUserOrSetEphemeral(
- userId, /* evenWhenDisallowed= */ false);
+ final int result = userManager.removeUserWhenPossible(
+ UserHandle.of(userId), /* overrideDevicePolicy= */ false);
if (result == UserManager.REMOVE_RESULT_ERROR) {
Slogf.e(TAG, "Can't remove user %d", userId);
return false;
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 023a11e..0961fcb3 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -1124,6 +1124,12 @@
private void makeLeAudioDeviceAvailable(String address, String name, int streamType, int device,
String eventSource) {
if (device != AudioSystem.DEVICE_NONE) {
+
+ /* Audio Policy sees Le Audio similar to A2DP. Let's make sure
+ * AUDIO_POLICY_FORCE_NO_BT_A2DP is not set
+ */
+ mDeviceBroker.setBluetoothA2dpOnInt(true, false /*fromA2dp*/, eventSource);
+
AudioSystem.setDeviceConnectionState(device, AudioSystem.DEVICE_STATE_AVAILABLE,
address, name, AudioSystem.AUDIO_FORMAT_DEFAULT);
mConnectedDevices.put(DeviceInfo.makeDeviceListKey(device, address),
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 805e45d..346ae0f 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -2611,6 +2611,16 @@
return getDevicesForAttributesInt(attributes);
}
+ /** @see AudioManager#getAudioDevicesForAttributes(AudioAttributes)
+ * This method is similar with AudioService#getDevicesForAttributes,
+ * only it doesn't enforce permissions because it is used by an unprivileged public API
+ * instead of the system API.
+ */
+ public @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributesUnprotected(
+ @NonNull AudioAttributes attributes) {
+ return getDevicesForAttributesInt(attributes);
+ }
+
/**
* @see AudioManager#isMusicActive()
* @param remotely true if query is for remote playback (cast), false for local playback.
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index 406b2dd2..74c8999 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -252,6 +252,9 @@
AudioAttributes.FLAG_BYPASS_MUTE;
private void checkVolumeForPrivilegedAlarm(AudioPlaybackConfiguration apc, int event) {
+ if (event == AudioPlaybackConfiguration.PLAYER_UPDATE_DEVICE_ID) {
+ return;
+ }
if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED ||
apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
if ((apc.getAudioAttributes().getAllFlags() & FLAGS_FOR_SILENCE_OVERRIDE)
diff --git a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
index b73e911..26bbb40 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
@@ -16,6 +16,8 @@
package com.android.server.biometrics.sensors;
+import static com.android.internal.annotations.VisibleForTesting.Visibility;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -48,7 +50,6 @@
* Interface that ClientMonitor holders should use to receive callbacks.
*/
public interface Callback {
-
/**
* Invoked when the ClientMonitor operation has been started (e.g. reached the head of
* the queue and becomes the current operation).
@@ -203,7 +204,8 @@
}
/** Signals this operation has completed its lifecycle and should no longer be used. */
- void destroy() {
+ @VisibleForTesting(visibility = Visibility.PACKAGE)
+ public void destroy() {
mAlreadyDone = true;
if (mToken != null) {
try {
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
index a358bc2..39c5944 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
@@ -17,10 +17,10 @@
package com.android.server.biometrics.sensors;
import android.annotation.IntDef;
+import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.IBiometricService;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.os.Handler;
@@ -55,6 +55,7 @@
* We currently assume (and require) that each biometric sensor have its own instance of a
* {@link BiometricScheduler}. See {@link CoexCoordinator}.
*/
+@MainThread
public class BiometricScheduler {
private static final String BASE_TAG = "BiometricScheduler";
@@ -110,123 +111,6 @@
}
}
- /**
- * Contains all the necessary information for a HAL operation.
- */
- @VisibleForTesting
- static final class Operation {
-
- /**
- * The operation is added to the list of pending operations and waiting for its turn.
- */
- static final int STATE_WAITING_IN_QUEUE = 0;
-
- /**
- * The operation is added to the list of pending operations, but a subsequent operation
- * has been added. This state only applies to {@link Interruptable} operations. When this
- * operation reaches the head of the queue, it will send ERROR_CANCELED and finish.
- */
- static final int STATE_WAITING_IN_QUEUE_CANCELING = 1;
-
- /**
- * The operation has reached the front of the queue and has started.
- */
- static final int STATE_STARTED = 2;
-
- /**
- * The operation was started, but is now canceling. Operations should wait for the HAL to
- * acknowledge that the operation was canceled, at which point it finishes.
- */
- static final int STATE_STARTED_CANCELING = 3;
-
- /**
- * The operation has reached the head of the queue but is waiting for BiometricService
- * to acknowledge and start the operation.
- */
- static final int STATE_WAITING_FOR_COOKIE = 4;
-
- /**
- * The {@link BaseClientMonitor.Callback} has been invoked and the client is finished.
- */
- static final int STATE_FINISHED = 5;
-
- @IntDef({STATE_WAITING_IN_QUEUE,
- STATE_WAITING_IN_QUEUE_CANCELING,
- STATE_STARTED,
- STATE_STARTED_CANCELING,
- STATE_WAITING_FOR_COOKIE,
- STATE_FINISHED})
- @Retention(RetentionPolicy.SOURCE)
- @interface OperationState {}
-
- @NonNull final BaseClientMonitor mClientMonitor;
- @Nullable final BaseClientMonitor.Callback mClientCallback;
- @OperationState int mState;
-
- Operation(
- @NonNull BaseClientMonitor clientMonitor,
- @Nullable BaseClientMonitor.Callback callback
- ) {
- this(clientMonitor, callback, STATE_WAITING_IN_QUEUE);
- }
-
- protected Operation(
- @NonNull BaseClientMonitor clientMonitor,
- @Nullable BaseClientMonitor.Callback callback,
- @OperationState int state
- ) {
- mClientMonitor = clientMonitor;
- mClientCallback = callback;
- mState = state;
- }
-
- public boolean isHalOperation() {
- return mClientMonitor instanceof HalClientMonitor<?>;
- }
-
- /**
- * @return true if the operation requires the HAL, and the HAL is null.
- */
- public boolean isUnstartableHalOperation() {
- if (isHalOperation()) {
- final HalClientMonitor<?> client = (HalClientMonitor<?>) mClientMonitor;
- if (client.getFreshDaemon() == null) {
- return true;
- }
- }
- return false;
- }
-
- @Override
- public String toString() {
- return mClientMonitor + ", State: " + mState;
- }
- }
-
- /**
- * Monitors an operation's cancellation. If cancellation takes too long, the watchdog will
- * kill the current operation and forcibly start the next.
- */
- private static final class CancellationWatchdog implements Runnable {
- static final int DELAY_MS = 3000;
-
- final String tag;
- final Operation operation;
- CancellationWatchdog(String tag, Operation operation) {
- this.tag = tag;
- this.operation = operation;
- }
-
- @Override
- public void run() {
- if (operation.mState != Operation.STATE_FINISHED) {
- Slog.e(tag, "[Watchdog Triggered]: " + operation);
- operation.mClientMonitor.mCallback
- .onClientFinished(operation.mClientMonitor, false /* success */);
- }
- }
- }
-
private static final class CrashState {
static final int NUM_ENTRIES = 10;
final String timestamp;
@@ -263,10 +147,9 @@
private final @SensorType int mSensorType;
@Nullable private final GestureAvailabilityDispatcher mGestureAvailabilityDispatcher;
@NonNull private final IBiometricService mBiometricService;
- @NonNull protected final Handler mHandler = new Handler(Looper.getMainLooper());
- @NonNull private final InternalCallback mInternalCallback;
- @VisibleForTesting @NonNull final Deque<Operation> mPendingOperations;
- @VisibleForTesting @Nullable Operation mCurrentOperation;
+ @NonNull protected final Handler mHandler;
+ @VisibleForTesting @NonNull final Deque<BiometricSchedulerOperation> mPendingOperations;
+ @VisibleForTesting @Nullable BiometricSchedulerOperation mCurrentOperation;
@NonNull private final ArrayDeque<CrashState> mCrashStates;
private int mTotalOperationsHandled;
@@ -277,7 +160,7 @@
// Internal callback, notified when an operation is complete. Notifies the requester
// that the operation is complete, before performing internal scheduler work (such as
// starting the next client).
- public class InternalCallback implements BaseClientMonitor.Callback {
+ private final BaseClientMonitor.Callback mInternalCallback = new BaseClientMonitor.Callback() {
@Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
Slog.d(getTag(), "[Started] " + clientMonitor);
@@ -286,16 +169,11 @@
mCoexCoordinator.addAuthenticationClient(mSensorType,
(AuthenticationClient<?>) clientMonitor);
}
-
- if (mCurrentOperation.mClientCallback != null) {
- mCurrentOperation.mClientCallback.onClientStarted(clientMonitor);
- }
}
@Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {
mHandler.post(() -> {
- clientMonitor.destroy();
if (mCurrentOperation == null) {
Slog.e(getTag(), "[Finishing] " + clientMonitor
+ " but current operation is null, success: " + success
@@ -303,9 +181,9 @@
return;
}
- if (clientMonitor != mCurrentOperation.mClientMonitor) {
+ if (!mCurrentOperation.isFor(clientMonitor)) {
Slog.e(getTag(), "[Ignoring Finish] " + clientMonitor + " does not match"
- + " current: " + mCurrentOperation.mClientMonitor);
+ + " current: " + mCurrentOperation);
return;
}
@@ -315,36 +193,33 @@
(AuthenticationClient<?>) clientMonitor);
}
- mCurrentOperation.mState = Operation.STATE_FINISHED;
-
- if (mCurrentOperation.mClientCallback != null) {
- mCurrentOperation.mClientCallback.onClientFinished(clientMonitor, success);
- }
-
if (mGestureAvailabilityDispatcher != null) {
mGestureAvailabilityDispatcher.markSensorActive(
- mCurrentOperation.mClientMonitor.getSensorId(), false /* active */);
+ mCurrentOperation.getSensorId(), false /* active */);
}
if (mRecentOperations.size() >= mRecentOperationsLimit) {
mRecentOperations.remove(0);
}
- mRecentOperations.add(mCurrentOperation.mClientMonitor.getProtoEnum());
+ mRecentOperations.add(mCurrentOperation.getProtoEnum());
mCurrentOperation = null;
mTotalOperationsHandled++;
startNextOperationIfIdle();
});
}
- }
+ };
@VisibleForTesting
- BiometricScheduler(@NonNull String tag, @SensorType int sensorType,
+ BiometricScheduler(@NonNull String tag,
+ @NonNull Handler handler,
+ @SensorType int sensorType,
@Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
- @NonNull IBiometricService biometricService, int recentOperationsLimit,
+ @NonNull IBiometricService biometricService,
+ int recentOperationsLimit,
@NonNull CoexCoordinator coexCoordinator) {
mBiometricTag = tag;
+ mHandler = handler;
mSensorType = sensorType;
- mInternalCallback = new InternalCallback();
mGestureAvailabilityDispatcher = gestureAvailabilityDispatcher;
mPendingOperations = new ArrayDeque<>();
mBiometricService = biometricService;
@@ -356,6 +231,7 @@
/**
* Creates a new scheduler.
+ *
* @param tag for the specific instance of the scheduler. Should be unique.
* @param sensorType the sensorType that this scheduler is handling.
* @param gestureAvailabilityDispatcher may be null if the sensor does not support gestures
@@ -364,16 +240,14 @@
public BiometricScheduler(@NonNull String tag,
@SensorType int sensorType,
@Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
- this(tag, sensorType, gestureAvailabilityDispatcher, IBiometricService.Stub.asInterface(
- ServiceManager.getService(Context.BIOMETRIC_SERVICE)), LOG_NUM_RECENT_OPERATIONS,
- CoexCoordinator.getInstance());
+ this(tag, new Handler(Looper.getMainLooper()), sensorType, gestureAvailabilityDispatcher,
+ IBiometricService.Stub.asInterface(
+ ServiceManager.getService(Context.BIOMETRIC_SERVICE)),
+ LOG_NUM_RECENT_OPERATIONS, CoexCoordinator.getInstance());
}
- /**
- * @return A reference to the internal callback that should be invoked whenever the scheduler
- * needs to (e.g. client started, client finished).
- */
- @NonNull protected InternalCallback getInternalCallback() {
+ @VisibleForTesting
+ public BaseClientMonitor.Callback getInternalCallback() {
return mInternalCallback;
}
@@ -392,72 +266,46 @@
}
mCurrentOperation = mPendingOperations.poll();
- final BaseClientMonitor currentClient = mCurrentOperation.mClientMonitor;
Slog.d(getTag(), "[Polled] " + mCurrentOperation);
// If the operation at the front of the queue has been marked for cancellation, send
// ERROR_CANCELED. No need to start this client.
- if (mCurrentOperation.mState == Operation.STATE_WAITING_IN_QUEUE_CANCELING) {
+ if (mCurrentOperation.isMarkedCanceling()) {
Slog.d(getTag(), "[Now Cancelling] " + mCurrentOperation);
- if (!(currentClient instanceof Interruptable)) {
- throw new IllegalStateException("Mis-implemented client or scheduler, "
- + "trying to cancel non-interruptable operation: " + mCurrentOperation);
- }
-
- final Interruptable interruptable = (Interruptable) currentClient;
- interruptable.cancelWithoutStarting(getInternalCallback());
+ mCurrentOperation.cancel(mHandler, mInternalCallback);
// Now we wait for the client to send its FinishCallback, which kicks off the next
// operation.
return;
}
- if (mGestureAvailabilityDispatcher != null
- && mCurrentOperation.mClientMonitor instanceof AcquisitionClient) {
+ if (mGestureAvailabilityDispatcher != null && mCurrentOperation.isAcquisitionOperation()) {
mGestureAvailabilityDispatcher.markSensorActive(
- mCurrentOperation.mClientMonitor.getSensorId(),
- true /* active */);
+ mCurrentOperation.getSensorId(), true /* active */);
}
// Not all operations start immediately. BiometricPrompt waits for its operation
// to arrive at the head of the queue, before pinging it to start.
- final boolean shouldStartNow = currentClient.getCookie() == 0;
- if (shouldStartNow) {
- if (mCurrentOperation.isUnstartableHalOperation()) {
- final HalClientMonitor<?> halClientMonitor =
- (HalClientMonitor<?>) mCurrentOperation.mClientMonitor;
+ final int cookie = mCurrentOperation.isReadyToStart();
+ if (cookie == 0) {
+ if (!mCurrentOperation.start(mInternalCallback)) {
// Note down current length of queue
final int pendingOperationsLength = mPendingOperations.size();
- final Operation lastOperation = mPendingOperations.peekLast();
+ final BiometricSchedulerOperation lastOperation = mPendingOperations.peekLast();
Slog.e(getTag(), "[Unable To Start] " + mCurrentOperation
+ ". Last pending operation: " + lastOperation);
- // For current operations, 1) unableToStart, which notifies the caller-side, then
- // 2) notify operation's callback, to notify applicable system service that the
- // operation failed.
- halClientMonitor.unableToStart();
- if (mCurrentOperation.mClientCallback != null) {
- mCurrentOperation.mClientCallback.onClientFinished(
- mCurrentOperation.mClientMonitor, false /* success */);
- }
-
// Then for each operation currently in the pending queue at the time of this
// failure, do the same as above. Otherwise, it's possible that something like
// setActiveUser fails, but then authenticate (for the wrong user) is invoked.
for (int i = 0; i < pendingOperationsLength; i++) {
- final Operation operation = mPendingOperations.pollFirst();
- if (operation == null) {
+ final BiometricSchedulerOperation operation = mPendingOperations.pollFirst();
+ if (operation != null) {
+ Slog.w(getTag(), "[Aborting Operation] " + operation);
+ operation.abort();
+ } else {
Slog.e(getTag(), "Null operation, index: " + i
+ ", expected length: " + pendingOperationsLength);
- break;
}
- if (operation.isHalOperation()) {
- ((HalClientMonitor<?>) operation.mClientMonitor).unableToStart();
- }
- if (operation.mClientCallback != null) {
- operation.mClientCallback.onClientFinished(operation.mClientMonitor,
- false /* success */);
- }
- Slog.w(getTag(), "[Aborted Operation] " + operation);
}
// It's possible that during cleanup a new set of operations came in. We can try to
@@ -465,25 +313,20 @@
// actually be multiple operations (i.e. updateActiveUser + authenticate).
mCurrentOperation = null;
startNextOperationIfIdle();
- } else {
- Slog.d(getTag(), "[Starting] " + mCurrentOperation);
- currentClient.start(getInternalCallback());
- mCurrentOperation.mState = Operation.STATE_STARTED;
}
} else {
try {
- mBiometricService.onReadyForAuthentication(currentClient.getCookie());
+ mBiometricService.onReadyForAuthentication(cookie);
} catch (RemoteException e) {
Slog.e(getTag(), "Remote exception when contacting BiometricService", e);
}
Slog.d(getTag(), "Waiting for cookie before starting: " + mCurrentOperation);
- mCurrentOperation.mState = Operation.STATE_WAITING_FOR_COOKIE;
}
}
/**
* Starts the {@link #mCurrentOperation} if
- * 1) its state is {@link Operation#STATE_WAITING_FOR_COOKIE} and
+ * 1) its state is {@link BiometricSchedulerOperation#STATE_WAITING_FOR_COOKIE} and
* 2) its cookie matches this cookie
*
* This is currently only used by {@link com.android.server.biometrics.BiometricService}, which
@@ -499,45 +342,13 @@
Slog.e(getTag(), "Current operation is null");
return;
}
- if (mCurrentOperation.mState != Operation.STATE_WAITING_FOR_COOKIE) {
- if (mCurrentOperation.mState == Operation.STATE_WAITING_IN_QUEUE_CANCELING) {
- Slog.d(getTag(), "Operation was marked for cancellation, cancelling now: "
- + mCurrentOperation);
- // This should trigger the internal onClientFinished callback, which clears the
- // operation and starts the next one.
- final ErrorConsumer errorConsumer =
- (ErrorConsumer) mCurrentOperation.mClientMonitor;
- errorConsumer.onError(BiometricConstants.BIOMETRIC_ERROR_CANCELED,
- 0 /* vendorCode */);
- return;
- } else {
- Slog.e(getTag(), "Operation is in the wrong state: " + mCurrentOperation
- + ", expected STATE_WAITING_FOR_COOKIE");
- return;
- }
- }
- if (mCurrentOperation.mClientMonitor.getCookie() != cookie) {
- Slog.e(getTag(), "Mismatched cookie for operation: " + mCurrentOperation
- + ", received: " + cookie);
- return;
- }
- if (mCurrentOperation.isUnstartableHalOperation()) {
+ if (mCurrentOperation.startWithCookie(mInternalCallback, cookie)) {
+ Slog.d(getTag(), "[Started] Prepared client: " + mCurrentOperation);
+ } else {
Slog.e(getTag(), "[Unable To Start] Prepared client: " + mCurrentOperation);
- // This is BiometricPrompt trying to auth but something's wrong with the HAL.
- final HalClientMonitor<?> halClientMonitor =
- (HalClientMonitor<?>) mCurrentOperation.mClientMonitor;
- halClientMonitor.unableToStart();
- if (mCurrentOperation.mClientCallback != null) {
- mCurrentOperation.mClientCallback.onClientFinished(mCurrentOperation.mClientMonitor,
- false /* success */);
- }
mCurrentOperation = null;
startNextOperationIfIdle();
- } else {
- Slog.d(getTag(), "[Starting] Prepared client: " + mCurrentOperation);
- mCurrentOperation.mState = Operation.STATE_STARTED;
- mCurrentOperation.mClientMonitor.start(getInternalCallback());
}
}
@@ -562,17 +373,14 @@
// pending clients as canceling. Once they reach the head of the queue, the scheduler will
// send ERROR_CANCELED and skip the operation.
if (clientMonitor.interruptsPrecedingClients()) {
- for (Operation operation : mPendingOperations) {
- if (operation.mClientMonitor instanceof Interruptable
- && operation.mState != Operation.STATE_WAITING_IN_QUEUE_CANCELING) {
- Slog.d(getTag(), "New client incoming, marking pending client as canceling: "
- + operation.mClientMonitor);
- operation.mState = Operation.STATE_WAITING_IN_QUEUE_CANCELING;
+ for (BiometricSchedulerOperation operation : mPendingOperations) {
+ if (operation.markCanceling()) {
+ Slog.d(getTag(), "New client, marking pending op as canceling: " + operation);
}
}
}
- mPendingOperations.add(new Operation(clientMonitor, clientCallback));
+ mPendingOperations.add(new BiometricSchedulerOperation(clientMonitor, clientCallback));
Slog.d(getTag(), "[Added] " + clientMonitor
+ ", new queue size: " + mPendingOperations.size());
@@ -580,67 +388,34 @@
// cancellable, start the cancellation process.
if (clientMonitor.interruptsPrecedingClients()
&& mCurrentOperation != null
- && mCurrentOperation.mClientMonitor instanceof Interruptable
- && mCurrentOperation.mState == Operation.STATE_STARTED) {
+ && mCurrentOperation.isInterruptable()
+ && mCurrentOperation.isStarted()) {
Slog.d(getTag(), "[Cancelling Interruptable]: " + mCurrentOperation);
- cancelInternal(mCurrentOperation);
- }
-
- startNextOperationIfIdle();
- }
-
- private void cancelInternal(Operation operation) {
- if (operation != mCurrentOperation) {
- Slog.e(getTag(), "cancelInternal invoked on non-current operation: " + operation);
- return;
- }
- if (!(operation.mClientMonitor instanceof Interruptable)) {
- Slog.w(getTag(), "Operation not interruptable: " + operation);
- return;
- }
- if (operation.mState == Operation.STATE_STARTED_CANCELING) {
- Slog.w(getTag(), "Cancel already invoked for operation: " + operation);
- return;
- }
- if (operation.mState == Operation.STATE_WAITING_FOR_COOKIE) {
- Slog.w(getTag(), "Skipping cancellation for non-started operation: " + operation);
- // We can set it to null immediately, since the HAL was never notified to start.
- if (mCurrentOperation != null) {
- mCurrentOperation.mClientMonitor.destroy();
- }
- mCurrentOperation = null;
+ mCurrentOperation.cancel(mHandler, mInternalCallback);
+ } else {
startNextOperationIfIdle();
- return;
}
- Slog.d(getTag(), "[Cancelling] Current client: " + operation.mClientMonitor);
- final Interruptable interruptable = (Interruptable) operation.mClientMonitor;
- interruptable.cancel();
- operation.mState = Operation.STATE_STARTED_CANCELING;
-
- // Add a watchdog. If the HAL does not acknowledge within the timeout, we will
- // forcibly finish this client.
- mHandler.postDelayed(new CancellationWatchdog(getTag(), operation),
- CancellationWatchdog.DELAY_MS);
}
/**
* Requests to cancel enrollment.
* @param token from the caller, should match the token passed in when requesting enrollment
*/
- public void cancelEnrollment(IBinder token) {
- if (mCurrentOperation == null) {
- Slog.e(getTag(), "Unable to cancel enrollment, null operation");
- return;
- }
- final boolean isEnrolling = mCurrentOperation.mClientMonitor instanceof EnrollClient;
- final boolean tokenMatches = mCurrentOperation.mClientMonitor.getToken() == token;
- if (!isEnrolling || !tokenMatches) {
- Slog.w(getTag(), "Not cancelling enrollment, isEnrolling: " + isEnrolling
- + " tokenMatches: " + tokenMatches);
- return;
- }
+ public void cancelEnrollment(IBinder token, long requestId) {
+ Slog.d(getTag(), "cancelEnrollment, requestId: " + requestId);
- cancelInternal(mCurrentOperation);
+ if (mCurrentOperation != null
+ && canCancelEnrollOperation(mCurrentOperation, token, requestId)) {
+ Slog.d(getTag(), "Cancelling enrollment op: " + mCurrentOperation);
+ mCurrentOperation.cancel(mHandler, mInternalCallback);
+ } else {
+ for (BiometricSchedulerOperation operation : mPendingOperations) {
+ if (canCancelEnrollOperation(operation, token, requestId)) {
+ Slog.d(getTag(), "Cancelling pending enrollment op: " + operation);
+ operation.markCanceling();
+ }
+ }
+ }
}
/**
@@ -649,62 +424,42 @@
* @param requestId the id returned when requesting authentication
*/
public void cancelAuthenticationOrDetection(IBinder token, long requestId) {
- Slog.d(getTag(), "cancelAuthenticationOrDetection, requestId: " + requestId
- + " current: " + mCurrentOperation
- + " stack size: " + mPendingOperations.size());
+ Slog.d(getTag(), "cancelAuthenticationOrDetection, requestId: " + requestId);
if (mCurrentOperation != null
&& canCancelAuthOperation(mCurrentOperation, token, requestId)) {
- Slog.d(getTag(), "Cancelling: " + mCurrentOperation);
- cancelInternal(mCurrentOperation);
+ Slog.d(getTag(), "Cancelling auth/detect op: " + mCurrentOperation);
+ mCurrentOperation.cancel(mHandler, mInternalCallback);
} else {
- // Look through the current queue for all authentication clients for the specified
- // token, and mark them as STATE_WAITING_IN_QUEUE_CANCELING. Note that we're marking
- // all of them, instead of just the first one, since the API surface currently doesn't
- // allow us to distinguish between multiple authentication requests from the same
- // process. However, this generally does not happen anyway, and would be a class of
- // bugs on its own.
- for (Operation operation : mPendingOperations) {
+ for (BiometricSchedulerOperation operation : mPendingOperations) {
if (canCancelAuthOperation(operation, token, requestId)) {
- Slog.d(getTag(), "Marking " + operation
- + " as STATE_WAITING_IN_QUEUE_CANCELING");
- operation.mState = Operation.STATE_WAITING_IN_QUEUE_CANCELING;
+ Slog.d(getTag(), "Cancelling pending auth/detect op: " + operation);
+ operation.markCanceling();
}
}
}
}
- private static boolean canCancelAuthOperation(Operation operation, IBinder token,
- long requestId) {
+ private static boolean canCancelEnrollOperation(BiometricSchedulerOperation operation,
+ IBinder token, long requestId) {
+ return operation.isEnrollOperation()
+ && operation.isMatchingToken(token)
+ && operation.isMatchingRequestId(requestId);
+ }
+
+ private static boolean canCancelAuthOperation(BiometricSchedulerOperation operation,
+ IBinder token, long requestId) {
// TODO: restrict callers that can cancel without requestId (negative value)?
- return isAuthenticationOrDetectionOperation(operation)
- && operation.mClientMonitor.getToken() == token
- && isMatchingRequestId(operation, requestId);
- }
-
- // By default, monitors are not associated with a request id to retain the original
- // behavior (i.e. if no requestId is explicitly set then assume it matches)
- private static boolean isMatchingRequestId(Operation operation, long requestId) {
- return !operation.mClientMonitor.hasRequestId()
- || operation.mClientMonitor.getRequestId() == requestId;
- }
-
- private static boolean isAuthenticationOrDetectionOperation(@NonNull Operation operation) {
- final boolean isAuthentication =
- operation.mClientMonitor instanceof AuthenticationConsumer;
- final boolean isDetection =
- operation.mClientMonitor instanceof DetectionConsumer;
- return isAuthentication || isDetection;
+ return operation.isAuthenticationOrDetectionOperation()
+ && operation.isMatchingToken(token)
+ && operation.isMatchingRequestId(requestId);
}
/**
* @return the current operation
*/
public BaseClientMonitor getCurrentClient() {
- if (mCurrentOperation == null) {
- return null;
- }
- return mCurrentOperation.mClientMonitor;
+ return mCurrentOperation != null ? mCurrentOperation.getClientMonitor() : null;
}
public int getCurrentPendingCount() {
@@ -719,7 +474,7 @@
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US);
final String timestamp = dateFormat.format(new Date(System.currentTimeMillis()));
final List<String> pendingOperations = new ArrayList<>();
- for (Operation operation : mPendingOperations) {
+ for (BiometricSchedulerOperation operation : mPendingOperations) {
pendingOperations.add(operation.toString());
}
@@ -735,7 +490,7 @@
pw.println("Type: " + mSensorType);
pw.println("Current operation: " + mCurrentOperation);
pw.println("Pending operations: " + mPendingOperations.size());
- for (Operation operation : mPendingOperations) {
+ for (BiometricSchedulerOperation operation : mPendingOperations) {
pw.println("Pending operation: " + operation);
}
for (CrashState crashState : mCrashStates) {
@@ -746,7 +501,7 @@
public byte[] dumpProtoState(boolean clearSchedulerBuffer) {
final ProtoOutputStream proto = new ProtoOutputStream();
proto.write(BiometricSchedulerProto.CURRENT_OPERATION, mCurrentOperation != null
- ? mCurrentOperation.mClientMonitor.getProtoEnum() : BiometricsProto.CM_NONE);
+ ? mCurrentOperation.getProtoEnum() : BiometricsProto.CM_NONE);
proto.write(BiometricSchedulerProto.TOTAL_OPERATIONS, mTotalOperationsHandled);
if (!mRecentOperations.isEmpty()) {
@@ -771,6 +526,7 @@
* HAL dies.
*/
public void reset() {
+ Slog.d(getTag(), "Resetting scheduler");
mPendingOperations.clear();
mCurrentOperation = null;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java b/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java
new file mode 100644
index 0000000..e8b50d9
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) 2021 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.biometrics.sensors;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.biometrics.BiometricConstants;
+import android.os.Handler;
+import android.os.IBinder;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Contains all the necessary information for a HAL operation.
+ */
+public class BiometricSchedulerOperation {
+ protected static final String TAG = "BiometricSchedulerOperation";
+
+ /**
+ * The operation is added to the list of pending operations and waiting for its turn.
+ */
+ protected static final int STATE_WAITING_IN_QUEUE = 0;
+
+ /**
+ * The operation is added to the list of pending operations, but a subsequent operation
+ * has been added. This state only applies to {@link Interruptable} operations. When this
+ * operation reaches the head of the queue, it will send ERROR_CANCELED and finish.
+ */
+ protected static final int STATE_WAITING_IN_QUEUE_CANCELING = 1;
+
+ /**
+ * The operation has reached the front of the queue and has started.
+ */
+ protected static final int STATE_STARTED = 2;
+
+ /**
+ * The operation was started, but is now canceling. Operations should wait for the HAL to
+ * acknowledge that the operation was canceled, at which point it finishes.
+ */
+ protected static final int STATE_STARTED_CANCELING = 3;
+
+ /**
+ * The operation has reached the head of the queue but is waiting for BiometricService
+ * to acknowledge and start the operation.
+ */
+ protected static final int STATE_WAITING_FOR_COOKIE = 4;
+
+ /**
+ * The {@link BaseClientMonitor.Callback} has been invoked and the client is finished.
+ */
+ protected static final int STATE_FINISHED = 5;
+
+ @IntDef({STATE_WAITING_IN_QUEUE,
+ STATE_WAITING_IN_QUEUE_CANCELING,
+ STATE_STARTED,
+ STATE_STARTED_CANCELING,
+ STATE_WAITING_FOR_COOKIE,
+ STATE_FINISHED})
+ @Retention(RetentionPolicy.SOURCE)
+ protected @interface OperationState {}
+
+ private static final int CANCEL_WATCHDOG_DELAY_MS = 3000;
+
+ @NonNull
+ private final BaseClientMonitor mClientMonitor;
+ @Nullable
+ private final BaseClientMonitor.Callback mClientCallback;
+ @OperationState
+ private int mState;
+ @VisibleForTesting
+ @NonNull
+ final Runnable mCancelWatchdog;
+
+ BiometricSchedulerOperation(
+ @NonNull BaseClientMonitor clientMonitor,
+ @Nullable BaseClientMonitor.Callback callback
+ ) {
+ this(clientMonitor, callback, STATE_WAITING_IN_QUEUE);
+ }
+
+ protected BiometricSchedulerOperation(
+ @NonNull BaseClientMonitor clientMonitor,
+ @Nullable BaseClientMonitor.Callback callback,
+ @OperationState int state
+ ) {
+ mClientMonitor = clientMonitor;
+ mClientCallback = callback;
+ mState = state;
+ mCancelWatchdog = () -> {
+ if (!isFinished()) {
+ Slog.e(TAG, "[Watchdog Triggered]: " + this);
+ getWrappedCallback().onClientFinished(mClientMonitor, false /* success */);
+ }
+ };
+ }
+
+ /**
+ * Zero if this operation is ready to start or has already started. A non-zero cookie
+ * is returned if the operation has not started and is waiting on
+ * {@link android.hardware.biometrics.IBiometricService#onReadyForAuthentication(int)}.
+ *
+ * @return cookie or 0 if ready/started
+ */
+ public int isReadyToStart() {
+ if (mState == STATE_WAITING_FOR_COOKIE || mState == STATE_WAITING_IN_QUEUE) {
+ final int cookie = mClientMonitor.getCookie();
+ if (cookie != 0) {
+ mState = STATE_WAITING_FOR_COOKIE;
+ }
+ return cookie;
+ }
+
+ return 0;
+ }
+
+ /**
+ * Start this operation without waiting for a cookie
+ * (i.e. {@link #isReadyToStart() returns zero}
+ *
+ * @param callback lifecycle callback
+ * @return if this operation started
+ */
+ public boolean start(@NonNull BaseClientMonitor.Callback callback) {
+ checkInState("start",
+ STATE_WAITING_IN_QUEUE,
+ STATE_WAITING_FOR_COOKIE,
+ STATE_WAITING_IN_QUEUE_CANCELING);
+
+ if (mClientMonitor.getCookie() != 0) {
+ throw new IllegalStateException("operation requires cookie");
+ }
+
+ return doStart(callback);
+ }
+
+ /**
+ * Start this operation after receiving the given cookie.
+ *
+ * @param callback lifecycle callback
+ * @param cookie cookie indicting the operation should begin
+ * @return if this operation started
+ */
+ public boolean startWithCookie(@NonNull BaseClientMonitor.Callback callback, int cookie) {
+ checkInState("start",
+ STATE_WAITING_IN_QUEUE,
+ STATE_WAITING_FOR_COOKIE,
+ STATE_WAITING_IN_QUEUE_CANCELING);
+
+ if (mClientMonitor.getCookie() != cookie) {
+ Slog.e(TAG, "Mismatched cookie for operation: " + this + ", received: " + cookie);
+ return false;
+ }
+
+ return doStart(callback);
+ }
+
+ private boolean doStart(@NonNull BaseClientMonitor.Callback callback) {
+ final BaseClientMonitor.Callback cb = getWrappedCallback(callback);
+
+ if (mState == STATE_WAITING_IN_QUEUE_CANCELING) {
+ Slog.d(TAG, "Operation marked for cancellation, cancelling now: " + this);
+
+ cb.onClientFinished(mClientMonitor, true /* success */);
+ if (mClientMonitor instanceof ErrorConsumer) {
+ final ErrorConsumer errorConsumer = (ErrorConsumer) mClientMonitor;
+ errorConsumer.onError(BiometricConstants.BIOMETRIC_ERROR_CANCELED,
+ 0 /* vendorCode */);
+ } else {
+ Slog.w(TAG, "monitor cancelled but does not implement ErrorConsumer");
+ }
+
+ return false;
+ }
+
+ if (isUnstartableHalOperation()) {
+ Slog.v(TAG, "unable to start: " + this);
+ ((HalClientMonitor<?>) mClientMonitor).unableToStart();
+ cb.onClientFinished(mClientMonitor, false /* success */);
+ return false;
+ }
+
+ mState = STATE_STARTED;
+ mClientMonitor.start(cb);
+
+ Slog.v(TAG, "started: " + this);
+ return true;
+ }
+
+ /**
+ * Abort a pending operation.
+ *
+ * This is similar to cancel but the operation must not have been started. It will
+ * immediately abort the operation and notify the client that it has finished unsuccessfully.
+ */
+ public void abort() {
+ checkInState("cannot abort a non-pending operation",
+ STATE_WAITING_IN_QUEUE,
+ STATE_WAITING_FOR_COOKIE,
+ STATE_WAITING_IN_QUEUE_CANCELING);
+
+ if (isHalOperation()) {
+ ((HalClientMonitor<?>) mClientMonitor).unableToStart();
+ }
+ getWrappedCallback().onClientFinished(mClientMonitor, false /* success */);
+
+ Slog.v(TAG, "Aborted: " + this);
+ }
+
+ /** Flags this operation as canceled, if possible, but does not cancel it until started. */
+ public boolean markCanceling() {
+ if (mState == STATE_WAITING_IN_QUEUE && isInterruptable()) {
+ mState = STATE_WAITING_IN_QUEUE_CANCELING;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Cancel the operation now.
+ *
+ * @param handler handler to use for the cancellation watchdog
+ * @param callback lifecycle callback (only used if this operation hasn't started, otherwise
+ * the callback used from {@link #start(BaseClientMonitor.Callback)} is used)
+ */
+ public void cancel(@NonNull Handler handler, @NonNull BaseClientMonitor.Callback callback) {
+ checkNotInState("cancel", STATE_FINISHED);
+
+ final int currentState = mState;
+ if (!isInterruptable()) {
+ Slog.w(TAG, "Cannot cancel - operation not interruptable: " + this);
+ return;
+ }
+ if (currentState == STATE_STARTED_CANCELING) {
+ Slog.w(TAG, "Cannot cancel - already invoked for operation: " + this);
+ return;
+ }
+
+ mState = STATE_STARTED_CANCELING;
+ if (currentState == STATE_WAITING_IN_QUEUE
+ || currentState == STATE_WAITING_IN_QUEUE_CANCELING
+ || currentState == STATE_WAITING_FOR_COOKIE) {
+ Slog.d(TAG, "[Cancelling] Current client (without start): " + mClientMonitor);
+ ((Interruptable) mClientMonitor).cancelWithoutStarting(getWrappedCallback(callback));
+ } else {
+ Slog.d(TAG, "[Cancelling] Current client: " + mClientMonitor);
+ ((Interruptable) mClientMonitor).cancel();
+ }
+
+ // forcibly finish this client if the HAL does not acknowledge within the timeout
+ handler.postDelayed(mCancelWatchdog, CANCEL_WATCHDOG_DELAY_MS);
+ }
+
+ @NonNull
+ private BaseClientMonitor.Callback getWrappedCallback() {
+ return getWrappedCallback(null);
+ }
+
+ @NonNull
+ private BaseClientMonitor.Callback getWrappedCallback(
+ @Nullable BaseClientMonitor.Callback callback) {
+ final BaseClientMonitor.Callback destroyCallback = new BaseClientMonitor.Callback() {
+ @Override
+ public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
+ boolean success) {
+ Slog.d(TAG, "[Finished / destroy]: " + clientMonitor);
+ mClientMonitor.destroy();
+ mState = STATE_FINISHED;
+ }
+ };
+ return new BaseClientMonitor.CompositeCallback(destroyCallback, callback, mClientCallback);
+ }
+
+ /** {@link BaseClientMonitor#getSensorId()}. */
+ public int getSensorId() {
+ return mClientMonitor.getSensorId();
+ }
+
+ /** {@link BaseClientMonitor#getProtoEnum()}. */
+ public int getProtoEnum() {
+ return mClientMonitor.getProtoEnum();
+ }
+
+ /** {@link BaseClientMonitor#getTargetUserId()}. */
+ public int getTargetUserId() {
+ return mClientMonitor.getTargetUserId();
+ }
+
+ /** If the given clientMonitor is the same as the one in the constructor. */
+ public boolean isFor(@NonNull BaseClientMonitor clientMonitor) {
+ return mClientMonitor == clientMonitor;
+ }
+
+ /** If this operation is {@link Interruptable}. */
+ public boolean isInterruptable() {
+ return mClientMonitor instanceof Interruptable;
+ }
+
+ private boolean isHalOperation() {
+ return mClientMonitor instanceof HalClientMonitor<?>;
+ }
+
+ private boolean isUnstartableHalOperation() {
+ if (isHalOperation()) {
+ final HalClientMonitor<?> client = (HalClientMonitor<?>) mClientMonitor;
+ if (client.getFreshDaemon() == null) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** If this operation is an enrollment. */
+ public boolean isEnrollOperation() {
+ return mClientMonitor instanceof EnrollClient;
+ }
+
+ /** If this operation is authentication. */
+ public boolean isAuthenticateOperation() {
+ return mClientMonitor instanceof AuthenticationClient;
+ }
+
+ /** If this operation is authentication or detection. */
+ public boolean isAuthenticationOrDetectionOperation() {
+ final boolean isAuthentication = mClientMonitor instanceof AuthenticationConsumer;
+ final boolean isDetection = mClientMonitor instanceof DetectionConsumer;
+ return isAuthentication || isDetection;
+ }
+
+ /** If this operation performs acquisition {@link AcquisitionClient}. */
+ public boolean isAcquisitionOperation() {
+ return mClientMonitor instanceof AcquisitionClient;
+ }
+
+ /**
+ * If this operation matches the original requestId.
+ *
+ * By default, monitors are not associated with a request id to retain the original
+ * behavior (i.e. if no requestId is explicitly set then assume it matches)
+ *
+ * @param requestId a unique id {@link BaseClientMonitor#setRequestId(long)}.
+ */
+ public boolean isMatchingRequestId(long requestId) {
+ return !mClientMonitor.hasRequestId()
+ || mClientMonitor.getRequestId() == requestId;
+ }
+
+ /** If the token matches */
+ public boolean isMatchingToken(@Nullable IBinder token) {
+ return mClientMonitor.getToken() == token;
+ }
+
+ /** If this operation has started. */
+ public boolean isStarted() {
+ return mState == STATE_STARTED;
+ }
+
+ /** If this operation is cancelling but has not yet completed. */
+ public boolean isCanceling() {
+ return mState == STATE_STARTED_CANCELING;
+ }
+
+ /** If this operation has finished and completed its lifecycle. */
+ public boolean isFinished() {
+ return mState == STATE_FINISHED;
+ }
+
+ /** If {@link #markCanceling()} was called but the operation hasn't been canceled. */
+ public boolean isMarkedCanceling() {
+ return mState == STATE_WAITING_IN_QUEUE_CANCELING;
+ }
+
+ /**
+ * The monitor passed to the constructor.
+ * @deprecated avoid using and move to encapsulate within the operation
+ */
+ @Deprecated
+ public BaseClientMonitor getClientMonitor() {
+ return mClientMonitor;
+ }
+
+ private void checkNotInState(String message, @OperationState int... states) {
+ for (int state : states) {
+ if (mState == state) {
+ throw new IllegalStateException(message + ": illegal state= " + state);
+ }
+ }
+ }
+
+ private void checkInState(String message, @OperationState int... states) {
+ for (int state : states) {
+ if (mState == state) {
+ return;
+ }
+ }
+ throw new IllegalStateException(message + ": illegal state= " + mState);
+ }
+
+ @Override
+ public String toString() {
+ return mClientMonitor + ", State: " + mState;
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/Interruptable.java b/services/core/java/com/android/server/biometrics/sensors/Interruptable.java
index fab98b6..d5093c75 100644
--- a/services/core/java/com/android/server/biometrics/sensors/Interruptable.java
+++ b/services/core/java/com/android/server/biometrics/sensors/Interruptable.java
@@ -32,6 +32,11 @@
* {@link BaseClientMonitor#start(BaseClientMonitor.Callback)} was invoked. This usually happens
* if the client is still waiting in the pending queue and got notified that a subsequent
* operation is preempting it.
+ *
+ * This method must invoke
+ * {@link BaseClientMonitor.Callback#onClientFinished(BaseClientMonitor, boolean)} on the
+ * given callback (with success).
+ *
* @param callback invoked when the operation is completed.
*/
void cancelWithoutStarting(@NonNull BaseClientMonitor.Callback callback);
diff --git a/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
index b056bf8..603cc22 100644
--- a/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
@@ -16,10 +16,14 @@
package com.android.server.biometrics.sensors;
+import static com.android.server.biometrics.sensors.BiometricSchedulerOperation.STATE_STARTED;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.biometrics.IBiometricService;
+import android.os.Handler;
+import android.os.Looper;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.Slog;
@@ -68,9 +72,8 @@
return;
}
- Slog.d(getTag(), "[Client finished] "
- + clientMonitor + ", success: " + success);
- if (mCurrentOperation != null && mCurrentOperation.mClientMonitor == mOwner) {
+ Slog.d(getTag(), "[Client finished] " + clientMonitor + ", success: " + success);
+ if (mCurrentOperation != null && mCurrentOperation.isFor(mOwner)) {
mCurrentOperation = null;
startNextOperationIfIdle();
} else {
@@ -83,26 +86,30 @@
}
@VisibleForTesting
- UserAwareBiometricScheduler(@NonNull String tag, @SensorType int sensorType,
+ public UserAwareBiometricScheduler(@NonNull String tag,
+ @NonNull Handler handler,
+ @SensorType int sensorType,
@Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
@NonNull IBiometricService biometricService,
@NonNull CurrentUserRetriever currentUserRetriever,
@NonNull UserSwitchCallback userSwitchCallback,
@NonNull CoexCoordinator coexCoordinator) {
- super(tag, sensorType, gestureAvailabilityDispatcher, biometricService,
+ super(tag, handler, sensorType, gestureAvailabilityDispatcher, biometricService,
LOG_NUM_RECENT_OPERATIONS, coexCoordinator);
mCurrentUserRetriever = currentUserRetriever;
mUserSwitchCallback = userSwitchCallback;
}
- public UserAwareBiometricScheduler(@NonNull String tag, @SensorType int sensorType,
+ public UserAwareBiometricScheduler(@NonNull String tag,
+ @SensorType int sensorType,
@Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
@NonNull CurrentUserRetriever currentUserRetriever,
@NonNull UserSwitchCallback userSwitchCallback) {
- this(tag, sensorType, gestureAvailabilityDispatcher, IBiometricService.Stub.asInterface(
- ServiceManager.getService(Context.BIOMETRIC_SERVICE)), currentUserRetriever,
- userSwitchCallback, CoexCoordinator.getInstance());
+ this(tag, new Handler(Looper.getMainLooper()), sensorType, gestureAvailabilityDispatcher,
+ IBiometricService.Stub.asInterface(
+ ServiceManager.getService(Context.BIOMETRIC_SERVICE)),
+ currentUserRetriever, userSwitchCallback, CoexCoordinator.getInstance());
}
@Override
@@ -122,7 +129,7 @@
}
final int currentUserId = mCurrentUserRetriever.getCurrentUserId();
- final int nextUserId = mPendingOperations.getFirst().mClientMonitor.getTargetUserId();
+ final int nextUserId = mPendingOperations.getFirst().getTargetUserId();
if (nextUserId == currentUserId) {
super.startNextOperationIfIdle();
@@ -133,8 +140,8 @@
new ClientFinishedCallback(startClient);
Slog.d(getTag(), "[Starting User] " + startClient);
- mCurrentOperation = new Operation(
- startClient, finishedCallback, Operation.STATE_STARTED);
+ mCurrentOperation = new BiometricSchedulerOperation(
+ startClient, finishedCallback, STATE_STARTED);
startClient.start(finishedCallback);
} else {
if (mStopUserClient != null) {
@@ -147,8 +154,8 @@
Slog.d(getTag(), "[Stopping User] current: " + currentUserId
+ ", next: " + nextUserId + ". " + mStopUserClient);
- mCurrentOperation = new Operation(
- mStopUserClient, finishedCallback, Operation.STATE_STARTED);
+ mCurrentOperation = new BiometricSchedulerOperation(
+ mStopUserClient, finishedCallback, STATE_STARTED);
mStopUserClient.start(finishedCallback);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index 675ee545..039b08e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -213,7 +213,7 @@
}
@Override // Binder call
- public void enroll(int userId, final IBinder token, final byte[] hardwareAuthToken,
+ public long enroll(int userId, final IBinder token, final byte[] hardwareAuthToken,
final IFaceServiceReceiver receiver, final String opPackageName,
final int[] disabledFeatures, Surface previewSurface, boolean debugConsent) {
Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
@@ -221,23 +221,24 @@
final Pair<Integer, ServiceProvider> provider = getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for enroll");
- return;
+ return -1;
}
- provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId,
+ return provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId,
receiver, opPackageName, disabledFeatures, previewSurface, debugConsent);
}
@Override // Binder call
- public void enrollRemotely(int userId, final IBinder token, final byte[] hardwareAuthToken,
+ public long enrollRemotely(int userId, final IBinder token, final byte[] hardwareAuthToken,
final IFaceServiceReceiver receiver, final String opPackageName,
final int[] disabledFeatures) {
Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
// TODO(b/145027036): Implement this.
+ return -1;
}
@Override // Binder call
- public void cancelEnrollment(final IBinder token) {
+ public void cancelEnrollment(final IBinder token, long requestId) {
Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
final Pair<Integer, ServiceProvider> provider = getSingleProvider();
@@ -246,7 +247,7 @@
return;
}
- provider.second.cancelEnrollment(provider.first, token);
+ provider.second.cancelEnrollment(provider.first, token, requestId);
}
@Override // Binder call
@@ -624,7 +625,7 @@
private void addHidlProviders(@NonNull List<FaceSensorPropertiesInternal> hidlSensors) {
for (FaceSensorPropertiesInternal hidlSensor : hidlSensors) {
mServiceProviders.add(
- new Face10(getContext(), hidlSensor, mLockoutResetDispatcher));
+ Face10.newInstance(getContext(), hidlSensor, mLockoutResetDispatcher));
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
index e099ba3..77e431c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
@@ -94,12 +94,12 @@
void scheduleRevokeChallenge(int sensorId, int userId, @NonNull IBinder token,
@NonNull String opPackageName, long challenge);
- void scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken,
+ long scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken,
int userId, @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName,
@NonNull int[] disabledFeatures, @Nullable Surface previewSurface,
boolean debugConsent);
- void cancelEnrollment(int sensorId, @NonNull IBinder token);
+ void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId);
long scheduleFaceDetect(int sensorId, @NonNull IBinder token, int userId,
@NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName,
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
index a806277..aae4fbe 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
@@ -82,13 +82,14 @@
FaceEnrollClient(@NonNull Context context, @NonNull LazyDaemon<ISession> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
- @NonNull byte[] hardwareAuthToken, @NonNull String opPackageName,
+ @NonNull byte[] hardwareAuthToken, @NonNull String opPackageName, long requestId,
@NonNull BiometricUtils<Face> utils, @NonNull int[] disabledFeatures, int timeoutSec,
@Nullable Surface previewSurface, int sensorId, int maxTemplatesPerUser,
boolean debugConsent) {
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, opPackageName, utils,
timeoutSec, BiometricsProtoEnums.MODALITY_FACE, sensorId,
false /* shouldVibrate */);
+ setRequestId(requestId);
mEnrollIgnoreList = getContext().getResources()
.getIntArray(R.array.config_face_acquire_enroll_ignorelist);
mEnrollIgnoreListVendor = getContext().getResources()
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index 4bae775..ae507ab 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -327,17 +327,18 @@
}
@Override
- public void scheduleEnroll(int sensorId, @NonNull IBinder token,
+ public long scheduleEnroll(int sensorId, @NonNull IBinder token,
@NonNull byte[] hardwareAuthToken, int userId, @NonNull IFaceServiceReceiver receiver,
@NonNull String opPackageName, @NonNull int[] disabledFeatures,
@Nullable Surface previewSurface, boolean debugConsent) {
+ final long id = mRequestCounter.incrementAndGet();
mHandler.post(() -> {
final int maxTemplatesPerUser = mSensors.get(
sensorId).getSensorProperties().maxEnrollmentsPerUser;
final FaceEnrollClient client = new FaceEnrollClient(mContext,
mSensors.get(sensorId).getLazySession(), token,
new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
- opPackageName, FaceUtils.getInstance(sensorId), disabledFeatures,
+ opPackageName, id, FaceUtils.getInstance(sensorId), disabledFeatures,
ENROLL_TIMEOUT_SEC, previewSurface, sensorId, maxTemplatesPerUser,
debugConsent);
scheduleForSensor(sensorId, client, new BaseClientMonitor.Callback() {
@@ -351,11 +352,13 @@
}
});
});
+ return id;
}
@Override
- public void cancelEnrollment(int sensorId, @NonNull IBinder token) {
- mHandler.post(() -> mSensors.get(sensorId).getScheduler().cancelEnrollment(token));
+ public void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId) {
+ mHandler.post(() ->
+ mSensors.get(sensorId).getScheduler().cancelEnrollment(token, requestId));
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java
index 525e508..15d6a89 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java
@@ -17,6 +17,7 @@
package com.android.server.biometrics.sensors.face.aidl;
import android.hardware.biometrics.common.ICancellationSignal;
+import android.hardware.biometrics.common.OperationContext;
import android.hardware.biometrics.face.EnrollmentStageConfig;
import android.hardware.biometrics.face.Error;
import android.hardware.biometrics.face.IFace;
@@ -188,6 +189,24 @@
Slog.w(TAG, "close");
cb.onSessionClosed();
}
+
+ @Override
+ public ICancellationSignal authenticateWithContext(
+ long operationId, OperationContext context) {
+ return authenticate(operationId);
+ }
+
+ @Override
+ public ICancellationSignal enrollWithContext(
+ HardwareAuthToken hat, byte enrollmentType, byte[] features,
+ NativeHandle previewSurface, OperationContext context) {
+ return enroll(hat, enrollmentType, features, previewSurface);
+ }
+
+ @Override
+ public ICancellationSignal detectInteractionWithContext(OperationContext context) {
+ return detectInteraction();
+ }
};
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index f4dcbbb..e957794 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -333,12 +333,13 @@
Face10(@NonNull Context context,
@NonNull FaceSensorPropertiesInternal sensorProps,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull Handler handler,
@NonNull BiometricScheduler scheduler) {
mSensorProperties = sensorProps;
mContext = context;
mSensorId = sensorProps.sensorId;
mScheduler = scheduler;
- mHandler = new Handler(Looper.getMainLooper());
+ mHandler = handler;
mUsageStats = new UsageStats(context);
mAuthenticatorIds = new HashMap<>();
mLazyDaemon = Face10.this::getDaemon;
@@ -357,9 +358,11 @@
}
}
- public Face10(@NonNull Context context, @NonNull FaceSensorPropertiesInternal sensorProps,
+ public static Face10 newInstance(@NonNull Context context,
+ @NonNull FaceSensorPropertiesInternal sensorProps,
@NonNull LockoutResetDispatcher lockoutResetDispatcher) {
- this(context, sensorProps, lockoutResetDispatcher,
+ final Handler handler = new Handler(Looper.getMainLooper());
+ return new Face10(context, sensorProps, lockoutResetDispatcher, handler,
new BiometricScheduler(TAG, BiometricScheduler.SENSOR_TYPE_FACE,
null /* gestureAvailabilityTracker */));
}
@@ -573,10 +576,11 @@
}
@Override
- public void scheduleEnroll(int sensorId, @NonNull IBinder token,
+ public long scheduleEnroll(int sensorId, @NonNull IBinder token,
@NonNull byte[] hardwareAuthToken, int userId, @NonNull IFaceServiceReceiver receiver,
@NonNull String opPackageName, @NonNull int[] disabledFeatures,
@Nullable Surface previewSurface, boolean debugConsent) {
+ final long id = mRequestCounter.incrementAndGet();
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
@@ -584,7 +588,7 @@
final FaceEnrollClient client = new FaceEnrollClient(mContext, mLazyDaemon, token,
new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
- opPackageName, FaceUtils.getLegacyInstance(mSensorId), disabledFeatures,
+ opPackageName, id, FaceUtils.getLegacyInstance(mSensorId), disabledFeatures,
ENROLL_TIMEOUT_SEC, previewSurface, mSensorId);
mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
@@ -598,13 +602,12 @@
}
});
});
+ return id;
}
@Override
- public void cancelEnrollment(int sensorId, @NonNull IBinder token) {
- mHandler.post(() -> {
- mScheduler.cancelEnrollment(token);
- });
+ public void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId) {
+ mHandler.post(() -> mScheduler.cancelEnrollment(token, requestId));
}
@Override
@@ -893,6 +896,8 @@
boolean success) {
if (success) {
mCurrentUserId = targetUserId;
+ } else {
+ Slog.w(TAG, "Failed to change user, still: " + mCurrentUserId);
}
}
});
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
index 80828cced..31e5c86 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
@@ -53,12 +53,13 @@
FaceEnrollClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFace> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
- @NonNull byte[] hardwareAuthToken, @NonNull String owner,
+ @NonNull byte[] hardwareAuthToken, @NonNull String owner, long requestId,
@NonNull BiometricUtils<Face> utils, @NonNull int[] disabledFeatures, int timeoutSec,
@Nullable Surface previewSurface, int sensorId) {
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
timeoutSec, BiometricsProtoEnums.MODALITY_FACE, sensorId,
false /* shouldVibrate */);
+ setRequestId(requestId);
mDisabledFeatures = Arrays.copyOf(disabledFeatures, disabledFeatures.length);
mEnrollIgnoreList = getContext().getResources()
.getIntArray(R.array.config_face_acquire_enroll_ignorelist);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index 3e70ee5..6366e19 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -249,7 +249,7 @@
}
@Override // Binder call
- public void enroll(final IBinder token, @NonNull final byte[] hardwareAuthToken,
+ public long enroll(final IBinder token, @NonNull final byte[] hardwareAuthToken,
final int userId, final IFingerprintServiceReceiver receiver,
final String opPackageName, @FingerprintManager.EnrollReason int enrollReason) {
Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
@@ -257,15 +257,15 @@
final Pair<Integer, ServiceProvider> provider = getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for enroll");
- return;
+ return -1;
}
- provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId,
+ return provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId,
receiver, opPackageName, enrollReason);
}
@Override // Binder call
- public void cancelEnrollment(final IBinder token) {
+ public void cancelEnrollment(final IBinder token, long requestId) {
Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
final Pair<Integer, ServiceProvider> provider = getSingleProvider();
@@ -274,7 +274,7 @@
return;
}
- provider.second.cancelEnrollment(provider.first, token);
+ provider.second.cancelEnrollment(provider.first, token, requestId);
}
@SuppressWarnings("deprecation")
@@ -818,7 +818,7 @@
mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
} else {
fingerprint21 = Fingerprint21.newInstance(getContext(),
- mFingerprintStateCallback, hidlSensor,
+ mFingerprintStateCallback, hidlSensor, mHandler,
mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
}
mServiceProviders.add(fingerprint21);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
index 1772f81..535705c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
@@ -88,11 +88,11 @@
/**
* Schedules fingerprint enrollment.
*/
- void scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken,
+ long scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken,
int userId, @NonNull IFingerprintServiceReceiver receiver,
@NonNull String opPackageName, @FingerprintManager.EnrollReason int enrollReason);
- void cancelEnrollment(int sensorId, @NonNull IBinder token);
+ void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId);
long scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
@NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index ccb34aa..67507cc 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -57,7 +57,7 @@
private boolean mIsPointerDown;
FingerprintEnrollClient(@NonNull Context context,
- @NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token,
+ @NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token, long requestId,
@NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull byte[] hardwareAuthToken, @NonNull String owner,
@NonNull BiometricUtils<Fingerprint> utils, int sensorId,
@@ -69,6 +69,7 @@
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
0 /* timeoutSec */, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId,
!sensorProps.isAnyUdfpsType() /* shouldVibrate */);
+ setRequestId(requestId);
mSensorProps = sensorProps;
mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
mMaxTemplatesPerUser = maxTemplatesPerUser;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index 734b173..eb16c76 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -347,15 +347,16 @@
}
@Override
- public void scheduleEnroll(int sensorId, @NonNull IBinder token,
+ public long scheduleEnroll(int sensorId, @NonNull IBinder token,
@NonNull byte[] hardwareAuthToken, int userId,
@NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
@FingerprintManager.EnrollReason int enrollReason) {
+ final long id = mRequestCounter.incrementAndGet();
mHandler.post(() -> {
final int maxTemplatesPerUser = mSensors.get(sensorId).getSensorProperties()
.maxEnrollmentsPerUser;
final FingerprintEnrollClient client = new FingerprintEnrollClient(mContext,
- mSensors.get(sensorId).getLazySession(), token,
+ mSensors.get(sensorId).getLazySession(), token, id,
new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
opPackageName, FingerprintUtils.getInstance(sensorId), sensorId,
mSensors.get(sensorId).getSensorProperties(),
@@ -378,11 +379,13 @@
}
});
});
+ return id;
}
@Override
- public void cancelEnrollment(int sensorId, @NonNull IBinder token) {
- mHandler.post(() -> mSensors.get(sensorId).getScheduler().cancelEnrollment(token));
+ public void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId) {
+ mHandler.post(() ->
+ mSensors.get(sensorId).getScheduler().cancelEnrollment(token, requestId));
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java
index e771923..1eb153c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java
@@ -17,10 +17,12 @@
package com.android.server.biometrics.sensors.fingerprint.aidl;
import android.hardware.biometrics.common.ICancellationSignal;
+import android.hardware.biometrics.common.OperationContext;
import android.hardware.biometrics.fingerprint.Error;
import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.biometrics.fingerprint.ISession;
import android.hardware.biometrics.fingerprint.ISessionCallback;
+import android.hardware.biometrics.fingerprint.PointerContext;
import android.hardware.biometrics.fingerprint.SensorProps;
import android.hardware.keymaster.HardwareAuthToken;
import android.os.RemoteException;
@@ -182,6 +184,34 @@
public void onUiReady() {
Slog.w(TAG, "onUiReady");
}
+
+ @Override
+ public ICancellationSignal authenticateWithContext(
+ long operationId, OperationContext context) {
+ return authenticate(operationId);
+ }
+
+ @Override
+ public ICancellationSignal enrollWithContext(
+ HardwareAuthToken hat, OperationContext context) {
+ return enroll(hat);
+ }
+
+ @Override
+ public ICancellationSignal detectInteractionWithContext(OperationContext context) {
+ return detectInteraction();
+ }
+
+ @Override
+ public void onPointerDownWithContext(PointerContext context) {
+ onPointerDown(
+ context.pointerId, context.x, context.y, context.minor, context.major);
+ }
+
+ @Override
+ public void onPointerUpWithContext(PointerContext context) {
+ onPointerUp(context.pointerId);
+ }
};
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index 5f2f4cf..6feb5fa 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -42,7 +42,6 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.IHwBinder;
-import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
@@ -320,7 +319,8 @@
Fingerprint21(@NonNull Context context,
@NonNull FingerprintStateCallback fingerprintStateCallback,
@NonNull FingerprintSensorPropertiesInternal sensorProps,
- @NonNull BiometricScheduler scheduler, @NonNull Handler handler,
+ @NonNull BiometricScheduler scheduler,
+ @NonNull Handler handler,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull HalResultController controller) {
mContext = context;
@@ -356,16 +356,15 @@
public static Fingerprint21 newInstance(@NonNull Context context,
@NonNull FingerprintStateCallback fingerprintStateCallback,
@NonNull FingerprintSensorPropertiesInternal sensorProps,
+ @NonNull Handler handler,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
- final Handler handler = new Handler(Looper.getMainLooper());
final BiometricScheduler scheduler =
new BiometricScheduler(TAG,
BiometricScheduler.sensorTypeFromFingerprintProperties(sensorProps),
gestureAvailabilityDispatcher);
final HalResultController controller = new HalResultController(sensorProps.sensorId,
- context, handler,
- scheduler);
+ context, handler, scheduler);
return new Fingerprint21(context, fingerprintStateCallback, sensorProps, scheduler, handler,
lockoutResetDispatcher, controller);
}
@@ -491,19 +490,25 @@
!getEnrolledFingerprints(mSensorProperties.sensorId, targetUserId).isEmpty();
final FingerprintUpdateActiveUserClient client =
new FingerprintUpdateActiveUserClient(mContext, mLazyDaemon, targetUserId,
- mContext.getOpPackageName(), mSensorProperties.sensorId, mCurrentUserId,
- hasEnrolled, mAuthenticatorIds, force);
+ mContext.getOpPackageName(), mSensorProperties.sensorId,
+ this::getCurrentUser, hasEnrolled, mAuthenticatorIds, force);
mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
@Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
boolean success) {
if (success) {
mCurrentUserId = targetUserId;
+ } else {
+ Slog.w(TAG, "Failed to change user, still: " + mCurrentUserId);
}
}
});
}
+ private int getCurrentUser() {
+ return mCurrentUserId;
+ }
+
@Override
public boolean containsSensor(int sensorId) {
return mSensorProperties.sensorId == sensorId;
@@ -558,18 +563,20 @@
}
@Override
- public void scheduleEnroll(int sensorId, @NonNull IBinder token,
+ public long scheduleEnroll(int sensorId, @NonNull IBinder token,
@NonNull byte[] hardwareAuthToken, int userId,
@NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
@FingerprintManager.EnrollReason int enrollReason) {
+ final long id = mRequestCounter.incrementAndGet();
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
final FingerprintEnrollClient client = new FingerprintEnrollClient(mContext,
- mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
- hardwareAuthToken, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId),
- ENROLL_TIMEOUT_SEC, mSensorProperties.sensorId, mUdfpsOverlayController,
- mSidefpsController, enrollReason);
+ mLazyDaemon, token, id, new ClientMonitorCallbackConverter(receiver),
+ userId, hardwareAuthToken, opPackageName,
+ FingerprintUtils.getLegacyInstance(mSensorId), ENROLL_TIMEOUT_SEC,
+ mSensorProperties.sensorId, mUdfpsOverlayController, mSidefpsController,
+ enrollReason);
mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
@Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
@@ -588,13 +595,12 @@
}
});
});
+ return id;
}
@Override
- public void cancelEnrollment(int sensorId, @NonNull IBinder token) {
- mHandler.post(() -> {
- mScheduler.cancelEnrollment(token);
- });
+ public void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId) {
+ mHandler.post(() -> mScheduler.cancelEnrollment(token, requestId));
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
index dd68b4d..273f8a5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
@@ -26,7 +26,6 @@
import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
-import android.hardware.fingerprint.FingerprintStateListener;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.Handler;
import android.os.IBinder;
@@ -135,43 +134,16 @@
@NonNull private final RestartAuthRunnable mRestartAuthRunnable;
private static class TestableBiometricScheduler extends BiometricScheduler {
- @NonNull private final TestableInternalCallback mInternalCallback;
@NonNull private Fingerprint21UdfpsMock mFingerprint21;
- TestableBiometricScheduler(@NonNull String tag,
+ TestableBiometricScheduler(@NonNull String tag, @NonNull Handler handler,
@Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
- super(tag, BiometricScheduler.SENSOR_TYPE_FP_OTHER,
- gestureAvailabilityDispatcher);
- mInternalCallback = new TestableInternalCallback();
- }
-
- class TestableInternalCallback extends InternalCallback {
- @Override
- public void onClientStarted(BaseClientMonitor clientMonitor) {
- super.onClientStarted(clientMonitor);
- Slog.d(TAG, "Client started: " + clientMonitor);
- mFingerprint21.setDebugMessage("Started: " + clientMonitor);
- }
-
- @Override
- public void onClientFinished(BaseClientMonitor clientMonitor, boolean success) {
- super.onClientFinished(clientMonitor, success);
- Slog.d(TAG, "Client finished: " + clientMonitor);
- mFingerprint21.setDebugMessage("Finished: " + clientMonitor);
- }
+ super(tag, BiometricScheduler.SENSOR_TYPE_FP_OTHER, gestureAvailabilityDispatcher);
}
void init(@NonNull Fingerprint21UdfpsMock fingerprint21) {
mFingerprint21 = fingerprint21;
}
-
- /**
- * Expose the internal finish callback so it can be used for testing
- */
- @Override
- @NonNull protected InternalCallback getInternalCallback() {
- return mInternalCallback;
- }
}
/**
@@ -280,7 +252,7 @@
final Handler handler = new Handler(Looper.getMainLooper());
final TestableBiometricScheduler scheduler =
- new TestableBiometricScheduler(TAG, gestureAvailabilityDispatcher);
+ new TestableBiometricScheduler(TAG, handler, gestureAvailabilityDispatcher);
final MockHalResultController controller =
new MockHalResultController(sensorProps.sensorId, context, handler, scheduler);
return new Fingerprint21UdfpsMock(context, fingerprintStateCallback, sensorProps, scheduler,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
index 1ebf44c..cc50bdf 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
@@ -55,7 +55,7 @@
FingerprintEnrollClient(@NonNull Context context,
@NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
- @NonNull ClientMonitorCallbackConverter listener, int userId,
+ long requestId, @NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull byte[] hardwareAuthToken, @NonNull String owner,
@NonNull BiometricUtils<Fingerprint> utils, int timeoutSec, int sensorId,
@Nullable IUdfpsOverlayController udfpsOverlayController,
@@ -64,6 +64,7 @@
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
timeoutSec, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId,
true /* shouldVibrate */);
+ setRequestId(requestId);
mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
mEnrollReason = enrollReason;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
index fd38bdd..a2c1892 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
@@ -31,6 +31,7 @@
import java.io.File;
import java.util.Map;
+import java.util.function.Supplier;
/**
* Sets the HAL's current active user, and updates the framework's authenticatorId cache.
@@ -40,7 +41,7 @@
private static final String TAG = "FingerprintUpdateActiveUserClient";
private static final String FP_DATA_DIR = "fpdata";
- private final int mCurrentUserId;
+ private final Supplier<Integer> mCurrentUserId;
private final boolean mForceUpdateAuthenticatorId;
private final boolean mHasEnrolledBiometrics;
private final Map<Integer, Long> mAuthenticatorIds;
@@ -48,8 +49,9 @@
FingerprintUpdateActiveUserClient(@NonNull Context context,
@NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, int userId,
- @NonNull String owner, int sensorId, int currentUserId, boolean hasEnrolledBiometrics,
- @NonNull Map<Integer, Long> authenticatorIds, boolean forceUpdateAuthenticatorId) {
+ @NonNull String owner, int sensorId, Supplier<Integer> currentUserId,
+ boolean hasEnrolledBiometrics, @NonNull Map<Integer, Long> authenticatorIds,
+ boolean forceUpdateAuthenticatorId) {
super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
@@ -63,7 +65,7 @@
public void start(@NonNull Callback callback) {
super.start(callback);
- if (mCurrentUserId == getTargetUserId() && !mForceUpdateAuthenticatorId) {
+ if (mCurrentUserId.get() == getTargetUserId() && !mForceUpdateAuthenticatorId) {
Slog.d(TAG, "Already user: " + mCurrentUserId + ", returning");
callback.onClientFinished(this, true /* success */);
return;
@@ -109,8 +111,10 @@
@Override
protected void startHalOperation() {
try {
- getFreshDaemon().setActiveGroup(getTargetUserId(), mDirectory.getAbsolutePath());
- mAuthenticatorIds.put(getTargetUserId(), mHasEnrolledBiometrics
+ final int targetId = getTargetUserId();
+ Slog.d(TAG, "Setting active user: " + targetId);
+ getFreshDaemon().setActiveGroup(targetId, mDirectory.getAbsolutePath());
+ mAuthenticatorIds.put(targetId, mHasEnrolledBiometrics
? getFreshDaemon().getAuthenticatorId() : 0L);
mCallback.onClientFinished(this, true /* success */);
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java
index 880dbf6..fe002ce 100644
--- a/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java
+++ b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java
@@ -25,6 +25,7 @@
import static com.android.server.compat.overrides.AppCompatOverridesParser.FLAG_OWNED_CHANGE_IDS;
import static com.android.server.compat.overrides.AppCompatOverridesParser.FLAG_REMOVE_OVERRIDES;
+import static java.util.Collections.emptyMap;
import static java.util.Collections.emptySet;
import android.annotation.NonNull;
@@ -157,17 +158,17 @@
Map<String, CompatibilityOverridesToRemoveConfig> packageNameToOverridesToRemove =
new ArrayMap<>();
for (String packageName : packageNames) {
- Long versionCode = getVersionCodeOrNull(packageName);
- if (versionCode == null) {
- // Package isn't installed yet.
- continue;
- }
-
Set<Long> changeIdsToSkip = packageToChangeIdsToSkip.getOrDefault(packageName,
emptySet());
- Map<Long, PackageOverride> overridesToAdd = mOverridesParser.parsePackageOverrides(
- properties.getString(packageName, /* defaultValue= */ ""), packageName,
- versionCode, changeIdsToSkip);
+
+ Map<Long, PackageOverride> overridesToAdd = emptyMap();
+ Long versionCode = getVersionCodeOrNull(packageName);
+ if (versionCode != null) {
+ // Only if package installed add overrides, otherwise just remove.
+ overridesToAdd = mOverridesParser.parsePackageOverrides(
+ properties.getString(packageName, /* defaultValue= */ ""), packageName,
+ versionCode, changeIdsToSkip);
+ }
if (!overridesToAdd.isEmpty()) {
packageNameToOverridesToAdd.put(packageName,
new CompatibilityOverrideConfig(overridesToAdd));
diff --git a/services/core/java/com/android/server/devicestate/DeviceState.java b/services/core/java/com/android/server/devicestate/DeviceState.java
index 7fe24ff..78d55b9 100644
--- a/services/core/java/com/android/server/devicestate/DeviceState.java
+++ b/services/core/java/com/android/server/devicestate/DeviceState.java
@@ -43,14 +43,14 @@
*/
public final class DeviceState {
/**
- * Flag that indicates sticky requests should be cancelled when this device state becomes the
+ * Flag that indicates override requests should be cancelled when this device state becomes the
* base device state.
*/
- public static final int FLAG_CANCEL_STICKY_REQUESTS = 1 << 0;
+ public static final int FLAG_CANCEL_OVERRIDE_REQUESTS = 1 << 0;
/** @hide */
@IntDef(prefix = {"FLAG_"}, flag = true, value = {
- FLAG_CANCEL_STICKY_REQUESTS,
+ FLAG_CANCEL_OVERRIDE_REQUESTS,
})
@Retention(RetentionPolicy.SOURCE)
public @interface DeviceStateFlags {}
@@ -114,4 +114,10 @@
public int hashCode() {
return Objects.hash(mIdentifier, mName, mFlags);
}
+
+ /** Checks if a specific flag is set
+ */
+ public boolean hasFlag(int flagToCheckFor) {
+ return (mFlags & flagToCheckFor) == flagToCheckFor;
+ }
}
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 792feea..709af91 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -20,6 +20,7 @@
import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
+import static com.android.server.devicestate.DeviceState.FLAG_CANCEL_OVERRIDE_REQUESTS;
import static com.android.server.devicestate.OverrideRequestController.STATUS_ACTIVE;
import static com.android.server.devicestate.OverrideRequestController.STATUS_CANCELED;
import static com.android.server.devicestate.OverrideRequestController.STATUS_SUSPENDED;
@@ -273,14 +274,14 @@
synchronized (mLock) {
final int[] oldStateIdentifiers = getSupportedStateIdentifiersLocked();
- // Whether or not at least one device state has the flag FLAG_CANCEL_STICKY_REQUESTS
+ // Whether or not at least one device state has the flag FLAG_CANCEL_OVERRIDE_REQUESTS
// set. If set to true, the OverrideRequestController will be configured to allow sticky
// requests.
boolean hasTerminalDeviceState = false;
mDeviceStates.clear();
for (int i = 0; i < supportedDeviceStates.length; i++) {
DeviceState state = supportedDeviceStates[i];
- if ((state.getFlags() & DeviceState.FLAG_CANCEL_STICKY_REQUESTS) != 0) {
+ if (state.hasFlag(FLAG_CANCEL_OVERRIDE_REQUESTS)) {
hasTerminalDeviceState = true;
}
mDeviceStates.put(state.getIdentifier(), state);
@@ -345,8 +346,8 @@
}
mBaseState = Optional.of(baseState);
- if ((baseState.getFlags() & DeviceState.FLAG_CANCEL_STICKY_REQUESTS) != 0) {
- mOverrideRequestController.cancelStickyRequests();
+ if (baseState.hasFlag(FLAG_CANCEL_OVERRIDE_REQUESTS)) {
+ mOverrideRequestController.cancelOverrideRequests();
}
mOverrideRequestController.handleBaseStateChanged();
updatePendingStateLocked();
diff --git a/services/core/java/com/android/server/devicestate/OverrideRequestController.java b/services/core/java/com/android/server/devicestate/OverrideRequestController.java
index 05c9eb2..36cb416 100644
--- a/services/core/java/com/android/server/devicestate/OverrideRequestController.java
+++ b/services/core/java/com/android/server/devicestate/OverrideRequestController.java
@@ -153,6 +153,16 @@
}
/**
+ * Cancels all override requests, this could be due to the device being put
+ * into a hardware state that declares the flag "FLAG_CANCEL_OVERRIDE_REQUESTS"
+ */
+ void cancelOverrideRequests() {
+ mTmpRequestsToCancel.clear();
+ mTmpRequestsToCancel.addAll(mRequests);
+ cancelRequestsLocked(mTmpRequestsToCancel);
+ }
+
+ /**
* Returns {@code true} if this controller is current managing a request with the specified
* {@code token}, {@code false} otherwise.
*/
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index c04032f..ffed68e 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -54,6 +54,10 @@
private static final boolean DEBUG_PRETEND_LIGHT_SENSOR_ABSENT = false;
+ public static final int AUTO_BRIGHTNESS_ENABLED = 1;
+ public static final int AUTO_BRIGHTNESS_DISABLED = 2;
+ public static final int AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE = 3;
+
// How long the current sensor reading is assumed to be valid beyond the current time.
// This provides a bit of prediction, as well as ensures that the weight for the last sample is
// non-zero, which in turn ensures that the total weight is non-zero.
@@ -214,6 +218,7 @@
private IActivityTaskManager mActivityTaskManager;
private PackageManager mPackageManager;
private Context mContext;
+ private int mState = AUTO_BRIGHTNESS_DISABLED;
private final Injector mInjector;
@@ -331,10 +336,11 @@
return mCurrentBrightnessMapper.getAutoBrightnessAdjustment();
}
- public void configure(boolean enable, @Nullable BrightnessConfiguration configuration,
+ public void configure(int state, @Nullable BrightnessConfiguration configuration,
float brightness, boolean userChangedBrightness, float adjustment,
boolean userChangedAutoBrightnessAdjustment, int displayPolicy) {
- mHbmController.setAutoBrightnessEnabled(enable);
+ mState = state;
+ mHbmController.setAutoBrightnessEnabled(mState);
// While dozing, the application processor may be suspended which will prevent us from
// receiving new information from the light sensor. On some devices, we may be able to
// switch to a wake-up light sensor instead but for now we will simply disable the sensor
@@ -346,6 +352,7 @@
if (userChangedAutoBrightnessAdjustment) {
changed |= setAutoBrightnessAdjustment(adjustment);
}
+ final boolean enable = mState == AUTO_BRIGHTNESS_ENABLED;
if (userChangedBrightness && enable) {
// Update the brightness curve with the new user control point. It's critical this
// happens after we update the autobrightness adjustment since it may reset it.
@@ -459,6 +466,7 @@
public void dump(PrintWriter pw) {
pw.println();
pw.println("Automatic Brightness Controller Configuration:");
+ pw.println(" mState=" + configStateToString(mState));
pw.println(" mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum);
pw.println(" mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum);
pw.println(" mDozeScaleFactor=" + mDozeScaleFactor);
@@ -520,6 +528,19 @@
mScreenBrightnessThresholds.dump(pw);
}
+ private String configStateToString(int state) {
+ switch (state) {
+ case AUTO_BRIGHTNESS_ENABLED:
+ return "AUTO_BRIGHTNESS_ENABLED";
+ case AUTO_BRIGHTNESS_DISABLED:
+ return "AUTO_BRIGHTNESS_DISABLED";
+ case AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE:
+ return "AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE";
+ default:
+ return String.valueOf(state);
+ }
+ }
+
private boolean setLightSensorEnabled(boolean enable) {
if (enable) {
if (!mLightSensorEnabled) {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index c4d02c7..faf0038 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -62,6 +62,7 @@
import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.LocalServices;
import com.android.server.am.BatteryStatsService;
import com.android.server.display.RampAnimator.DualRampAnimator;
@@ -127,6 +128,7 @@
private static final int MSG_STOP = 9;
private static final int MSG_UPDATE_BRIGHTNESS = 10;
private static final int MSG_UPDATE_RBC = 11;
+ private static final int MSG_STATSD_HBM_BRIGHTNESS = 12;
private static final int PROXIMITY_UNKNOWN = -1;
private static final int PROXIMITY_NEGATIVE = 0;
@@ -136,6 +138,8 @@
private static final int PROXIMITY_SENSOR_POSITIVE_DEBOUNCE_DELAY = 0;
private static final int PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY = 250;
+ private static final int BRIGHTNESS_CHANGE_STATSD_REPORT_INTERVAL_MS = 500;
+
// Trigger proximity if distance is less than 5 cm.
private static final float TYPICAL_PROXIMITY_THRESHOLD = 5.0f;
@@ -356,6 +360,9 @@
private float mBrightnessRampRateSlowDecrease;
private float mBrightnessRampRateSlowIncrease;
+ // Report HBM brightness change to StatsD
+ private int mDisplayStatsId;
+ private float mLastStatsBrightness = PowerManager.BRIGHTNESS_MIN;
// Whether or not to skip the initial brightness ramps into STATE_ON.
private final boolean mSkipScreenOnBrightnessRamp;
@@ -466,6 +473,7 @@
TAG = "DisplayPowerController[" + mDisplayId + "]";
mDisplayDevice = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
mUniqueDisplayId = logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
+ mDisplayStatsId = mUniqueDisplayId.hashCode();
mHandler = new DisplayControllerHandler(handler.getLooper());
if (mDisplayId == Display.DEFAULT_DISPLAY) {
@@ -763,6 +771,7 @@
if (mDisplayDevice != device) {
mDisplayDevice = device;
mUniqueDisplayId = uniqueId;
+ mDisplayStatsId = mUniqueDisplayId.hashCode();
mDisplayDeviceConfig = config;
loadFromDisplayDeviceConfig(token, info);
updatePowerState();
@@ -816,7 +825,7 @@
loadNitsRange(mContext.getResources());
setUpAutoBrightness(mContext.getResources(), mHandler);
reloadReduceBrightColours();
- mHbmController.resetHbmData(info.width, info.height, token,
+ mHbmController.resetHbmData(info.width, info.height, token, info.uniqueId,
mDisplayDeviceConfig.getHighBrightnessModeData());
}
@@ -1004,7 +1013,15 @@
}
};
- private final RampAnimator.Listener mRampAnimatorListener = this::sendUpdatePowerState;
+ private final RampAnimator.Listener mRampAnimatorListener = new RampAnimator.Listener() {
+ @Override
+ public void onAnimationEnd() {
+ sendUpdatePowerState();
+
+ final float brightness = mPowerState.getScreenBrightness();
+ reportStats(brightness);
+ }
+ };
/** Clean up all resources that are accessed via the {@link #mHandler} thread. */
private void cleanupHandlerThreadAfterStop() {
@@ -1179,6 +1196,13 @@
&& (state == Display.STATE_ON || autoBrightnessEnabledInDoze)
&& Float.isNaN(brightnessState)
&& mAutomaticBrightnessController != null;
+ final boolean autoBrightnessDisabledDueToDisplayOff = mPowerRequest.useAutoBrightness
+ && !(state == Display.STATE_ON || autoBrightnessEnabledInDoze);
+ final int autoBrightnessState = autoBrightnessEnabled
+ ? AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED
+ : autoBrightnessDisabledDueToDisplayOff
+ ? AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE
+ : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED;
final boolean userSetBrightnessChanged = updateUserSetScreenBrightness();
@@ -1229,7 +1253,7 @@
// Configure auto-brightness.
if (mAutomaticBrightnessController != null) {
hadUserBrightnessPoint = mAutomaticBrightnessController.hasUserDataPoints();
- mAutomaticBrightnessController.configure(autoBrightnessEnabled,
+ mAutomaticBrightnessController.configure(autoBrightnessState,
mBrightnessConfiguration,
mLastUserSetScreenBrightness,
userSetBrightnessChanged, autoBrightnessAdjustment,
@@ -1626,11 +1650,13 @@
final DisplayDeviceConfig ddConfig = device.getDisplayDeviceConfig();
final IBinder displayToken =
mLogicalDisplay.getPrimaryDisplayDeviceLocked().getDisplayTokenLocked();
+ final String displayUniqueId =
+ mLogicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
final DisplayDeviceConfig.HighBrightnessModeData hbmData =
ddConfig != null ? ddConfig.getHighBrightnessModeData() : null;
final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
return new HighBrightnessModeController(mHandler, info.width, info.height, displayToken,
- PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, hbmData,
+ displayUniqueId, PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, hbmData,
() -> {
sendUpdatePowerStateLocked();
postBrightnessChangeRunnable();
@@ -2462,6 +2488,42 @@
}
}
+ private void reportStats(float brightness) {
+ float hbmTransitionPoint = PowerManager.BRIGHTNESS_MAX;
+ synchronized(mCachedBrightnessInfo) {
+ if (mCachedBrightnessInfo.hbmTransitionPoint == null) {
+ return;
+ }
+ hbmTransitionPoint = mCachedBrightnessInfo.hbmTransitionPoint.value;
+ }
+
+ final boolean aboveTransition = brightness > hbmTransitionPoint;
+ final boolean oldAboveTransition = mLastStatsBrightness > hbmTransitionPoint;
+
+ if (aboveTransition || oldAboveTransition) {
+ mLastStatsBrightness = brightness;
+ mHandler.removeMessages(MSG_STATSD_HBM_BRIGHTNESS);
+ if (aboveTransition != oldAboveTransition) {
+ // report immediately
+ logHbmBrightnessStats(brightness, mDisplayStatsId);
+ } else {
+ // delay for rate limiting
+ Message msg = mHandler.obtainMessage();
+ msg.what = MSG_STATSD_HBM_BRIGHTNESS;
+ msg.arg1 = Float.floatToIntBits(brightness);
+ msg.arg2 = mDisplayStatsId;
+ mHandler.sendMessageDelayed(msg, BRIGHTNESS_CHANGE_STATSD_REPORT_INTERVAL_MS);
+ }
+ }
+ }
+
+ private final void logHbmBrightnessStats(float brightness, int displayStatsId) {
+ synchronized (mHandler) {
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.DISPLAY_HBM_BRIGHTNESS_CHANGED, displayStatsId, brightness);
+ }
+ }
+
private final class DisplayControllerHandler extends Handler {
public DisplayControllerHandler(Looper looper) {
super(looper, null, true /*async*/);
@@ -2526,6 +2588,10 @@
final int justActivated = msg.arg2;
handleRbcChanged(strengthChanged == 1, justActivated == 1);
break;
+
+ case MSG_STATSD_HBM_BRIGHTNESS:
+ logHbmBrightnessStats(Float.intBitsToFloat(msg.arg1), msg.arg2);
+ break;
}
}
}
diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java
index 1e1cfeb..16273ce 100644
--- a/services/core/java/com/android/server/display/HighBrightnessModeController.java
+++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java
@@ -37,6 +37,7 @@
import android.view.SurfaceControlHdrLayerInfoListener;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.display.DisplayDeviceConfig.HighBrightnessModeData;
import com.android.server.display.DisplayManagerService.Clock;
@@ -80,6 +81,7 @@
private boolean mIsInAllowedAmbientRange = false;
private boolean mIsTimeAvailable = false;
private boolean mIsAutoBrightnessEnabled = false;
+ private boolean mIsAutoBrightnessOffByState = false;
private float mBrightness;
private int mHbmMode = BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
private boolean mIsHdrLayerPresent = false;
@@ -88,6 +90,7 @@
private int mWidth;
private int mHeight;
private float mAmbientLux;
+ private int mDisplayStatsId;
/**
* If HBM is currently running, this is the start time for the current HBM session.
@@ -102,15 +105,15 @@
private LinkedList<HbmEvent> mEvents = new LinkedList<>();
HighBrightnessModeController(Handler handler, int width, int height, IBinder displayToken,
- float brightnessMin, float brightnessMax, HighBrightnessModeData hbmData,
- Runnable hbmChangeCallback, Context context) {
- this(new Injector(), handler, width, height, displayToken, brightnessMin, brightnessMax,
- hbmData, hbmChangeCallback, context);
+ String displayUniqueId, float brightnessMin, float brightnessMax,
+ HighBrightnessModeData hbmData, Runnable hbmChangeCallback, Context context) {
+ this(new Injector(), handler, width, height, displayToken, displayUniqueId, brightnessMin,
+ brightnessMax, hbmData, hbmChangeCallback, context);
}
@VisibleForTesting
HighBrightnessModeController(Injector injector, Handler handler, int width, int height,
- IBinder displayToken, float brightnessMin, float brightnessMax,
+ IBinder displayToken, String displayUniqueId, float brightnessMin, float brightnessMax,
HighBrightnessModeData hbmData, Runnable hbmChangeCallback,
Context context) {
mInjector = injector;
@@ -126,10 +129,13 @@
mRecalcRunnable = this::recalculateTimeAllowance;
mHdrListener = new HdrListener();
- resetHbmData(width, height, displayToken, hbmData);
+ resetHbmData(width, height, displayToken, displayUniqueId, hbmData);
}
- void setAutoBrightnessEnabled(boolean isEnabled) {
+ void setAutoBrightnessEnabled(int state) {
+ final boolean isEnabled = state == AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED;
+ mIsAutoBrightnessOffByState =
+ state == AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE;
if (!deviceSupportsHbm() || isEnabled == mIsAutoBrightnessEnabled) {
return;
}
@@ -231,10 +237,12 @@
mSettingsObserver.stopObserving();
}
- void resetHbmData(int width, int height, IBinder displayToken, HighBrightnessModeData hbmData) {
+ void resetHbmData(int width, int height, IBinder displayToken, String displayUniqueId,
+ HighBrightnessModeData hbmData) {
mWidth = width;
mHeight = height;
mHbmData = hbmData;
+ mDisplayStatsId = displayUniqueId.hashCode();
unregisterHdrListener();
mSkinThermalStatusObserver.stopObserving();
@@ -275,6 +283,7 @@
+ (mIsAutoBrightnessEnabled ? "" : " (old/invalid)"));
pw.println(" mIsInAllowedAmbientRange=" + mIsInAllowedAmbientRange);
pw.println(" mIsAutoBrightnessEnabled=" + mIsAutoBrightnessEnabled);
+ pw.println(" mIsAutoBrightnessOffByState=" + mIsAutoBrightnessOffByState);
pw.println(" mIsHdrLayerPresent=" + mIsHdrLayerPresent);
pw.println(" mBrightnessMin=" + mBrightnessMin);
pw.println(" mBrightnessMax=" + mBrightnessMax);
@@ -436,11 +445,52 @@
private void updateHbmMode() {
int newHbmMode = calculateHighBrightnessMode();
if (mHbmMode != newHbmMode) {
+ updateHbmStats(mHbmMode, newHbmMode);
mHbmMode = newHbmMode;
mHbmChangeCallback.run();
}
}
+ private void updateHbmStats(int mode, int newMode) {
+ int state = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF;
+ if (newMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR) {
+ state = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_HDR;
+ } else if (newMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT) {
+ state = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT;
+ }
+
+ int reason =
+ FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN;
+ boolean oldHbmSv = (mode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT);
+ boolean newHbmSv = (newMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT);
+ if (oldHbmSv && !newHbmSv) {
+ // If more than one conditions are flipped and turn off HBM sunlight
+ // visibility, only one condition will be reported to make it simple.
+ if (!mIsAutoBrightnessEnabled && mIsAutoBrightnessOffByState) {
+ reason = FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_DISPLAY_OFF;
+ } else if (!mIsAutoBrightnessEnabled) {
+ reason = FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_AUTOBRIGHTNESS_OFF;
+ } else if (!mIsInAllowedAmbientRange) {
+ reason = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_LUX_DROP;
+ } else if (!mIsTimeAvailable) {
+ reason = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_TIME_LIMIT;
+ } else if (!mIsThermalStatusWithinLimit) {
+ reason = FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_THERMAL_LIMIT;
+ } else if (mIsHdrLayerPresent) {
+ reason = FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_HDR_PLAYING;
+ } else if (mIsBlockedByLowPowerMode) {
+ reason = FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_BATTERY_SAVE_ON;
+ }
+ }
+
+ mInjector.reportHbmStateChange(mDisplayStatsId, state, reason);
+ }
+
private int calculateHighBrightnessMode() {
if (!deviceSupportsHbm()) {
return BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
@@ -642,5 +692,10 @@
return IThermalService.Stub.asInterface(
ServiceManager.getService(Context.THERMAL_SERVICE));
}
+
+ public void reportHbmStateChange(int display, int state, int reason) {
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED, display, state, reason);
+ }
}
}
diff --git a/services/core/java/com/android/server/locales/AppLocaleChangedAtomRecord.java b/services/core/java/com/android/server/locales/AppLocaleChangedAtomRecord.java
new file mode 100644
index 0000000..282e3c1
--- /dev/null
+++ b/services/core/java/com/android/server/locales/AppLocaleChangedAtomRecord.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2021 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.locales;
+
+import static android.os.Process.INVALID_UID;
+
+import com.android.internal.util.FrameworkStatsLog;
+
+/**
+ * Holds data used to report the ApplicationLocalesChanged atom.
+ */
+public final class AppLocaleChangedAtomRecord {
+ final int mCallingUid;
+ int mTargetUid = INVALID_UID;
+ String mNewLocales = "";
+ String mPrevLocales = "";
+ int mStatus = FrameworkStatsLog
+ .APPLICATION_LOCALES_CHANGED__STATUS__STATUS_UNSPECIFIED;
+
+ AppLocaleChangedAtomRecord(int callingUid) {
+ this.mCallingUid = callingUid;
+ }
+
+ void setNewLocales(String newLocales) {
+ this.mNewLocales = newLocales;
+ }
+
+ void setTargetUid(int targetUid) {
+ this.mTargetUid = targetUid;
+ }
+
+ void setPrevLocales(String prevLocales) {
+ this.mPrevLocales = prevLocales;
+ }
+
+ void setStatus(int status) {
+ this.mStatus = status;
+ }
+}
diff --git a/services/core/java/com/android/server/locales/LocaleManagerService.java b/services/core/java/com/android/server/locales/LocaleManagerService.java
index 6aabdb5..1657b22 100644
--- a/services/core/java/com/android/server/locales/LocaleManagerService.java
+++ b/services/core/java/com/android/server/locales/LocaleManagerService.java
@@ -39,6 +39,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -148,40 +149,52 @@
*/
public void setApplicationLocales(@NonNull String appPackageName, @UserIdInt int userId,
@NonNull LocaleList locales) throws RemoteException, IllegalArgumentException {
- requireNonNull(appPackageName);
- requireNonNull(locales);
-
- //Allow apps with INTERACT_ACROSS_USERS permission to set locales for different user.
- userId = mActivityManagerInternal.handleIncomingUser(
- Binder.getCallingPid(), Binder.getCallingUid(), userId,
- false /* allowAll */, ActivityManagerInternal.ALLOW_NON_FULL,
- "setApplicationLocales", appPackageName);
-
- // This function handles two types of set operations:
- // 1.) A normal, non-privileged app setting its own locale.
- // 2.) A privileged system service setting locales of another package.
- // The least privileged case is a normal app performing a set, so check that first and
- // set locales if the package name is owned by the app. Next, check if the caller has the
- // necessary permission and set locales.
- boolean isCallerOwner = isPackageOwnedByCaller(appPackageName, userId);
- if (!isCallerOwner) {
- enforceChangeConfigurationPermission();
- }
-
- final long token = Binder.clearCallingIdentity();
+ AppLocaleChangedAtomRecord atomRecordForMetrics = new
+ AppLocaleChangedAtomRecord(Binder.getCallingUid());
try {
- setApplicationLocalesUnchecked(appPackageName, userId, locales);
+ requireNonNull(appPackageName);
+ requireNonNull(locales);
+ atomRecordForMetrics.setNewLocales(locales.toLanguageTags());
+ //Allow apps with INTERACT_ACROSS_USERS permission to set locales for different user.
+ userId = mActivityManagerInternal.handleIncomingUser(
+ Binder.getCallingPid(), Binder.getCallingUid(), userId,
+ false /* allowAll */, ActivityManagerInternal.ALLOW_NON_FULL,
+ "setApplicationLocales", appPackageName);
+
+ // This function handles two types of set operations:
+ // 1.) A normal, non-privileged app setting its own locale.
+ // 2.) A privileged system service setting locales of another package.
+ // The least privileged case is a normal app performing a set, so check that first and
+ // set locales if the package name is owned by the app. Next, check if the caller has
+ // the necessary permission and set locales.
+ boolean isCallerOwner = isPackageOwnedByCaller(appPackageName, userId,
+ atomRecordForMetrics);
+ if (!isCallerOwner) {
+ enforceChangeConfigurationPermission(atomRecordForMetrics);
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ setApplicationLocalesUnchecked(appPackageName, userId, locales,
+ atomRecordForMetrics);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
} finally {
- Binder.restoreCallingIdentity(token);
+ logMetric(atomRecordForMetrics);
}
}
private void setApplicationLocalesUnchecked(@NonNull String appPackageName,
- @UserIdInt int userId, @NonNull LocaleList locales) {
+ @UserIdInt int userId, @NonNull LocaleList locales,
+ @NonNull AppLocaleChangedAtomRecord atomRecordForMetrics) {
if (DEBUG) {
Slog.d(TAG, "setApplicationLocales: setting locales for package " + appPackageName
+ " and user " + userId);
}
+
+ atomRecordForMetrics.setPrevLocales(getApplicationLocalesUnchecked(appPackageName, userId)
+ .toLanguageTags());
final ActivityTaskManagerInternal.PackageConfigurationUpdater updater =
mActivityTaskManagerInternal.createPackageConfigurationUpdater(appPackageName,
userId);
@@ -194,6 +207,11 @@
notifyRegisteredReceivers(appPackageName, userId, locales);
mBackupHelper.notifyBackupManager();
+ atomRecordForMetrics.setStatus(
+ FrameworkStatsLog.APPLICATION_LOCALES_CHANGED__STATUS__CONFIG_COMMITTED);
+ } else {
+ atomRecordForMetrics.setStatus(FrameworkStatsLog
+ .APPLICATION_LOCALES_CHANGED__STATUS__CONFIG_UNCOMMITTED);
}
}
@@ -259,26 +277,49 @@
}
/**
+ * Same as {@link LocaleManagerService#isPackageOwnedByCaller(String, int,
+ * AppLocaleChangedAtomRecord)}, but for methods that do not log locale atom.
+ */
+ private boolean isPackageOwnedByCaller(String appPackageName, int userId) {
+ return isPackageOwnedByCaller(appPackageName, userId, /* atomRecordForMetrics= */null);
+ }
+
+ /**
* Checks if the package is owned by the calling app or not for the given user id.
*
* @throws IllegalArgumentException if package not found for given userid
*/
- private boolean isPackageOwnedByCaller(String appPackageName, int userId) {
+ private boolean isPackageOwnedByCaller(String appPackageName, int userId,
+ @Nullable AppLocaleChangedAtomRecord atomRecordForMetrics) {
final int uid = mPackageManagerInternal
.getPackageUid(appPackageName, /* flags */ 0, userId);
if (uid < 0) {
Slog.w(TAG, "Unknown package " + appPackageName + " for user " + userId);
+ if (atomRecordForMetrics != null) {
+ atomRecordForMetrics.setStatus(FrameworkStatsLog
+ .APPLICATION_LOCALES_CHANGED__STATUS__FAILURE_INVALID_TARGET_PACKAGE);
+ }
throw new IllegalArgumentException("Unknown package: " + appPackageName
+ " for user " + userId);
}
+ if (atomRecordForMetrics != null) {
+ atomRecordForMetrics.setTargetUid(uid);
+ }
//Once valid package found, ignore the userId part for validating package ownership
//as apps with INTERACT_ACROSS_USERS permission could be changing locale for different user.
return UserHandle.isSameApp(Binder.getCallingUid(), uid);
}
- private void enforceChangeConfigurationPermission() {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CHANGE_CONFIGURATION, "setApplicationLocales");
+ private void enforceChangeConfigurationPermission(@NonNull AppLocaleChangedAtomRecord
+ atomRecordForMetrics) {
+ try {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CHANGE_CONFIGURATION, "setApplicationLocales");
+ } catch (SecurityException e) {
+ atomRecordForMetrics.setStatus(FrameworkStatsLog
+ .APPLICATION_LOCALES_CHANGED__STATUS__FAILURE_PERMISSION_ABSENT);
+ throw e;
+ }
}
/**
@@ -312,6 +353,7 @@
}
}
+ @NonNull
private LocaleList getApplicationLocalesUnchecked(@NonNull String appPackageName,
@UserIdInt int userId) {
if (DEBUG) {
@@ -345,4 +387,13 @@
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
// TODO(b/201766221): Implement when there is state.
}
+
+ private void logMetric(@NonNull AppLocaleChangedAtomRecord atomRecordForMetrics) {
+ FrameworkStatsLog.write(FrameworkStatsLog.APPLICATION_LOCALES_CHANGED,
+ atomRecordForMetrics.mCallingUid,
+ atomRecordForMetrics.mTargetUid,
+ atomRecordForMetrics.mNewLocales,
+ atomRecordForMetrics.mPrevLocales,
+ atomRecordForMetrics.mStatus);
+ }
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index 03a63b9..8ef42ff 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -16,11 +16,8 @@
package com.android.server.net;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.Network;
-import android.net.NetworkTemplate;
-import android.net.netstats.provider.NetworkStatsProvider;
import android.os.PowerExemptionManager.ReasonCode;
import android.telephony.SubscriptionPlan;
@@ -56,11 +53,6 @@
*/
public abstract SubscriptionPlan getSubscriptionPlan(Network network);
- /**
- * Return the active {@link SubscriptionPlan} for the given template.
- */
- public abstract SubscriptionPlan getSubscriptionPlan(NetworkTemplate template);
-
public static final int QUOTA_TYPE_JOBS = 1;
public static final int QUOTA_TYPE_MULTIPATH = 2;
@@ -99,13 +91,4 @@
*/
public abstract void setMeteredRestrictedPackagesAsync(
Set<String> packageNames, int userId);
-
- /**
- * Notifies that the specified {@link NetworkStatsProvider} has reached its quota
- * which was set through {@link NetworkStatsProvider#onSetLimit(String, long)} or
- * {@link NetworkStatsProvider#onSetWarningAndLimit(String, long, long)}.
- *
- * @param tag the human readable identifier of the custom network stats provider.
- */
- public abstract void onStatsProviderWarningOrLimitReached(@NonNull String tag);
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 5660951..9bc090f 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -151,6 +151,8 @@
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
+import android.app.usage.NetworkStats;
+import android.app.usage.NetworkStatsManager;
import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -181,7 +183,6 @@
import android.net.NetworkSpecifier;
import android.net.NetworkStack;
import android.net.NetworkStateSnapshot;
-import android.net.NetworkStats;
import android.net.NetworkTemplate;
import android.net.TelephonyNetworkSpecifier;
import android.net.TrafficStats;
@@ -441,7 +442,7 @@
private final Context mContext;
private final IActivityManager mActivityManager;
- private NetworkStatsManagerInternal mNetworkStats;
+ private NetworkStatsManager mNetworkStats;
private final INetworkManagementService mNetworkManager;
private UsageStatsManagerInternal mUsageStats;
private AppStandbyInternal mAppStandby;
@@ -453,6 +454,8 @@
private ConnectivityManager mConnManager;
private PowerManagerInternal mPowerManagerInternal;
private PowerWhitelistManager mPowerWhitelistManager;
+ @NonNull
+ private final Dependencies mDeps;
/** Current cached value of the current Battery Saver mode's setting for restrict background. */
@GuardedBy("mUidRulesFirstLock")
@@ -704,7 +707,7 @@
public NetworkPolicyManagerService(Context context, IActivityManager activityManager,
INetworkManagementService networkManagement) {
this(context, activityManager, networkManagement, AppGlobals.getPackageManager(),
- getDefaultClock(), getDefaultSystemDir(), false);
+ getDefaultClock(), getDefaultSystemDir(), false, new Dependencies(context));
}
private static @NonNull File getDefaultSystemDir() {
@@ -716,9 +719,59 @@
Clock.systemUTC());
}
+ static class Dependencies {
+ final Context mContext;
+ final NetworkStatsManager mNetworkStatsManager;
+ Dependencies(Context context) {
+ mContext = context;
+ mNetworkStatsManager = mContext.getSystemService(NetworkStatsManager.class);
+ // Query stats from NetworkStatsService will trigger a poll by default.
+ // But since NPMS listens stats updated event, and will query stats
+ // after the event. A polling -> updated -> query -> polling loop will be introduced
+ // if polls on open. Hence, while NPMS manages it's poll requests explicitly, set
+ // flag to false to prevent a polling loop.
+ mNetworkStatsManager.setPollOnOpen(false);
+ }
+
+ long getNetworkTotalBytes(NetworkTemplate template, long start, long end) {
+ Trace.traceBegin(TRACE_TAG_NETWORK, "getNetworkTotalBytes");
+ try {
+ final NetworkStats.Bucket ret = mNetworkStatsManager
+ .querySummaryForDevice(template, start, end);
+ return ret.getRxBytes() + ret.getTxBytes();
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Failed to read network stats: " + e);
+ return 0;
+ } finally {
+ Trace.traceEnd(TRACE_TAG_NETWORK);
+ }
+ }
+
+ @NonNull
+ List<NetworkStats.Bucket> getNetworkUidBytes(
+ @NonNull NetworkTemplate template, long start, long end) {
+ Trace.traceBegin(TRACE_TAG_NETWORK, "getNetworkUidBytes");
+ final List<NetworkStats.Bucket> buckets = new ArrayList<>();
+ try {
+ final NetworkStats stats = mNetworkStatsManager.querySummary(template, start, end);
+ while (stats.hasNextBucket()) {
+ final NetworkStats.Bucket bucket = new NetworkStats.Bucket();
+ stats.getNextBucket(bucket);
+ buckets.add(bucket);
+ }
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Failed to read network stats: " + e);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_NETWORK);
+ }
+ return buckets;
+ }
+ }
+
+ @VisibleForTesting
public NetworkPolicyManagerService(Context context, IActivityManager activityManager,
INetworkManagementService networkManagement, IPackageManager pm, Clock clock,
- File systemDir, boolean suppressDefaultPolicy) {
+ File systemDir, boolean suppressDefaultPolicy, Dependencies deps) {
mContext = Objects.requireNonNull(context, "missing context");
mActivityManager = Objects.requireNonNull(activityManager, "missing activityManager");
mNetworkManager = Objects.requireNonNull(networkManagement, "missing networkManagement");
@@ -739,10 +792,12 @@
mUidEventHandler = new Handler(mUidEventThread.getLooper(), mUidEventHandlerCallback);
mSuppressDefaultPolicy = suppressDefaultPolicy;
+ mDeps = Objects.requireNonNull(deps, "missing Dependencies");
mPolicyFile = new AtomicFile(new File(systemDir, "netpolicy.xml"), "net-policy");
mAppOps = context.getSystemService(AppOpsManager.class);
+ mNetworkStats = context.getSystemService(NetworkStatsManager.class);
mMultipathPolicyTracker = new MultipathPolicyTracker(mContext, mHandler);
// Expose private service for system components to use.
LocalServices.addService(NetworkPolicyManagerInternal.class,
@@ -842,7 +897,6 @@
mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
mAppStandby = LocalServices.getService(AppStandbyInternal.class);
- mNetworkStats = LocalServices.getService(NetworkStatsManagerInternal.class);
synchronized (mUidRulesFirstLock) {
synchronized (mNetworkPoliciesSecondLock) {
@@ -1161,21 +1215,34 @@
};
/**
- * Receiver that watches for {@link INetworkStatsService} updates, which we
+ * Receiver that watches for {@link NetworkStatsManager} updates, which we
* use to check against {@link NetworkPolicy#warningBytes}.
*/
- final private BroadcastReceiver mStatsReceiver = new BroadcastReceiver() {
+ private final NetworkStatsBroadcastReceiver mStatsReceiver =
+ new NetworkStatsBroadcastReceiver();
+ private class NetworkStatsBroadcastReceiver extends BroadcastReceiver {
+ private boolean mIsAnyIntentReceived = false;
@Override
public void onReceive(Context context, Intent intent) {
// on background handler thread, and verified
// READ_NETWORK_USAGE_HISTORY permission above.
+ mIsAnyIntentReceived = true;
+
synchronized (mNetworkPoliciesSecondLock) {
updateNetworkRulesNL();
updateNetworkEnabledNL();
updateNotificationsNL();
}
}
+
+ /**
+ * Return whether any {@code ACTION_NETWORK_STATS_UPDATED} intent is received.
+ * Used to determine if NetworkStatsService is ready.
+ */
+ public boolean isAnyIntentReceived() {
+ return mIsAnyIntentReceived;
+ }
};
/**
@@ -1385,15 +1452,17 @@
long maxBytes = 0;
int maxUid = 0;
- final NetworkStats stats = getNetworkUidBytes(template, start, end);
- NetworkStats.Entry entry = null;
- for (int i = 0; i < stats.size(); i++) {
- entry = stats.getValues(i, entry);
- final long bytes = entry.rxBytes + entry.txBytes;
+ // Skip if not ready. NetworkStatsService will block public API calls until it is
+ // ready. To prevent NPMS be blocked on that, skip and fail fast instead.
+ if (!mStatsReceiver.isAnyIntentReceived()) return null;
+
+ final List<NetworkStats.Bucket> stats = mDeps.getNetworkUidBytes(template, start, end);
+ for (final NetworkStats.Bucket entry : stats) {
+ final long bytes = entry.getRxBytes() + entry.getTxBytes();
totalBytes += bytes;
if (bytes > maxBytes) {
maxBytes = bytes;
- maxUid = entry.uid;
+ maxUid = entry.getUid();
}
}
@@ -3363,6 +3432,35 @@
return result;
}
+ /**
+ * Get subscription plan for the given networkTemplate.
+ *
+ * @param template the networkTemplate to get the subscription plan for.
+ */
+ @Override
+ public SubscriptionPlan getSubscriptionPlan(@NonNull NetworkTemplate template) {
+ enforceAnyPermissionOf(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+ synchronized (mNetworkPoliciesSecondLock) {
+ final int subId = findRelevantSubIdNL(template);
+ return getPrimarySubscriptionPlanLocked(subId);
+ }
+ }
+
+ /**
+ * Notifies that the specified {@link NetworkStatsProvider} has reached its quota
+ * which was set through {@link NetworkStatsProvider#onSetLimit(String, long)} or
+ * {@link NetworkStatsProvider#onSetWarningAndLimit(String, long, long)}.
+ */
+ @Override
+ public void onStatsProviderWarningOrLimitReached() {
+ enforceAnyPermissionOf(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+ // This API may be called before the system is ready.
+ synchronized (mNetworkPoliciesSecondLock) {
+ if (!mSystemReady) return;
+ }
+ mHandler.obtainMessage(MSG_STATS_PROVIDER_WARNING_OR_LIMIT_REACHED).sendToTarget();
+ }
+
@Override
public SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage) {
enforceSubscriptionPlanAccess(subId, Binder.getCallingUid(), callingPackage);
@@ -5349,25 +5447,10 @@
@Deprecated
private long getTotalBytes(NetworkTemplate template, long start, long end) {
- return getNetworkTotalBytes(template, start, end);
- }
-
- private long getNetworkTotalBytes(NetworkTemplate template, long start, long end) {
- try {
- return mNetworkStats.getNetworkTotalBytes(template, start, end);
- } catch (RuntimeException e) {
- Slog.w(TAG, "Failed to read network stats: " + e);
- return 0;
- }
- }
-
- private NetworkStats getNetworkUidBytes(NetworkTemplate template, long start, long end) {
- try {
- return mNetworkStats.getNetworkUidBytes(template, start, end);
- } catch (RuntimeException e) {
- Slog.w(TAG, "Failed to read network stats: " + e);
- return new NetworkStats(SystemClock.elapsedRealtime(), 0);
- }
+ // Skip if not ready. NetworkStatsService will block public API calls until it is
+ // ready. To prevent NPMS be blocked on that, skip and fail fast instead.
+ if (!mStatsReceiver.isAnyIntentReceived()) return 0;
+ return mDeps.getNetworkTotalBytes(template, start, end);
}
private boolean isBandwidthControlEnabled() {
@@ -5583,14 +5666,6 @@
}
@Override
- public SubscriptionPlan getSubscriptionPlan(NetworkTemplate template) {
- synchronized (mNetworkPoliciesSecondLock) {
- final int subId = findRelevantSubIdNL(template);
- return getPrimarySubscriptionPlanLocked(subId);
- }
- }
-
- @Override
public long getSubscriptionOpportunisticQuota(Network network, int quotaType) {
final long quotaBytes;
synchronized (mNetworkPoliciesSecondLock) {
@@ -5632,12 +5707,6 @@
mHandler.obtainMessage(MSG_METERED_RESTRICTED_PACKAGES_CHANGED,
userId, 0, packageNames).sendToTarget();
}
-
- @Override
- public void onStatsProviderWarningOrLimitReached(@NonNull String tag) {
- Log.v(TAG, "onStatsProviderWarningOrLimitReached: " + tag);
- mHandler.obtainMessage(MSG_STATS_PROVIDER_WARNING_OR_LIMIT_REACHED).sendToTarget();
- }
}
private void setMeteredRestrictedPackagesInternal(Set<String> packageNames, int userId) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index a5edfed..86b385b 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2657,16 +2657,20 @@
}
private void sendAppBlockStateChangedBroadcast(String pkg, int uid, boolean blocked) {
- try {
- getContext().sendBroadcastAsUser(
- new Intent(ACTION_APP_BLOCK_STATE_CHANGED)
- .putExtra(NotificationManager.EXTRA_BLOCKED_STATE, blocked)
- .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
- .setPackage(pkg),
- UserHandle.of(UserHandle.getUserId(uid)), null);
- } catch (SecurityException e) {
- Slog.w(TAG, "Can't notify app about app block change", e);
- }
+ // From Android T, revoking the notification permission will cause the app to be killed.
+ // delay this broadcast so it doesn't race with that process death
+ mHandler.postDelayed(() -> {
+ try {
+ getContext().sendBroadcastAsUser(
+ new Intent(ACTION_APP_BLOCK_STATE_CHANGED)
+ .putExtra(NotificationManager.EXTRA_BLOCKED_STATE, blocked)
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+ .setPackage(pkg),
+ UserHandle.of(UserHandle.getUserId(uid)), null);
+ } catch (SecurityException e) {
+ Slog.w(TAG, "Can't notify app about app block change", e);
+ }
+ }, 500);
}
@Override
@@ -3094,6 +3098,13 @@
if (mPreferencesHelper.setValidMessageSent(
r.getSbn().getPackageName(), r.getUid())) {
handleSavePolicyFile();
+ } else if (r.getNotification().getBubbleMetadata() != null) {
+ // If bubble metadata is present it is valid (if invalid it's removed
+ // via BubbleExtractor).
+ if (mPreferencesHelper.setValidBubbleSent(
+ r.getSbn().getPackageName(), r.getUid())) {
+ handleSavePolicyFile();
+ }
}
} else {
if (mPreferencesHelper.setInvalidMessageSent(
@@ -3597,6 +3608,12 @@
}
@Override
+ public boolean hasSentValidBubble(String pkg, int uid) {
+ checkCallerIsSystem();
+ return mPreferencesHelper.hasSentValidBubble(pkg, uid);
+ }
+
+ @Override
public void setNotificationDelegate(String callingPkg, String delegate) {
checkCallerIsSameApp(callingPkg);
final int callingUid = Binder.getCallingUid();
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 258ae8c..5e333da 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -808,6 +808,23 @@
}
}
+ /** Sets whether this package has sent a notification with valid bubble metadata. */
+ public boolean setValidBubbleSent(String packageName, int uid) {
+ synchronized (mPackagePreferences) {
+ PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
+ boolean valueChanged = !r.hasSentValidBubble;
+ r.hasSentValidBubble = true;
+ return valueChanged;
+ }
+ }
+
+ boolean hasSentValidBubble(String packageName, int uid) {
+ synchronized (mPackagePreferences) {
+ PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
+ return r.hasSentValidBubble;
+ }
+ }
+
@Override
public boolean isGroupBlocked(String packageName, int uid, String groupId) {
if (groupId == null) {
@@ -848,6 +865,9 @@
if (r == null) {
throw new IllegalArgumentException("Invalid package");
}
+ if (fromTargetApp) {
+ group.setBlocked(false);
+ }
final NotificationChannelGroup oldGroup = r.groups.get(group.getId());
if (oldGroup != null) {
group.setChannels(oldGroup.getChannels());
@@ -2813,8 +2833,9 @@
boolean hasSentInvalidMessage = false;
boolean hasSentValidMessage = false;
- // notE: only valid while hasSentMessage is false and hasSentInvalidMessage is true
+ // note: only valid while hasSentMessage is false and hasSentInvalidMessage is true
boolean userDemotedMsgApp = false;
+ boolean hasSentValidBubble = false;
Delegate delegate = null;
ArrayMap<String, NotificationChannel> channels = new ArrayMap<>();
diff --git a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java b/services/core/java/com/android/server/pm/InitAppsHelper.java
similarity index 61%
rename from services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
rename to services/core/java/com/android/server/pm/InitAppsHelper.java
index dfa6c66..a5e6d6f 100644
--- a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InitAppsHelper.java
@@ -31,6 +31,7 @@
import static com.android.server.pm.PackageManagerService.SYSTEM_PARTITIONS;
import static com.android.server.pm.PackageManagerService.TAG;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.parsing.ParsingPackageUtils;
import android.os.Environment;
@@ -59,14 +60,25 @@
* further cleanup and eventually all the installation/scanning related logic will go to another
* class.
*/
-final class InitAndSystemPackageHelper {
+final class InitAppsHelper {
private final PackageManagerService mPm;
-
private final List<ScanPartition> mDirsToScanAsSystem;
private final int mScanFlags;
private final int mSystemParseFlags;
private final int mSystemScanFlags;
private final InstallPackageHelper mInstallPackageHelper;
+ private final ApexManager mApexManager;
+ private final PackageParser2 mPackageParser;
+ private final ExecutorService mExecutorService;
+ /* Tracks how long system scan took */
+ private long mSystemScanTime;
+ /* Track of the number of cached system apps */
+ private int mCachedSystemApps;
+ /* Track of the number of system apps */
+ private int mSystemPackagesCount;
+ private final boolean mIsDeviceUpgrading;
+ private final boolean mIsOnlyCoreApps;
+ private final List<ScanPartition> mSystemPartitions;
/**
* Tracks new system packages [received in an OTA] that we expect to
@@ -74,26 +86,39 @@
* are package location.
*/
private final ArrayMap<String, File> mExpectingBetter = new ArrayMap<>();
+ /* Tracks of any system packages that no longer exist that needs to be pruned. */
+ private final List<String> mPossiblyDeletedUpdatedSystemApps = new ArrayList<>();
+ // Tracks of stub packages that must either be replaced with full versions in the /data
+ // partition or be disabled.
+ private final List<String> mStubSystemApps = new ArrayList<>();
// TODO(b/198166813): remove PMS dependency
- InitAndSystemPackageHelper(PackageManagerService pm) {
+ InitAppsHelper(PackageManagerService pm, ApexManager apexManager,
+ InstallPackageHelper installPackageHelper, PackageParser2 packageParser,
+ List<ScanPartition> systemPartitions) {
mPm = pm;
- mInstallPackageHelper = new InstallPackageHelper(pm);
+ mApexManager = apexManager;
+ mInstallPackageHelper = installPackageHelper;
+ mPackageParser = packageParser;
+ mSystemPartitions = systemPartitions;
mDirsToScanAsSystem = getSystemScanPartitions();
+ mIsDeviceUpgrading = mPm.isDeviceUpgrading();
+ mIsOnlyCoreApps = mPm.isOnlyCoreApps();
// Set flag to monitor and not change apk file paths when scanning install directories.
int scanFlags = SCAN_BOOTING | SCAN_INITIAL;
- if (mPm.isDeviceUpgrading() || mPm.isFirstBoot()) {
+ if (mIsDeviceUpgrading || mPm.isFirstBoot()) {
mScanFlags = scanFlags | SCAN_FIRST_BOOT_OR_UPGRADE;
} else {
mScanFlags = scanFlags;
}
mSystemParseFlags = mPm.getDefParseFlags() | ParsingPackageUtils.PARSE_IS_SYSTEM_DIR;
mSystemScanFlags = scanFlags | SCAN_AS_SYSTEM;
+ mExecutorService = ParallelPackageParser.makeExecutorService();
}
private List<ScanPartition> getSystemScanPartitions() {
final List<ScanPartition> scanPartitions = new ArrayList<>();
- scanPartitions.addAll(mPm.mInjector.getSystemPartitions());
+ scanPartitions.addAll(mSystemPartitions);
scanPartitions.addAll(getApexScanPartitions());
Slog.d(TAG, "Directories scanned as system partitions: " + scanPartitions);
return scanPartitions;
@@ -101,8 +126,7 @@
private List<ScanPartition> getApexScanPartitions() {
final List<ScanPartition> scanPartitions = new ArrayList<>();
- final List<ApexManager.ActiveApexInfo> activeApexInfos =
- mPm.mApexManager.getActiveApexInfos();
+ final List<ApexManager.ActiveApexInfo> activeApexInfos = mApexManager.getActiveApexInfos();
for (int i = 0; i < activeApexInfos.size(); i++) {
final ScanPartition scanPartition = resolveApexToScanPartition(activeApexInfos.get(i));
if (scanPartition != null) {
@@ -119,116 +143,133 @@
if (apexInfo.preInstalledApexPath.getAbsolutePath().equals(
sp.getFolder().getAbsolutePath())
|| apexInfo.preInstalledApexPath.getAbsolutePath().startsWith(
- sp.getFolder().getAbsolutePath() + File.separator)) {
+ sp.getFolder().getAbsolutePath() + File.separator)) {
return new ScanPartition(apexInfo.apexDirectory, sp, SCAN_AS_APK_IN_APEX);
}
}
return null;
}
- public OverlayConfig initPackages(
- WatchedArrayMap<String, PackageSetting> packageSettings, int[] userIds,
- long startTime) {
- PackageParser2 packageParser = mPm.mInjector.getScanningCachingPackageParser();
-
- ExecutorService executorService = ParallelPackageParser.makeExecutorService();
+ /**
+ * Install apps from system dirs.
+ */
+ @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+ public OverlayConfig initSystemApps(WatchedArrayMap<String, PackageSetting> packageSettings,
+ int[] userIds, long startTime) {
// Prepare apex package info before scanning APKs, this information is needed when
// scanning apk in apex.
- mPm.mApexManager.scanApexPackagesTraced(packageParser, executorService);
+ mApexManager.scanApexPackagesTraced(mPackageParser, mExecutorService);
- scanSystemDirs(packageParser, executorService);
+ scanSystemDirs(mPackageParser, mExecutorService);
// Parse overlay configuration files to set default enable state, mutability, and
// priority of system overlays.
final ArrayMap<String, File> apkInApexPreInstalledPaths = new ArrayMap<>();
- for (ApexManager.ActiveApexInfo apexInfo : mPm.mApexManager.getActiveApexInfos()) {
- for (String packageName : mPm.mApexManager.getApksInApex(apexInfo.apexModuleName)) {
+ for (ApexManager.ActiveApexInfo apexInfo : mApexManager.getActiveApexInfos()) {
+ for (String packageName : mApexManager.getApksInApex(apexInfo.apexModuleName)) {
apkInApexPreInstalledPaths.put(packageName, apexInfo.preInstalledApexPath);
}
}
- OverlayConfig overlayConfig = OverlayConfig.initializeSystemInstance(
+ final OverlayConfig overlayConfig = OverlayConfig.initializeSystemInstance(
consumer -> mPm.forEachPackage(
pkg -> consumer.accept(pkg, pkg.isSystem(),
- apkInApexPreInstalledPaths.get(pkg.getPackageName()))));
- // Prune any system packages that no longer exist.
- final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>();
- // Stub packages must either be replaced with full versions in the /data
- // partition or be disabled.
- final List<String> stubSystemApps = new ArrayList<>();
+ apkInApexPreInstalledPaths.get(pkg.getPackageName()))));
- if (!mPm.isOnlyCoreApps()) {
+ if (!mIsOnlyCoreApps) {
// do this first before mucking with mPackages for the "expecting better" case
- updateStubSystemAppsList(stubSystemApps);
+ updateStubSystemAppsList(mStubSystemApps);
mInstallPackageHelper.prepareSystemPackageCleanUp(packageSettings,
- possiblyDeletedUpdatedSystemApps, mExpectingBetter, userIds);
+ mPossiblyDeletedUpdatedSystemApps, mExpectingBetter, userIds);
}
- final int cachedSystemApps = PackageCacher.sCachedPackageReadCount.get();
+ logSystemAppsScanningTime(startTime);
+ return overlayConfig;
+ }
+
+ @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+ private void logSystemAppsScanningTime(long startTime) {
+ mCachedSystemApps = PackageCacher.sCachedPackageReadCount.get();
// Remove any shared userIDs that have no associated packages
mPm.mSettings.pruneSharedUsersLPw();
- final long systemScanTime = SystemClock.uptimeMillis() - startTime;
- final int systemPackagesCount = mPm.mPackages.size();
- Slog.i(TAG, "Finished scanning system apps. Time: " + systemScanTime
- + " ms, packageCount: " + systemPackagesCount
+ mSystemScanTime = SystemClock.uptimeMillis() - startTime;
+ mSystemPackagesCount = mPm.mPackages.size();
+ Slog.i(TAG, "Finished scanning system apps. Time: " + mSystemScanTime
+ + " ms, packageCount: " + mSystemPackagesCount
+ " , timePerPackage: "
- + (systemPackagesCount == 0 ? 0 : systemScanTime / systemPackagesCount)
- + " , cached: " + cachedSystemApps);
- if (mPm.isDeviceUpgrading() && systemPackagesCount > 0) {
+ + (mSystemPackagesCount == 0 ? 0 : mSystemScanTime / mSystemPackagesCount)
+ + " , cached: " + mCachedSystemApps);
+ if (mIsDeviceUpgrading && mSystemPackagesCount > 0) {
//CHECKSTYLE:OFF IndentationCheck
FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME,
- systemScanTime / systemPackagesCount);
+ mSystemScanTime / mSystemPackagesCount);
//CHECKSTYLE:ON IndentationCheck
}
+ }
- if (!mPm.isOnlyCoreApps()) {
+ /**
+ * Install apps/updates from data dir and fix system apps that are affected.
+ */
+ @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+ public void initNonSystemApps(@NonNull int[] userIds, long startTime) {
+ if (!mIsOnlyCoreApps) {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
SystemClock.uptimeMillis());
- scanDirTracedLI(mPm.getAppInstallDir(), 0, mScanFlags | SCAN_REQUIRE_KNOWN, 0,
- packageParser, executorService);
+ scanDirTracedLI(mPm.getAppInstallDir(), 0, mScanFlags | SCAN_REQUIRE_KNOWN,
+ mPackageParser, mExecutorService);
}
- List<Runnable> unfinishedTasks = executorService.shutdownNow();
+ List<Runnable> unfinishedTasks = mExecutorService.shutdownNow();
if (!unfinishedTasks.isEmpty()) {
throw new IllegalStateException("Not all tasks finished before calling close: "
+ unfinishedTasks);
}
-
- if (!mPm.isOnlyCoreApps()) {
- mInstallPackageHelper.cleanupDisabledPackageSettings(possiblyDeletedUpdatedSystemApps,
- userIds, mScanFlags);
- mInstallPackageHelper.checkExistingBetterPackages(mExpectingBetter,
- stubSystemApps, mSystemScanFlags, mSystemParseFlags);
-
- // Uncompress and install any stubbed system applications.
- // This must be done last to ensure all stubs are replaced or disabled.
- mInstallPackageHelper.installSystemStubPackages(stubSystemApps, mScanFlags);
-
- final int cachedNonSystemApps = PackageCacher.sCachedPackageReadCount.get()
- - cachedSystemApps;
-
- final long dataScanTime = SystemClock.uptimeMillis() - systemScanTime - startTime;
- final int dataPackagesCount = mPm.mPackages.size() - systemPackagesCount;
- Slog.i(TAG, "Finished scanning non-system apps. Time: " + dataScanTime
- + " ms, packageCount: " + dataPackagesCount
- + " , timePerPackage: "
- + (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount)
- + " , cached: " + cachedNonSystemApps);
- if (mPm.isDeviceUpgrading() && dataPackagesCount > 0) {
- //CHECKSTYLE:OFF IndentationCheck
- FrameworkStatsLog.write(
- FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
- BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME,
- dataScanTime / dataPackagesCount);
- //CHECKSTYLE:OFF IndentationCheck
- }
+ if (!mIsOnlyCoreApps) {
+ fixSystemPackages(userIds);
+ logNonSystemAppScanningTime(startTime);
}
mExpectingBetter.clear();
-
mPm.mSettings.pruneRenamedPackagesLPw();
- packageParser.close();
- return overlayConfig;
+ mPackageParser.close();
+ }
+
+ /**
+ * Clean up system packages now that some system package updates have been installed from
+ * the data dir. Also install system stub packages as the last step.
+ */
+ @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+ private void fixSystemPackages(@NonNull int[] userIds) {
+ mInstallPackageHelper.cleanupDisabledPackageSettings(mPossiblyDeletedUpdatedSystemApps,
+ userIds, mScanFlags);
+ mInstallPackageHelper.checkExistingBetterPackages(mExpectingBetter,
+ mStubSystemApps, mSystemScanFlags, mSystemParseFlags);
+
+ // Uncompress and install any stubbed system applications.
+ // This must be done last to ensure all stubs are replaced or disabled.
+ mInstallPackageHelper.installSystemStubPackages(mStubSystemApps, mScanFlags);
+ }
+
+ @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+ private void logNonSystemAppScanningTime(long startTime) {
+ final int cachedNonSystemApps = PackageCacher.sCachedPackageReadCount.get()
+ - mCachedSystemApps;
+
+ final long dataScanTime = SystemClock.uptimeMillis() - mSystemScanTime - startTime;
+ final int dataPackagesCount = mPm.mPackages.size() - mSystemPackagesCount;
+ Slog.i(TAG, "Finished scanning non-system apps. Time: " + dataScanTime
+ + " ms, packageCount: " + dataPackagesCount
+ + " , timePerPackage: "
+ + (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount)
+ + " , cached: " + cachedNonSystemApps);
+ if (mIsDeviceUpgrading && dataPackagesCount > 0) {
+ //CHECKSTYLE:OFF IndentationCheck
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
+ BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME,
+ dataScanTime / dataPackagesCount);
+ //CHECKSTYLE:OFF IndentationCheck
+ }
}
/**
@@ -248,12 +289,12 @@
continue;
}
scanDirTracedLI(partition.getOverlayFolder(), mSystemParseFlags,
- mSystemScanFlags | partition.scanFlag, 0,
+ mSystemScanFlags | partition.scanFlag,
packageParser, executorService);
}
scanDirTracedLI(frameworkDir, mSystemParseFlags,
- mSystemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0,
+ mSystemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED,
packageParser, executorService);
if (!mPm.mPackages.containsKey("android")) {
throw new IllegalStateException(
@@ -264,11 +305,11 @@
final ScanPartition partition = mDirsToScanAsSystem.get(i);
if (partition.getPrivAppFolder() != null) {
scanDirTracedLI(partition.getPrivAppFolder(), mSystemParseFlags,
- mSystemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, 0,
+ mSystemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag,
packageParser, executorService);
}
scanDirTracedLI(partition.getAppFolder(), mSystemParseFlags,
- mSystemScanFlags | partition.scanFlag, 0,
+ mSystemScanFlags | partition.scanFlag,
packageParser, executorService);
}
}
@@ -286,11 +327,11 @@
@GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags,
- long currentTime, PackageParser2 packageParser, ExecutorService executorService) {
+ PackageParser2 packageParser, ExecutorService executorService) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
try {
mInstallPackageHelper.installPackagesFromDir(scanDir, parseFlags, scanFlags,
- currentTime, packageParser, executorService);
+ packageParser, executorService);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 14c0761..d002c26 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -91,7 +91,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
-import android.app.AppOpsManager;
import android.app.ApplicationPackageManager;
import android.app.backup.IBackupManager;
import android.content.ContentResolver;
@@ -195,38 +194,32 @@
private final AppDataHelper mAppDataHelper;
private final BroadcastHelper mBroadcastHelper;
private final RemovePackageHelper mRemovePackageHelper;
- private final StorageManager mStorageManager;
- private final RollbackManagerInternal mRollbackManager;
private final IncrementalManager mIncrementalManager;
private final ApexManager mApexManager;
private final DexManager mDexManager;
private final ArtManagerService mArtManagerService;
- private final AppOpsManager mAppOpsManager;
private final Context mContext;
private final PackageDexOptimizer mPackageDexOptimizer;
private final PackageAbiHelper mPackageAbiHelper;
private final ViewCompiler mViewCompiler;
- private final IBackupManager mIBackupManager;
private final SharedLibrariesImpl mSharedLibraries;
+ private final PackageManagerServiceInjector mInjector;
// TODO(b/198166813): remove PMS dependency
InstallPackageHelper(PackageManagerService pm, AppDataHelper appDataHelper) {
mPm = pm;
+ mInjector = pm.mInjector;
mAppDataHelper = appDataHelper;
mBroadcastHelper = new BroadcastHelper(pm.mInjector);
mRemovePackageHelper = new RemovePackageHelper(pm);
- mStorageManager = pm.mInjector.getSystemService(StorageManager.class);
- mRollbackManager = pm.mInjector.getLocalService(RollbackManagerInternal.class);
mIncrementalManager = pm.mInjector.getIncrementalManager();
mApexManager = pm.mInjector.getApexManager();
mDexManager = pm.mInjector.getDexManager();
mArtManagerService = pm.mInjector.getArtManagerService();
- mAppOpsManager = pm.mInjector.getSystemService(AppOpsManager.class);
mContext = pm.mInjector.getContext();
mPackageDexOptimizer = pm.mInjector.getPackageDexOptimizer();
mPackageAbiHelper = pm.mInjector.getAbiHelper();
mViewCompiler = pm.mInjector.getViewCompiler();
- mIBackupManager = pm.mInjector.getIBackupManager();
mSharedLibraries = pm.mInjector.getSharedLibrariesImpl();
}
@@ -693,7 +686,8 @@
* Returns whether the restore successfully completed.
*/
private boolean performBackupManagerRestore(int userId, int token, PackageInstalledInfo res) {
- if (mIBackupManager != null) {
+ IBackupManager iBackupManager = mInjector.getIBackupManager();
+ if (iBackupManager != null) {
// For backwards compatibility as USER_ALL previously routed directly to USER_SYSTEM
// in the BackupManager. USER_ALL is used in compatibility tests.
if (userId == UserHandle.USER_ALL) {
@@ -704,8 +698,8 @@
}
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "restore", token);
try {
- if (mIBackupManager.isUserReadyForBackup(userId)) {
- mIBackupManager.restoreAtInstallForUser(
+ if (iBackupManager.isUserReadyForBackup(userId)) {
+ iBackupManager.restoreAtInstallForUser(
userId, res.mPkg.getPackageName(), token);
} else {
Slog.w(TAG, "User " + userId + " is not ready. Restore at install "
@@ -756,7 +750,9 @@
if (ps != null && doSnapshotOrRestore) {
final String seInfo = AndroidPackageUtils.getSeInfo(res.mPkg, ps);
- mRollbackManager.snapshotAndRestoreUserData(packageName,
+ final RollbackManagerInternal rollbackManager =
+ mInjector.getLocalService(RollbackManagerInternal.class);
+ rollbackManager.snapshotAndRestoreUserData(packageName,
UserHandle.toUserHandles(installedUsers), appId, ceDataInode, seInfo, token);
return true;
}
@@ -2788,8 +2784,10 @@
// Send broadcast package appeared if external for all users
if (res.mPkg.isExternalStorage()) {
if (!update) {
+ final StorageManager storageManager =
+ mInjector.getSystemService(StorageManager.class);
VolumeInfo volume =
- mStorageManager.findVolumeByUuid(
+ storageManager.findVolumeByUuid(
StorageManager.convert(
res.mPkg.getVolumeUuid()).toString());
int packageExternalStorageType =
@@ -3056,7 +3054,7 @@
final RemovePackageHelper removePackageHelper = new RemovePackageHelper(mPm);
removePackageHelper.removePackageLI(stubPkg, true /*chatty*/);
try {
- return scanSystemPackageTracedLI(scanFile, parseFlags, scanFlags, 0, null);
+ return scanSystemPackageTracedLI(scanFile, parseFlags, scanFlags, null);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to install compressed system package:" + stubPkg.getPackageName(),
e);
@@ -3189,7 +3187,7 @@
| ParsingPackageUtils.PARSE_IS_SYSTEM_DIR;
@PackageManagerService.ScanFlags int scanFlags = mPm.getSystemPackageScanFlags(codePath);
final AndroidPackage pkg = scanSystemPackageTracedLI(
- codePath, parseFlags, scanFlags, 0 /*currentTime*/, null);
+ codePath, parseFlags, scanFlags, null);
PackageSetting pkgSetting = mPm.mSettings.getPackageLPr(pkg.getPackageName());
@@ -3364,7 +3362,7 @@
mRemovePackageHelper.removePackageLI(pkg, true);
try {
final File codePath = new File(pkg.getPath());
- scanSystemPackageTracedLI(codePath, 0, scanFlags, 0, null);
+ scanSystemPackageTracedLI(codePath, 0, scanFlags, null);
} catch (PackageManagerException e) {
Slog.e(TAG, "Failed to parse updated, ex-system package: "
+ e.getMessage());
@@ -3385,7 +3383,7 @@
@GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
public void installPackagesFromDir(File scanDir, int parseFlags, int scanFlags,
- long currentTime, PackageParser2 packageParser, ExecutorService executorService) {
+ PackageParser2 packageParser, ExecutorService executorService) {
final File[] files = scanDir.listFiles();
if (ArrayUtils.isEmpty(files)) {
Log.d(TAG, "No files in app dir " + scanDir);
@@ -3427,7 +3425,7 @@
parseResult.parsedPackage);
}
try {
- addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags, currentTime,
+ addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags,
null);
} catch (PackageManagerException e) {
errorCode = e.error;
@@ -3490,7 +3488,7 @@
try {
final AndroidPackage newPkg = scanSystemPackageTracedLI(
- scanFile, reparseFlags, rescanFlags, 0, null);
+ scanFile, reparseFlags, rescanFlags, null);
// We rescanned a stub, add it to the list of stubbed system packages
if (newPkg.isStub()) {
stubSystemApps.add(packageName);
@@ -3504,14 +3502,14 @@
/**
* Traces a package scan.
- * @see #scanSystemPackageLI(File, int, int, long, UserHandle)
+ * @see #scanSystemPackageLI(File, int, int, UserHandle)
*/
@GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
public AndroidPackage scanSystemPackageTracedLI(File scanFile, final int parseFlags,
- int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
+ int scanFlags, UserHandle user) throws PackageManagerException {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage [" + scanFile.toString() + "]");
try {
- return scanSystemPackageLI(scanFile, parseFlags, scanFlags, currentTime, user);
+ return scanSystemPackageLI(scanFile, parseFlags, scanFlags, user);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -3523,7 +3521,7 @@
*/
@GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
private AndroidPackage scanSystemPackageLI(File scanFile, int parseFlags, int scanFlags,
- long currentTime, UserHandle user) throws PackageManagerException {
+ UserHandle user) throws PackageManagerException {
if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
@@ -3539,7 +3537,7 @@
PackageManagerService.renameStaticSharedLibraryPackage(parsedPackage);
}
- return addForInitLI(parsedPackage, parseFlags, scanFlags, currentTime, user);
+ return addForInitLI(parsedPackage, parseFlags, scanFlags, user);
}
/**
@@ -3558,11 +3556,11 @@
@GuardedBy({"mPm.mLock", "mPm.mInstallLock"})
private AndroidPackage addForInitLI(ParsedPackage parsedPackage,
@ParsingPackageUtils.ParseFlags int parseFlags,
- @PackageManagerService.ScanFlags int scanFlags, long currentTime,
+ @PackageManagerService.ScanFlags int scanFlags,
@Nullable UserHandle user) throws PackageManagerException {
final Pair<ScanResult, Boolean> scanResultPair = scanSystemPackageLI(
- parsedPackage, parseFlags, scanFlags, currentTime, user);
+ parsedPackage, parseFlags, scanFlags, user);
final ScanResult scanResult = scanResultPair.first;
boolean shouldHideSystemApp = scanResultPair.second;
if (scanResult.mSuccess) {
@@ -3740,7 +3738,7 @@
private Pair<ScanResult, Boolean> scanSystemPackageLI(ParsedPackage parsedPackage,
@ParsingPackageUtils.ParseFlags int parseFlags,
- @PackageManagerService.ScanFlags int scanFlags, long currentTime,
+ @PackageManagerService.ScanFlags int scanFlags,
@Nullable UserHandle user) throws PackageManagerException {
final boolean scanSystemPartition =
(parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0;
@@ -3927,7 +3925,7 @@
}
final ScanResult scanResult = scanPackageNewLI(parsedPackage, parseFlags,
- scanFlags | SCAN_UPDATE_SIGNATURE, currentTime, user, null);
+ scanFlags | SCAN_UPDATE_SIGNATURE, 0 /* currentTime */, user, null);
return new Pair<>(scanResult, shouldHideSystemApp);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 248944e..548eb58 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -241,7 +241,6 @@
import com.android.server.pm.pkg.SuspendParams;
import com.android.server.pm.pkg.mutate.PackageStateMutator;
import com.android.server.pm.pkg.mutate.PackageStateWrite;
-import com.android.server.pm.pkg.mutate.PackageUserStateWrite;
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
import com.android.server.pm.verify.domain.DomainVerificationService;
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
@@ -965,7 +964,7 @@
private final BroadcastHelper mBroadcastHelper;
private final RemovePackageHelper mRemovePackageHelper;
private final DeletePackageHelper mDeletePackageHelper;
- private final InitAndSystemPackageHelper mInitAndSystemPackageHelper;
+ private final InitAppsHelper mInitAppsHelper;
private final AppDataHelper mAppDataHelper;
private final InstallPackageHelper mInstallPackageHelper;
private final PreferredActivityHelper mPreferredActivityHelper;
@@ -1701,7 +1700,7 @@
mAppDataHelper = testParams.appDataHelper;
mInstallPackageHelper = testParams.installPackageHelper;
mRemovePackageHelper = testParams.removePackageHelper;
- mInitAndSystemPackageHelper = testParams.initAndSystemPackageHelper;
+ mInitAppsHelper = testParams.initAndSystemPackageHelper;
mDeletePackageHelper = testParams.deletePackageHelper;
mPreferredActivityHelper = testParams.preferredActivityHelper;
mResolveIntentHelper = testParams.resolveIntentHelper;
@@ -1845,7 +1844,8 @@
mAppDataHelper = new AppDataHelper(this);
mInstallPackageHelper = new InstallPackageHelper(this, mAppDataHelper);
mRemovePackageHelper = new RemovePackageHelper(this, mAppDataHelper);
- mInitAndSystemPackageHelper = new InitAndSystemPackageHelper(this);
+ mInitAppsHelper = new InitAppsHelper(this, mApexManager, mInstallPackageHelper,
+ mInjector.getScanningPackageParser(), mInjector.getSystemPartitions());
mDeletePackageHelper = new DeletePackageHelper(this, mRemovePackageHelper,
mAppDataHelper);
mSharedLibraries.setDeletePackageHelper(mDeletePackageHelper);
@@ -1977,8 +1977,8 @@
mIsEngBuild, mIsUserDebugBuild, mIncrementalVersion);
final int[] userIds = mUserManager.getUserIds();
- mOverlayConfig = mInitAndSystemPackageHelper.initPackages(packageSettings,
- userIds, startTime);
+ mOverlayConfig = mInitAppsHelper.initSystemApps(packageSettings, userIds, startTime);
+ mInitAppsHelper.initNonSystemApps(userIds, startTime);
// Resolve the storage manager.
mStorageManagerPackage = getStorageManagerPackageName();
@@ -9147,7 +9147,7 @@
}
boolean isExpectingBetter(String packageName) {
- return mInitAndSystemPackageHelper.isExpectingBetter(packageName);
+ return mInitAppsHelper.isExpectingBetter(packageName);
}
int getDefParseFlags() {
@@ -9256,7 +9256,7 @@
@ScanFlags int getSystemPackageScanFlags(File codePath) {
List<ScanPartition> dirsToScanAsSystem =
- mInitAndSystemPackageHelper.getDirsToScanAsSystem();
+ mInitAppsHelper.getDirsToScanAsSystem();
@PackageManagerService.ScanFlags int scanFlags = SCAN_AS_SYSTEM;
for (int i = dirsToScanAsSystem.size() - 1; i >= 0; i--) {
ScanPartition partition = dirsToScanAsSystem.get(i);
@@ -9274,7 +9274,7 @@
Pair<Integer, Integer> getSystemPackageRescanFlagsAndReparseFlags(File scanFile,
int systemScanFlags, int systemParseFlags) {
List<ScanPartition> dirsToScanAsSystem =
- mInitAndSystemPackageHelper.getDirsToScanAsSystem();
+ mInitAppsHelper.getDirsToScanAsSystem();
@ParsingPackageUtils.ParseFlags int reparseFlags = 0;
@PackageManagerService.ScanFlags int rescanFlags = 0;
for (int i1 = dirsToScanAsSystem.size() - 1; i1 >= 0; i1--) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
index a1acc38..168401a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
@@ -106,7 +106,7 @@
public AppDataHelper appDataHelper;
public InstallPackageHelper installPackageHelper;
public RemovePackageHelper removePackageHelper;
- public InitAndSystemPackageHelper initAndSystemPackageHelper;
+ public InitAppsHelper initAndSystemPackageHelper;
public DeletePackageHelper deletePackageHelper;
public PreferredActivityHelper preferredActivityHelper;
public ResolveIntentHelper resolveIntentHelper;
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index be2bdaa..e27ad17 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2745,7 +2745,7 @@
IUserManager um = IUserManager.Stub.asInterface(
ServiceManager.getService(Context.USER_SERVICE));
if (setEphemeralIfInUse) {
- return removeUserOrSetEphemeral(um, userId);
+ return removeUserWhenPossible(um, userId);
} else {
final boolean success = wait ? removeUserAndWait(um, userId) : removeUser(um, userId);
if (success) {
@@ -2808,10 +2808,10 @@
}
}
- private int removeUserOrSetEphemeral(IUserManager um, @UserIdInt int userId)
+ private int removeUserWhenPossible(IUserManager um, @UserIdInt int userId)
throws RemoteException {
Slog.i(TAG, "Removing " + userId + " or set as ephemeral if in use.");
- int result = um.removeUserOrSetEphemeral(userId, /* evenWhenDisallowed= */ false);
+ int result = um.removeUserWhenPossible(userId, /* overrideDevicePolicy= */ false);
switch (result) {
case UserManager.REMOVE_RESULT_REMOVED:
getOutPrintWriter().printf("Success: user %d removed\n", userId);
diff --git a/services/core/java/com/android/server/pm/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java
index 1433abd..de64405 100644
--- a/services/core/java/com/android/server/pm/StorageEventHelper.java
+++ b/services/core/java/com/android/server/pm/StorageEventHelper.java
@@ -150,7 +150,7 @@
final AndroidPackage pkg;
try {
pkg = installPackageHelper.scanSystemPackageTracedLI(
- ps.getPath(), parseFlags, SCAN_INITIAL, 0, null);
+ ps.getPath(), parseFlags, SCAN_INITIAL, null);
loaded.add(pkg);
} catch (PackageManagerException e) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index fb3ca91..d29dbbc 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -2487,20 +2487,25 @@
return false;
}
- // Limit the number of profiles that can be created
- final int maxUsersOfType = getMaxUsersOfTypePerParent(type);
- if (maxUsersOfType != UserTypeDetails.UNLIMITED_NUMBER_OF_USERS) {
- final int userTypeCount = getProfileIds(userId, userType, false).length;
- final int profilesRemovedCount = userTypeCount > 0 && allowedToRemoveOne ? 1 : 0;
- if (userTypeCount - profilesRemovedCount >= maxUsersOfType) {
+ final int userTypeCount = getProfileIds(userId, userType, false).length;
+ final int profilesRemovedCount = userTypeCount > 0 && allowedToRemoveOne ? 1 : 0;
+ final int usersCountAfterRemoving = getAliveUsersExcludingGuestsCountLU()
+ - profilesRemovedCount;
+
+ // Limit total number of users that can be created
+ if (usersCountAfterRemoving >= UserManager.getMaxSupportedUsers()) {
+ // Special case: Allow creating a managed profile anyway if there's only 1 user
+ // Otherwise, disallow.
+ if (!(isManagedProfile && usersCountAfterRemoving == 1)) {
return false;
}
- // Allow creating a managed profile in the special case where there is only one user
- if (isManagedProfile) {
- int usersCountAfterRemoving = getAliveUsersExcludingGuestsCountLU()
- - profilesRemovedCount;
- return usersCountAfterRemoving == 1
- || usersCountAfterRemoving < UserManager.getMaxSupportedUsers();
+ }
+
+ // Limit the number of profiles of this type that can be created.
+ final int maxUsersOfType = getMaxUsersOfTypePerParent(type);
+ if (maxUsersOfType != UserTypeDetails.UNLIMITED_NUMBER_OF_USERS) {
+ if (userTypeCount - profilesRemovedCount >= maxUsersOfType) {
+ return false;
}
}
}
@@ -3753,6 +3758,7 @@
final boolean isGuest = UserManager.isUserTypeGuest(userType);
final boolean isRestricted = UserManager.isUserTypeRestricted(userType);
final boolean isDemo = UserManager.isUserTypeDemo(userType);
+ final boolean isManagedProfile = UserManager.isUserTypeManagedProfile(userType);
final long ident = Binder.clearCallingIdentity();
UserInfo userInfo;
@@ -3776,6 +3782,14 @@
+ ". Maximum number of that type already exists.",
UserManager.USER_OPERATION_ERROR_MAX_USERS);
}
+ if (!isGuest && !isManagedProfile && !isDemo && isUserLimitReached()) {
+ // If the user limit has been reached, we cannot add a user (except guest/demo).
+ // Note that managed profiles can bypass it in certain circumstances (taken
+ // into account in the profile check below).
+ throwCheckedUserOperationException(
+ "Cannot add user. Maximum user limit is reached.",
+ UserManager.USER_OPERATION_ERROR_MAX_USERS);
+ }
// TODO(b/142482943): Perhaps let the following code apply to restricted users too.
if (isProfile && !canAddMoreProfilesToUser(userType, parentId, false)) {
throwCheckedUserOperationException(
@@ -3783,13 +3797,6 @@
+ " for user " + parentId,
UserManager.USER_OPERATION_ERROR_MAX_USERS);
}
- if (!isGuest && !isProfile && !isDemo && isUserLimitReached()) {
- // If we're not adding a guest/demo user or a profile and the 'user limit' has
- // been reached, cannot add a user.
- throwCheckedUserOperationException(
- "Cannot add user. Maximum user limit is reached.",
- UserManager.USER_OPERATION_ERROR_MAX_USERS);
- }
// In legacy mode, restricted profile's parent can only be the owner user
if (isRestricted && !UserManager.isSplitSystemUser()
&& (parentId != UserHandle.USER_SYSTEM)) {
@@ -4430,11 +4437,11 @@
}
@Override
- public @UserManager.RemoveResult int removeUserOrSetEphemeral(@UserIdInt int userId,
- boolean evenWhenDisallowed) {
+ public @UserManager.RemoveResult int removeUserWhenPossible(@UserIdInt int userId,
+ boolean overrideDevicePolicy) {
checkCreateUsersPermission("Only the system can remove users");
- if (!evenWhenDisallowed) {
+ if (!overrideDevicePolicy) {
final String restriction = getUserRemovalRestriction(userId);
if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean(restriction, false)) {
Slog.w(LOG_TAG, "Cannot remove user. " + restriction + " is enabled.");
diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
index 27a16e9..17a5fd0 100644
--- a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
+++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
@@ -94,6 +94,7 @@
private static final String VENDOR_CONFIG_FILE_PATH = "etc/devicestate/";
private static final String DATA_CONFIG_FILE_PATH = "system/devicestate/";
private static final String CONFIG_FILE_NAME = "device_state_configuration.xml";
+ private static final String FLAG_CANCEL_OVERRIDE_REQUESTS = "FLAG_CANCEL_OVERRIDE_REQUESTS";
/** Interface that allows reading the device state configuration. */
interface ReadableConfig {
@@ -141,8 +142,8 @@
for (int i = 0; i < configFlagStrings.size(); i++) {
final String configFlagString = configFlagStrings.get(i);
switch (configFlagString) {
- case "FLAG_CANCEL_STICKY_REQUESTS":
- flags |= DeviceState.FLAG_CANCEL_STICKY_REQUESTS;
+ case FLAG_CANCEL_OVERRIDE_REQUESTS:
+ flags |= DeviceState.FLAG_CANCEL_OVERRIDE_REQUESTS;
break;
default:
Slog.w(TAG, "Parsed unknown flag with name: "
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 07a5849..16176f0 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -3436,7 +3436,10 @@
metricsState.getLatestTelephonySuggestion()),
convertTimeZoneSuggestionToProtoBytes(
metricsState.getLatestGeolocationSuggestion()),
- metricsState.isTelephonyTimeZoneFallbackSupported()
+ metricsState.isTelephonyTimeZoneFallbackSupported(),
+ metricsState.getDeviceTimeZoneId(),
+ metricsState.isEnhancedMetricsCollectionEnabled(),
+ metricsState.getGeoDetectionRunInBackgroundEnabled()
));
} catch (RuntimeException e) {
Slog.e(TAG, "Getting time zone detection state failed: ", e);
@@ -3483,6 +3486,14 @@
android.app.time.MetricsTimeZoneSuggestion.TIME_ZONE_ORDINALS,
zoneIdOrdinal);
}
+ String[] zoneIds = suggestion.getZoneIds();
+ if (zoneIds != null) {
+ for (String zoneId : zoneIds) {
+ protoOutputStream.write(
+ android.app.time.MetricsTimeZoneSuggestion.TIME_ZONE_IDS,
+ zoneId);
+ }
+ }
}
protoOutputStream.flush();
closeQuietly(byteArrayOutputStream);
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
index b23f11a..36ab111d 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
@@ -281,15 +281,7 @@
LocationTimeZoneProvider primary = mPrimaryProviderConfig.createProvider();
LocationTimeZoneProvider secondary = mSecondaryProviderConfig.createProvider();
LocationTimeZoneProviderController.MetricsLogger metricsLogger =
- new LocationTimeZoneProviderController.MetricsLogger() {
- @Override
- public void onStateChange(
- @LocationTimeZoneProviderController.State String state) {
- // TODO b/200279201 - wire this up to metrics code
- // No-op.
- }
- };
-
+ new RealControllerMetricsLogger();
boolean recordStateChanges = mServiceConfigAccessor.getRecordStateChangesForTests();
LocationTimeZoneProviderController controller =
new LocationTimeZoneProviderController(mThreadingDomain, metricsLogger,
diff --git a/services/core/java/com/android/server/timezonedetector/location/RealControllerMetricsLogger.java b/services/core/java/com/android/server/timezonedetector/location/RealControllerMetricsLogger.java
new file mode 100644
index 0000000..9cb36ef
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/location/RealControllerMetricsLogger.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2021 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.timezonedetector.location;
+
+import static com.android.internal.util.FrameworkStatsLog.LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__CERTAIN;
+import static com.android.internal.util.FrameworkStatsLog.LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__DESTROYED;
+import static com.android.internal.util.FrameworkStatsLog.LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__FAILED;
+import static com.android.internal.util.FrameworkStatsLog.LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__INITIALIZING;
+import static com.android.internal.util.FrameworkStatsLog.LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__PROVIDERS_INITIALIZING;
+import static com.android.internal.util.FrameworkStatsLog.LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__STOPPED;
+import static com.android.internal.util.FrameworkStatsLog.LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__UNCERTAIN;
+import static com.android.internal.util.FrameworkStatsLog.LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__UNKNOWN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProviderController.STATE_CERTAIN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProviderController.STATE_DESTROYED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProviderController.STATE_FAILED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProviderController.STATE_INITIALIZING;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProviderController.STATE_PROVIDERS_INITIALIZING;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProviderController.STATE_STOPPED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProviderController.STATE_UNCERTAIN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProviderController.STATE_UNKNOWN;
+
+import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.timezonedetector.location.LocationTimeZoneProviderController.State;
+
+/**
+ * The real implementation of {@link LocationTimeZoneProviderController.MetricsLogger} which logs
+ * using {@link FrameworkStatsLog}.
+ */
+final class RealControllerMetricsLogger
+ implements LocationTimeZoneProviderController.MetricsLogger {
+
+ RealControllerMetricsLogger() {
+ }
+
+ @Override
+ public void onStateChange(@State String state) {
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED,
+ metricsState(state));
+ }
+
+ private static int metricsState(@State String state) {
+ switch (state) {
+ case STATE_PROVIDERS_INITIALIZING:
+ // Disable lint check (line length) for generated long constant name.
+ // CHECKSTYLE:OFF Generated code
+ return LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__PROVIDERS_INITIALIZING;
+ // CHECKSTYLE:ON Generated code
+ case STATE_STOPPED:
+ return LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__STOPPED;
+ case STATE_INITIALIZING:
+ return LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__INITIALIZING;
+ case STATE_CERTAIN:
+ return LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__CERTAIN;
+ case STATE_UNCERTAIN:
+ return LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__UNCERTAIN;
+ case STATE_DESTROYED:
+ return LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__DESTROYED;
+ case STATE_FAILED:
+ return LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__FAILED;
+ case STATE_UNKNOWN:
+ default:
+ return LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__UNKNOWN;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/timezonedetector/location/RealProviderMetricsLogger.java b/services/core/java/com/android/server/timezonedetector/location/RealProviderMetricsLogger.java
index e19ec84..fe543ad 100644
--- a/services/core/java/com/android/server/timezonedetector/location/RealProviderMetricsLogger.java
+++ b/services/core/java/com/android/server/timezonedetector/location/RealProviderMetricsLogger.java
@@ -41,12 +41,12 @@
* The real implementation of {@link ProviderMetricsLogger} which logs using
* {@link FrameworkStatsLog}.
*/
-public class RealProviderMetricsLogger implements ProviderMetricsLogger {
+final class RealProviderMetricsLogger implements ProviderMetricsLogger {
@IntRange(from = 0, to = 1)
private final int mProviderIndex;
- public RealProviderMetricsLogger(@IntRange(from = 0, to = 1) int providerIndex) {
+ RealProviderMetricsLogger(@IntRange(from = 0, to = 1) int providerIndex) {
mProviderIndex = providerIndex;
}
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index e066ca3..e786fa2 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -1851,18 +1851,19 @@
}
@Override
- public void setIAppNotificationEnabled(IBinder sessionToken, boolean enabled, int userId) {
+ public void setInteractiveAppNotificationEnabled(
+ IBinder sessionToken, boolean enabled, int userId) {
final int callingUid = Binder.getCallingUid();
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
- userId, "setIAppNotificationEnabled");
+ userId, "setInteractiveAppNotificationEnabled");
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
try {
getSessionLocked(sessionToken, callingUid, resolvedUserId)
- .setIAppNotificationEnabled(enabled);
+ .setInteractiveAppNotificationEnabled(enabled);
} catch (RemoteException | SessionNotFoundException e) {
- Slog.e(TAG, "error in setIAppNotificationEnabled", e);
+ Slog.e(TAG, "error in setInteractiveAppNotificationEnabled", e);
}
}
} finally {
@@ -3538,6 +3539,23 @@
}
@Override
+ public void onSignalStrength(int strength) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onSignalStrength(" + strength + ")");
+ }
+ if (mSessionState.session == null || mSessionState.client == null) {
+ return;
+ }
+ try {
+ mSessionState.client.onSignalStrength(strength, mSessionState.seq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onSignalStrength", e);
+ }
+ }
+ }
+
+ @Override
public void onTuned(Uri channelUri) {
synchronized (mLock) {
if (DEBUG) {
diff --git a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
index a4732c1..6058d88 100644
--- a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
+++ b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
@@ -34,15 +34,15 @@
import android.media.tv.BroadcastInfoRequest;
import android.media.tv.BroadcastInfoResponse;
import android.media.tv.TvTrackInfo;
-import android.media.tv.interactive.ITvIAppClient;
import android.media.tv.interactive.ITvIAppManager;
-import android.media.tv.interactive.ITvIAppManagerCallback;
-import android.media.tv.interactive.ITvIAppService;
-import android.media.tv.interactive.ITvIAppServiceCallback;
-import android.media.tv.interactive.ITvIAppSession;
-import android.media.tv.interactive.ITvIAppSessionCallback;
-import android.media.tv.interactive.TvIAppInfo;
+import android.media.tv.interactive.ITvInteractiveAppClient;
+import android.media.tv.interactive.ITvInteractiveAppManagerCallback;
+import android.media.tv.interactive.ITvInteractiveAppService;
+import android.media.tv.interactive.ITvInteractiveAppServiceCallback;
+import android.media.tv.interactive.ITvInteractiveAppSession;
+import android.media.tv.interactive.ITvInteractiveAppSessionCallback;
import android.media.tv.interactive.TvIAppService;
+import android.media.tv.interactive.TvInteractiveAppInfo;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
@@ -53,6 +53,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.util.ArrayMap;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.view.InputChannel;
@@ -112,54 +113,55 @@
}
@GuardedBy("mLock")
- private void buildTvIAppServiceListLocked(int userId, String[] updatedPackages) {
+ private void buildTvInteractiveAppServiceListLocked(int userId, String[] updatedPackages) {
UserState userState = getOrCreateUserStateLocked(userId);
userState.mPackageSet.clear();
if (DEBUG) {
- Slogf.d(TAG, "buildTvIAppServiceListLocked");
+ Slogf.d(TAG, "buildTvInteractiveAppServiceListLocked");
}
PackageManager pm = mContext.getPackageManager();
List<ResolveInfo> services = pm.queryIntentServicesAsUser(
new Intent(TvIAppService.SERVICE_INTERFACE),
PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
userId);
- List<TvIAppInfo> iAppList = new ArrayList<>();
+ List<TvInteractiveAppInfo> iAppList = new ArrayList<>();
for (ResolveInfo ri : services) {
ServiceInfo si = ri.serviceInfo;
- // TODO: add BIND_TV_IAPP permission and check it here
+ // TODO: add BIND_TV_INTERACTIVE_APP permission and check it here
ComponentName component = new ComponentName(si.packageName, si.name);
try {
- TvIAppInfo info = new TvIAppInfo.Builder(mContext, component).build();
+ TvInteractiveAppInfo info =
+ new TvInteractiveAppInfo(mContext, component);
iAppList.add(info);
} catch (Exception e) {
- Slogf.e(TAG, "failed to load TV IApp service " + si.name, e);
+ Slogf.e(TAG, "failed to load TV Interactive App service " + si.name, e);
continue;
}
userState.mPackageSet.add(si.packageName);
}
// sort the iApp list by iApp service id
- Collections.sort(iAppList, Comparator.comparing(TvIAppInfo::getId));
- Map<String, TvIAppState> iAppMap = new HashMap<>();
+ Collections.sort(iAppList, Comparator.comparing(TvInteractiveAppInfo::getId));
+ Map<String, TvInteractiveAppState> iAppMap = new HashMap<>();
ArrayMap<String, Integer> tiasAppCount = new ArrayMap<>(iAppMap.size());
- for (TvIAppInfo info : iAppList) {
+ for (TvInteractiveAppInfo info : iAppList) {
String iAppServiceId = info.getId();
if (DEBUG) {
Slogf.d(TAG, "add " + iAppServiceId);
}
- // Running count of IApp for each IApp service
+ // Running count of Interactive App for each Interactive App service
Integer count = tiasAppCount.get(iAppServiceId);
count = count == null ? 1 : count + 1;
tiasAppCount.put(iAppServiceId, count);
- TvIAppState iAppState = userState.mIAppMap.get(iAppServiceId);
+ TvInteractiveAppState iAppState = userState.mIAppMap.get(iAppServiceId);
if (iAppState == null) {
- iAppState = new TvIAppState();
+ iAppState = new TvInteractiveAppState();
}
iAppState.mInfo = info;
- iAppState.mUid = getIAppUid(info);
+ iAppState.mUid = getInteractiveAppUid(info);
iAppState.mComponentName = info.getComponent();
iAppMap.put(iAppServiceId, iAppState);
iAppState.mIAppNumber = count;
@@ -167,14 +169,14 @@
for (String iAppServiceId : iAppMap.keySet()) {
if (!userState.mIAppMap.containsKey(iAppServiceId)) {
- notifyIAppServiceAddedLocked(userState, iAppServiceId);
+ notifyInteractiveAppServiceAddedLocked(userState, iAppServiceId);
} else if (updatedPackages != null) {
// Notify the package updates
ComponentName component = iAppMap.get(iAppServiceId).mInfo.getComponent();
for (String updatedPackage : updatedPackages) {
if (component.getPackageName().equals(updatedPackage)) {
updateServiceConnectionLocked(component, userId);
- notifyIAppServiceUpdatedLocked(userState, iAppServiceId);
+ notifyInteractiveAppServiceUpdatedLocked(userState, iAppServiceId);
break;
}
}
@@ -183,12 +185,12 @@
for (String iAppServiceId : userState.mIAppMap.keySet()) {
if (!iAppMap.containsKey(iAppServiceId)) {
- TvIAppInfo info = userState.mIAppMap.get(iAppServiceId).mInfo;
+ TvInteractiveAppInfo info = userState.mIAppMap.get(iAppServiceId).mInfo;
ServiceState serviceState = userState.mServiceStateMap.get(info.getComponent());
if (serviceState != null) {
abortPendingCreateSessionRequestsLocked(serviceState, iAppServiceId, userId);
}
- notifyIAppServiceRemovedLocked(userState, iAppServiceId);
+ notifyInteractiveAppServiceRemovedLocked(userState, iAppServiceId);
}
}
@@ -197,48 +199,56 @@
}
@GuardedBy("mLock")
- private void notifyIAppServiceAddedLocked(UserState userState, String iAppServiceId) {
+ private void notifyInteractiveAppServiceAddedLocked(UserState userState, String iAppServiceId) {
if (DEBUG) {
- Slog.d(TAG, "notifyIAppServiceAddedLocked(iAppServiceId=" + iAppServiceId + ")");
+ Slog.d(TAG, "notifyInteractiveAppServiceAddedLocked(iAppServiceId="
+ + iAppServiceId + ")");
}
int n = userState.mCallbacks.beginBroadcast();
for (int i = 0; i < n; ++i) {
try {
- userState.mCallbacks.getBroadcastItem(i).onIAppServiceAdded(iAppServiceId);
+ userState.mCallbacks.getBroadcastItem(i)
+ .onInteractiveAppServiceAdded(iAppServiceId);
} catch (RemoteException e) {
- Slog.e(TAG, "failed to report added IApp service to callback", e);
+ Slog.e(TAG, "failed to report added Interactive App service to callback", e);
}
}
userState.mCallbacks.finishBroadcast();
}
@GuardedBy("mLock")
- private void notifyIAppServiceRemovedLocked(UserState userState, String iAppServiceId) {
+ private void notifyInteractiveAppServiceRemovedLocked(
+ UserState userState, String iAppServiceId) {
if (DEBUG) {
- Slog.d(TAG, "notifyIAppServiceRemovedLocked(iAppServiceId=" + iAppServiceId + ")");
+ Slog.d(TAG, "notifyInteractiveAppServiceRemovedLocked(iAppServiceId="
+ + iAppServiceId + ")");
}
int n = userState.mCallbacks.beginBroadcast();
for (int i = 0; i < n; ++i) {
try {
- userState.mCallbacks.getBroadcastItem(i).onIAppServiceRemoved(iAppServiceId);
+ userState.mCallbacks.getBroadcastItem(i)
+ .onInteractiveAppServiceRemoved(iAppServiceId);
} catch (RemoteException e) {
- Slog.e(TAG, "failed to report removed IApp service to callback", e);
+ Slog.e(TAG, "failed to report removed Interactive App service to callback", e);
}
}
userState.mCallbacks.finishBroadcast();
}
@GuardedBy("mLock")
- private void notifyIAppServiceUpdatedLocked(UserState userState, String iAppServiceId) {
+ private void notifyInteractiveAppServiceUpdatedLocked(
+ UserState userState, String iAppServiceId) {
if (DEBUG) {
- Slog.d(TAG, "notifyIAppServiceUpdatedLocked(iAppServiceId=" + iAppServiceId + ")");
+ Slog.d(TAG, "notifyInteractiveAppServiceUpdatedLocked(iAppServiceId="
+ + iAppServiceId + ")");
}
int n = userState.mCallbacks.beginBroadcast();
for (int i = 0; i < n; ++i) {
try {
- userState.mCallbacks.getBroadcastItem(i).onIAppServiceUpdated(iAppServiceId);
+ userState.mCallbacks.getBroadcastItem(i)
+ .onInteractiveAppServiceUpdated(iAppServiceId);
} catch (RemoteException e) {
- Slog.e(TAG, "failed to report updated IApp service to callback", e);
+ Slog.e(TAG, "failed to report updated Interactive App service to callback", e);
}
}
userState.mCallbacks.finishBroadcast();
@@ -262,7 +272,7 @@
userState.mCallbacks.finishBroadcast();
}
- private int getIAppUid(TvIAppInfo info) {
+ private int getInteractiveAppUid(TvInteractiveAppInfo info) {
try {
return getContext().getPackageManager().getApplicationInfo(
info.getServiceInfo().packageName, 0).uid;
@@ -286,18 +296,18 @@
registerBroadcastReceivers();
} else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
synchronized (mLock) {
- buildTvIAppServiceListLocked(mCurrentUserId, null);
+ buildTvInteractiveAppServiceListLocked(mCurrentUserId, null);
}
}
}
private void registerBroadcastReceivers() {
PackageMonitor monitor = new PackageMonitor() {
- private void buildTvIAppServiceList(String[] packages) {
+ private void buildTvInteractiveAppServiceList(String[] packages) {
int userId = getChangingUserId();
synchronized (mLock) {
if (mCurrentUserId == userId || mRunningProfiles.contains(userId)) {
- buildTvIAppServiceListLocked(userId, packages);
+ buildTvInteractiveAppServiceListLocked(userId, packages);
}
}
}
@@ -305,9 +315,9 @@
@Override
public void onPackageUpdateFinished(String packageName, int uid) {
if (DEBUG) Slogf.d(TAG, "onPackageUpdateFinished(packageName=" + packageName + ")");
- // This callback is invoked when the TV iApp service is reinstalled.
+ // This callback is invoked when the TV interactive App service is reinstalled.
// In this case, isReplacing() always returns true.
- buildTvIAppServiceList(new String[] { packageName });
+ buildTvInteractiveAppServiceList(new String[] { packageName });
}
@Override
@@ -318,7 +328,7 @@
// This callback is invoked when the media on which some packages exist become
// available.
if (isReplacing()) {
- buildTvIAppServiceList(packages);
+ buildTvInteractiveAppServiceList(packages);
}
}
@@ -331,7 +341,7 @@
+ ")");
}
if (isReplacing()) {
- buildTvIAppServiceList(packages);
+ buildTvInteractiveAppServiceList(packages);
}
}
@@ -339,17 +349,19 @@
public void onSomePackagesChanged() {
if (DEBUG) Slogf.d(TAG, "onSomePackagesChanged()");
if (isReplacing()) {
- if (DEBUG) Slogf.d(TAG, "Skipped building TV iApp list due to replacing");
- // When the package is updated, buildTvIAppServiceListLocked is called in other
- // methods instead.
+ if (DEBUG) {
+ Slogf.d(TAG, "Skipped building TV interactive App list due to replacing");
+ }
+ // When the package is updated, buildTvInteractiveAppServiceListLocked is called
+ // in other methods instead.
return;
}
- buildTvIAppServiceList(null);
+ buildTvInteractiveAppServiceList(null);
}
@Override
public boolean onPackageChanged(String packageName, int uid, String[] components) {
- // The iApp list needs to be updated in any cases, regardless of whether
+ // The interactive App list needs to be updated in any cases, regardless of whether
// it happened to the whole package or a specific component. Returning true so that
// the update can be handled in {@link #onSomePackagesChanged}.
return true;
@@ -401,7 +413,7 @@
unbindServiceOfUserLocked(mCurrentUserId);
mCurrentUserId = userId;
- buildTvIAppServiceListLocked(userId, null);
+ buildTvInteractiveAppServiceListLocked(userId, null);
}
}
@@ -486,7 +498,7 @@
@GuardedBy("mLock")
private void startProfileLocked(int userId) {
mRunningProfiles.add(userId);
- buildTvIAppServiceListLocked(userId, null);
+ buildTvInteractiveAppServiceListLocked(userId, null);
}
@GuardedBy("mLock")
@@ -601,13 +613,14 @@
}
@GuardedBy("mLock")
- private ITvIAppSession getSessionLocked(IBinder sessionToken, int callingUid, int userId) {
+ private ITvInteractiveAppSession getSessionLocked(
+ IBinder sessionToken, int callingUid, int userId) {
return getSessionLocked(getSessionStateLocked(sessionToken, callingUid, userId));
}
@GuardedBy("mLock")
- private ITvIAppSession getSessionLocked(SessionState sessionState) {
- ITvIAppSession session = sessionState.mSession;
+ private ITvInteractiveAppSession getSessionLocked(SessionState sessionState) {
+ ITvInteractiveAppSession session = sessionState.mSession;
if (session == null) {
throw new IllegalStateException("Session not yet created for token "
+ sessionState.mSessionToken);
@@ -618,15 +631,15 @@
private final class BinderService extends ITvIAppManager.Stub {
@Override
- public List<TvIAppInfo> getTvIAppServiceList(int userId) {
+ public List<TvInteractiveAppInfo> getTvInteractiveAppServiceList(int userId) {
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
- Binder.getCallingUid(), userId, "getTvIAppServiceList");
+ Binder.getCallingUid(), userId, "getTvInteractiveAppServiceList");
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
UserState userState = getOrCreateUserStateLocked(resolvedUserId);
- List<TvIAppInfo> iAppList = new ArrayList<>();
- for (TvIAppState state : userState.mIAppMap.values()) {
+ List<TvInteractiveAppInfo> iAppList = new ArrayList<>();
+ for (TvInteractiveAppState state : userState.mIAppMap.values()) {
iAppList.add(state.mInfo);
}
return iAppList;
@@ -645,7 +658,7 @@
try {
synchronized (mLock) {
UserState userState = getOrCreateUserStateLocked(resolvedUserId);
- TvIAppState iAppState = userState.mIAppMap.get(tiasId);
+ TvInteractiveAppState iAppState = userState.mIAppMap.get(tiasId);
if (iAppState == null) {
Slogf.e(TAG, "failed to prepare TIAS - unknown TIAS id " + tiasId);
return;
@@ -673,16 +686,16 @@
}
@Override
- public void notifyAppLinkInfo(String tiasId, Bundle appLinkInfo, int userId) {
+ public void registerAppLinkInfo(String tiasId, Bundle appLinkInfo, int userId) {
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
- Binder.getCallingUid(), userId, "notifyAppLinkInfo");
+ Binder.getCallingUid(), userId, "registerAppLinkInfo");
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
UserState userState = getOrCreateUserStateLocked(resolvedUserId);
- TvIAppState iAppState = userState.mIAppMap.get(tiasId);
+ TvInteractiveAppState iAppState = userState.mIAppMap.get(tiasId);
if (iAppState == null) {
- Slogf.e(TAG, "failed to notifyAppLinkInfo - unknown TIAS id "
+ Slogf.e(TAG, "failed to registerAppLinkInfo - unknown TIAS id "
+ tiasId);
return;
}
@@ -691,18 +704,54 @@
if (serviceState == null) {
serviceState = new ServiceState(
componentName, tiasId, resolvedUserId);
- serviceState.addPendingAppLink(appLinkInfo);
+ serviceState.addPendingAppLink(appLinkInfo, true);
userState.mServiceStateMap.put(componentName, serviceState);
updateServiceConnectionLocked(componentName, resolvedUserId);
} else if (serviceState.mService != null) {
- serviceState.mService.notifyAppLinkInfo(appLinkInfo);
+ serviceState.mService.registerAppLinkInfo(appLinkInfo);
} else {
- serviceState.addPendingAppLink(appLinkInfo);
+ serviceState.addPendingAppLink(appLinkInfo, true);
updateServiceConnectionLocked(componentName, resolvedUserId);
}
}
} catch (RemoteException e) {
- Slogf.e(TAG, "error in notifyAppLinkInfo", e);
+ Slogf.e(TAG, "error in registerAppLinkInfo", e);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void unregisterAppLinkInfo(String tiasId, Bundle appLinkInfo, int userId) {
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
+ Binder.getCallingUid(), userId, "unregisterAppLinkInfo");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ UserState userState = getOrCreateUserStateLocked(resolvedUserId);
+ TvInteractiveAppState iAppState = userState.mIAppMap.get(tiasId);
+ if (iAppState == null) {
+ Slogf.e(TAG, "failed to unregisterAppLinkInfo - unknown TIAS id "
+ + tiasId);
+ return;
+ }
+ ComponentName componentName = iAppState.mInfo.getComponent();
+ ServiceState serviceState = userState.mServiceStateMap.get(componentName);
+ if (serviceState == null) {
+ serviceState = new ServiceState(
+ componentName, tiasId, resolvedUserId);
+ serviceState.addPendingAppLink(appLinkInfo, false);
+ userState.mServiceStateMap.put(componentName, serviceState);
+ updateServiceConnectionLocked(componentName, resolvedUserId);
+ } else if (serviceState.mService != null) {
+ serviceState.mService.unregisterAppLinkInfo(appLinkInfo);
+ } else {
+ serviceState.addPendingAppLink(appLinkInfo, false);
+ updateServiceConnectionLocked(componentName, resolvedUserId);
+ }
+ }
+ } catch (RemoteException e) {
+ Slogf.e(TAG, "error in unregisterAppLinkInfo", e);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -716,7 +765,7 @@
try {
synchronized (mLock) {
UserState userState = getOrCreateUserStateLocked(resolvedUserId);
- TvIAppState iAppState = userState.mIAppMap.get(tiasId);
+ TvInteractiveAppState iAppState = userState.mIAppMap.get(tiasId);
if (iAppState == null) {
Slogf.e(TAG, "failed to sendAppLinkCommand - unknown TIAS id "
+ tiasId);
@@ -743,7 +792,8 @@
}
@Override
- public void createSession(final ITvIAppClient client, final String iAppServiceId, int type,
+ public void createSession(
+ final ITvInteractiveAppClient client, final String iAppServiceId, int type,
int seq, int userId) {
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
@@ -760,7 +810,7 @@
return;
}
UserState userState = getOrCreateUserStateLocked(resolvedUserId);
- TvIAppState iAppState = userState.mIAppMap.get(iAppServiceId);
+ TvInteractiveAppState iAppState = userState.mIAppMap.get(iAppServiceId);
if (iAppState == null) {
Slogf.w(TAG, "Failed to find state for iAppServiceId=" + iAppServiceId);
sendSessionTokenToClientLocked(client, iAppServiceId, null, null, seq);
@@ -994,13 +1044,35 @@
}
@Override
- public void startIApp(IBinder sessionToken, int userId) {
+ public void notifySignalStrength(IBinder sessionToken, int strength, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId,
+ "notifySignalStrength");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
+ resolvedUserId);
+ getSessionLocked(sessionState).notifySignalStrength(strength);
+ } catch (RemoteException | SessionNotFoundException e) {
+ Slogf.e(TAG, "error in notifySignalStrength", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void startInteractiveApp(IBinder sessionToken, int userId) {
if (DEBUG) {
Slogf.d(TAG, "BinderService#start(userId=%d)", userId);
}
final int callingUid = Binder.getCallingUid();
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
- userId, "startIApp");
+ userId, "startInteractiveApp");
SessionState sessionState = null;
final long identity = Binder.clearCallingIdentity();
try {
@@ -1008,7 +1080,7 @@
try {
sessionState = getSessionStateLocked(sessionToken, callingUid,
resolvedUserId);
- getSessionLocked(sessionState).startIApp();
+ getSessionLocked(sessionState).startInteractiveApp();
} catch (RemoteException | SessionNotFoundException e) {
Slogf.e(TAG, "error in start", e);
}
@@ -1019,13 +1091,13 @@
}
@Override
- public void stopIApp(IBinder sessionToken, int userId) {
+ public void stopInteractiveApp(IBinder sessionToken, int userId) {
if (DEBUG) {
Slogf.d(TAG, "BinderService#stop(userId=%d)", userId);
}
final int callingUid = Binder.getCallingUid();
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
- userId, "stopIApp");
+ userId, "stopInteractiveApp");
SessionState sessionState = null;
final long identity = Binder.clearCallingIdentity();
try {
@@ -1033,7 +1105,7 @@
try {
sessionState = getSessionStateLocked(sessionToken, callingUid,
resolvedUserId);
- getSessionLocked(sessionState).stopIApp();
+ getSessionLocked(sessionState).stopInteractiveApp();
} catch (RemoteException | SessionNotFoundException e) {
Slogf.e(TAG, "error in stop", e);
}
@@ -1044,6 +1116,31 @@
}
@Override
+ public void resetInteractiveApp(IBinder sessionToken, int userId) {
+ if (DEBUG) {
+ Slogf.d(TAG, "BinderService#reset(userId=%d)", userId);
+ }
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "resetInteractiveApp");
+ SessionState sessionState = null;
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ sessionState = getSessionStateLocked(sessionToken, callingUid,
+ resolvedUserId);
+ getSessionLocked(sessionState).resetInteractiveApp();
+ } catch (RemoteException | SessionNotFoundException e) {
+ Slogf.e(TAG, "error in reset", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public void createBiInteractiveApp(
IBinder sessionToken, Uri biIAppUri, Bundle params, int userId) {
if (DEBUG) {
@@ -1096,6 +1193,31 @@
}
@Override
+ public void setTeletextAppEnabled(IBinder sessionToken, boolean enable, int userId) {
+ if (DEBUG) {
+ Slogf.d(TAG, "setTeletextAppEnabled(enable=%d)", enable);
+ }
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "setTeletextAppEnabled");
+ SessionState sessionState = null;
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ sessionState = getSessionStateLocked(sessionToken, callingUid,
+ resolvedUserId);
+ getSessionLocked(sessionState).setTeletextAppEnabled(enable);
+ } catch (RemoteException | SessionNotFoundException e) {
+ Slogf.e(TAG, "error in setTeletextAppEnabled", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public void sendCurrentChannelUri(IBinder sessionToken, Uri channelUri, int userId) {
if (DEBUG) {
Slogf.d(TAG, "sendCurrentChannelUri(channelUri=%s)", channelUri.toString());
@@ -1196,6 +1318,31 @@
}
@Override
+ public void sendCurrentTvInputId(IBinder sessionToken, String inputId, int userId) {
+ if (DEBUG) {
+ Slogf.d(TAG, "sendCurrentTvInputId(inputId=%s)", inputId);
+ }
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "sendCurrentTvInputId");
+ SessionState sessionState = null;
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ sessionState = getSessionStateLocked(sessionToken, callingUid,
+ resolvedUserId);
+ getSessionLocked(sessionState).sendCurrentTvInputId(inputId);
+ } catch (RemoteException | SessionNotFoundException e) {
+ Slogf.e(TAG, "error in sendCurrentTvInputId", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public void setSurface(IBinder sessionToken, Surface surface, int userId) {
final int callingUid = Binder.getCallingUid();
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
@@ -1290,7 +1437,7 @@
}
@Override
- public void registerCallback(final ITvIAppManagerCallback callback, int userId) {
+ public void registerCallback(final ITvInteractiveAppManagerCallback callback, int userId) {
int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId,
@@ -1309,7 +1456,7 @@
}
@Override
- public void unregisterCallback(ITvIAppManagerCallback callback, int userId) {
+ public void unregisterCallback(ITvInteractiveAppManagerCallback callback, int userId) {
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
Binder.getCallingUid(), userId, "unregisterCallback");
final long identity = Binder.clearCallingIdentity();
@@ -1335,7 +1482,7 @@
try {
getSessionLocked(sessionToken, callingUid, resolvedUserId)
.createMediaView(windowToken, frame);
- } catch (RemoteException | TvIAppManagerService.SessionNotFoundException e) {
+ } catch (RemoteException | SessionNotFoundException e) {
Slog.e(TAG, "error in createMediaView", e);
}
}
@@ -1355,7 +1502,7 @@
try {
getSessionLocked(sessionToken, callingUid, resolvedUserId)
.relayoutMediaView(frame);
- } catch (RemoteException | TvIAppManagerService.SessionNotFoundException e) {
+ } catch (RemoteException | SessionNotFoundException e) {
Slog.e(TAG, "error in relayoutMediaView", e);
}
}
@@ -1375,7 +1522,7 @@
try {
getSessionLocked(sessionToken, callingUid, resolvedUserId)
.removeMediaView();
- } catch (RemoteException | TvIAppManagerService.SessionNotFoundException e) {
+ } catch (RemoteException | SessionNotFoundException e) {
Slog.e(TAG, "error in removeMediaView", e);
}
}
@@ -1386,8 +1533,9 @@
}
@GuardedBy("mLock")
- private void sendSessionTokenToClientLocked(ITvIAppClient client, String iAppServiceId,
- IBinder sessionToken, InputChannel channel, int seq) {
+ private void sendSessionTokenToClientLocked(
+ ITvInteractiveAppClient client, String iAppServiceId, IBinder sessionToken,
+ InputChannel channel, int seq) {
try {
client.onSessionCreated(iAppServiceId, sessionToken, channel, seq);
} catch (RemoteException e) {
@@ -1396,8 +1544,8 @@
}
@GuardedBy("mLock")
- private boolean createSessionInternalLocked(ITvIAppService service, IBinder sessionToken,
- int userId) {
+ private boolean createSessionInternalLocked(
+ ITvInteractiveAppService service, IBinder sessionToken, int userId) {
UserState userState = getOrCreateUserStateLocked(userId);
SessionState sessionState = userState.mSessionStateMap.get(sessionToken);
if (DEBUG) {
@@ -1407,7 +1555,7 @@
InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString());
// Set up a callback to send the session token.
- ITvIAppSessionCallback callback = new SessionCallback(sessionState, channels);
+ ITvInteractiveAppSessionCallback callback = new SessionCallback(sessionState, channels);
boolean created = true;
// Create a session. When failed, send a null token immediately.
@@ -1514,7 +1662,8 @@
}
boolean shouldBind = (!serviceState.mSessionTokens.isEmpty())
- || (serviceState.mPendingPrepare) || (!serviceState.mPendingAppLinkInfo.isEmpty());
+ || (serviceState.mPendingPrepare)
+ || (!serviceState.mPendingAppLinkInfo.isEmpty());
if (serviceState.mService == null && shouldBind) {
// This means that the service is not yet connected but its state indicates that we
@@ -1528,7 +1677,8 @@
Slogf.d(TAG, "bindServiceAsUser(service=" + component + ", userId=" + userId + ")");
}
- Intent i = new Intent(TvIAppService.SERVICE_INTERFACE).setComponent(component);
+ Intent i =
+ new Intent(TvIAppService.SERVICE_INTERFACE).setComponent(component);
serviceState.mBound = mContext.bindServiceAsUser(
i, serviceState.mConnection,
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
@@ -1546,20 +1696,20 @@
private static final class UserState {
private final int mUserId;
- // A mapping from the TV IApp ID to its TvIAppState.
- private Map<String, TvIAppState> mIAppMap = new HashMap<>();
+ // A mapping from the TV Interactive App ID to its TvInteractiveAppState.
+ private Map<String, TvInteractiveAppState> mIAppMap = new HashMap<>();
// A mapping from the token of a client to its state.
private final Map<IBinder, ClientState> mClientStateMap = new HashMap<>();
- // A mapping from the name of a TV IApp service to its state.
+ // A mapping from the name of a TV Interactive App service to its state.
private final Map<ComponentName, ServiceState> mServiceStateMap = new HashMap<>();
- // A mapping from the token of a TV IApp session to its state.
+ // A mapping from the token of a TV Interactive App session to its state.
private final Map<IBinder, SessionState> mSessionStateMap = new HashMap<>();
- // A set of all TV IApp service packages.
+ // A set of all TV Interactive App service packages.
private final Set<String> mPackageSet = new HashSet<>();
// A list of callbacks.
- private final RemoteCallbackList<ITvIAppManagerCallback> mCallbacks =
+ private final RemoteCallbackList<ITvInteractiveAppManagerCallback> mCallbacks =
new RemoteCallbackList<>();
private UserState(int userId) {
@@ -1567,20 +1717,20 @@
}
}
- private static final class TvIAppState {
+ private static final class TvInteractiveAppState {
private String mIAppServiceId;
private ComponentName mComponentName;
- private TvIAppInfo mInfo;
+ private TvInteractiveAppInfo mInfo;
private int mUid;
private int mIAppNumber;
}
private final class SessionState implements IBinder.DeathRecipient {
private final IBinder mSessionToken;
- private ITvIAppSession mSession;
+ private ITvInteractiveAppSession mSession;
private final String mIAppServiceId;
private final int mType;
- private final ITvIAppClient mClient;
+ private final ITvInteractiveAppClient mClient;
private final int mSeq;
private final ComponentName mComponent;
@@ -1595,8 +1745,8 @@
private final int mUserId;
private SessionState(IBinder sessionToken, String iAppServiceId, int type,
- ComponentName componentName, ITvIAppClient client, int seq, int callingUid,
- int callingPid, int userId) {
+ ComponentName componentName, ITvInteractiveAppClient client, int seq,
+ int callingUid, int callingPid, int userId) {
mSessionToken = sessionToken;
mIAppServiceId = iAppServiceId;
mComponent = componentName;
@@ -1660,12 +1810,12 @@
private final ServiceConnection mConnection;
private final ComponentName mComponent;
private final String mIAppServiceId;
- private final List<Bundle> mPendingAppLinkInfo = new ArrayList<>();
+ private final List<Pair<Bundle, Boolean>> mPendingAppLinkInfo = new ArrayList<>();
private final List<Bundle> mPendingAppLinkCommand = new ArrayList<>();
private boolean mPendingPrepare = false;
private Integer mPendingPrepareType = null;
- private ITvIAppService mService;
+ private ITvInteractiveAppService mService;
private ServiceCallback mCallback;
private boolean mBound;
private boolean mReconnecting;
@@ -1679,12 +1829,12 @@
mComponent = component;
mPendingPrepare = pendingPrepare;
mPendingPrepareType = prepareType;
- mConnection = new IAppServiceConnection(component, userId);
+ mConnection = new InteractiveAppServiceConnection(component, userId);
mIAppServiceId = tias;
}
- private void addPendingAppLink(Bundle info) {
- mPendingAppLinkInfo.add(info);
+ private void addPendingAppLink(Bundle info, boolean register) {
+ mPendingAppLinkInfo.add(Pair.create(info, register));
}
private void addPendingAppLinkCommand(Bundle command) {
@@ -1692,11 +1842,11 @@
}
}
- private final class IAppServiceConnection implements ServiceConnection {
+ private final class InteractiveAppServiceConnection implements ServiceConnection {
private final ComponentName mComponent;
private final int mUserId;
- private IAppServiceConnection(ComponentName component, int userId) {
+ private InteractiveAppServiceConnection(ComponentName component, int userId) {
mComponent = component;
mUserId = userId;
}
@@ -1714,7 +1864,7 @@
return;
}
ServiceState serviceState = userState.mServiceStateMap.get(mComponent);
- serviceState.mService = ITvIAppService.Stub.asInterface(service);
+ serviceState.mService = ITvInteractiveAppService.Stub.asInterface(service);
if (serviceState.mPendingPrepare) {
final long identity = Binder.clearCallingIdentity();
@@ -1730,15 +1880,20 @@
}
if (!serviceState.mPendingAppLinkInfo.isEmpty()) {
- for (Iterator<Bundle> it = serviceState.mPendingAppLinkInfo.iterator();
+ for (Iterator<Pair<Bundle, Boolean>> it =
+ serviceState.mPendingAppLinkInfo.iterator();
it.hasNext(); ) {
- Bundle appLinkInfo = it.next();
+ Pair<Bundle, Boolean> appLinkInfoPair = it.next();
final long identity = Binder.clearCallingIdentity();
try {
- serviceState.mService.notifyAppLinkInfo(appLinkInfo);
+ if (appLinkInfoPair.second) {
+ serviceState.mService.registerAppLinkInfo(appLinkInfoPair.first);
+ } else {
+ serviceState.mService.unregisterAppLinkInfo(appLinkInfoPair.first);
+ }
it.remove();
} catch (RemoteException e) {
- Slogf.e(TAG, "error in notifyAppLinkInfo(" + appLinkInfo
+ Slogf.e(TAG, "error in notifyAppLinkInfo(" + appLinkInfoPair
+ ") when onServiceConnected", e);
} finally {
Binder.restoreCallingIdentity(identity);
@@ -1803,7 +1958,7 @@
}
}
- private final class ServiceCallback extends ITvIAppServiceCallback.Stub {
+ private final class ServiceCallback extends ITvInteractiveAppServiceCallback.Stub {
private final ComponentName mComponent;
private final int mUserId;
@@ -1828,7 +1983,7 @@
}
}
- private final class SessionCallback extends ITvIAppSessionCallback.Stub {
+ private final class SessionCallback extends ITvInteractiveAppSessionCallback.Stub {
private final SessionState mSessionState;
private final InputChannel[] mInputChannels;
@@ -1838,7 +1993,7 @@
}
@Override
- public void onSessionCreated(ITvIAppSession session) {
+ public void onSessionCreated(ITvInteractiveAppSession session) {
if (DEBUG) {
Slogf.d(TAG, "onSessionCreated(iAppServiceId="
+ mSessionState.mIAppServiceId + ")");
@@ -1916,7 +2071,8 @@
}
@Override
- public void onCommandRequest(@TvIAppService.IAppServiceCommandType String cmdType,
+ public void onCommandRequest(
+ @TvIAppService.InteractiveAppServiceCommandType String cmdType,
Bundle parameters) {
synchronized (mLock) {
if (DEBUG) {
@@ -2020,6 +2176,23 @@
}
@Override
+ public void onRequestCurrentTvInputId() {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slogf.d(TAG, "onRequestCurrentTvInputId");
+ }
+ if (mSessionState.mSession == null || mSessionState.mClient == null) {
+ return;
+ }
+ try {
+ mSessionState.mClient.onRequestCurrentTvInputId(mSessionState.mSeq);
+ } catch (RemoteException e) {
+ Slogf.e(TAG, "error in onRequestCurrentTvInputId", e);
+ }
+ }
+ }
+
+ @Override
public void onAdRequest(AdRequest request) {
synchronized (mLock) {
if (DEBUG) {
@@ -2072,8 +2245,25 @@
}
}
+ @Override
+ public void onTeletextAppStateChanged(int state) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slogf.d(TAG, "onTeletextAppStateChanged (state=" + state + ")");
+ }
+ if (mSessionState.mSession == null || mSessionState.mClient == null) {
+ return;
+ }
+ try {
+ mSessionState.mClient.onTeletextAppStateChanged(state, mSessionState.mSeq);
+ } catch (RemoteException e) {
+ Slogf.e(TAG, "error in onTeletextAppStateChanged", e);
+ }
+ }
+ }
+
@GuardedBy("mLock")
- private boolean addSessionTokenToClientStateLocked(ITvIAppSession session) {
+ private boolean addSessionTokenToClientStateLocked(ITvInteractiveAppSession session) {
try {
session.asBinder().linkToDeath(mSessionState, 0);
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
index 6db25b7..c96c1ee 100644
--- a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
+++ b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
@@ -22,8 +22,6 @@
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
-import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_ANY;
-import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK;
import static com.android.server.VcnManagementService.LOCAL_LOG;
@@ -47,6 +45,7 @@
import com.android.server.vcn.VcnContext;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
/** @hide */
@@ -121,9 +120,10 @@
TelephonySubscriptionSnapshot snapshot,
UnderlyingNetworkRecord currentlySelected,
PersistableBundle carrierConfig) {
- // TODO: Check Network Quality reported by metric monitors/probers.
-
final NetworkCapabilities caps = networkRecord.networkCapabilities;
+ final boolean isSelectedUnderlyingNetwork =
+ currentlySelected != null
+ && Objects.equals(currentlySelected.network, networkRecord.network);
final int meteredMatch = networkPriority.getMetered();
final boolean isMetered = !caps.hasCapability(NET_CAPABILITY_NOT_METERED);
@@ -132,6 +132,23 @@
return false;
}
+ // Fails bandwidth requirements if either (a) less than exit threshold, or (b), not
+ // selected, but less than entry threshold
+ if (caps.getLinkUpstreamBandwidthKbps() < networkPriority.getMinExitUpstreamBandwidthKbps()
+ || (caps.getLinkUpstreamBandwidthKbps()
+ < networkPriority.getMinEntryUpstreamBandwidthKbps()
+ && !isSelectedUnderlyingNetwork)) {
+ return false;
+ }
+
+ if (caps.getLinkDownstreamBandwidthKbps()
+ < networkPriority.getMinExitDownstreamBandwidthKbps()
+ || (caps.getLinkDownstreamBandwidthKbps()
+ < networkPriority.getMinEntryDownstreamBandwidthKbps()
+ && !isSelectedUnderlyingNetwork)) {
+ return false;
+ }
+
if (vcnContext.isInTestMode() && caps.hasTransport(TRANSPORT_TEST)) {
return true;
}
@@ -172,8 +189,7 @@
}
// TODO: Move the Network Quality check to the network metric monitor framework.
- if (networkPriority.getNetworkQuality()
- > getWifiQuality(networkRecord, currentlySelected, carrierConfig)) {
+ if (!isWifiRssiAcceptable(networkRecord, currentlySelected, carrierConfig)) {
return false;
}
@@ -185,7 +201,7 @@
return true;
}
- private static int getWifiQuality(
+ private static boolean isWifiRssiAcceptable(
UnderlyingNetworkRecord networkRecord,
UnderlyingNetworkRecord currentlySelected,
PersistableBundle carrierConfig) {
@@ -196,14 +212,14 @@
if (isSelectedNetwork
&& caps.getSignalStrength() >= getWifiExitRssiThreshold(carrierConfig)) {
- return NETWORK_QUALITY_OK;
+ return true;
}
if (caps.getSignalStrength() >= getWifiEntryRssiThreshold(carrierConfig)) {
- return NETWORK_QUALITY_OK;
+ return true;
}
- return NETWORK_QUALITY_ANY;
+ return false;
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 8f703c5..0396a11 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -45,6 +45,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowTracing.WINSCOPE_EXT;
+import static com.android.server.wm.utils.RegionUtils.forEachRect;
import android.accessibilityservice.AccessibilityTrace;
import android.animation.ObjectAnimator;
@@ -100,7 +101,6 @@
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
import com.android.server.policy.WindowManagerPolicy;
-import com.android.server.wm.AccessibilityWindowsPopulator.AccessibilityWindow;
import com.android.server.wm.WindowManagerInternal.AccessibilityControllerInternal;
import com.android.server.wm.WindowManagerInternal.MagnificationCallbacks;
import com.android.server.wm.WindowManagerInternal.WindowsForAccessibilityCallback;
@@ -133,22 +133,19 @@
private static final Rect EMPTY_RECT = new Rect();
private static final float[] sTempFloats = new float[9];
- private final SparseArray<DisplayMagnifier> mDisplayMagnifiers = new SparseArray<>();
- private final SparseArray<WindowsForAccessibilityObserver> mWindowsForAccessibilityObserver =
+ private SparseArray<DisplayMagnifier> mDisplayMagnifiers = new SparseArray<>();
+ private SparseArray<WindowsForAccessibilityObserver> mWindowsForAccessibilityObserver =
new SparseArray<>();
private SparseArray<IBinder> mFocusedWindow = new SparseArray<>();
private int mFocusedDisplay = -1;
private boolean mIsImeVisible = false;
// Set to true if initializing window population complete.
private boolean mAllObserversInitialized = true;
- private final AccessibilityWindowsPopulator mAccessibilityWindowsPopulator;
AccessibilityController(WindowManagerService service) {
mService = service;
mAccessibilityTracing =
AccessibilityController.getAccessibilityControllerInternal(service);
-
- mAccessibilityWindowsPopulator = new AccessibilityWindowsPopulator(mService, this);
}
boolean setMagnificationCallbacks(int displayId, MagnificationCallbacks callbacks) {
@@ -212,9 +209,7 @@
}
mWindowsForAccessibilityObserver.remove(displayId);
}
- mAccessibilityWindowsPopulator.setWindowsNotification(true);
- observer = new WindowsForAccessibilityObserver(mService, displayId, callback,
- mAccessibilityWindowsPopulator);
+ observer = new WindowsForAccessibilityObserver(mService, displayId, callback);
mWindowsForAccessibilityObserver.put(displayId, observer);
mAllObserversInitialized &= observer.mInitialized;
} else {
@@ -229,10 +224,6 @@
}
}
mWindowsForAccessibilityObserver.remove(displayId);
-
- if (mWindowsForAccessibilityObserver.size() <= 0) {
- mAccessibilityWindowsPopulator.setWindowsNotification(false);
- }
}
}
@@ -318,6 +309,11 @@
if (displayMagnifier != null) {
displayMagnifier.onDisplaySizeChanged(displayContent);
}
+ final WindowsForAccessibilityObserver windowsForA11yObserver =
+ mWindowsForAccessibilityObserver.get(displayId);
+ if (windowsForA11yObserver != null) {
+ windowsForA11yObserver.scheduleComputeChangedWindows();
+ }
}
void onAppWindowTransition(int displayId, int transition) {
@@ -345,6 +341,11 @@
if (displayMagnifier != null) {
displayMagnifier.onWindowTransition(windowState, transition);
}
+ final WindowsForAccessibilityObserver windowsForA11yObserver =
+ mWindowsForAccessibilityObserver.get(displayId);
+ if (windowsForA11yObserver != null) {
+ windowsForA11yObserver.scheduleComputeChangedWindows();
+ }
}
void onWindowFocusChangedNot(int displayId) {
@@ -454,19 +455,6 @@
return null;
}
- boolean getMagnificationSpecForDisplay(int displayId, MagnificationSpec outSpec) {
- if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
- mAccessibilityTracing.logTrace(TAG + ".getMagnificationSpecForDisplay",
- FLAGS_MAGNIFICATION_CALLBACK, "displayId=" + displayId);
- }
- final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
- if (displayMagnifier == null) {
- return false;
- }
-
- return displayMagnifier.getMagnificationSpec(outSpec);
- }
-
boolean hasCallbacks() {
if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
| FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
@@ -768,25 +756,6 @@
return spec;
}
- boolean getMagnificationSpec(MagnificationSpec outSpec) {
- if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
- mAccessibilityTracing.logTrace(LOG_TAG + ".getMagnificationSpec",
- FLAGS_MAGNIFICATION_CALLBACK);
- }
- MagnificationSpec spec = mMagnifedViewport.getMagnificationSpec();
- if (spec == null) {
- return false;
- }
-
- outSpec.setTo(spec);
- if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
- mAccessibilityTracing.logTrace(LOG_TAG + ".getMagnificationSpec",
- FLAGS_MAGNIFICATION_CALLBACK, "outSpec={" + outSpec + "}");
- }
-
- return true;
- }
-
void getMagnificationRegion(Region outMagnificationRegion) {
if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
mAccessibilityTracing.logTrace(LOG_TAG + ".getMagnificationRegion",
@@ -1434,18 +1403,20 @@
private static final boolean DEBUG = false;
- private final List<AccessibilityWindow> mTempA11yWindows = new ArrayList<>();
+ private final SparseArray<WindowState> mTempWindowStates = new SparseArray<>();
private final Set<IBinder> mTempBinderSet = new ArraySet<>();
+ private final RectF mTempRectF = new RectF();
+
+ private final Matrix mTempMatrix = new Matrix();
+
private final Point mTempPoint = new Point();
private final Region mTempRegion = new Region();
private final Region mTempRegion1 = new Region();
- private final Region mTempRegion2 = new Region();
-
private final WindowManagerService mService;
private final Handler mHandler;
@@ -1460,11 +1431,10 @@
// Set to true if initializing window population complete.
private boolean mInitialized;
- private final AccessibilityWindowsPopulator mA11yWindowsPopulator;
WindowsForAccessibilityObserver(WindowManagerService windowManagerService,
- int displayId, WindowsForAccessibilityCallback callback,
- AccessibilityWindowsPopulator accessibilityWindowsPopulator) {
+ int displayId,
+ WindowsForAccessibilityCallback callback) {
mService = windowManagerService;
mCallback = callback;
mDisplayId = displayId;
@@ -1473,7 +1443,6 @@
AccessibilityController.getAccessibilityControllerInternal(mService);
mRecurringAccessibilityEventsIntervalMillis = ViewConfiguration
.getSendRecurringAccessibilityEventsInterval();
- mA11yWindowsPopulator = accessibilityWindowsPopulator;
computeChangedWindows(true);
}
@@ -1497,6 +1466,52 @@
}
}
+ boolean shellRootIsAbove(WindowState windowState, ShellRoot shellRoot) {
+ int wsLayer = mService.mPolicy.getWindowLayerLw(windowState);
+ int shellLayer = mService.mPolicy.getWindowLayerFromTypeLw(shellRoot.getWindowType(),
+ true);
+ return shellLayer >= wsLayer;
+ }
+
+ int addShellRootsIfAbove(WindowState windowState, ArrayList<ShellRoot> shellRoots,
+ int shellRootIndex, List<WindowInfo> windows, Set<IBinder> addedWindows,
+ Region unaccountedSpace, boolean focusedWindowAdded) {
+ while (shellRootIndex < shellRoots.size()
+ && shellRootIsAbove(windowState, shellRoots.get(shellRootIndex))) {
+ ShellRoot shellRoot = shellRoots.get(shellRootIndex);
+ shellRootIndex++;
+ final WindowInfo info = shellRoot.getWindowInfo();
+ if (info == null) {
+ continue;
+ }
+
+ info.layer = addedWindows.size();
+ windows.add(info);
+ addedWindows.add(info.token);
+ unaccountedSpace.op(info.regionInScreen, unaccountedSpace,
+ Region.Op.REVERSE_DIFFERENCE);
+ if (unaccountedSpace.isEmpty() && focusedWindowAdded) {
+ break;
+ }
+ }
+ return shellRootIndex;
+ }
+
+ private ArrayList<ShellRoot> getSortedShellRoots(
+ SparseArray<ShellRoot> originalShellRoots) {
+ ArrayList<ShellRoot> sortedShellRoots = new ArrayList<>(originalShellRoots.size());
+ for (int i = originalShellRoots.size() - 1; i >= 0; --i) {
+ sortedShellRoots.add(originalShellRoots.valueAt(i));
+ }
+
+ sortedShellRoots.sort((left, right) ->
+ mService.mPolicy.getWindowLayerFromTypeLw(right.getWindowType(), true)
+ - mService.mPolicy.getWindowLayerFromTypeLw(left.getWindowType(),
+ true));
+
+ return sortedShellRoots;
+ }
+
/**
* Check if windows have changed, and send them to the accessibility subsystem if they have.
*
@@ -1546,29 +1561,44 @@
Region unaccountedSpace = mTempRegion;
unaccountedSpace.set(0, 0, screenWidth, screenHeight);
- final List<AccessibilityWindow> visibleWindows = mTempA11yWindows;
- mA11yWindowsPopulator.populateVisibleWindowsOnScreenLocked(
- mDisplayId, visibleWindows);
+ final SparseArray<WindowState> visibleWindows = mTempWindowStates;
+ populateVisibleWindowsOnScreen(visibleWindows);
Set<IBinder> addedWindows = mTempBinderSet;
addedWindows.clear();
boolean focusedWindowAdded = false;
final int visibleWindowCount = visibleWindows.size();
+ ArrayList<TaskFragment> skipRemainingWindowsForTaskFragments = new ArrayList<>();
+
+ ArrayList<ShellRoot> shellRoots = getSortedShellRoots(dc.mShellRoots);
// Iterate until we figure out what is touchable for the entire screen.
- for (int i = 0; i < visibleWindowCount; i++) {
- final AccessibilityWindow a11yWindow = visibleWindows.get(i);
- final Region regionInWindow = new Region();
- a11yWindow.getTouchableRegionInWindow(regionInWindow);
- if (windowMattersToAccessibility(a11yWindow, regionInWindow,
- unaccountedSpace)) {
- addPopulatedWindowInfo(a11yWindow, regionInWindow, windows, addedWindows);
- if (windowMattersToUnaccountedSpaceComputation(a11yWindow)) {
- updateUnaccountedSpace(a11yWindow, unaccountedSpace);
+ int shellRootIndex = 0;
+ for (int i = visibleWindowCount - 1; i >= 0; i--) {
+ final WindowState windowState = visibleWindows.valueAt(i);
+ int prevShellRootIndex = shellRootIndex;
+ shellRootIndex = addShellRootsIfAbove(windowState, shellRoots, shellRootIndex,
+ windows, addedWindows, unaccountedSpace, focusedWindowAdded);
+
+ // If a Shell Root was added, it could have accounted for all the space already.
+ if (shellRootIndex > prevShellRootIndex && unaccountedSpace.isEmpty()
+ && focusedWindowAdded) {
+ break;
+ }
+
+ final Region regionInScreen = new Region();
+ computeWindowRegionInScreen(windowState, regionInScreen);
+ if (windowMattersToAccessibility(windowState,
+ regionInScreen, unaccountedSpace,
+ skipRemainingWindowsForTaskFragments)) {
+ addPopulatedWindowInfo(windowState, regionInScreen, windows, addedWindows);
+ if (windowMattersToUnaccountedSpaceComputation(windowState)) {
+ updateUnaccountedSpace(windowState, regionInScreen, unaccountedSpace,
+ skipRemainingWindowsForTaskFragments);
}
- focusedWindowAdded |= a11yWindow.isFocused();
- } else if (a11yWindow.isUntouchableNavigationBar()) {
+ focusedWindowAdded |= windowState.isFocused();
+ } else if (isUntouchableNavigationBar(windowState, mTempRegion1)) {
// If this widow is navigation bar without touchable region, accounting the
// region of navigation bar inset because all touch events from this region
// would be received by launcher, i.e. this region is a un-touchable one
@@ -1617,39 +1647,47 @@
// Some windows should be excluded from unaccounted space computation, though they still
// should be reported
- private boolean windowMattersToUnaccountedSpaceComputation(AccessibilityWindow a11yWindow) {
+ private boolean windowMattersToUnaccountedSpaceComputation(WindowState windowState) {
// Do not account space of trusted non-touchable windows, except the split-screen
// divider.
// If it's not trusted, touch events are not sent to the windows behind it.
- if (((a11yWindow.getFlags() & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0)
- && (a11yWindow.getType() != TYPE_DOCK_DIVIDER)
- && a11yWindow.isTrustedOverlay()) {
+ if (((windowState.mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0)
+ && (windowState.mAttrs.type != TYPE_DOCK_DIVIDER)
+ && windowState.isTrustedOverlay()) {
return false;
}
- if (a11yWindow.getType() == WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) {
+ if (windowState.mAttrs.type
+ == WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) {
return false;
}
return true;
}
- private boolean windowMattersToAccessibility(AccessibilityWindow a11yWindow,
- Region regionInScreen, Region unaccountedSpace) {
- if (a11yWindow.ignoreRecentsAnimationForAccessibility()) {
+ private boolean windowMattersToAccessibility(WindowState windowState,
+ Region regionInScreen, Region unaccountedSpace,
+ ArrayList<TaskFragment> skipRemainingWindowsForTaskFragments) {
+ final RecentsAnimationController controller = mService.getRecentsAnimationController();
+ if (controller != null && controller.shouldIgnoreForAccessibility(windowState)) {
return false;
}
- if (a11yWindow.isFocused()) {
+ if (windowState.isFocused()) {
return true;
}
+ // If the window is part of a task that we're finished with - ignore.
+ final TaskFragment taskFragment = windowState.getTaskFragment();
+ if (taskFragment != null
+ && skipRemainingWindowsForTaskFragments.contains(taskFragment)) {
+ return false;
+ }
+
// Ignore non-touchable windows, except the split-screen divider, which is
// occasionally non-touchable but still useful for identifying split-screen
- // mode and the PIP menu.
- if (((a11yWindow.getFlags()
- & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0)
- && (a11yWindow.getType() != TYPE_DOCK_DIVIDER
- && !a11yWindow.isPIPMenu())) {
+ // mode.
+ if (((windowState.mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0)
+ && (windowState.mAttrs.type != TYPE_DOCK_DIVIDER)) {
return false;
}
@@ -1659,36 +1697,88 @@
}
// Add windows of certain types not covered by modal windows.
- if (isReportedWindowType(a11yWindow.getType())) {
+ if (isReportedWindowType(windowState.mAttrs.type)) {
return true;
}
return false;
}
- private void updateUnaccountedSpace(AccessibilityWindow a11yWindow,
- Region unaccountedSpace) {
- if (a11yWindow.getType()
- != WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) {
- // Account for the space this window takes if the window
- // is not an accessibility overlay which does not change
- // the reported windows.
- final Region touchableRegion = mTempRegion2;
- a11yWindow.getTouchableRegionInScreen(touchableRegion);
- unaccountedSpace.op(touchableRegion, unaccountedSpace,
- Region.Op.REVERSE_DIFFERENCE);
- // Account for the space of letterbox.
- final Region letterboxBounds = mTempRegion1;
- if (a11yWindow.setLetterBoxBoundsIfNeeded(letterboxBounds)) {
- unaccountedSpace.op(letterboxBounds,
- unaccountedSpace, Region.Op.REVERSE_DIFFERENCE);
+ private void updateUnaccountedSpace(WindowState windowState, Region regionInScreen,
+ Region unaccountedSpace,
+ ArrayList<TaskFragment> skipRemainingWindowsForTaskFragments) {
+ // Account for the space this window takes if the window
+ // is not an accessibility overlay which does not change
+ // the reported windows.
+ unaccountedSpace.op(regionInScreen, unaccountedSpace,
+ Region.Op.REVERSE_DIFFERENCE);
+
+ // If a window is modal it prevents other windows from being touched
+ if ((windowState.mAttrs.flags & (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)) == 0) {
+ if (!windowState.hasTapExcludeRegion()) {
+ // Account for all space in the task, whether the windows in it are
+ // touchable or not. The modal window blocks all touches from the task's
+ // area.
+ unaccountedSpace.op(windowState.getDisplayFrame(), unaccountedSpace,
+ Region.Op.REVERSE_DIFFERENCE);
+ } else {
+ // If a window has tap exclude region, we need to account it.
+ final Region displayRegion = new Region(windowState.getDisplayFrame());
+ final Region tapExcludeRegion = new Region();
+ windowState.getTapExcludeRegion(tapExcludeRegion);
+ displayRegion.op(tapExcludeRegion, displayRegion,
+ Region.Op.REVERSE_DIFFERENCE);
+ unaccountedSpace.op(displayRegion, unaccountedSpace,
+ Region.Op.REVERSE_DIFFERENCE);
}
+
+ final TaskFragment taskFragment = windowState.getTaskFragment();
+ if (taskFragment != null) {
+ // If the window is associated with a particular task, we can skip the
+ // rest of the windows for that task.
+ skipRemainingWindowsForTaskFragments.add(taskFragment);
+ } else if (!windowState.hasTapExcludeRegion()) {
+ // If the window is not associated with a particular task, then it is
+ // globally modal. In this case we can skip all remaining windows when
+ // it doesn't has tap exclude region.
+ unaccountedSpace.setEmpty();
+ }
+ }
+
+ // Account for the space of letterbox.
+ if (windowState.areAppWindowBoundsLetterboxed()) {
+ unaccountedSpace.op(getLetterboxBounds(windowState), unaccountedSpace,
+ Region.Op.REVERSE_DIFFERENCE);
}
}
- private static void addPopulatedWindowInfo(AccessibilityWindow a11yWindow,
- Region regionInScreen, List<WindowInfo> out, Set<IBinder> tokenOut) {
- final WindowInfo window = a11yWindow.getWindowInfo();
+ private void computeWindowRegionInScreen(WindowState windowState, Region outRegion) {
+ // Get the touchable frame.
+ Region touchableRegion = mTempRegion1;
+ windowState.getTouchableRegion(touchableRegion);
+
+ // Map the frame to get what appears on the screen.
+ Matrix matrix = mTempMatrix;
+ populateTransformationMatrix(windowState, matrix);
+
+ forEachRect(touchableRegion, rect -> {
+ // Move to origin as all transforms are captured by the matrix.
+ RectF windowFrame = mTempRectF;
+ windowFrame.set(rect);
+ windowFrame.offset(-windowState.getFrame().left, -windowState.getFrame().top);
+
+ matrix.mapRect(windowFrame);
+
+ // Union all rects.
+ outRegion.union(new Rect((int) windowFrame.left, (int) windowFrame.top,
+ (int) windowFrame.right, (int) windowFrame.bottom));
+ });
+ }
+
+ private static void addPopulatedWindowInfo(WindowState windowState, Region regionInScreen,
+ List<WindowInfo> out, Set<IBinder> tokenOut) {
+ final WindowInfo window = windowState.getWindowInfo();
window.regionInScreen.set(regionInScreen);
window.layer = tokenOut.size();
out.add(window);
@@ -1715,6 +1805,23 @@
&& windowType != WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION);
}
+ private void populateVisibleWindowsOnScreen(SparseArray<WindowState> outWindows) {
+ final List<WindowState> tempWindowStatesList = new ArrayList<>();
+ final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId);
+ if (dc == null) {
+ return;
+ }
+
+ dc.forAllWindows(w -> {
+ if (w.isVisible()) {
+ tempWindowStatesList.add(w);
+ }
+ }, false /* traverseTopToBottom */);
+ for (int i = 0; i < tempWindowStatesList.size(); i++) {
+ outWindows.put(i, tempWindowStatesList.get(i));
+ }
+ }
+
private WindowState getTopFocusWindow() {
return mService.mRoot.getTopFocusedDisplayContent().mCurrentFocus;
}
diff --git a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
deleted file mode 100644
index f31ae06..0000000
--- a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
+++ /dev/null
@@ -1,625 +0,0 @@
-/*
- * Copyright (C) 2021 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.wm;
-
-import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
-
-import static com.android.server.wm.utils.RegionUtils.forEachRect;
-
-import android.annotation.NonNull;
-import android.graphics.Matrix;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.Region;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.view.IWindow;
-import android.view.InputWindowHandle;
-import android.view.MagnificationSpec;
-import android.view.WindowInfo;
-import android.view.WindowManager;
-import android.window.WindowInfosListener;
-
-import com.android.internal.annotations.GuardedBy;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * This class is the accessibility windows population adapter.
- */
-public final class AccessibilityWindowsPopulator extends WindowInfosListener {
-
- private static final String TAG = AccessibilityWindowsPopulator.class.getSimpleName();
- // If the surface flinger callback is not coming within in 2 frames time, i.e. about
- // 35ms, then assuming the windows become stable.
- private static final int SURFACE_FLINGER_CALLBACK_WINDOWS_STABLE_TIMES_MS = 35;
- // To avoid the surface flinger callbacks always comes within in 2 frames, then no windows
- // are reported to the A11y framework, and the animation duration time is 500ms, so setting
- // this value as the max timeout value to force computing changed windows.
- private static final int WINDOWS_CHANGED_NOTIFICATION_MAX_DURATION_TIMES_MS = 500;
-
- private static final float[] sTempFloats = new float[9];
-
- private final WindowManagerService mService;
- private final AccessibilityController mAccessibilityController;
- @GuardedBy("mLock")
- private final SparseArray<List<InputWindowHandle>> mInputWindowHandlesOnDisplays =
- new SparseArray<>();
- @GuardedBy("mLock")
- private final SparseArray<Matrix> mMagnificationSpecInverseMatrix = new SparseArray<>();
- @GuardedBy("mLock")
- private final SparseArray<DisplayInfo> mDisplayInfos = new SparseArray<>();
- @GuardedBy("mLock")
- private final List<InputWindowHandle> mVisibleWindows = new ArrayList<>();
- @GuardedBy("mLock")
- private boolean mWindowsNotificationEnabled = false;
- private final Object mLock = new Object();
- private final Handler mHandler;
-
- AccessibilityWindowsPopulator(WindowManagerService service,
- AccessibilityController accessibilityController) {
- mService = service;
- mAccessibilityController = accessibilityController;
- mHandler = new MyHandler(mService.mH.getLooper());
-
- register();
- }
-
- /**
- * Gets the visible windows list with the window layer on the specified display.
- *
- * @param displayId The display.
- * @param outWindows The visible windows list. The z-order of each window in the list
- * is from the top to bottom.
- */
- public void populateVisibleWindowsOnScreenLocked(int displayId,
- List<AccessibilityWindow> outWindows) {
- List<InputWindowHandle> inputWindowHandles;
- final Matrix inverseMatrix = new Matrix();
- final Matrix displayMatrix = new Matrix();
-
- synchronized (mLock) {
- inputWindowHandles = mInputWindowHandlesOnDisplays.get(displayId);
- if (inputWindowHandles == null) {
- outWindows.clear();
-
- return;
- }
- inverseMatrix.set(mMagnificationSpecInverseMatrix.get(displayId));
-
- final DisplayInfo displayInfo = mDisplayInfos.get(displayId);
- if (displayInfo != null) {
- displayMatrix.set(displayInfo.mTransform);
- } else {
- Slog.w(TAG, "The displayInfo of this displayId (" + displayId + ") called "
- + "back from the surface fligner is null");
- }
- }
-
- final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
- final ShellRoot shellroot = dc.mShellRoots.get(WindowManager.SHELL_ROOT_LAYER_PIP);
- final IBinder pipMenuIBinder =
- shellroot != null ? shellroot.getAccessibilityWindowToken() : null;
-
- for (final InputWindowHandle windowHandle : inputWindowHandles) {
- final AccessibilityWindow accessibilityWindow =
- AccessibilityWindow.initializeData(mService, windowHandle, inverseMatrix,
- pipMenuIBinder, displayMatrix);
-
- outWindows.add(accessibilityWindow);
- }
- }
-
- @Override
- public void onWindowInfosChanged(InputWindowHandle[] windowHandles,
- DisplayInfo[] displayInfos) {
- synchronized (mLock) {
- mVisibleWindows.clear();
- for (InputWindowHandle window : windowHandles) {
- if (window.visible && window.getWindow() != null) {
- mVisibleWindows.add(window);
- }
- }
-
- mDisplayInfos.clear();
- for (final DisplayInfo displayInfo : displayInfos) {
- mDisplayInfos.put(displayInfo.mDisplayId, displayInfo);
- }
-
- if (mWindowsNotificationEnabled) {
- if (!mHandler.hasMessages(
- MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_TIMEOUT)) {
- mHandler.sendEmptyMessageDelayed(
- MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_TIMEOUT,
- WINDOWS_CHANGED_NOTIFICATION_MAX_DURATION_TIMES_MS);
- }
- populateVisibleWindowHandlesAndNotifyWindowsChangeIfNeededLocked();
- }
- }
- }
-
- /**
- * Sets to notify the accessibilityController to compute changed windows on
- * the display after populating the visible windows if the windows reported
- * from the surface flinger changes.
- *
- * @param register {@code true} means starting windows population.
- */
- public void setWindowsNotification(boolean register) {
- synchronized (mLock) {
- if (mWindowsNotificationEnabled == register) {
- return;
- }
- mWindowsNotificationEnabled = register;
- if (mWindowsNotificationEnabled) {
- populateVisibleWindowHandlesAndNotifyWindowsChangeIfNeededLocked();
- } else {
- releaseResources();
- }
- }
- }
-
- private void populateVisibleWindowHandlesAndNotifyWindowsChangeIfNeededLocked() {
- final SparseArray<List<InputWindowHandle>> tempWindowHandleList = new SparseArray<>();
-
- for (final InputWindowHandle windowHandle : mVisibleWindows) {
- List<InputWindowHandle> inputWindowHandles = tempWindowHandleList.get(
- windowHandle.displayId);
-
- if (inputWindowHandles == null) {
- inputWindowHandles = new ArrayList<>();
- tempWindowHandleList.put(windowHandle.displayId, inputWindowHandles);
- generateMagnificationSpecInverseMatrixLocked(windowHandle.displayId);
- }
- inputWindowHandles.add(windowHandle);
- }
-
- final List<Integer> displayIdsForWindowsChanged = new ArrayList<>();
-
- getDisplaysForWindowsChangedLocked(displayIdsForWindowsChanged, tempWindowHandleList,
- mInputWindowHandlesOnDisplays);
- // Clones all windows from the callback of the surface flinger.
- mInputWindowHandlesOnDisplays.clear();
- for (int i = 0; i < tempWindowHandleList.size(); i++) {
- final int displayId = tempWindowHandleList.keyAt(i);
- mInputWindowHandlesOnDisplays.put(displayId, tempWindowHandleList.get(displayId));
- }
-
- if (displayIdsForWindowsChanged.size() > 0) {
- if (!mHandler.hasMessages(MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED)) {
- mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED,
- displayIdsForWindowsChanged).sendToTarget();
- }
-
- return;
- }
- mHandler.removeMessages(MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_UI_STABLE);
- mHandler.sendEmptyMessageDelayed(MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_UI_STABLE,
- SURFACE_FLINGER_CALLBACK_WINDOWS_STABLE_TIMES_MS);
- }
-
- private void getDisplaysForWindowsChangedLocked(List<Integer> outDisplayIdsForWindowsChanged,
- SparseArray<List<InputWindowHandle>> newWindowsList,
- SparseArray<List<InputWindowHandle>> oldWindowsList) {
- for (int i = 0; i < newWindowsList.size(); i++) {
- final int displayId = newWindowsList.keyAt(i);
- final List<InputWindowHandle> newWindows = newWindowsList.get(displayId);
- final List<InputWindowHandle> oldWindows = oldWindowsList.get(displayId);
-
- if (hasWindowsChangedLocked(newWindows, oldWindows)) {
- outDisplayIdsForWindowsChanged.add(displayId);
- }
- }
- }
-
- private boolean hasWindowsChangedLocked(List<InputWindowHandle> newWindows,
- List<InputWindowHandle> oldWindows) {
- if (oldWindows == null || oldWindows.size() != newWindows.size()) {
- return true;
- }
-
- final int windowsCount = newWindows.size();
- // Since we always traverse windows from high to low layer,
- // the old and new windows at the same index should be the
- // same, otherwise something changed.
- for (int i = 0; i < windowsCount; i++) {
- final InputWindowHandle newWindow = newWindows.get(i);
- final InputWindowHandle oldWindow = oldWindows.get(i);
-
- if (!newWindow.getWindow().asBinder().equals(oldWindow.getWindow().asBinder())) {
- return true;
- }
- }
-
- return false;
- }
-
- private void generateMagnificationSpecInverseMatrixLocked(int displayId) {
- MagnificationSpec spec = new MagnificationSpec();
- if (!mAccessibilityController.getMagnificationSpecForDisplay(displayId, spec)) {
- return;
- }
- sTempFloats[Matrix.MSCALE_X] = spec.scale;
- sTempFloats[Matrix.MSKEW_Y] = 0;
- sTempFloats[Matrix.MSKEW_X] = 0;
- sTempFloats[Matrix.MSCALE_Y] = spec.scale;
- sTempFloats[Matrix.MTRANS_X] = spec.offsetX;
- sTempFloats[Matrix.MTRANS_Y] = spec.offsetY;
- sTempFloats[Matrix.MPERSP_0] = 0;
- sTempFloats[Matrix.MPERSP_1] = 0;
- sTempFloats[Matrix.MPERSP_2] = 1;
-
- final Matrix tempMatrix = new Matrix();
- tempMatrix.setValues(sTempFloats);
-
- final Matrix inverseMatrix = new Matrix();
- final boolean result = tempMatrix.invert(inverseMatrix);
-
- if (!result) {
- Slog.e(TAG, "Can't inverse the magnification spec matrix with the "
- + "magnification spec = " + spec + " on the displayId = " + displayId);
- return;
- }
- mMagnificationSpecInverseMatrix.set(displayId, inverseMatrix);
- }
-
- private void notifyWindowsChanged(@NonNull List<Integer> displayIdsForWindowsChanged) {
- mHandler.removeMessages(MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_TIMEOUT);
-
- for (int i = 0; i < displayIdsForWindowsChanged.size(); i++) {
- mAccessibilityController.performComputeChangedWindowsNot(
- displayIdsForWindowsChanged.get(i), false);
- }
- }
-
- private void forceUpdateWindows() {
- final List<Integer> displayIdsForWindowsChanged = new ArrayList<>();
-
- synchronized (mLock) {
- for (int i = 0; i < mInputWindowHandlesOnDisplays.size(); i++) {
- final int displayId = mInputWindowHandlesOnDisplays.keyAt(i);
- displayIdsForWindowsChanged.add(displayId);
- }
- }
- notifyWindowsChanged(displayIdsForWindowsChanged);
- }
-
- @GuardedBy("mLock")
- private void releaseResources() {
- mInputWindowHandlesOnDisplays.clear();
- mMagnificationSpecInverseMatrix.clear();
- mVisibleWindows.clear();
- mDisplayInfos.clear();
- mWindowsNotificationEnabled = false;
- mHandler.removeCallbacksAndMessages(null);
- }
-
- private class MyHandler extends Handler {
- public static final int MESSAGE_NOTIFY_WINDOWS_CHANGED = 1;
- public static final int MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_UI_STABLE = 2;
- public static final int MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_TIMEOUT = 3;
-
- MyHandler(Looper looper) {
- super(looper, null, false);
- }
-
- @Override
- public void handleMessage(Message message) {
- switch (message.what) {
- case MESSAGE_NOTIFY_WINDOWS_CHANGED: {
- final List<Integer> displayIdsForWindowsChanged = (List<Integer>) message.obj;
- notifyWindowsChanged(displayIdsForWindowsChanged);
- } break;
-
- case MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_UI_STABLE: {
- forceUpdateWindows();
- } break;
-
- case MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_TIMEOUT: {
- Slog.w(TAG, "Windows change within in 2 frames continuously over 500 ms "
- + "and notify windows changed immediately");
- mHandler.removeMessages(
- MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_UI_STABLE);
-
- forceUpdateWindows();
- } break;
- }
- }
- }
-
- /**
- * This class represents information about a window from the
- * surface flinger to the accessibility framework.
- */
- public static class AccessibilityWindow {
- private static final Region TEMP_REGION = new Region();
- private static final RectF TEMP_RECTF = new RectF();
- // Data
- private IWindow mWindow;
- private int mDisplayId;
- private int mFlags;
- private int mType;
- private int mPrivateFlags;
- private boolean mIsPIPMenu;
- private boolean mIsFocused;
- private boolean mShouldMagnify;
- private boolean mIgnoreDuetoRecentsAnimation;
- private boolean mIsTrustedOverlay;
- private final Region mTouchableRegionInScreen = new Region();
- private final Region mTouchableRegionInWindow = new Region();
- private final Region mLetterBoxBounds = new Region();
- private WindowInfo mWindowInfo;
-
- /**
- * Returns the instance after initializing the internal data.
- * @param service The window manager service.
- * @param inputWindowHandle The window from the surface flinger.
- * @param inverseMatrix The magnification spec inverse matrix.
- */
- public static AccessibilityWindow initializeData(WindowManagerService service,
- InputWindowHandle inputWindowHandle, Matrix inverseMatrix, IBinder pipIBinder,
- Matrix displayMatrix) {
- final IWindow window = inputWindowHandle.getWindow();
- final WindowState windowState = window != null ? service.mWindowMap.get(
- window.asBinder()) : null;
-
- final AccessibilityWindow instance = new AccessibilityWindow();
-
- instance.mWindow = inputWindowHandle.getWindow();
- instance.mDisplayId = inputWindowHandle.displayId;
- instance.mFlags = inputWindowHandle.layoutParamsFlags;
- instance.mType = inputWindowHandle.layoutParamsType;
- instance.mIsPIPMenu = inputWindowHandle.getWindow().asBinder().equals(pipIBinder);
-
- // TODO (b/199357848): gets the private flag of the window from other way.
- instance.mPrivateFlags = windowState != null ? windowState.mAttrs.privateFlags : 0;
- // TODO (b/199358208) : using new way to implement the focused window.
- instance.mIsFocused = windowState != null && windowState.isFocused();
- instance.mShouldMagnify = windowState == null || windowState.shouldMagnify();
-
- final RecentsAnimationController controller = service.getRecentsAnimationController();
- instance.mIgnoreDuetoRecentsAnimation = windowState != null && controller != null
- && controller.shouldIgnoreForAccessibility(windowState);
- instance.mIsTrustedOverlay = inputWindowHandle.trustedOverlay;
-
- // TODO (b/199358388) : gets the letterbox bounds of the window from other way.
- if (windowState != null && windowState.areAppWindowBoundsLetterboxed()) {
- getLetterBoxBounds(windowState, instance.mLetterBoxBounds);
- }
-
- final Rect windowFrame = new Rect(inputWindowHandle.frameLeft,
- inputWindowHandle.frameTop, inputWindowHandle.frameRight,
- inputWindowHandle.frameBottom);
- getTouchableRegionInWindow(instance.mShouldMagnify, inputWindowHandle.touchableRegion,
- instance.mTouchableRegionInWindow, windowFrame, inverseMatrix, displayMatrix);
- getUnMagnifiedTouchableRegion(instance.mShouldMagnify,
- inputWindowHandle.touchableRegion, instance.mTouchableRegionInScreen,
- inverseMatrix, displayMatrix);
- instance.mWindowInfo = windowState != null
- ? windowState.getWindowInfo() : getWindowInfoForWindowlessWindows(instance);
-
- return instance;
- }
-
- /**
- * Returns the touchable region in the screen.
- * @param outRegion The touchable region.
- */
- public void getTouchableRegionInScreen(Region outRegion) {
- outRegion.set(mTouchableRegionInScreen);
- }
-
- /**
- * Returns the touchable region in the window.
- * @param outRegion The touchable region.
- */
- public void getTouchableRegionInWindow(Region outRegion) {
- outRegion.set(mTouchableRegionInWindow);
- }
-
- /**
- * @return the layout parameter flag {@link android.view.WindowManager.LayoutParams#flags}.
- */
- public int getFlags() {
- return mFlags;
- }
-
- /**
- * @return the layout parameter type {@link android.view.WindowManager.LayoutParams#type}.
- */
- public int getType() {
- return mType;
- }
-
- /**
- * @return the layout parameter private flag
- * {@link android.view.WindowManager.LayoutParams#privateFlags}.
- */
- public int getPrivateFlag() {
- return mPrivateFlags;
- }
-
- /**
- * @return the windowInfo {@link WindowInfo}.
- */
- public WindowInfo getWindowInfo() {
- return mWindowInfo;
- }
-
- /**
- * Gets the letter box bounds if activity bounds are letterboxed
- * or letterboxed for display cutout.
- *
- * @return {@code true} there's a letter box bounds.
- */
- public Boolean setLetterBoxBoundsIfNeeded(Region outBounds) {
- if (mLetterBoxBounds.isEmpty()) {
- return false;
- }
-
- outBounds.set(mLetterBoxBounds);
- return true;
- }
-
- /**
- * @return true if this window should be magnified.
- */
- public boolean shouldMagnify() {
- return mShouldMagnify;
- }
-
- /**
- * @return true if this window is focused.
- */
- public boolean isFocused() {
- return mIsFocused;
- }
-
- /**
- * @return true if it's running the recent animation but not the target app.
- */
- public boolean ignoreRecentsAnimationForAccessibility() {
- return mIgnoreDuetoRecentsAnimation;
- }
-
- /**
- * @return true if this window is the trusted overlay.
- */
- public boolean isTrustedOverlay() {
- return mIsTrustedOverlay;
- }
-
- /**
- * @return true if this window is the navigation bar with the gesture mode.
- */
- public boolean isUntouchableNavigationBar() {
- if (mType != WindowManager.LayoutParams.TYPE_NAVIGATION_BAR) {
- return false;
- }
-
- return mTouchableRegionInScreen.isEmpty();
- }
-
- /**
- * @return true if this window is PIP menu.
- */
- public boolean isPIPMenu() {
- return mIsPIPMenu;
- }
-
- private static void getTouchableRegionInWindow(boolean shouldMagnify, Region inRegion,
- Region outRegion, Rect frame, Matrix inverseMatrix, Matrix displayMatrix) {
- // Some modal windows, like the activity with Theme.dialog, has the full screen
- // as its touchable region, but its window frame is smaller than the touchable
- // region. The region we report should be the touchable area in the window frame
- // for the consistency and match developers expectation.
- // So we need to make the intersection between the frame and touchable region to
- // obtain the real touch region in the screen.
- Region touchRegion = TEMP_REGION;
- touchRegion.set(inRegion);
- touchRegion.op(frame, Region.Op.INTERSECT);
-
- getUnMagnifiedTouchableRegion(shouldMagnify, touchRegion, outRegion, inverseMatrix,
- displayMatrix);
- }
-
- /**
- * Gets the un-magnified touchable region. If this window can be magnified and magnifying,
- * we will transform the input touchable region by applying the inverse matrix of the
- * magnification spec to get the un-magnified touchable region.
- * @param shouldMagnify The window can be magnified.
- * @param inRegion The touchable region of this window.
- * @param outRegion The un-magnified touchable region of this window.
- * @param inverseMatrix The inverse matrix of the magnification spec.
- * @param displayMatrix The display transform matrix which takes display coordinates to
- * logical display coordinates.
- */
- private static void getUnMagnifiedTouchableRegion(boolean shouldMagnify, Region inRegion,
- Region outRegion, Matrix inverseMatrix, Matrix displayMatrix) {
- if ((!shouldMagnify || inverseMatrix.isIdentity()) && displayMatrix.isIdentity()) {
- outRegion.set(inRegion);
- return;
- }
-
- forEachRect(inRegion, rect -> {
- // Move to origin as all transforms are captured by the matrix.
- RectF windowFrame = TEMP_RECTF;
- windowFrame.set(rect);
-
- inverseMatrix.mapRect(windowFrame);
- displayMatrix.mapRect(windowFrame);
- // Union all rects.
- outRegion.union(new Rect((int) windowFrame.left, (int) windowFrame.top,
- (int) windowFrame.right, (int) windowFrame.bottom));
- });
- }
-
- private static WindowInfo getWindowInfoForWindowlessWindows(AccessibilityWindow window) {
- WindowInfo windowInfo = WindowInfo.obtain();
- windowInfo.displayId = window.mDisplayId;
- windowInfo.type = window.mType;
- windowInfo.token = window.mWindow.asBinder();
- windowInfo.hasFlagWatchOutsideTouch = (window.mFlags
- & WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH) != 0;
- windowInfo.inPictureInPicture = false;
-
- // There only are two windowless windows now, one is split window, and the other
- // one is PIP.
- if (windowInfo.type == TYPE_DOCK_DIVIDER) {
- windowInfo.title = "Splitscreen Divider";
- } else if (window.mIsPIPMenu) {
- windowInfo.title = "Picture-in-Picture menu";
- }
- return windowInfo;
- }
-
- private static void getLetterBoxBounds(WindowState windowState, Region outRegion) {
- final Rect letterboxInsets = windowState.mActivityRecord.getLetterboxInsets();
- final Rect nonLetterboxRect = windowState.getBounds();
-
- nonLetterboxRect.inset(letterboxInsets);
- outRegion.set(windowState.getBounds());
- outRegion.op(nonLetterboxRect, Region.Op.DIFFERENCE);
- }
-
- @Override
- public String toString() {
- String builder = "A11yWindow=[" + mWindow.asBinder()
- + ", displayId=" + mDisplayId
- + ", flag=0x" + Integer.toHexString(mFlags)
- + ", type=" + mType
- + ", privateFlag=0x" + Integer.toHexString(mPrivateFlags)
- + ", focused=" + mIsFocused
- + ", shouldMagnify=" + mShouldMagnify
- + ", ignoreDuetoRecentsAnimation=" + mIgnoreDuetoRecentsAnimation
- + ", isTrustedOverlay=" + mIsTrustedOverlay
- + ", regionInScreen=" + mTouchableRegionInScreen
- + ", touchableRegion=" + mTouchableRegionInWindow
- + ", letterBoxBounds=" + mLetterBoxBounds
- + ", isPIPMenu=" + mIsPIPMenu
- + ", windowInfo=" + mWindowInfo
- + "]";
-
- return builder;
- }
- }
-}
diff --git a/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
index 1bb9ca7..48dd2f4 100644
--- a/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
+++ b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
@@ -55,6 +55,7 @@
FIRST_ORDERED_ID,
COMMUNAL_MODE_ORDERED_ID,
PERMISSION_POLICY_ORDERED_ID,
+ VIRTUAL_DEVICE_SERVICE_ORDERED_ID,
LAST_ORDERED_ID // Update this when adding new ids
})
@Retention(RetentionPolicy.SOURCE)
@@ -76,10 +77,16 @@
public static final int PERMISSION_POLICY_ORDERED_ID = 2;
/**
+ * The identifier for {@link com.android.server.companion.virtual.VirtualDeviceManagerService}
+ * interceptor.
+ */
+ public static final int VIRTUAL_DEVICE_SERVICE_ORDERED_ID = 3;
+
+ /**
* The final id, used by the framework to determine the valid range of ids. Update this when
* adding new ids.
*/
- static final int LAST_ORDERED_ID = PERMISSION_POLICY_ORDERED_ID;
+ static final int LAST_ORDERED_ID = VIRTUAL_DEVICE_SERVICE_ORDERED_ID;
/**
* Data class for storing the various arguments needed for activity interception.
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index c929cbb..c2765db 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1546,10 +1546,17 @@
void onDisplayChanged(DisplayContent dc) {
DisplayContent prevDc = mDisplayContent;
super.onDisplayChanged(dc);
- if (prevDc == null || prevDc == mDisplayContent) {
+ if (prevDc == mDisplayContent) {
return;
}
+ mDisplayContent.onRunningActivityChanged();
+
+ if (prevDc == null) {
+ return;
+ }
+ prevDc.onRunningActivityChanged();
+
// TODO(b/169035022): move to a more-appropriate place.
mTransitionController.collect(this);
if (prevDc.mOpeningApps.remove(this)) {
@@ -1623,6 +1630,11 @@
}
// Trigger TaskInfoChanged to update the camera compat UI.
getTask().dispatchTaskInfoChangedIfNeeded(true /* force */);
+ // TaskOrganizerController#onTaskInfoChanged adds pending task events to the queue waiting
+ // for the surface placement to be ready. So need to trigger surface placement to dispatch
+ // events to avoid stale state for the camera compat control.
+ getDisplayContent().setLayoutNeeded();
+ mWmService.mWindowPlacerLocked.performSurfacePlacement();
}
void updateCameraCompatStateFromUser(@CameraCompatControlState int state) {
@@ -3900,6 +3912,7 @@
// Reset the last saved PiP snap fraction on removal.
mDisplayContent.mPinnedTaskController.onActivityHidden(mActivityComponent);
+ mDisplayContent.onRunningActivityChanged();
mWmService.mEmbeddedWindowController.onActivityRemoved(this);
mRemovingFromDisplay = false;
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
index 9353f6d..1681348 100644
--- a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
+++ b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
@@ -26,6 +26,7 @@
import android.os.IBinder;
import android.os.InputConstants;
import android.os.Looper;
+import android.os.Process;
import android.util.Slog;
import android.view.InputChannel;
import android.view.InputEvent;
@@ -113,14 +114,13 @@
}
private InputWindowHandle createInputWindowHandle() {
- InputWindowHandle inputWindowHandle = new InputWindowHandle(
- mActivityRecord.getInputApplicationHandle(false),
+ InputWindowHandle inputWindowHandle = new InputWindowHandle(null,
mActivityRecord.getDisplayId());
inputWindowHandle.replaceTouchableRegionWithCrop(
mActivityRecord.getParentSurfaceControl());
inputWindowHandle.name = mName;
- inputWindowHandle.ownerUid = mActivityRecord.getUid();
- inputWindowHandle.ownerPid = mActivityRecord.getPid();
+ inputWindowHandle.ownerUid = Process.myUid();
+ inputWindowHandle.ownerPid = Process.myPid();
inputWindowHandle.layoutParamsFlags =
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 3cecce2..9d53e5a 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -364,6 +364,17 @@
/** Check if placing task or activity on specified display is allowed. */
boolean canPlaceEntityOnDisplay(int displayId, int callingPid, int callingUid,
ActivityInfo activityInfo) {
+ return canPlaceEntityOnDisplay(displayId, callingPid, callingUid, null /* task */,
+ activityInfo);
+ }
+
+ boolean canPlaceEntityOnDisplay(int displayId, int callingPid, int callingUid, Task task) {
+ return canPlaceEntityOnDisplay(displayId, callingPid, callingUid, task,
+ null /* activityInfo */);
+ }
+
+ private boolean canPlaceEntityOnDisplay(int displayId, int callingPid, int callingUid,
+ Task task, ActivityInfo activityInfo) {
if (displayId == DEFAULT_DISPLAY) {
// No restrictions for the default display.
return true;
@@ -372,12 +383,31 @@
// Can't launch on secondary displays if feature is not supported.
return false;
}
+
if (!isCallerAllowedToLaunchOnDisplay(callingPid, callingUid, displayId, activityInfo)) {
// Can't place activities to a display that has restricted launch rules.
// In this case the request should be made by explicitly adding target display id and
// by caller with corresponding permissions. See #isCallerAllowedToLaunchOnDisplay().
return false;
}
+
+ final DisplayContent displayContent =
+ mRootWindowContainer.getDisplayContentOrCreate(displayId);
+ if (displayContent != null && displayContent.mDwpcHelper.hasController()) {
+ final ArrayList<ActivityInfo> activities = new ArrayList<>();
+ if (activityInfo != null) {
+ activities.add(activityInfo);
+ }
+ if (task != null) {
+ task.forAllActivities((r) -> {
+ activities.add(r.info);
+ });
+ }
+ if (!displayContent.mDwpcHelper.canContainActivities(activities)) {
+ return false;
+ }
+ }
+
return true;
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 4fc591d..d91f48e 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -222,7 +222,6 @@
import android.view.WindowManager;
import android.view.WindowManager.DisplayImePolicy;
import android.view.WindowManagerPolicyConstants.PointerEventListener;
-import android.window.DisplayWindowPolicyController;
import android.window.IDisplayAreaOrganizer;
import android.window.TransitionRequestInfo;
@@ -698,12 +697,11 @@
boolean mDontMoveToTop;
/**
- * The policy controller of the windows that can be displayed on the virtual display.
+ * The helper of policy controller.
*
- * @see DisplayWindowPolicyController
+ * @see DisplayWindowPolicyControllerHelper
*/
- @Nullable
- DisplayWindowPolicyController mDisplayWindowPolicyController;
+ DisplayWindowPolicyControllerHelper mDwpcHelper;
private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
WindowStateAnimator winAnimator = w.mWinAnimator;
@@ -2739,8 +2737,7 @@
mDisplayInfo.copyFrom(newDisplayInfo);
}
- mDisplayWindowPolicyController =
- displayManagerInternal.getDisplayWindowPolicyController(mDisplayId);
+ mDwpcHelper = new DisplayWindowPolicyControllerHelper(this);
}
updateBaseDisplayMetrics(mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight,
@@ -3453,10 +3450,7 @@
mInputMonitor.dump(pw, " ");
pw.println();
mInsetsStateController.dump(prefix, pw);
- if (mDisplayWindowPolicyController != null) {
- pw.println();
- mDisplayWindowPolicyController.dump(prefix, pw);
- }
+ mDwpcHelper.dump(prefix, pw);
}
@Override
@@ -3684,6 +3678,11 @@
return true;
}
+ /** Update the top activity and the uids of non-finishing activity */
+ void onRunningActivityChanged() {
+ mDwpcHelper.onRunningActivityChanged();
+ }
+
/** Called when the focused {@link TaskDisplayArea} on this display may have changed. */
void onLastFocusedTaskDisplayAreaChanged(@Nullable TaskDisplayArea taskDisplayArea) {
// Only record the TaskDisplayArea that handles orientation request.
diff --git a/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java b/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java
new file mode 100644
index 0000000..60d2a5d
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java
@@ -0,0 +1,135 @@
+/*
+ * 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.wm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.ActivityInfo;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import android.window.DisplayWindowPolicyController;
+
+import java.io.PrintWriter;
+import java.util.List;
+
+class DisplayWindowPolicyControllerHelper {
+
+ private final DisplayContent mDisplayContent;
+
+ /**
+ * The policy controller of the windows that can be displayed on the virtual display.
+ *
+ * @see DisplayWindowPolicyController
+ */
+ @Nullable
+ private DisplayWindowPolicyController mDisplayWindowPolicyController;
+
+ /**
+ * The top non-finishing activity of this display.
+ */
+ private ActivityRecord mTopRunningActivity = null;
+
+ /**
+ * All the uids of non-finishing activity on this display.
+ * @see DisplayWindowPolicyController#onRunningAppsChanged(ArraySet)
+ */
+ private ArraySet<Integer> mRunningUid = new ArraySet<>();
+
+ DisplayWindowPolicyControllerHelper(DisplayContent displayContent) {
+ mDisplayContent = displayContent;
+ mDisplayWindowPolicyController = mDisplayContent.mWmService.mDisplayManagerInternal
+ .getDisplayWindowPolicyController(mDisplayContent.mDisplayId);
+ }
+
+ /**
+ * Return {@code true} if there is DisplayWindowPolicyController.
+ */
+ public boolean hasController() {
+ return mDisplayWindowPolicyController != null;
+ }
+
+ /**
+ * @see DisplayWindowPolicyController#canContainActivities(List)
+ */
+ public boolean canContainActivities(@NonNull List<ActivityInfo> activities) {
+ if (mDisplayWindowPolicyController == null) {
+ return true;
+ }
+ return mDisplayWindowPolicyController.canContainActivities(activities);
+ }
+
+ /**
+ * @see DisplayWindowPolicyController#keepActivityOnWindowFlagsChanged(ActivityInfo, int, int)
+ */
+ boolean keepActivityOnWindowFlagsChanged(ActivityInfo aInfo, int flagChanges,
+ int privateFlagChanges) {
+ if (mDisplayWindowPolicyController == null) {
+ return true;
+ }
+
+ if (!mDisplayWindowPolicyController.isInterestedWindowFlags(
+ flagChanges, privateFlagChanges)) {
+ return true;
+ }
+
+ return mDisplayWindowPolicyController.keepActivityOnWindowFlagsChanged(
+ aInfo, flagChanges, privateFlagChanges);
+ }
+
+ /** Update the top activity and the uids of non-finishing activity */
+ void onRunningActivityChanged() {
+ if (mDisplayWindowPolicyController == null) {
+ return;
+ }
+
+ // Update top activity.
+ ActivityRecord topActivity = mDisplayContent.getTopActivity(false /* includeFinishing */,
+ true /* includeOverlays */);
+ if (topActivity != mTopRunningActivity) {
+ mTopRunningActivity = topActivity;
+ mDisplayWindowPolicyController.onTopActivityChanged(
+ topActivity == null ? null : topActivity.info.getComponentName(),
+ topActivity == null
+ ? UserHandle.USER_NULL : topActivity.info.applicationInfo.uid);
+ }
+
+ // Update running uid.
+ final boolean[] notifyChanged = {false};
+ ArraySet<Integer> runningUids = new ArraySet<>();
+ mDisplayContent.forAllActivities((r) -> {
+ if (!r.finishing) {
+ notifyChanged[0] |= runningUids.add(r.getUid());
+ }
+ });
+
+ // We need to compare the size because if it is the following case, we can't know the
+ // existence of 3 in the forAllActivities() loop.
+ // Old set: 1,2,3
+ // New set: 1,2
+ if (notifyChanged[0] || (mRunningUid.size() != runningUids.size())) {
+ mRunningUid = runningUids;
+ mDisplayWindowPolicyController.onRunningAppsChanged(runningUids);
+ }
+ }
+
+ void dump(String prefix, PrintWriter pw) {
+ if (mDisplayWindowPolicyController != null) {
+ pw.println();
+ mDisplayWindowPolicyController.dump(prefix, pw);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/OverlayHost.java b/services/core/java/com/android/server/wm/OverlayHost.java
new file mode 100644
index 0000000..14f8983
--- /dev/null
+++ b/services/core/java/com/android/server/wm/OverlayHost.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2021 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.wm;
+
+import android.content.res.Configuration;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+
+import java.util.ArrayList;
+
+/**
+ * Utility class to assist WindowContainer in the hosting of
+ * SurfacePackage based overlays. Manages overlays inside
+ * one parent control, and manages the lifetime of that parent control
+ * in order to obscure details from WindowContainer.
+ *
+ * Also handles multiplexing of event dispatch and tracking of overlays
+ * to make things easier for WindowContainer.
+ */
+class OverlayHost {
+ // Lazily initialized when required
+ SurfaceControl mSurfaceControl;
+ final ArrayList<SurfaceControlViewHost.SurfacePackage> mOverlays = new ArrayList<>();
+ final WindowManagerService mWmService;
+
+ OverlayHost(WindowManagerService wms) {
+ mWmService = wms;
+ }
+
+ void requireOverlaySurfaceControl() {
+ if (mSurfaceControl == null) {
+ final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(null)
+ .setContainerLayer()
+ .setHidden(true)
+ .setName("Overlay Host Leash");
+
+ mSurfaceControl = b.build();
+ }
+ }
+
+ void setParent(SurfaceControl.Transaction t, SurfaceControl newParent) {
+ if (mSurfaceControl == null) {
+ return;
+ }
+ t.reparent(mSurfaceControl, newParent);
+ if (newParent != null) {
+ t.show(mSurfaceControl);
+ } else {
+ t.hide(mSurfaceControl);
+ }
+ }
+
+ void setLayer(SurfaceControl.Transaction t, int layer) {
+ if (mSurfaceControl != null) {
+ t.setLayer(mSurfaceControl, layer);
+ }
+ }
+
+ void addOverlay(SurfaceControlViewHost.SurfacePackage p, SurfaceControl currentParent) {
+ requireOverlaySurfaceControl();
+ mOverlays.add(p);
+
+ SurfaceControl.Transaction t = mWmService.mTransactionFactory.get();
+ t.reparent(p.getSurfaceControl(), mSurfaceControl)
+ .show(p.getSurfaceControl());
+ setParent(t,currentParent);
+ t.apply();
+ }
+
+ boolean removeOverlay(SurfaceControlViewHost.SurfacePackage p) {
+ final SurfaceControl.Transaction t = mWmService.mTransactionFactory.get();
+
+ for (int i = mOverlays.size() - 1; i >= 0; i--) {
+ SurfaceControlViewHost.SurfacePackage l = mOverlays.get(i);
+ if (l.getSurfaceControl().isSameSurface(p.getSurfaceControl())) {
+ mOverlays.remove(i);
+ t.reparent(l.getSurfaceControl(), null);
+ l.release();
+ }
+ }
+ t.apply();
+ return mOverlays.size() > 0;
+ }
+
+ void dispatchConfigurationChanged(Configuration c) {
+ for (int i = mOverlays.size() - 1; i >= 0; i--) {
+ SurfaceControlViewHost.SurfacePackage l = mOverlays.get(i);
+ try {
+ l.getRemoteInterface().onConfigurationChanged(c);
+ } catch (Exception e) {
+ removeOverlay(l);
+ }
+ }
+ }
+
+ private void dispatchDetachedFromWindow() {
+ for (int i = mOverlays.size() - 1; i >= 0; i--) {
+ SurfaceControlViewHost.SurfacePackage l = mOverlays.get(i);
+ try {
+ l.getRemoteInterface().onDispatchDetachedFromWindow();
+ } catch (Exception e) {
+ // Oh well we are tearing down anyway.
+ }
+ l.release();
+ }
+ }
+
+ void release() {
+ dispatchDetachedFromWindow();
+ mOverlays.clear();
+ final SurfaceControl.Transaction t = mWmService.mTransactionFactory.get();
+ t.remove(mSurfaceControl).apply();
+ mSurfaceControl = null;
+ }
+}
diff --git a/services/core/java/com/android/server/wm/ShellRoot.java b/services/core/java/com/android/server/wm/ShellRoot.java
index f9d7b53..6ed59e9 100644
--- a/services/core/java/com/android/server/wm/ShellRoot.java
+++ b/services/core/java/com/android/server/wm/ShellRoot.java
@@ -25,14 +25,15 @@
import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.graphics.Point;
+import android.graphics.Rect;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
import android.view.DisplayInfo;
import android.view.IWindow;
import android.view.SurfaceControl;
+import android.view.WindowInfo;
import android.view.WindowManager;
import android.view.animation.Animation;
@@ -135,12 +136,47 @@
ANIMATION_TYPE_WINDOW_ANIMATION);
}
- @Nullable
- IBinder getAccessibilityWindowToken() {
- if (mAccessibilityWindow != null) {
- return mAccessibilityWindow.asBinder();
+ WindowInfo getWindowInfo() {
+ if (mShellRootLayer != SHELL_ROOT_LAYER_DIVIDER
+ && mShellRootLayer != SHELL_ROOT_LAYER_PIP) {
+ return null;
}
- return null;
+ if (mShellRootLayer == SHELL_ROOT_LAYER_DIVIDER
+ && !mDisplayContent.getDefaultTaskDisplayArea().isSplitScreenModeActivated()) {
+ return null;
+ }
+ if (mShellRootLayer == SHELL_ROOT_LAYER_PIP
+ && mDisplayContent.getDefaultTaskDisplayArea().getRootPinnedTask() == null) {
+ return null;
+ }
+ if (mAccessibilityWindow == null) {
+ return null;
+ }
+ WindowInfo windowInfo = WindowInfo.obtain();
+ windowInfo.displayId = mToken.getDisplayArea().getDisplayContent().mDisplayId;
+ windowInfo.type = mToken.windowType;
+ windowInfo.layer = mToken.getWindowLayerFromType();
+ windowInfo.token = mAccessibilityWindow.asBinder();
+ windowInfo.focused = false;
+ windowInfo.hasFlagWatchOutsideTouch = false;
+ final Rect regionRect = new Rect();
+
+
+ // DividerView
+ if (mShellRootLayer == SHELL_ROOT_LAYER_DIVIDER) {
+ windowInfo.inPictureInPicture = false;
+ mDisplayContent.getDockedDividerController().getTouchRegion(regionRect);
+ windowInfo.regionInScreen.set(regionRect);
+ windowInfo.title = "Splitscreen Divider";
+ }
+ // PipMenuView
+ if (mShellRootLayer == SHELL_ROOT_LAYER_PIP) {
+ windowInfo.inPictureInPicture = true;
+ mDisplayContent.getDefaultTaskDisplayArea().getRootPinnedTask().getBounds(regionRect);
+ windowInfo.regionInScreen.set(regionRect);
+ windowInfo.title = "Picture-in-Picture menu";
+ }
+ return windowInfo;
}
void setAccessibilityWindow(IWindow window) {
@@ -161,5 +197,9 @@
mAccessibilityWindow = null;
}
}
+ if (mDisplayContent.mWmService.mAccessibilityController.hasCallbacks()) {
+ mDisplayContent.mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(
+ mDisplayContent.getDisplayId());
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 43038ce..cfd1f6e 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -125,6 +125,8 @@
import static com.android.server.wm.TaskProto.SURFACE_WIDTH;
import static com.android.server.wm.TaskProto.TASK_FRAGMENT;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
+import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
+import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainerChildProto.TASK;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ROOT_TASK;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
@@ -609,6 +611,8 @@
*/
ActivityRecord mChildPipActivity;
+ boolean mLastSurfaceShowing = true;
+
private Task(ActivityTaskManagerService atmService, int _taskId, Intent _intent,
Intent _affinityIntent, String _affinity, String _rootAffinity,
ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset,
@@ -1749,7 +1753,7 @@
*/
boolean canBeLaunchedOnDisplay(int displayId) {
return mTaskSupervisor.canPlaceEntityOnDisplay(displayId,
- -1 /* don't check PID */, -1 /* don't check UID */, null /* activityInfo */);
+ -1 /* don't check PID */, -1 /* don't check UID */, this);
}
/**
@@ -3308,6 +3312,17 @@
if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) {
scheduleAnimation();
}
+
+ // We intend to let organizer manage task visibility but it doesn't
+ // have enough information until we finish shell transitions.
+ // In the mean time we do an easy fix here.
+ final boolean show = isVisible() || isAnimating(TRANSITION | PARENTS);
+ if (mSurfaceControl != null) {
+ if (show != mLastSurfaceShowing) {
+ getSyncTransaction().setVisibility(mSurfaceControl, show);
+ }
+ }
+ mLastSurfaceShowing = show;
}
@Override
@@ -5971,7 +5986,19 @@
}
void reparent(TaskDisplayArea newParent, boolean onTop) {
- reparent(newParent, onTop ? POSITION_TOP : POSITION_BOTTOM);
+ if (newParent == null) {
+ throw new IllegalArgumentException("Task can't reparent to null " + this);
+ }
+
+ if (getParent() == newParent) {
+ throw new IllegalArgumentException("Task=" + this + " already child of " + newParent);
+ }
+
+ if (canBeLaunchedOnDisplay(newParent.getDisplayId())) {
+ reparent(newParent, onTop ? POSITION_TOP : POSITION_BOTTOM);
+ } else {
+ Slog.w(TAG, "Task=" + this + " can't reparent to " + newParent);
+ }
}
void setLastRecentsAnimationTransaction(@NonNull PictureInPictureSurfaceTransaction transaction,
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index d133ca9..b681a96 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -398,8 +398,16 @@
Slog.d(TAG, "setResumedActivity taskFrag:" + this + " + from: "
+ mResumedActivity + " to:" + r + " reason:" + reason);
}
+ final ActivityRecord prevR = mResumedActivity;
mResumedActivity = r;
mTaskSupervisor.updateTopResumedActivityIfNeeded();
+ if (r == null && prevR.mDisplayContent != null
+ && prevR.mDisplayContent.getFocusedRootTask() == null) {
+ // Only need to notify DWPC when no activity will resume.
+ prevR.mDisplayContent.onRunningActivityChanged();
+ } else if (r != null) {
+ r.mDisplayContent.onRunningActivityChanged();
+ }
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index c7fdefc..123ca88 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -22,6 +22,7 @@
import static com.android.server.wm.WindowOrganizerController.configurationsAreEqualForOrganizer;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -497,6 +498,23 @@
return null;
}
+ private boolean shouldSendEventWhenTaskInvisible(@NonNull Task task,
+ @NonNull PendingTaskFragmentEvent event) {
+ final TaskFragmentOrganizerState state =
+ mTaskFragmentOrganizerState.get(event.mTaskFragmentOrg.asBinder());
+ final TaskFragmentInfo lastInfo = state.mLastSentTaskFragmentInfos.get(event.mTaskFragment);
+ final TaskFragmentInfo info = event.mTaskFragment.getTaskFragmentInfo();
+ // Send an info changed callback if this event is for the last activities to finish in a
+ // Task so that the {@link TaskFragmentOrganizer} can delete this TaskFragment. Otherwise,
+ // the Task may be removed before it becomes visible again to send this event because it no
+ // longer has activities. As a result, the organizer will never get this info changed event
+ // and will not delete the TaskFragment because the organizer thinks the TaskFragment still
+ // has running activities.
+ return event.mEventType == PendingTaskFragmentEvent.EVENT_INFO_CHANGED
+ && task.topRunningActivity() == null && lastInfo != null
+ && lastInfo.getRunningActivityCount() > 0 && info.getRunningActivityCount() == 0;
+ }
+
void dispatchPendingEvents() {
if (mAtmService.mWindowManager.mWindowPlacerLocked.isLayoutDeferred()
|| mPendingTaskFragmentEvents.isEmpty()) {
@@ -510,7 +528,8 @@
final PendingTaskFragmentEvent event = mPendingTaskFragmentEvents.get(i);
final Task task = event.mTaskFragment != null ? event.mTaskFragment.getTask() : null;
if (task != null && (task.lastActiveTime <= event.mDeferTime
- || !isTaskVisible(task, visibleTasks, invisibleTasks))) {
+ || !(isTaskVisible(task, visibleTasks, invisibleTasks)
+ || shouldSendEventWhenTaskInvisible(task, event)))) {
// Defer sending events to the TaskFragment until the host task is active again.
event.mDeferTime = task.lastActiveTime;
continue;
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 61acb97..4006848 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -85,6 +85,7 @@
import android.view.RemoteAnimationDefinition;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
import android.view.SurfaceControl.Builder;
import android.view.SurfaceSession;
import android.view.TaskTransitionSpec;
@@ -312,6 +313,8 @@
private final List<WindowContainerListener> mListeners = new ArrayList<>();
+ private OverlayHost mOverlayHost;
+
WindowContainer(WindowManagerService wms) {
mWmService = wms;
mTransitionController = mWmService.mAtmService.getTransitionController();
@@ -341,6 +344,9 @@
super.onConfigurationChanged(newParentConfig);
updateSurfacePositionNonOrganized();
scheduleAnimation();
+ if (mOverlayHost != null) {
+ mOverlayHost.dispatchConfigurationChanged(getConfiguration());
+ }
}
void reparent(WindowContainer newParent, int position) {
@@ -387,6 +393,8 @@
if (mParent != null) {
mParent.onChildAdded(this);
+ } else if (mSurfaceAnimator.hasLeash()) {
+ mSurfaceAnimator.cancelAnimation();
}
if (!mReparenting) {
onSyncReparent(oldParent, mParent);
@@ -487,6 +495,11 @@
t.reparent(sc, mSurfaceControl);
}
}
+
+ if (mOverlayHost != null) {
+ mOverlayHost.setParent(t, mSurfaceControl);
+ }
+
scheduleAnimation();
}
@@ -632,6 +645,10 @@
mLastSurfacePosition.set(0, 0);
scheduleAnimation();
}
+ if (mOverlayHost != null) {
+ mOverlayHost.release();
+ mOverlayHost = null;
+ }
// This must happen after updating the surface so that sync transactions can be handled
// properly.
@@ -2308,6 +2325,9 @@
wc.assignLayer(t, layer++);
}
}
+ if (mOverlayHost != null) {
+ mOverlayHost.setLayer(t, layer++);
+ }
}
void assignChildLayers() {
@@ -3570,4 +3590,18 @@
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type, @Nullable AnimationAdapter snapshotAnim);
}
+
+ void addOverlay(SurfaceControlViewHost.SurfacePackage overlay) {
+ if (mOverlayHost == null) {
+ mOverlayHost = new OverlayHost(mWmService);
+ }
+ mOverlayHost.addOverlay(overlay, mSurfaceControl);
+ }
+
+ void removeOverlay(SurfaceControlViewHost.SurfacePackage overlay) {
+ if (mOverlayHost != null && !mOverlayHost.removeOverlay(overlay)) {
+ mOverlayHost.release();
+ mOverlayHost = null;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 62c674b..1ab191b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -32,6 +32,7 @@
import android.view.InputChannel;
import android.view.MagnificationSpec;
import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControlViewHost;
import android.view.WindowInfo;
import android.view.WindowManager.DisplayImePolicy;
@@ -767,4 +768,16 @@
* {@code false} otherwise.
*/
public abstract boolean shouldRestoreImeVisibility(IBinder imeTargetWindowToken);
+
+ /**
+ * Internal methods for other parts of SystemServer to manage
+ * SurfacePackage based overlays on tasks.
+ *
+ * Callers prepare a view hierarchy with SurfaceControlViewHost
+ * and send the package to WM here. The remote view hierarchy will receive
+ * configuration change, lifecycle events, etc, forwarded over the
+ * ISurfaceControlViewHost interface inside the SurfacePackage.
+ */
+ public abstract void addTaskOverlay(int taskId, SurfaceControlViewHost.SurfacePackage overlay);
+ public abstract void removeTaskOverlay(int taskId, SurfaceControlViewHost.SurfacePackage overlay);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c239e68..e4216bf 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -267,6 +267,7 @@
import android.view.ScrollCaptureResponse;
import android.view.Surface;
import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
import android.view.SurfaceSession;
import android.view.TaskTransitionSpec;
import android.view.View;
@@ -2245,6 +2246,15 @@
winAnimator.setColorSpaceAgnosticLocked((win.mAttrs.privateFlags
& WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC) != 0);
}
+ if (win.mActivityRecord != null
+ && !displayContent.mDwpcHelper.keepActivityOnWindowFlagsChanged(
+ win.mActivityRecord.info, flagChanges, privateFlagChanges)) {
+ mH.sendMessage(mH.obtainMessage(H.REPARENT_TASK_TO_DEFAULT_DISPLAY,
+ win.mActivityRecord.getTask()));
+ Slog.w(TAG_WM, "Activity " + win.mActivityRecord + " window flag changed,"
+ + " can't remain on display " + displayContent.getDisplayId());
+ return 0;
+ }
}
if (DEBUG_LAYOUT) Slog.v(TAG_WM, "Relayout " + win + ": viewVisibility=" + viewVisibility
@@ -5064,6 +5074,7 @@
public static final int ON_POINTER_DOWN_OUTSIDE_FOCUS = 62;
public static final int LAYOUT_AND_ASSIGN_WINDOW_LAYERS_IF_NEEDED = 63;
public static final int WINDOW_STATE_BLAST_SYNC_TIMEOUT = 64;
+ public static final int REPARENT_TASK_TO_DEFAULT_DISPLAY = 65;
/**
* Used to denote that an integer field in a message will not be used.
@@ -5381,6 +5392,15 @@
}
break;
}
+ case REPARENT_TASK_TO_DEFAULT_DISPLAY: {
+ synchronized (mGlobalLock) {
+ Task task = (Task) msg.obj;
+ task.reparent(mRoot.getDefaultTaskDisplayArea(), true /* onTop */);
+ // Resume focusable root task after reparenting to another display area.
+ task.resumeNextFocusAfterReparent();
+ }
+ break;
+ }
}
if (DEBUG_WINDOW_TRACE) {
Slog.v(TAG_WM, "handleMessage: exit");
@@ -7891,6 +7911,28 @@
public boolean shouldRestoreImeVisibility(IBinder imeTargetWindowToken) {
return WindowManagerService.this.shouldRestoreImeVisibility(imeTargetWindowToken);
}
+
+ @Override
+ public void addTaskOverlay(int taskId, SurfaceControlViewHost.SurfacePackage overlay) {
+ synchronized (mGlobalLock) {
+ final Task task = mRoot.getRootTask(taskId);
+ if (task == null) {
+ throw new IllegalArgumentException("no task with taskId" + taskId);
+ }
+ task.addOverlay(overlay);
+ }
+ }
+
+ @Override
+ public void removeTaskOverlay(int taskId, SurfaceControlViewHost.SurfacePackage overlay) {
+ synchronized (mGlobalLock) {
+ final Task task = mRoot.getRootTask(taskId);
+ if (task == null) {
+ throw new IllegalArgumentException("no task with taskId" + taskId);
+ }
+ task.removeOverlay(overlay);
+ }
+ }
}
void registerAppFreezeListener(AppFreezeListener listener) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 5bbe2cd..94d4a77 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -4809,6 +4809,9 @@
if (isAnimating()) {
return;
}
+ if (mWmService.mAccessibilityController.hasCallbacks()) {
+ mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(getDisplayId());
+ }
if (!isSelfOrAncestorWindowAnimatingExit()) {
return;
diff --git a/services/core/jni/gnss/GnssMeasurementCallback.cpp b/services/core/jni/gnss/GnssMeasurementCallback.cpp
index 34ae469..5a7cee9 100644
--- a/services/core/jni/gnss/GnssMeasurementCallback.cpp
+++ b/services/core/jni/gnss/GnssMeasurementCallback.cpp
@@ -27,12 +27,16 @@
using hardware::gnss::GnssData;
using hardware::gnss::GnssMeasurement;
using hardware::gnss::SatellitePvt;
+using GnssAgc = hardware::gnss::GnssData::GnssAgc;
namespace {
jclass class_arrayList;
jclass class_clockInfo;
jclass class_correlationVectorBuilder;
+jclass class_gnssAgc;
+jclass class_gnssAgcBuilder;
jclass class_gnssMeasurementsEvent;
+jclass class_gnssMeasurementsEventBuilder;
jclass class_gnssMeasurement;
jclass class_gnssClock;
jclass class_positionEcef;
@@ -47,7 +51,16 @@
jmethodID method_correlationVectorBuilderSetMagnitude;
jmethodID method_correlationVectorBuilderSetSamplingStartMeters;
jmethodID method_correlationVectorBuilderSetSamplingWidthMeters;
-jmethodID method_gnssMeasurementsEventCtor;
+jmethodID method_gnssAgcBuilderCtor;
+jmethodID method_gnssAgcBuilderSetLevelDb;
+jmethodID method_gnssAgcBuilderSetConstellationType;
+jmethodID method_gnssAgcBuilderSetCarrierFrequencyHz;
+jmethodID method_gnssAgcBuilderBuild;
+jmethodID method_gnssMeasurementsEventBuilderCtor;
+jmethodID method_gnssMeasurementsEventBuilderSetClock;
+jmethodID method_gnssMeasurementsEventBuilderSetMeasurements;
+jmethodID method_gnssMeasurementsEventBuilderSetGnssAutomaticGainControls;
+jmethodID method_gnssMeasurementsEventBuilderBuild;
jmethodID method_gnssMeasurementsSetCorrelationVectors;
jmethodID method_gnssMeasurementsSetSatellitePvt;
jmethodID method_gnssClockCtor;
@@ -69,12 +82,55 @@
void GnssMeasurement_class_init_once(JNIEnv* env, jclass& clazz) {
method_reportMeasurementData = env->GetMethodID(clazz, "reportMeasurementData",
"(Landroid/location/GnssMeasurementsEvent;)V");
+
+ // Initialize GnssMeasurement related classes and methods
jclass gnssMeasurementsEventClass = env->FindClass("android/location/GnssMeasurementsEvent");
class_gnssMeasurementsEvent = (jclass)env->NewGlobalRef(gnssMeasurementsEventClass);
- method_gnssMeasurementsEventCtor =
- env->GetMethodID(class_gnssMeasurementsEvent, "<init>",
- "(Landroid/location/GnssClock;[Landroid/location/GnssMeasurement;)V");
+ jclass gnssMeasurementsEventBuilderClass =
+ env->FindClass("android/location/GnssMeasurementsEvent$Builder");
+ class_gnssMeasurementsEventBuilder =
+ (jclass)env->NewGlobalRef(gnssMeasurementsEventBuilderClass);
+ method_gnssMeasurementsEventBuilderCtor =
+ env->GetMethodID(class_gnssMeasurementsEventBuilder, "<init>", "()V");
+ method_gnssMeasurementsEventBuilderSetClock =
+ env->GetMethodID(class_gnssMeasurementsEventBuilder, "setClock",
+ "(Landroid/location/GnssClock;)"
+ "Landroid/location/GnssMeasurementsEvent$Builder;");
+ method_gnssMeasurementsEventBuilderSetMeasurements =
+ env->GetMethodID(class_gnssMeasurementsEventBuilder, "setMeasurements",
+ "([Landroid/location/GnssMeasurement;)"
+ "Landroid/location/GnssMeasurementsEvent$Builder;");
+ method_gnssMeasurementsEventBuilderSetGnssAutomaticGainControls =
+ env->GetMethodID(class_gnssMeasurementsEventBuilder, "setGnssAutomaticGainControls",
+ "([Landroid/location/GnssAutomaticGainControl;)"
+ "Landroid/location/GnssMeasurementsEvent$Builder;");
+ method_gnssMeasurementsEventBuilderBuild =
+ env->GetMethodID(class_gnssMeasurementsEventBuilder, "build",
+ "()Landroid/location/GnssMeasurementsEvent;");
+ // Initialize GnssAgc related classes and methods
+ jclass gnssAgcClass = env->FindClass("android/location/GnssAutomaticGainControl");
+ class_gnssAgc = (jclass)env->NewGlobalRef(gnssAgcClass);
+ jclass gnssAgcBuilderClass =
+ env->FindClass("android/location/GnssAutomaticGainControl$Builder");
+ class_gnssAgcBuilder = (jclass)env->NewGlobalRef(gnssAgcBuilderClass);
+ method_gnssAgcBuilderCtor = env->GetMethodID(class_gnssAgcBuilder, "<init>", "()V");
+ method_gnssAgcBuilderSetLevelDb =
+ env->GetMethodID(class_gnssAgcBuilder, "setLevelDb",
+ "(D)"
+ "Landroid/location/GnssAutomaticGainControl$Builder;");
+ method_gnssAgcBuilderSetConstellationType =
+ env->GetMethodID(class_gnssAgcBuilder, "setConstellationType",
+ "(I)"
+ "Landroid/location/GnssAutomaticGainControl$Builder;");
+ method_gnssAgcBuilderSetCarrierFrequencyHz =
+ env->GetMethodID(class_gnssAgcBuilder, "setCarrierFrequencyHz",
+ "(J)"
+ "Landroid/location/GnssAutomaticGainControl$Builder;");
+ method_gnssAgcBuilderBuild = env->GetMethodID(class_gnssAgcBuilder, "build",
+ "()Landroid/location/GnssAutomaticGainControl;");
+
+ // Initialize GnssMeasurement related classes and methods
jclass gnssMeasurementClass = env->FindClass("android/location/GnssMeasurement");
class_gnssMeasurement = (jclass)env->NewGlobalRef(gnssMeasurementClass);
method_gnssMeasurementCtor = env->GetMethodID(class_gnssMeasurement, "<init>", "()V");
@@ -152,14 +208,25 @@
}
void setMeasurementData(JNIEnv* env, jobject& callbacksObj, jobject clock,
- jobjectArray measurementArray) {
- jobject gnssMeasurementsEvent =
- env->NewObject(class_gnssMeasurementsEvent, method_gnssMeasurementsEventCtor, clock,
- measurementArray);
+ jobjectArray measurementArray, jobjectArray gnssAgcArray) {
+ jobject gnssMeasurementsEventBuilderObject =
+ env->NewObject(class_gnssMeasurementsEventBuilder,
+ method_gnssMeasurementsEventBuilderCtor);
+ env->CallObjectMethod(gnssMeasurementsEventBuilderObject,
+ method_gnssMeasurementsEventBuilderSetClock, clock);
+ env->CallObjectMethod(gnssMeasurementsEventBuilderObject,
+ method_gnssMeasurementsEventBuilderSetMeasurements, measurementArray);
+ env->CallObjectMethod(gnssMeasurementsEventBuilderObject,
+ method_gnssMeasurementsEventBuilderSetGnssAutomaticGainControls,
+ gnssAgcArray);
+ jobject gnssMeasurementsEventObject =
+ env->CallObjectMethod(gnssMeasurementsEventBuilderObject,
+ method_gnssMeasurementsEventBuilderBuild);
- env->CallVoidMethod(callbacksObj, method_reportMeasurementData, gnssMeasurementsEvent);
+ env->CallVoidMethod(callbacksObj, method_reportMeasurementData, gnssMeasurementsEventObject);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
- env->DeleteLocalRef(gnssMeasurementsEvent);
+ env->DeleteLocalRef(gnssMeasurementsEventBuilderObject);
+ env->DeleteLocalRef(gnssMeasurementsEventObject);
}
template <class T_Measurement, class T_Flags>
@@ -289,9 +356,13 @@
JavaObject gnssClockJavaObject(env, class_gnssClock, method_gnssClockCtor);
translateGnssClock(env, data, gnssClockJavaObject);
jobject clock = gnssClockJavaObject.get();
-
jobjectArray measurementArray = translateAllGnssMeasurements(env, data.measurements);
- setMeasurementData(env, mCallbacksObj, clock, measurementArray);
+
+ jobjectArray gnssAgcArray = nullptr;
+ if (data.gnssAgcs.has_value()) {
+ gnssAgcArray = translateAllGnssAgcs(env, data.gnssAgcs.value());
+ }
+ setMeasurementData(env, mCallbacksObj, clock, measurementArray, gnssAgcArray);
env->DeleteLocalRef(clock);
env->DeleteLocalRef(measurementArray);
@@ -436,6 +507,38 @@
return gnssMeasurementArray;
}
+jobjectArray GnssMeasurementCallbackAidl::translateAllGnssAgcs(
+ JNIEnv* env, const std::vector<std::optional<GnssAgc>>& agcs) {
+ if (agcs.size() == 0) {
+ return nullptr;
+ }
+
+ jobjectArray gnssAgcArray =
+ env->NewObjectArray(agcs.size(), class_gnssAgc, nullptr /* initialElement */);
+
+ for (uint16_t i = 0; i < agcs.size(); ++i) {
+ if (!agcs[i].has_value()) {
+ continue;
+ }
+ const GnssAgc& gnssAgc = agcs[i].value();
+
+ jobject agcBuilderObject = env->NewObject(class_gnssAgcBuilder, method_gnssAgcBuilderCtor);
+ env->CallObjectMethod(agcBuilderObject, method_gnssAgcBuilderSetLevelDb,
+ gnssAgc.agcLevelDb);
+ env->CallObjectMethod(agcBuilderObject, method_gnssAgcBuilderSetConstellationType,
+ (int)gnssAgc.constellation);
+ env->CallObjectMethod(agcBuilderObject, method_gnssAgcBuilderSetCarrierFrequencyHz,
+ gnssAgc.carrierFrequencyHz);
+ jobject agcObject = env->CallObjectMethod(agcBuilderObject, method_gnssAgcBuilderBuild);
+
+ env->SetObjectArrayElement(gnssAgcArray, i, agcObject);
+ env->DeleteLocalRef(agcBuilderObject);
+ env->DeleteLocalRef(agcObject);
+ }
+
+ return gnssAgcArray;
+}
+
void GnssMeasurementCallbackAidl::translateGnssClock(JNIEnv* env, const GnssData& data,
JavaObject& object) {
setElapsedRealtimeFields<ElapsedRealtime, ElapsedRealtime>(data.elapsedRealtime, object);
diff --git a/services/core/jni/gnss/GnssMeasurementCallback.h b/services/core/jni/gnss/GnssMeasurementCallback.h
index 32200fd..9b346312 100644
--- a/services/core/jni/gnss/GnssMeasurementCallback.h
+++ b/services/core/jni/gnss/GnssMeasurementCallback.h
@@ -48,7 +48,7 @@
void GnssMeasurement_class_init_once(JNIEnv* env, jclass& clazz);
void setMeasurementData(JNIEnv* env, jobject& callbacksObj, jobject clock,
- jobjectArray measurementArray);
+ jobjectArray measurementArray, jobjectArray gnssAgcArray);
class GnssMeasurementCallbackAidl : public hardware::gnss::BnGnssMeasurementCallback {
public:
@@ -62,6 +62,8 @@
jobjectArray translateAllGnssMeasurements(
JNIEnv* env, const std::vector<hardware::gnss::GnssMeasurement>& measurements);
+ jobjectArray translateAllGnssAgcs(
+ JNIEnv* env, const std::vector<std::optional<hardware::gnss::GnssData::GnssAgc>>& agcs);
void translateAndSetGnssData(const hardware::gnss::GnssData& data);
@@ -139,7 +141,7 @@
size_t count = getMeasurementCount(data);
jobjectArray measurementArray =
translateAllGnssMeasurements(env, data.measurements.data(), count);
- setMeasurementData(env, mCallbacksObj, clock, measurementArray);
+ setMeasurementData(env, mCallbacksObj, clock, measurementArray, nullptr);
env->DeleteLocalRef(clock);
env->DeleteLocalRef(measurementArray);
diff --git a/services/tests/mockingservicestests/src/com/android/server/MasterClearReceiverTest.java b/services/tests/mockingservicestests/src/com/android/server/MasterClearReceiverTest.java
index 5a6275d..cc97b8f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/MasterClearReceiverTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/MasterClearReceiverTest.java
@@ -24,7 +24,6 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.same;
@@ -226,7 +225,7 @@
}
private void expectWipeNonSystemUser() {
- when(mUserManager.removeUserOrSetEphemeral(anyInt(), anyBoolean()))
+ when(mUserManager.removeUserWhenPossible(any(), anyBoolean()))
.thenReturn(UserManager.REMOVE_RESULT_REMOVED);
}
@@ -266,7 +265,7 @@
}
private void verifyWipeNonSystemUser() {
- verify(mUserManager).removeUserOrSetEphemeral(anyInt(), anyBoolean());
+ verify(mUserManager).removeUserWhenPossible(any(), anyBoolean());
}
private void setPendingResultForUser(int userId) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java
index edf6816..1a5888e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java
@@ -183,7 +183,8 @@
mOverridesToRemoveByPackageConfigCaptor.getValue().packageNameToOverridesToRemove;
Map<Long, PackageOverride> addedOverrides;
assertThat(packageNameToAddedOverrides.keySet()).containsExactly(PACKAGE_1, PACKAGE_3);
- assertThat(packageNameToRemovedOverrides.keySet()).containsExactly(PACKAGE_3, PACKAGE_4);
+ assertThat(packageNameToRemovedOverrides.keySet()).containsExactly(PACKAGE_2, PACKAGE_3,
+ PACKAGE_4);
// Package 1
addedOverrides = packageNameToAddedOverrides.get(PACKAGE_1).overrides;
assertThat(addedOverrides).hasSize(3);
@@ -193,6 +194,9 @@
new PackageOverride.Builder().setMinVersionCode(2).setEnabled(true).build());
assertThat(addedOverrides.get(789L)).isEqualTo(
new PackageOverride.Builder().setEnabled(false).build());
+ // Package 2
+ assertThat(packageNameToRemovedOverrides.get(PACKAGE_2).changeIds).containsExactly(123L,
+ 456L, 789L);
// Package 3
addedOverrides = packageNameToAddedOverrides.get(PACKAGE_3).overrides;
assertThat(addedOverrides).hasSize(1);
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/BatteryControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/BatteryControllerTest.java
new file mode 100644
index 0000000..52d0494
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/BatteryControllerTest.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.job.controllers;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.verify;
+
+import android.app.AppGlobals;
+import android.app.job.JobInfo;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.ServiceInfo;
+import android.os.BatteryManagerInternal;
+import android.os.RemoteException;
+import android.util.ArraySet;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.JobSchedulerBackgroundThread;
+import com.android.server.LocalServices;
+import com.android.server.job.JobSchedulerService;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatchers;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+@RunWith(AndroidJUnit4.class)
+public class BatteryControllerTest {
+ private static final int CALLING_UID = 1000;
+ private static final String SOURCE_PACKAGE = "com.android.frameworks.mockingservicestests";
+ private static final int SOURCE_USER_ID = 0;
+
+ private BatteryController mBatteryController;
+ private BroadcastReceiver mPowerReceiver;
+ private JobSchedulerService.Constants mConstants = new JobSchedulerService.Constants();
+ private int mSourceUid;
+
+ private MockitoSession mMockingSession;
+ @Mock
+ private Context mContext;
+ @Mock
+ private BatteryManagerInternal mBatteryManagerInternal;
+ @Mock
+ private JobSchedulerService mJobSchedulerService;
+ @Mock
+ private PackageManagerInternal mPackageManagerInternal;
+
+ @Before
+ public void setUp() {
+ mMockingSession = mockitoSession()
+ .initMocks(this)
+ .strictness(Strictness.LENIENT)
+ .mockStatic(LocalServices.class)
+ .startMocking();
+
+ // Called in StateController constructor.
+ when(mJobSchedulerService.getTestableContext()).thenReturn(mContext);
+ when(mJobSchedulerService.getLock()).thenReturn(mJobSchedulerService);
+ when(mJobSchedulerService.getConstants()).thenReturn(mConstants);
+ // Called in BatteryController constructor.
+ doReturn(mBatteryManagerInternal)
+ .when(() -> LocalServices.getService(BatteryManagerInternal.class));
+ // Used in JobStatus.
+ doReturn(mPackageManagerInternal)
+ .when(() -> LocalServices.getService(PackageManagerInternal.class));
+
+ // Initialize real objects.
+ // Capture the listeners.
+ ArgumentCaptor<BroadcastReceiver> receiverCaptor =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
+ mBatteryController = new BatteryController(mJobSchedulerService);
+
+ verify(mContext).registerReceiver(receiverCaptor.capture(),
+ ArgumentMatchers.argThat(filter ->
+ filter.hasAction(Intent.ACTION_POWER_CONNECTED)
+ && filter.hasAction(Intent.ACTION_POWER_DISCONNECTED)));
+ mPowerReceiver = receiverCaptor.getValue();
+ try {
+ mSourceUid = AppGlobals.getPackageManager().getPackageUid(SOURCE_PACKAGE, 0, 0);
+ // Need to do this since we're using a mock JS and not a real object.
+ doReturn(new ArraySet<>(new String[]{SOURCE_PACKAGE}))
+ .when(mJobSchedulerService).getPackagesForUidLocked(mSourceUid);
+ } catch (RemoteException e) {
+ fail(e.getMessage());
+ }
+ setPowerConnected(false);
+ }
+
+ @After
+ public void tearDown() {
+ if (mMockingSession != null) {
+ mMockingSession.finishMocking();
+ }
+ }
+
+ private void setBatteryNotLow(boolean notLow) {
+ doReturn(notLow).when(mJobSchedulerService).isBatteryNotLow();
+ synchronized (mBatteryController.mLock) {
+ mBatteryController.onBatteryStateChangedLocked();
+ }
+ waitForNonDelayedMessagesProcessed();
+ }
+
+ private void setCharging() {
+ doReturn(true).when(mJobSchedulerService).isBatteryCharging();
+ synchronized (mBatteryController.mLock) {
+ mBatteryController.onBatteryStateChangedLocked();
+ }
+ waitForNonDelayedMessagesProcessed();
+ }
+
+ private void setDischarging() {
+ doReturn(false).when(mJobSchedulerService).isBatteryCharging();
+ synchronized (mBatteryController.mLock) {
+ mBatteryController.onBatteryStateChangedLocked();
+ }
+ waitForNonDelayedMessagesProcessed();
+ }
+
+ private void setPowerConnected(boolean connected) {
+ Intent intent = new Intent(
+ connected ? Intent.ACTION_POWER_CONNECTED : Intent.ACTION_POWER_DISCONNECTED);
+ mPowerReceiver.onReceive(mContext, intent);
+ }
+
+ private void setUidBias(int uid, int bias) {
+ int prevBias = mJobSchedulerService.getUidBias(uid);
+ doReturn(bias).when(mJobSchedulerService).getUidBias(uid);
+ synchronized (mBatteryController.mLock) {
+ mBatteryController.onUidBiasChangedLocked(uid, prevBias, bias);
+ }
+ }
+
+ private void trackJobs(JobStatus... jobs) {
+ for (JobStatus job : jobs) {
+ synchronized (mBatteryController.mLock) {
+ mBatteryController.maybeStartTrackingJobLocked(job, null);
+ }
+ }
+ }
+
+ private void waitForNonDelayedMessagesProcessed() {
+ JobSchedulerBackgroundThread.getHandler().runWithScissors(() -> {}, 15_000);
+ }
+
+ private JobInfo.Builder createBaseJobInfoBuilder(int jobId) {
+ return new JobInfo.Builder(jobId, new ComponentName(mContext, "TestBatteryJobService"));
+ }
+
+ private JobInfo.Builder createBaseJobInfoBuilder(int jobId, String pkgName) {
+ return new JobInfo.Builder(jobId, new ComponentName(pkgName, "TestBatteryJobService"));
+ }
+
+ private JobStatus createJobStatus(String testTag, String packageName, int callingUid,
+ JobInfo jobInfo) {
+ JobStatus js = JobStatus.createFromJobInfo(
+ jobInfo, callingUid, packageName, SOURCE_USER_ID, testTag);
+ js.serviceInfo = mock(ServiceInfo.class);
+ // Make sure tests aren't passing just because the default bucket is likely ACTIVE.
+ js.setStandbyBucket(FREQUENT_INDEX);
+ return js;
+ }
+
+ @Test
+ public void testBatteryNotLow() {
+ JobStatus job1 = createJobStatus("testBatteryNotLow", SOURCE_PACKAGE, CALLING_UID,
+ createBaseJobInfoBuilder(1).setRequiresBatteryNotLow(true).build());
+ JobStatus job2 = createJobStatus("testBatteryNotLow", SOURCE_PACKAGE, CALLING_UID,
+ createBaseJobInfoBuilder(2).setRequiresBatteryNotLow(true).build());
+
+ setBatteryNotLow(false);
+ trackJobs(job1);
+ assertFalse(job1.isConstraintSatisfied(JobStatus.CONSTRAINT_BATTERY_NOT_LOW));
+
+ setBatteryNotLow(true);
+ assertTrue(job1.isConstraintSatisfied(JobStatus.CONSTRAINT_BATTERY_NOT_LOW));
+
+ trackJobs(job2);
+ assertTrue(job2.isConstraintSatisfied(JobStatus.CONSTRAINT_BATTERY_NOT_LOW));
+ }
+
+ @Test
+ public void testCharging_BatteryNotLow() {
+ JobStatus job1 = createJobStatus("testCharging_BatteryNotLow", SOURCE_PACKAGE, CALLING_UID,
+ createBaseJobInfoBuilder(1)
+ .setRequiresCharging(true)
+ .setRequiresBatteryNotLow(true).build());
+ JobStatus job2 = createJobStatus("testCharging_BatteryNotLow", SOURCE_PACKAGE, CALLING_UID,
+ createBaseJobInfoBuilder(2)
+ .setRequiresCharging(true)
+ .setRequiresBatteryNotLow(false).build());
+
+ setBatteryNotLow(true);
+ setDischarging();
+ trackJobs(job1, job2);
+ assertFalse(job1.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+ assertFalse(job2.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+
+ setCharging();
+ assertTrue(job1.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+ assertTrue(job2.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+ }
+
+ @Test
+ public void testTopPowerConnectedExemption() {
+ final int uid1 = mSourceUid;
+ final int uid2 = mSourceUid + 1;
+ final int uid3 = mSourceUid + 2;
+ JobStatus jobFg = createJobStatus("testTopPowerConnectedExemption", SOURCE_PACKAGE, uid1,
+ createBaseJobInfoBuilder(1).setRequiresCharging(true).build());
+ JobStatus jobFgRunner = createJobStatus("testTopPowerConnectedExemption",
+ SOURCE_PACKAGE, uid1,
+ createBaseJobInfoBuilder(2).setRequiresCharging(true).build());
+ JobStatus jobFgLow = createJobStatus("testTopPowerConnectedExemption", SOURCE_PACKAGE, uid1,
+ createBaseJobInfoBuilder(3)
+ .setRequiresCharging(true)
+ .setPriority(JobInfo.PRIORITY_LOW)
+ .build());
+ JobStatus jobBg = createJobStatus("testTopPowerConnectedExemption",
+ "some.background.app", uid2,
+ createBaseJobInfoBuilder(4, "some.background.app")
+ .setRequiresCharging(true)
+ .build());
+ JobStatus jobLateFg = createJobStatus("testTopPowerConnectedExemption",
+ "switch.to.fg", uid3,
+ createBaseJobInfoBuilder(5, "switch.to.fg").setRequiresCharging(true).build());
+ JobStatus jobLateFgLow = createJobStatus("testTopPowerConnectedExemption",
+ "switch.to.fg", uid3,
+ createBaseJobInfoBuilder(6, "switch.to.fg")
+ .setRequiresCharging(true)
+ .setPriority(JobInfo.PRIORITY_MIN)
+ .build());
+
+ setBatteryNotLow(false);
+ setDischarging();
+ setUidBias(uid1, JobInfo.BIAS_TOP_APP);
+ setUidBias(uid2, JobInfo.BIAS_DEFAULT);
+ setUidBias(uid3, JobInfo.BIAS_DEFAULT);
+
+ // Jobs are scheduled when power isn't connected.
+ setPowerConnected(false);
+ trackJobs(jobFg, jobFgLow, jobBg, jobLateFg, jobLateFgLow);
+ assertFalse(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+ assertFalse(jobFgLow.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+ assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+ assertFalse(jobLateFg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+ assertFalse(jobLateFgLow.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+
+ // Power is connected. TOP app should be allowed to start job DEFAULT+ jobs.
+ setPowerConnected(true);
+ assertTrue(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+ assertFalse(jobFgLow.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+ assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+ assertFalse(jobLateFg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+ assertFalse(jobLateFgLow.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+
+ // Test that newly scheduled job of TOP app is correctly allowed to run.
+ trackJobs(jobFgRunner);
+ assertTrue(jobFgRunner.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+
+ // Switch top app. New TOP app should be allowed to run job and the running job of
+ // previously TOP app should be allowed to continue to run.
+ synchronized (mBatteryController.mLock) {
+ mBatteryController.prepareForExecutionLocked(jobFgRunner);
+ }
+ setUidBias(uid1, JobInfo.BIAS_DEFAULT);
+ setUidBias(uid2, JobInfo.BIAS_DEFAULT);
+ setUidBias(uid3, JobInfo.BIAS_TOP_APP);
+ assertFalse(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+ assertTrue(jobFgRunner.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+ assertFalse(jobFgLow.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+ assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+ assertTrue(jobLateFg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+ assertFalse(jobLateFgLow.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+
+ setPowerConnected(false);
+ assertFalse(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+ assertFalse(jobFgRunner.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+ assertFalse(jobFgLow.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+ assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+ assertFalse(jobLateFg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+ assertFalse(jobLateFgLow.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java
index 95912b2..d741459 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java
@@ -200,8 +200,10 @@
}
private void setUidBias(int uid, int bias) {
+ int prevBias = mJobSchedulerService.getUidBias(uid);
+ doReturn(bias).when(mJobSchedulerService).getUidBias(uid);
synchronized (mPrefetchController.mLock) {
- mPrefetchController.onUidBiasChangedLocked(uid, bias);
+ mPrefetchController.onUidBiasChangedLocked(uid, prevBias, bias);
}
}
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 202a54d..587447a 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -95,6 +95,7 @@
<uses-permission android:name="android.permission.CONTROL_DEVICE_STATE"/>
<uses-permission android:name="android.permission.READ_PROJECTION_STATE"/>
<uses-permission android:name="android.permission.KILL_UID"/>
+ <uses-permission android:name="android.permission.MAINLINE_NETWORK_STACK"/>
<uses-permission
android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD"/>
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
new file mode 100644
index 0000000..d4bac2c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2021 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.biometrics.sensors;
+
+import static android.testing.TestableLooper.RunWithLooper;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertThrows;
+
+import android.os.Handler;
+import android.platform.test.annotations.Presubmit;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@Presubmit
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper(setAsMainLooper = true)
+@SmallTest
+public class BiometricSchedulerOperationTest {
+
+ public interface FakeHal {}
+ public abstract static class InterruptableMonitor<T>
+ extends HalClientMonitor<T> implements Interruptable {
+ public InterruptableMonitor() {
+ super(null, null, null, null, 0, null, 0, 0, 0, 0, 0);
+ }
+ }
+
+ @Mock
+ private InterruptableMonitor<FakeHal> mClientMonitor;
+ @Mock
+ private BaseClientMonitor.Callback mClientCallback;
+ @Mock
+ private FakeHal mHal;
+ @Captor
+ ArgumentCaptor<BaseClientMonitor.Callback> mStartCallback;
+
+ private Handler mHandler;
+ private BiometricSchedulerOperation mOperation;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mHandler = new Handler(TestableLooper.get(this).getLooper());
+ mOperation = new BiometricSchedulerOperation(mClientMonitor, mClientCallback);
+ }
+
+ @Test
+ public void testStartWithCookie() {
+ final int cookie = 200;
+ when(mClientMonitor.getCookie()).thenReturn(cookie);
+ when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+ assertThat(mOperation.isReadyToStart()).isEqualTo(cookie);
+ assertThat(mOperation.isStarted()).isFalse();
+ assertThat(mOperation.isCanceling()).isFalse();
+ assertThat(mOperation.isFinished()).isFalse();
+
+ final boolean started = mOperation.startWithCookie(
+ mock(BaseClientMonitor.Callback.class), cookie);
+
+ assertThat(started).isTrue();
+ verify(mClientMonitor).start(mStartCallback.capture());
+ mStartCallback.getValue().onClientStarted(mClientMonitor);
+ assertThat(mOperation.isStarted()).isTrue();
+ }
+
+ @Test
+ public void testNoStartWithoutCookie() {
+ final int goodCookie = 20;
+ final int badCookie = 22;
+ when(mClientMonitor.getCookie()).thenReturn(goodCookie);
+ when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+ assertThat(mOperation.isReadyToStart()).isEqualTo(goodCookie);
+ final boolean started = mOperation.startWithCookie(
+ mock(BaseClientMonitor.Callback.class), badCookie);
+
+ assertThat(started).isFalse();
+ assertThat(mOperation.isStarted()).isFalse();
+ assertThat(mOperation.isCanceling()).isFalse();
+ assertThat(mOperation.isFinished()).isFalse();
+ }
+
+ @Test
+ public void startsWhenReadyAndHalAvailable() {
+ when(mClientMonitor.getCookie()).thenReturn(0);
+ when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+ final BaseClientMonitor.Callback cb = mock(BaseClientMonitor.Callback.class);
+ mOperation.start(cb);
+ verify(mClientMonitor).start(mStartCallback.capture());
+ mStartCallback.getValue().onClientStarted(mClientMonitor);
+
+ assertThat(mOperation.isStarted()).isTrue();
+ assertThat(mOperation.isCanceling()).isFalse();
+ assertThat(mOperation.isFinished()).isFalse();
+
+ verify(mClientCallback).onClientStarted(eq(mClientMonitor));
+ verify(cb).onClientStarted(eq(mClientMonitor));
+ verify(mClientCallback, never()).onClientFinished(any(), anyBoolean());
+ verify(cb, never()).onClientFinished(any(), anyBoolean());
+
+ mStartCallback.getValue().onClientFinished(mClientMonitor, true);
+
+ assertThat(mOperation.isFinished()).isTrue();
+ assertThat(mOperation.isCanceling()).isFalse();
+ verify(mClientMonitor).destroy();
+ verify(cb).onClientFinished(eq(mClientMonitor), eq(true));
+ }
+
+ @Test
+ public void startFailsWhenReadyButHalNotAvailable() {
+ when(mClientMonitor.getCookie()).thenReturn(0);
+ when(mClientMonitor.getFreshDaemon()).thenReturn(null);
+
+ final BaseClientMonitor.Callback cb = mock(BaseClientMonitor.Callback.class);
+ mOperation.start(cb);
+ verify(mClientMonitor, never()).start(any());
+
+ assertThat(mOperation.isStarted()).isFalse();
+ assertThat(mOperation.isCanceling()).isFalse();
+ assertThat(mOperation.isFinished()).isTrue();
+
+ verify(mClientCallback, never()).onClientStarted(eq(mClientMonitor));
+ verify(cb, never()).onClientStarted(eq(mClientMonitor));
+ verify(mClientCallback).onClientFinished(eq(mClientMonitor), eq(false));
+ verify(cb).onClientFinished(eq(mClientMonitor), eq(false));
+ }
+
+ @Test
+ public void doesNotStartWithCookie() {
+ when(mClientMonitor.getCookie()).thenReturn(9);
+ assertThrows(IllegalStateException.class,
+ () -> mOperation.start(mock(BaseClientMonitor.Callback.class)));
+ }
+
+ @Test
+ public void cannotRestart() {
+ when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+ mOperation.start(mock(BaseClientMonitor.Callback.class));
+
+ assertThrows(IllegalStateException.class,
+ () -> mOperation.start(mock(BaseClientMonitor.Callback.class)));
+ }
+
+ @Test
+ public void abortsNotRunning() {
+ when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+ mOperation.abort();
+
+ assertThat(mOperation.isFinished()).isTrue();
+ verify(mClientMonitor).unableToStart();
+ verify(mClientMonitor).destroy();
+ assertThrows(IllegalStateException.class,
+ () -> mOperation.start(mock(BaseClientMonitor.Callback.class)));
+ }
+
+ @Test
+ public void cannotAbortRunning() {
+ when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+ mOperation.start(mock(BaseClientMonitor.Callback.class));
+
+ assertThrows(IllegalStateException.class, () -> mOperation.abort());
+ }
+
+ @Test
+ public void cancel() {
+ when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+ final BaseClientMonitor.Callback startCb = mock(BaseClientMonitor.Callback.class);
+ final BaseClientMonitor.Callback cancelCb = mock(BaseClientMonitor.Callback.class);
+ mOperation.start(startCb);
+ verify(mClientMonitor).start(mStartCallback.capture());
+ mStartCallback.getValue().onClientStarted(mClientMonitor);
+ mOperation.cancel(mHandler, cancelCb);
+
+ assertThat(mOperation.isCanceling()).isTrue();
+ verify(mClientMonitor).cancel();
+ verify(mClientMonitor, never()).cancelWithoutStarting(any());
+ verify(mClientMonitor, never()).destroy();
+
+ mStartCallback.getValue().onClientFinished(mClientMonitor, true);
+
+ assertThat(mOperation.isFinished()).isTrue();
+ assertThat(mOperation.isCanceling()).isFalse();
+ verify(mClientMonitor).destroy();
+
+ // should be unused since the operation was started
+ verify(cancelCb, never()).onClientStarted(any());
+ verify(cancelCb, never()).onClientFinished(any(), anyBoolean());
+ }
+
+ @Test
+ public void cancelWithoutStarting() {
+ when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+ final BaseClientMonitor.Callback cancelCb = mock(BaseClientMonitor.Callback.class);
+ mOperation.cancel(mHandler, cancelCb);
+
+ assertThat(mOperation.isCanceling()).isTrue();
+ ArgumentCaptor<BaseClientMonitor.Callback> cbCaptor =
+ ArgumentCaptor.forClass(BaseClientMonitor.Callback.class);
+ verify(mClientMonitor).cancelWithoutStarting(cbCaptor.capture());
+
+ cbCaptor.getValue().onClientFinished(mClientMonitor, true);
+ verify(cancelCb).onClientFinished(eq(mClientMonitor), eq(true));
+ verify(mClientMonitor, never()).start(any());
+ verify(mClientMonitor, never()).cancel();
+ verify(mClientMonitor).destroy();
+ }
+
+ @Test
+ public void markCanceling() {
+ when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+ mOperation.markCanceling();
+
+ assertThat(mOperation.isMarkedCanceling()).isTrue();
+ assertThat(mOperation.isCanceling()).isFalse();
+ assertThat(mOperation.isFinished()).isFalse();
+ verify(mClientMonitor, never()).start(any());
+ verify(mClientMonitor, never()).cancel();
+ verify(mClientMonitor, never()).cancelWithoutStarting(any());
+ verify(mClientMonitor, never()).unableToStart();
+ verify(mClientMonitor, never()).destroy();
+ }
+
+ @Test
+ public void cancelPendingWithCookie() {
+ markCancellingAndStart(2);
+ }
+
+ @Test
+ public void cancelPendingWithoutCookie() {
+ markCancellingAndStart(null);
+ }
+
+ private void markCancellingAndStart(Integer withCookie) {
+ when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+ if (withCookie != null) {
+ when(mClientMonitor.getCookie()).thenReturn(withCookie);
+ }
+
+ mOperation.markCanceling();
+ final BaseClientMonitor.Callback cb = mock(BaseClientMonitor.Callback.class);
+ if (withCookie != null) {
+ mOperation.startWithCookie(cb, withCookie);
+ } else {
+ mOperation.start(cb);
+ }
+
+ assertThat(mOperation.isFinished()).isTrue();
+ verify(cb).onClientFinished(eq(mClientMonitor), eq(true));
+ verify(mClientMonitor, never()).start(any());
+ verify(mClientMonitor, never()).cancel();
+ verify(mClientMonitor, never()).cancelWithoutStarting(any());
+ verify(mClientMonitor, never()).unableToStart();
+ verify(mClientMonitor).destroy();
+ }
+
+ @Test
+ public void cancelWatchdogWhenStarted() {
+ cancelWatchdog(true);
+ }
+
+ @Test
+ public void cancelWatchdogWithoutStarting() {
+ cancelWatchdog(false);
+ }
+
+ private void cancelWatchdog(boolean start) {
+ when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+ mOperation.start(mock(BaseClientMonitor.Callback.class));
+ if (start) {
+ verify(mClientMonitor).start(mStartCallback.capture());
+ mStartCallback.getValue().onClientStarted(mClientMonitor);
+ }
+ mOperation.cancel(mHandler, mock(BaseClientMonitor.Callback.class));
+
+ assertThat(mOperation.isCanceling()).isTrue();
+
+ // omit call to onClientFinished and trigger watchdog
+ mOperation.mCancelWatchdog.run();
+
+ assertThat(mOperation.isFinished()).isTrue();
+ assertThat(mOperation.isCanceling()).isFalse();
+ verify(mClientMonitor).destroy();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
index d192697..ac08319 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
@@ -16,10 +16,14 @@
package com.android.server.biometrics.sensors;
+import static android.testing.TestableLooper.RunWithLooper;
+
import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -34,10 +38,13 @@
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.IBiometricService;
import android.os.Binder;
+import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
+import android.testing.AndroidTestingRunner;
import android.testing.TestableContext;
+import android.testing.TestableLooper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -46,16 +53,18 @@
import com.android.server.biometrics.nano.BiometricSchedulerProto;
import com.android.server.biometrics.nano.BiometricsProto;
-import com.android.server.biometrics.sensors.BiometricScheduler.Operation;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@Presubmit
@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper(setAsMainLooper = true)
public class BiometricSchedulerTest {
private static final String TAG = "BiometricSchedulerTest";
@@ -76,8 +85,9 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
mToken = new Binder();
- mScheduler = new BiometricScheduler(TAG, BiometricScheduler.SENSOR_TYPE_UNKNOWN,
- null /* gestureAvailabilityTracker */, mBiometricService, LOG_NUM_RECENT_OPERATIONS,
+ mScheduler = new BiometricScheduler(TAG, new Handler(TestableLooper.get(this).getLooper()),
+ BiometricScheduler.SENSOR_TYPE_UNKNOWN, null /* gestureAvailabilityTracker */,
+ mBiometricService, LOG_NUM_RECENT_OPERATIONS,
CoexCoordinator.getInstance());
}
@@ -86,9 +96,9 @@
final HalClientMonitor.LazyDaemon<Object> nonNullDaemon = () -> mock(Object.class);
final HalClientMonitor<Object> client1 =
- new TestClientMonitor(mContext, mToken, nonNullDaemon);
+ new TestHalClientMonitor(mContext, mToken, nonNullDaemon);
final HalClientMonitor<Object> client2 =
- new TestClientMonitor(mContext, mToken, nonNullDaemon);
+ new TestHalClientMonitor(mContext, mToken, nonNullDaemon);
mScheduler.scheduleClientMonitor(client1);
mScheduler.scheduleClientMonitor(client2);
@@ -99,20 +109,17 @@
@Test
public void testRemovesPendingOperations_whenNullHal_andNotBiometricPrompt() {
// Even if second client has a non-null daemon, it needs to be canceled.
- Object daemon2 = mock(Object.class);
-
- final HalClientMonitor.LazyDaemon<Object> lazyDaemon1 = () -> null;
- final HalClientMonitor.LazyDaemon<Object> lazyDaemon2 = () -> daemon2;
-
- final TestClientMonitor client1 = new TestClientMonitor(mContext, mToken, lazyDaemon1);
- final TestClientMonitor client2 = new TestClientMonitor(mContext, mToken, lazyDaemon2);
+ final TestHalClientMonitor client1 = new TestHalClientMonitor(
+ mContext, mToken, () -> null);
+ final TestHalClientMonitor client2 = new TestHalClientMonitor(
+ mContext, mToken, () -> mock(Object.class));
final BaseClientMonitor.Callback callback1 = mock(BaseClientMonitor.Callback.class);
final BaseClientMonitor.Callback callback2 = mock(BaseClientMonitor.Callback.class);
// Pretend the scheduler is busy so the first operation doesn't start right away. We want
// to pretend like there are two operations in the queue before kicking things off
- mScheduler.mCurrentOperation = new BiometricScheduler.Operation(
+ mScheduler.mCurrentOperation = new BiometricSchedulerOperation(
mock(BaseClientMonitor.class), mock(BaseClientMonitor.Callback.class));
mScheduler.scheduleClientMonitor(client1, callback1);
@@ -122,11 +129,11 @@
mScheduler.scheduleClientMonitor(client2, callback2);
waitForIdle();
- assertTrue(client1.wasUnableToStart());
+ assertTrue(client1.mUnableToStart);
verify(callback1).onClientFinished(eq(client1), eq(false) /* success */);
verify(callback1, never()).onClientStarted(any());
- assertTrue(client2.wasUnableToStart());
+ assertTrue(client2.mUnableToStart);
verify(callback2).onClientFinished(eq(client2), eq(false) /* success */);
verify(callback2, never()).onClientStarted(any());
@@ -138,21 +145,19 @@
// Second non-BiometricPrompt client has a valid daemon
final Object daemon2 = mock(Object.class);
- final HalClientMonitor.LazyDaemon<Object> lazyDaemon1 = () -> null;
- final HalClientMonitor.LazyDaemon<Object> lazyDaemon2 = () -> daemon2;
-
final ClientMonitorCallbackConverter listener1 = mock(ClientMonitorCallbackConverter.class);
final TestAuthenticationClient client1 =
- new TestAuthenticationClient(mContext, lazyDaemon1, mToken, listener1);
- final TestClientMonitor client2 = new TestClientMonitor(mContext, mToken, lazyDaemon2);
+ new TestAuthenticationClient(mContext, () -> null, mToken, listener1);
+ final TestHalClientMonitor client2 =
+ new TestHalClientMonitor(mContext, mToken, () -> daemon2);
final BaseClientMonitor.Callback callback1 = mock(BaseClientMonitor.Callback.class);
final BaseClientMonitor.Callback callback2 = mock(BaseClientMonitor.Callback.class);
// Pretend the scheduler is busy so the first operation doesn't start right away. We want
// to pretend like there are two operations in the queue before kicking things off
- mScheduler.mCurrentOperation = new BiometricScheduler.Operation(
+ mScheduler.mCurrentOperation = new BiometricSchedulerOperation(
mock(BaseClientMonitor.class), mock(BaseClientMonitor.Callback.class));
mScheduler.scheduleClientMonitor(client1, callback1);
@@ -172,8 +177,8 @@
verify(callback1, never()).onClientStarted(any());
// Client 2 was able to start
- assertFalse(client2.wasUnableToStart());
- assertTrue(client2.hasStarted());
+ assertFalse(client2.mUnableToStart);
+ assertTrue(client2.mStarted);
verify(callback2).onClientStarted(eq(client2));
}
@@ -187,16 +192,18 @@
// Schedule a BiometricPrompt authentication request
mScheduler.scheduleClientMonitor(client1, callback1);
- assertEquals(Operation.STATE_WAITING_FOR_COOKIE, mScheduler.mCurrentOperation.mState);
- assertEquals(client1, mScheduler.mCurrentOperation.mClientMonitor);
+ assertNotEquals(0, mScheduler.mCurrentOperation.isReadyToStart());
+ assertEquals(client1, mScheduler.mCurrentOperation.getClientMonitor());
assertEquals(0, mScheduler.mPendingOperations.size());
// Request it to be canceled. The operation can be canceled immediately, and the scheduler
// should go back to idle, since in this case the framework has not even requested the HAL
// to authenticate yet.
mScheduler.cancelAuthenticationOrDetection(mToken, 1 /* requestId */);
+ waitForIdle();
assertTrue(client1.isAlreadyDone());
assertTrue(client1.mDestroyed);
+ assertFalse(client1.mStartedHal);
assertNull(mScheduler.mCurrentOperation);
}
@@ -210,8 +217,8 @@
// assertEquals(0, bsp.recentOperations.length);
// Pretend the scheduler is busy enrolling, and check the proto dump again.
- final TestClientMonitor2 client = new TestClientMonitor2(mContext, mToken,
- () -> mock(Object.class), BiometricsProto.CM_ENROLL);
+ final TestHalClientMonitor client = new TestHalClientMonitor(mContext, mToken,
+ () -> mock(Object.class), 0, BiometricsProto.CM_ENROLL);
mScheduler.scheduleClientMonitor(client);
waitForIdle();
bsp = getDump(true /* clearSchedulerBuffer */);
@@ -230,8 +237,8 @@
@Test
public void testProtoDump_fifo() throws Exception {
// Add the first operation
- final TestClientMonitor2 client = new TestClientMonitor2(mContext, mToken,
- () -> mock(Object.class), BiometricsProto.CM_ENROLL);
+ final TestHalClientMonitor client = new TestHalClientMonitor(mContext, mToken,
+ () -> mock(Object.class), 0, BiometricsProto.CM_ENROLL);
mScheduler.scheduleClientMonitor(client);
waitForIdle();
BiometricSchedulerProto bsp = getDump(false /* clearSchedulerBuffer */);
@@ -244,8 +251,8 @@
client.getCallback().onClientFinished(client, true);
// Add another operation
- final TestClientMonitor2 client2 = new TestClientMonitor2(mContext, mToken,
- () -> mock(Object.class), BiometricsProto.CM_REMOVE);
+ final TestHalClientMonitor client2 = new TestHalClientMonitor(mContext, mToken,
+ () -> mock(Object.class), 0, BiometricsProto.CM_REMOVE);
mScheduler.scheduleClientMonitor(client2);
waitForIdle();
bsp = getDump(false /* clearSchedulerBuffer */);
@@ -256,8 +263,8 @@
client2.getCallback().onClientFinished(client2, true);
// And another operation
- final TestClientMonitor2 client3 = new TestClientMonitor2(mContext, mToken,
- () -> mock(Object.class), BiometricsProto.CM_AUTHENTICATE);
+ final TestHalClientMonitor client3 = new TestHalClientMonitor(mContext, mToken,
+ () -> mock(Object.class), 0, BiometricsProto.CM_AUTHENTICATE);
mScheduler.scheduleClientMonitor(client3);
waitForIdle();
bsp = getDump(false /* clearSchedulerBuffer */);
@@ -290,8 +297,7 @@
@Test
public void testCancelPendingAuth() throws RemoteException {
final HalClientMonitor.LazyDaemon<Object> lazyDaemon = () -> mock(Object.class);
-
- final TestClientMonitor client1 = new TestClientMonitor(mContext, mToken, lazyDaemon);
+ final TestHalClientMonitor client1 = new TestHalClientMonitor(mContext, mToken, lazyDaemon);
final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class);
final TestAuthenticationClient client2 = new TestAuthenticationClient(mContext, lazyDaemon,
mToken, callback);
@@ -302,14 +308,12 @@
waitForIdle();
assertEquals(mScheduler.getCurrentClient(), client1);
- assertEquals(Operation.STATE_WAITING_IN_QUEUE,
- mScheduler.mPendingOperations.getFirst().mState);
+ assertFalse(mScheduler.mPendingOperations.getFirst().isStarted());
// Request cancel before the authentication client has started
mScheduler.cancelAuthenticationOrDetection(mToken, 1 /* requestId */);
waitForIdle();
- assertEquals(Operation.STATE_WAITING_IN_QUEUE_CANCELING,
- mScheduler.mPendingOperations.getFirst().mState);
+ assertTrue(mScheduler.mPendingOperations.getFirst().isMarkedCanceling());
// Finish the blocking client. The authentication client should send ERROR_CANCELED
client1.getCallback().onClientFinished(client1, true /* success */);
@@ -326,67 +330,109 @@
@Test
public void testCancels_whenAuthRequestIdNotSet() {
- testCancelsWhenRequestId(null /* requestId */, 2, true /* started */);
+ testCancelsAuthDetectWhenRequestId(null /* requestId */, 2, true /* started */);
}
@Test
public void testCancels_whenAuthRequestIdNotSet_notStarted() {
- testCancelsWhenRequestId(null /* requestId */, 2, false /* started */);
+ testCancelsAuthDetectWhenRequestId(null /* requestId */, 2, false /* started */);
}
@Test
public void testCancels_whenAuthRequestIdMatches() {
- testCancelsWhenRequestId(200L, 200, true /* started */);
+ testCancelsAuthDetectWhenRequestId(200L, 200, true /* started */);
}
@Test
public void testCancels_whenAuthRequestIdMatches_noStarted() {
- testCancelsWhenRequestId(200L, 200, false /* started */);
+ testCancelsAuthDetectWhenRequestId(200L, 200, false /* started */);
}
@Test
public void testDoesNotCancel_whenAuthRequestIdMismatched() {
- testCancelsWhenRequestId(10L, 20, true /* started */);
+ testCancelsAuthDetectWhenRequestId(10L, 20, true /* started */);
}
@Test
public void testDoesNotCancel_whenAuthRequestIdMismatched_notStarted() {
- testCancelsWhenRequestId(10L, 20, false /* started */);
+ testCancelsAuthDetectWhenRequestId(10L, 20, false /* started */);
+ }
+
+ private void testCancelsAuthDetectWhenRequestId(@Nullable Long requestId, long cancelRequestId,
+ boolean started) {
+ final HalClientMonitor.LazyDaemon<Object> lazyDaemon = () -> mock(Object.class);
+ final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class);
+ testCancelsWhenRequestId(requestId, cancelRequestId, started,
+ new TestAuthenticationClient(mContext, lazyDaemon, mToken, callback));
+ }
+
+ @Test
+ public void testCancels_whenEnrollRequestIdNotSet() {
+ testCancelsEnrollWhenRequestId(null /* requestId */, 2, false /* started */);
+ }
+
+ @Test
+ public void testCancels_whenEnrollRequestIdMatches() {
+ testCancelsEnrollWhenRequestId(200L, 200, false /* started */);
+ }
+
+ @Test
+ public void testDoesNotCancel_whenEnrollRequestIdMismatched() {
+ testCancelsEnrollWhenRequestId(10L, 20, false /* started */);
+ }
+
+ private void testCancelsEnrollWhenRequestId(@Nullable Long requestId, long cancelRequestId,
+ boolean started) {
+ final HalClientMonitor.LazyDaemon<Object> lazyDaemon = () -> mock(Object.class);
+ final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class);
+ testCancelsWhenRequestId(requestId, cancelRequestId, started,
+ new TestEnrollClient(mContext, lazyDaemon, mToken, callback));
}
private void testCancelsWhenRequestId(@Nullable Long requestId, long cancelRequestId,
- boolean started) {
+ boolean started, HalClientMonitor<?> client) {
final boolean matches = requestId == null || requestId == cancelRequestId;
- final HalClientMonitor.LazyDaemon<Object> lazyDaemon = () -> mock(Object.class);
- final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class);
- final TestAuthenticationClient client = new TestAuthenticationClient(
- mContext, lazyDaemon, mToken, callback);
if (requestId != null) {
client.setRequestId(requestId);
}
+ final boolean isAuth = client instanceof TestAuthenticationClient;
+ final boolean isEnroll = client instanceof TestEnrollClient;
+
mScheduler.scheduleClientMonitor(client);
if (started) {
mScheduler.startPreparedClient(client.getCookie());
}
waitForIdle();
- mScheduler.cancelAuthenticationOrDetection(mToken, cancelRequestId);
+ if (isAuth) {
+ mScheduler.cancelAuthenticationOrDetection(mToken, cancelRequestId);
+ } else if (isEnroll) {
+ mScheduler.cancelEnrollment(mToken, cancelRequestId);
+ } else {
+ fail("unexpected operation type");
+ }
waitForIdle();
- assertEquals(matches && started ? 1 : 0, client.mNumCancels);
+ if (isAuth) {
+ // auth clients that were waiting for cookie when canceled should never invoke the hal
+ final TestAuthenticationClient authClient = (TestAuthenticationClient) client;
+ assertEquals(matches && started ? 1 : 0, authClient.mNumCancels);
+ assertEquals(started, authClient.mStartedHal);
+ } else if (isEnroll) {
+ final TestEnrollClient enrollClient = (TestEnrollClient) client;
+ assertEquals(matches ? 1 : 0, enrollClient.mNumCancels);
+ assertTrue(enrollClient.mStartedHal);
+ }
if (matches) {
- if (started) {
- assertEquals(Operation.STATE_STARTED_CANCELING,
- mScheduler.mCurrentOperation.mState);
+ if (started || isEnroll) { // prep'd auth clients and enroll clients
+ assertTrue(mScheduler.mCurrentOperation.isCanceling());
}
} else {
- if (started) {
- assertEquals(Operation.STATE_STARTED,
- mScheduler.mCurrentOperation.mState);
+ if (started || isEnroll) { // prep'd auth clients and enroll clients
+ assertTrue(mScheduler.mCurrentOperation.isStarted());
} else {
- assertEquals(Operation.STATE_WAITING_FOR_COOKIE,
- mScheduler.mCurrentOperation.mState);
+ assertNotEquals(0, mScheduler.mCurrentOperation.isReadyToStart());
}
}
}
@@ -411,18 +457,14 @@
mScheduler.cancelAuthenticationOrDetection(mToken, 9999);
waitForIdle();
- assertEquals(Operation.STATE_STARTED,
- mScheduler.mCurrentOperation.mState);
- assertEquals(Operation.STATE_WAITING_IN_QUEUE,
- mScheduler.mPendingOperations.getFirst().mState);
+ assertTrue(mScheduler.mCurrentOperation.isStarted());
+ assertFalse(mScheduler.mPendingOperations.getFirst().isStarted());
mScheduler.cancelAuthenticationOrDetection(mToken, requestId2);
waitForIdle();
- assertEquals(Operation.STATE_STARTED,
- mScheduler.mCurrentOperation.mState);
- assertEquals(Operation.STATE_WAITING_IN_QUEUE_CANCELING,
- mScheduler.mPendingOperations.getFirst().mState);
+ assertTrue(mScheduler.mCurrentOperation.isStarted());
+ assertTrue(mScheduler.mPendingOperations.getFirst().isMarkedCanceling());
}
@Test
@@ -459,12 +501,12 @@
@Test
public void testClientDestroyed_afterFinish() {
final HalClientMonitor.LazyDaemon<Object> nonNullDaemon = () -> mock(Object.class);
- final TestClientMonitor client =
- new TestClientMonitor(mContext, mToken, nonNullDaemon);
+ final TestHalClientMonitor client =
+ new TestHalClientMonitor(mContext, mToken, nonNullDaemon);
mScheduler.scheduleClientMonitor(client);
client.mCallback.onClientFinished(client, true /* success */);
waitForIdle();
- assertTrue(client.wasDestroyed());
+ assertTrue(client.mDestroyed);
}
private BiometricSchedulerProto getDump(boolean clearSchedulerBuffer) throws Exception {
@@ -472,8 +514,10 @@
}
private static class TestAuthenticationClient extends AuthenticationClient<Object> {
- int mNumCancels = 0;
+ boolean mStartedHal = false;
+ boolean mStoppedHal = false;
boolean mDestroyed = false;
+ int mNumCancels = 0;
public TestAuthenticationClient(@NonNull Context context,
@NonNull LazyDaemon<Object> lazyDaemon, @NonNull IBinder token,
@@ -488,18 +532,16 @@
@Override
protected void stopHalOperation() {
-
+ mStoppedHal = true;
}
@Override
protected void startHalOperation() {
-
+ mStartedHal = true;
}
@Override
- protected void handleLifecycleAfterAuth(boolean authenticated) {
-
- }
+ protected void handleLifecycleAfterAuth(boolean authenticated) {}
@Override
public boolean wasUserDetected() {
@@ -519,36 +561,59 @@
}
}
- private static class TestClientMonitor2 extends TestClientMonitor {
- private final int mProtoEnum;
+ private static class TestEnrollClient extends EnrollClient<Object> {
+ boolean mStartedHal = false;
+ boolean mStoppedHal = false;
+ int mNumCancels = 0;
- public TestClientMonitor2(@NonNull Context context, @NonNull IBinder token,
- @NonNull LazyDaemon<Object> lazyDaemon, int protoEnum) {
- super(context, token, lazyDaemon);
- mProtoEnum = protoEnum;
+ TestEnrollClient(@NonNull Context context,
+ @NonNull LazyDaemon<Object> lazyDaemon, @NonNull IBinder token,
+ @NonNull ClientMonitorCallbackConverter listener) {
+ super(context, lazyDaemon, token, listener, 0 /* userId */, new byte[69],
+ "test" /* owner */, mock(BiometricUtils.class),
+ 5 /* timeoutSec */, 0 /* statsModality */, TEST_SENSOR_ID,
+ true /* shouldVibrate */);
}
@Override
- public int getProtoEnum() {
- return mProtoEnum;
+ protected void stopHalOperation() {
+ mStoppedHal = true;
+ }
+
+ @Override
+ protected void startHalOperation() {
+ mStartedHal = true;
+ }
+
+ @Override
+ protected boolean hasReachedEnrollmentLimit() {
+ return false;
+ }
+
+ @Override
+ public void cancel() {
+ mNumCancels++;
+ super.cancel();
}
}
- private static class TestClientMonitor extends HalClientMonitor<Object> {
+ private static class TestHalClientMonitor extends HalClientMonitor<Object> {
+ private final int mProtoEnum;
private boolean mUnableToStart;
private boolean mStarted;
private boolean mDestroyed;
- public TestClientMonitor(@NonNull Context context, @NonNull IBinder token,
+ TestHalClientMonitor(@NonNull Context context, @NonNull IBinder token,
@NonNull LazyDaemon<Object> lazyDaemon) {
- this(context, token, lazyDaemon, 0 /* cookie */);
+ this(context, token, lazyDaemon, 0 /* cookie */, BiometricsProto.CM_UPDATE_ACTIVE_USER);
}
- public TestClientMonitor(@NonNull Context context, @NonNull IBinder token,
- @NonNull LazyDaemon<Object> lazyDaemon, int cookie) {
+ TestHalClientMonitor(@NonNull Context context, @NonNull IBinder token,
+ @NonNull LazyDaemon<Object> lazyDaemon, int cookie, int protoEnum) {
super(context, lazyDaemon, token /* token */, null /* listener */, 0 /* userId */,
TAG, cookie, TEST_SENSOR_ID, 0 /* statsModality */,
0 /* statsAction */, 0 /* statsClient */);
+ mProtoEnum = protoEnum;
}
@Override
@@ -559,9 +624,7 @@
@Override
public int getProtoEnum() {
- // Anything other than CM_NONE, which is used to represent "idle". Tests that need
- // real proto enums should use TestClientMonitor2
- return BiometricsProto.CM_UPDATE_ACTIVE_USER;
+ return mProtoEnum;
}
@Override
@@ -573,7 +636,7 @@
@Override
protected void startHalOperation() {
-
+ mStarted = true;
}
@Override
@@ -581,22 +644,9 @@
super.destroy();
mDestroyed = true;
}
-
- public boolean wasUnableToStart() {
- return mUnableToStart;
- }
-
- public boolean hasStarted() {
- return mStarted;
- }
-
- public boolean wasDestroyed() {
- return mDestroyed;
- }
-
}
- private static void waitForIdle() {
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ private void waitForIdle() {
+ TestableLooper.get(this).processAllMessages();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
index 7fccd49..407f5fb 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
@@ -16,6 +16,8 @@
package com.android.server.biometrics.sensors;
+import static android.testing.TestableLooper.RunWithLooper;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
@@ -28,52 +30,53 @@
import android.content.Context;
import android.hardware.biometrics.IBiometricService;
import android.os.Binder;
+import android.os.Handler;
import android.os.IBinder;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@Presubmit
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
@SmallTest
public class UserAwareBiometricSchedulerTest {
- private static final String TAG = "BiometricSchedulerTest";
+ private static final String TAG = "UserAwareBiometricSchedulerTest";
private static final int TEST_SENSOR_ID = 0;
+ private Handler mHandler;
private UserAwareBiometricScheduler mScheduler;
- private IBinder mToken;
+ private IBinder mToken = new Binder();
@Mock
private Context mContext;
@Mock
private IBiometricService mBiometricService;
- private TestUserStartedCallback mUserStartedCallback;
- private TestUserStoppedCallback mUserStoppedCallback;
+ private TestUserStartedCallback mUserStartedCallback = new TestUserStartedCallback();
+ private TestUserStoppedCallback mUserStoppedCallback = new TestUserStoppedCallback();
private int mCurrentUserId = UserHandle.USER_NULL;
- private boolean mStartOperationsFinish;
- private int mStartUserClientCount;
+ private boolean mStartOperationsFinish = true;
+ private int mStartUserClientCount = 0;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
-
- mToken = new Binder();
- mStartOperationsFinish = true;
- mStartUserClientCount = 0;
- mUserStartedCallback = new TestUserStartedCallback();
- mUserStoppedCallback = new TestUserStoppedCallback();
-
+ mHandler = new Handler(TestableLooper.get(this).getLooper());
mScheduler = new UserAwareBiometricScheduler(TAG,
+ mHandler,
BiometricScheduler.SENSOR_TYPE_UNKNOWN,
null /* gestureAvailabilityDispatcher */,
mBiometricService,
@@ -117,7 +120,7 @@
mCurrentUserId = UserHandle.USER_NULL;
mStartOperationsFinish = false;
- final BaseClientMonitor[] nextClients = new BaseClientMonitor[] {
+ final BaseClientMonitor[] nextClients = new BaseClientMonitor[]{
mock(BaseClientMonitor.class),
mock(BaseClientMonitor.class),
mock(BaseClientMonitor.class)
@@ -147,11 +150,11 @@
waitForIdle();
final TestStartUserClient startUserClient =
- (TestStartUserClient) mScheduler.mCurrentOperation.mClientMonitor;
+ (TestStartUserClient) mScheduler.mCurrentOperation.getClientMonitor();
mScheduler.reset();
assertNull(mScheduler.mCurrentOperation);
- final BiometricScheduler.Operation fakeOperation = new BiometricScheduler.Operation(
+ final BiometricSchedulerOperation fakeOperation = new BiometricSchedulerOperation(
mock(BaseClientMonitor.class), new BaseClientMonitor.Callback() {});
mScheduler.mCurrentOperation = fakeOperation;
startUserClient.mCallback.onClientFinished(startUserClient, true);
@@ -194,8 +197,8 @@
verify(nextClient).start(any());
}
- private static void waitForIdle() {
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ private void waitForIdle() {
+ TestableLooper.get(this).processAllMessages();
}
private class TestUserStoppedCallback implements StopUserClient.UserStoppedCallback {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
index a13dff2..2718bf9 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
@@ -33,6 +33,7 @@
import androidx.test.filters.SmallTest;
import com.android.server.biometrics.sensors.BiometricScheduler;
+import com.android.server.biometrics.sensors.CoexCoordinator;
import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.LockoutTracker;
@@ -79,10 +80,13 @@
when(mContext.getSystemService(Context.BIOMETRIC_SERVICE)).thenReturn(mBiometricService);
mScheduler = new UserAwareBiometricScheduler(TAG,
+ new Handler(mLooper.getLooper()),
BiometricScheduler.SENSOR_TYPE_FACE,
null /* gestureAvailabilityDispatcher */,
+ mBiometricService,
() -> USER_ID,
- mUserSwitchCallback);
+ mUserSwitchCallback,
+ CoexCoordinator.getInstance());
mHalCallback = new Sensor.HalSessionCallback(mContext, new Handler(mLooper.getLooper()),
TAG, mScheduler, SENSOR_ID,
USER_ID, mLockoutCache, mLockoutResetDispatcher, mHalSessionCallback);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java
index 39c51d5..21a7a8a 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java
@@ -32,7 +32,9 @@
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.face.IFaceServiceReceiver;
import android.os.Binder;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
@@ -69,6 +71,7 @@
@Mock
private BiometricScheduler mScheduler;
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
private LockoutResetDispatcher mLockoutResetDispatcher;
private com.android.server.biometrics.sensors.face.hidl.Face10 mFace10;
private IBinder mBinder;
@@ -97,7 +100,7 @@
resetLockoutRequiresChallenge);
Face10.sSystemClock = Clock.fixed(Instant.ofEpochMilli(100), ZoneId.of("PST"));
- mFace10 = new Face10(mContext, sensorProps, mLockoutResetDispatcher, mScheduler);
+ mFace10 = new Face10(mContext, sensorProps, mLockoutResetDispatcher, mHandler, mScheduler);
mBinder = new Binder();
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
index 0d520ca..d4609b5 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
@@ -33,6 +33,7 @@
import androidx.test.filters.SmallTest;
import com.android.server.biometrics.sensors.BiometricScheduler;
+import com.android.server.biometrics.sensors.CoexCoordinator;
import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.LockoutTracker;
@@ -79,10 +80,13 @@
when(mContext.getSystemService(Context.BIOMETRIC_SERVICE)).thenReturn(mBiometricService);
mScheduler = new UserAwareBiometricScheduler(TAG,
+ new Handler(mLooper.getLooper()),
BiometricScheduler.SENSOR_TYPE_FP_OTHER,
null /* gestureAvailabilityDispatcher */,
+ mBiometricService,
() -> USER_ID,
- mUserSwitchCallback);
+ mUserSwitchCallback,
+ CoexCoordinator.getInstance());
mHalCallback = new Sensor.HalSessionCallback(mContext, new Handler(mLooper.getLooper()),
TAG, mScheduler, SENSOR_ID,
USER_ID, mLockoutCache, mLockoutResetDispatcher, mHalSessionCallback);
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index d79a833..0a0f7d7 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -71,6 +71,8 @@
private InputController.NativeWrapper mNativeWrapperMock;
@Mock
private DisplayManagerInternal mDisplayManagerInternalMock;
+ @Mock
+ private VirtualDeviceImpl.PendingTrampolineCallback mPendingTrampolineCallback;
@Before
public void setUp() {
@@ -85,7 +87,8 @@
mInputController = new InputController(new Object(), mNativeWrapperMock);
mDeviceImpl = new VirtualDeviceImpl(mContext,
/* association info */ null, new Binder(), /* uid */ 0, mInputController,
- (int associationId) -> {}, new VirtualDeviceParams.Builder().build());
+ (int associationId) -> {}, mPendingTrampolineCallback,
+ new VirtualDeviceParams.Builder().build());
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateTest.java
index e286cb2..d54524e 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateTest.java
@@ -41,10 +41,10 @@
@Test
public void testConstruct() {
final DeviceState state = new DeviceState(MINIMUM_DEVICE_STATE /* identifier */,
- "CLOSED" /* name */, DeviceState.FLAG_CANCEL_STICKY_REQUESTS /* flags */);
+ "TEST_CLOSED" /* name */, DeviceState.FLAG_CANCEL_OVERRIDE_REQUESTS /* flags */);
assertEquals(state.getIdentifier(), MINIMUM_DEVICE_STATE);
- assertEquals(state.getName(), "CLOSED");
- assertEquals(state.getFlags(), DeviceState.FLAG_CANCEL_STICKY_REQUESTS);
+ assertEquals(state.getName(), "TEST_CLOSED");
+ assertEquals(state.getFlags(), DeviceState.FLAG_CANCEL_OVERRIDE_REQUESTS);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java b/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java
index c9cf2f0..b94fc43 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java
@@ -213,6 +213,25 @@
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED);
}
+ @Test
+ public void cancelOverrideRequestsTest() {
+ OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
+ 1 /* requestedState */, 0 /* flags */);
+ OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* pid */,
+ 2 /* requestedState */, 0 /* flags */);
+
+ mController.addRequest(firstRequest);
+ mController.addRequest(secondRequest);
+
+ assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE);
+ assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_SUSPENDED);
+
+ mController.cancelOverrideRequests();
+
+ assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_CANCELED);
+ assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED);
+ }
+
private static final class TestStatusChangeListener implements
OverrideRequestController.StatusChangeListener {
private Map<OverrideRequest, Integer> mLastStatusMap = new HashMap<>();
diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index abe7d89..176e5a9 100644
--- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -16,6 +16,8 @@
package com.android.server.display;
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED;
+
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
@@ -111,7 +113,7 @@
// Configure the brightness controller and grab an instance of the sensor listener,
// through which we can deliver fake (for test) sensor values.
- controller.configure(true /* enable */, null /* configuration */,
+ controller.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration */,
0 /* brightness */, false /* userChangedBrightness */, 0 /* adjustment */,
false /* userChanged */, DisplayPowerRequest.POLICY_BRIGHT);
@@ -227,7 +229,7 @@
listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 1000));
// User sets brightness to 100
- mController.configure(true /* enable */, null /* configuration */,
+ mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration */,
0.5f /* brightness */, true /* userChangedBrightness */, 0 /* adjustment */,
false /* userChanged */, DisplayPowerRequest.POLICY_BRIGHT);
@@ -250,7 +252,7 @@
listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 1000));
// User sets brightness to 100
- mController.configure(true /* enable */, null /* configuration */,
+ mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration */,
0.5f /* brightness */, true /* userChangedBrightness */, 0 /* adjustment */,
false /* userChanged */, DisplayPowerRequest.POLICY_BRIGHT);
@@ -267,7 +269,7 @@
verifyNoMoreInteractions(mBrightnessMappingStrategy);
// User sets idle brightness to 0.5
- mController.configure(true /* enable */, null /* configuration */,
+ mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration */,
0.5f /* brightness */, true /* userChangedBrightness */, 0 /* adjustment */,
false /* userChanged */, DisplayPowerRequest.POLICY_BRIGHT);
diff --git a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
index aca8632..beecdad 100644
--- a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
@@ -20,6 +20,11 @@
import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT;
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED;
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED;
+import static com.android.server.display.AutomaticBrightnessController
+ .AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE;
+
import static com.android.server.display.HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID;
import static org.junit.Assert.assertEquals;
@@ -47,6 +52,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.util.test.FakeSettingsProviderRule;
import com.android.server.display.DisplayDeviceConfig.HighBrightnessModeData;
@@ -87,6 +93,7 @@
private TestLooper mTestLooper;
private Handler mHandler;
private Binder mDisplayToken;
+ private String mDisplayUniqueId;
private Context mContextSpy;
@Rule
@@ -108,6 +115,7 @@
mClock = new OffsettableClock.Stopped();
mTestLooper = new TestLooper(mClock::now);
mDisplayToken = null;
+ mDisplayUniqueId = "unique_id";
mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
final MockContentResolver resolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
when(mContextSpy.getContentResolver()).thenReturn(resolver);
@@ -123,8 +131,8 @@
public void testNoHbmData() {
initHandler(null);
final HighBrightnessModeController hbmc = new HighBrightnessModeController(
- mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken, DEFAULT_MIN,
- DEFAULT_MAX, null, () -> {}, mContextSpy);
+ mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken,
+ mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, null, () -> {}, mContextSpy);
assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF);
assertEquals(hbmc.getTransitionPoint(), HBM_TRANSITION_POINT_INVALID, 0.0f);
}
@@ -133,9 +141,9 @@
public void testNoHbmData_Enabled() {
initHandler(null);
final HighBrightnessModeController hbmc = new HighBrightnessModeController(
- mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken, DEFAULT_MIN,
- DEFAULT_MAX, null, () -> {}, mContextSpy);
- hbmc.setAutoBrightnessEnabled(true);
+ mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken,
+ mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, null, () -> {}, mContextSpy);
+ hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
hbmc.onAmbientLuxChange(MINIMUM_LUX - 1); // below allowed range
assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF);
assertEquals(hbmc.getTransitionPoint(), HBM_TRANSITION_POINT_INVALID, 0.0f);
@@ -152,7 +160,7 @@
public void testAutoBrightnessEnabled_NoLux() {
final HighBrightnessModeController hbmc = createDefaultHbm();
- hbmc.setAutoBrightnessEnabled(true);
+ hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
assertState(hbmc, DEFAULT_MIN, TRANSITION_POINT, HIGH_BRIGHTNESS_MODE_OFF);
}
@@ -160,7 +168,7 @@
public void testAutoBrightnessEnabled_LowLux() {
final HighBrightnessModeController hbmc = createDefaultHbm();
- hbmc.setAutoBrightnessEnabled(true);
+ hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
hbmc.onAmbientLuxChange(MINIMUM_LUX - 1); // below allowed range
assertState(hbmc, DEFAULT_MIN, TRANSITION_POINT, HIGH_BRIGHTNESS_MODE_OFF);
}
@@ -169,7 +177,7 @@
public void testAutoBrightnessEnabled_HighLux() {
final HighBrightnessModeController hbmc = createDefaultHbm();
- hbmc.setAutoBrightnessEnabled(true);
+ hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT);
}
@@ -178,9 +186,9 @@
public void testAutoBrightnessEnabled_HighLux_ThenDisable() {
final HighBrightnessModeController hbmc = createDefaultHbm();
- hbmc.setAutoBrightnessEnabled(true);
+ hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
- hbmc.setAutoBrightnessEnabled(false);
+ hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_DISABLED);
assertState(hbmc, DEFAULT_MIN, TRANSITION_POINT, HIGH_BRIGHTNESS_MODE_OFF);
}
@@ -189,7 +197,7 @@
public void testWithinHighRange_thenOverTime_thenEarnBackTime() {
final HighBrightnessModeController hbmc = createDefaultHbm();
- hbmc.setAutoBrightnessEnabled(true);
+ hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
@@ -221,7 +229,7 @@
public void testInHBM_ThenLowLux() {
final HighBrightnessModeController hbmc = createDefaultHbm();
- hbmc.setAutoBrightnessEnabled(true);
+ hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
@@ -245,7 +253,7 @@
public void testInHBM_TestMultipleEvents_DueToAutoBrightness() {
final HighBrightnessModeController hbmc = createDefaultHbm();
- hbmc.setAutoBrightnessEnabled(true);
+ hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
@@ -274,7 +282,7 @@
public void testInHBM_TestMultipleEvents_DueToLux() {
final HighBrightnessModeController hbmc = createDefaultHbm();
- hbmc.setAutoBrightnessEnabled(true);
+ hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
// Go into HBM for half the allowed window
@@ -316,7 +324,7 @@
listener.notifyThrottling(getSkinTemp(Temperature.THROTTLING_CRITICAL));
// Try to go into HBM mode but fail
- hbmc.setAutoBrightnessEnabled(true);
+ hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
advanceTime(10);
@@ -335,7 +343,7 @@
listener.notifyThrottling(getSkinTemp(Temperature.THROTTLING_LIGHT));
// Try to go into HBM mode
- hbmc.setAutoBrightnessEnabled(true);
+ hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
advanceTime(1);
@@ -378,7 +386,7 @@
final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock());
// Turn on sunlight
- hbmc.setAutoBrightnessEnabled(true);
+ hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
advanceTime(0);
assertEquals(HIGH_BRIGHTNESS_MODE_SUNLIGHT, hbmc.getHighBrightnessMode());
@@ -451,6 +459,137 @@
assertEquals(expectedHdrBrightness, hbmc.getHdrBrightnessValue(), EPSILON);
}
+ @Test
+ public void testHbmStats_StateChange() {
+ final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock());
+ final int displayStatsId = mDisplayUniqueId.hashCode();
+
+ hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
+ hbmc.onBrightnessChanged(TRANSITION_POINT);
+ hbmc.getHdrListener().onHdrInfoChanged(null /*displayToken*/, 1 /*numberOfHdrLayers*/,
+ DISPLAY_WIDTH, DISPLAY_HEIGHT, 0 /*flags*/);
+ advanceTime(0);
+ assertEquals(HIGH_BRIGHTNESS_MODE_HDR, hbmc.getHighBrightnessMode());
+
+ // Verify Stats HBM_ON_HDR
+ verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_HDR),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+
+ hbmc.getHdrListener().onHdrInfoChanged(null /*displayToken*/, 0 /*numberOfHdrLayers*/,
+ 0, 0, 0 /*flags*/);
+ advanceTime(0);
+
+ // Verify Stats HBM_OFF
+ verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+
+ hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
+ hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
+ hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
+
+ // Verify Stats HBM_ON_SUNLIGHT
+ verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+
+ hbmc.onAmbientLuxChange(1);
+ advanceTime(TIME_ALLOWED_IN_WINDOW_MILLIS / 2 + 1);
+
+ // Verify Stats HBM_OFF
+ verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_LUX_DROP));
+ }
+
+ @Test
+ public void testHbmStats_ThermalOff() throws Exception {
+ final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock());
+ final int displayStatsId = mDisplayUniqueId.hashCode();
+
+ verify(mThermalServiceMock).registerThermalEventListenerWithType(
+ mThermalEventListenerCaptor.capture(), eq(Temperature.TYPE_SKIN));
+ final IThermalEventListener thermListener = mThermalEventListenerCaptor.getValue();
+
+ hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
+ hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
+ hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
+ advanceTime(1);
+ verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+
+ thermListener.notifyThrottling(getSkinTemp(Temperature.THROTTLING_CRITICAL));
+ advanceTime(10);
+ assertEquals(HIGH_BRIGHTNESS_MODE_OFF, hbmc.getHighBrightnessMode());
+ verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_THERMAL_LIMIT));
+ }
+
+ @Test
+ public void testHbmStats_TimeOut() {
+ final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock());
+ final int displayStatsId = mDisplayUniqueId.hashCode();
+
+ hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
+ hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
+ hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
+ advanceTime(0);
+ verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+
+ // Use up all the time in the window.
+ advanceTime(TIME_WINDOW_MILLIS + 1);
+
+ verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_TIME_LIMIT));
+ }
+
+ @Test
+ public void testHbmStats_DisplayOff() {
+ final HighBrightnessModeController hbmc = createDefaultHbm();
+ final int displayStatsId = mDisplayUniqueId.hashCode();
+
+ hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
+ hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
+ hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
+ advanceTime(0);
+ verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+
+ hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE);
+ verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_DISPLAY_OFF));
+ }
+
+ @Test
+ public void testHbmStats_HdrPlaying() {
+ final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock());
+ final int displayStatsId = mDisplayUniqueId.hashCode();
+
+ hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
+ hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
+ hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
+ advanceTime(0);
+ verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+
+ hbmc.getHdrListener().onHdrInfoChanged(null /*displayToken*/, 1 /*numberOfHdrLayers*/,
+ DISPLAY_WIDTH, DISPLAY_HEIGHT, 0 /*flags*/);
+ advanceTime(0);
+
+ verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_HDR),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_HDR_PLAYING));
+ }
+
private void assertState(HighBrightnessModeController hbmc,
float brightnessMin, float brightnessMax, int hbmMode) {
assertEquals(brightnessMin, hbmc.getCurrentBrightnessMin(), EPSILON);
@@ -466,8 +605,8 @@
private HighBrightnessModeController createDefaultHbm(OffsettableClock clock) {
initHandler(clock);
return new HighBrightnessModeController(mInjectorMock, mHandler, DISPLAY_WIDTH,
- DISPLAY_HEIGHT, mDisplayToken, DEFAULT_MIN, DEFAULT_MAX, DEFAULT_HBM_DATA, () -> {},
- mContextSpy);
+ DISPLAY_HEIGHT, mDisplayToken, mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX,
+ DEFAULT_HBM_DATA, () -> {}, mContextSpy);
}
private void initHandler(OffsettableClock clock) {
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index b811e28..9a6f61e 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -49,12 +49,8 @@
import static android.net.NetworkPolicyManager.uidPoliciesToString;
import static android.net.NetworkPolicyManager.uidRulesToString;
import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
-import static android.net.NetworkStats.IFACE_ALL;
import static android.net.NetworkStats.METERED_NO;
import static android.net.NetworkStats.METERED_YES;
-import static android.net.NetworkStats.SET_ALL;
-import static android.net.NetworkStats.TAG_ALL;
-import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkTemplate.buildTemplateCarrierMetered;
import static android.net.NetworkTemplate.buildTemplateWifi;
import static android.net.TrafficStats.MB_IN_BYTES;
@@ -75,6 +71,7 @@
import static com.android.server.net.NetworkPolicyManagerService.TYPE_RAPID;
import static com.android.server.net.NetworkPolicyManagerService.TYPE_WARNING;
import static com.android.server.net.NetworkPolicyManagerService.UidBlockedState.getEffectiveBlockedReasons;
+import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_UPDATED;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -108,6 +105,8 @@
import android.app.IUidObserver;
import android.app.Notification;
import android.app.NotificationManager;
+import android.app.usage.NetworkStats;
+import android.app.usage.NetworkStatsManager;
import android.app.usage.UsageStatsManagerInternal;
import android.content.Context;
import android.content.Intent;
@@ -125,8 +124,6 @@
import android.net.NetworkCapabilities;
import android.net.NetworkPolicy;
import android.net.NetworkStateSnapshot;
-import android.net.NetworkStats;
-import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.net.TelephonyNetworkSpecifier;
import android.net.wifi.WifiInfo;
@@ -138,7 +135,6 @@
import android.os.PowerSaveState;
import android.os.RemoteException;
import android.os.SimpleClock;
-import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
@@ -263,12 +259,13 @@
private @Mock CarrierConfigManager mCarrierConfigManager;
private @Mock TelephonyManager mTelephonyManager;
private @Mock UserManager mUserManager;
+ private @Mock NetworkStatsManager mStatsManager;
+ private TestDependencies mDeps;
private ArgumentCaptor<ConnectivityManager.NetworkCallback> mNetworkCallbackCaptor =
ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
private ActivityManagerInternal mActivityManagerInternal;
- private NetworkStatsManagerInternal mStatsService;
private IUidObserver mUidObserver;
private INetworkManagementEventObserver mNetworkObserver;
@@ -335,8 +332,47 @@
.setBatterySaverEnabled(false).build();
final PowerManagerInternal pmInternal = addLocalServiceMock(PowerManagerInternal.class);
when(pmInternal.getLowPowerState(anyInt())).thenReturn(state);
+ }
- mStatsService = addLocalServiceMock(NetworkStatsManagerInternal.class);
+ private class TestDependencies extends NetworkPolicyManagerService.Dependencies {
+ private final SparseArray<NetworkStats.Bucket> mMockedStats = new SparseArray<>();
+
+ TestDependencies(Context context) {
+ super(context);
+ }
+
+ @Override
+ long getNetworkTotalBytes(NetworkTemplate template, long start, long end) {
+ int total = 0;
+ for (int i = 0; i < mMockedStats.size(); i++) {
+ NetworkStats.Bucket bucket = mMockedStats.valueAt(i);
+ total += bucket.getRxBytes() + bucket.getTxBytes();
+ }
+ return total;
+ }
+
+ @Override
+ List<NetworkStats.Bucket> getNetworkUidBytes(NetworkTemplate template, long start,
+ long end) {
+ final List<NetworkStats.Bucket> ret = new ArrayList<>();
+ for (int i = 0; i < mMockedStats.size(); i++) {
+ ret.add(mMockedStats.valueAt(i));
+ }
+ return ret;
+ }
+
+ private void setMockedTotalBytes(int uid, long rxBytes, long txBytes) {
+ final NetworkStats.Bucket bucket = mock(NetworkStats.Bucket.class);
+ when(bucket.getUid()).thenReturn(uid);
+ when(bucket.getRxBytes()).thenReturn(rxBytes);
+ when(bucket.getTxBytes()).thenReturn(txBytes);
+ mMockedStats.set(uid, bucket);
+ }
+
+ private void increaseMockedTotalBytes(int uid, long rxBytes, long txBytes) {
+ final NetworkStats.Bucket bucket = mMockedStats.get(uid);
+ setMockedTotalBytes(uid, bucket.getRxBytes() + rxBytes, bucket.getTxBytes() + txBytes);
+ }
}
@Before
@@ -376,6 +412,8 @@
return mConnManager;
case Context.USER_SERVICE:
return mUserManager;
+ case Context.NETWORK_STATS_SERVICE:
+ return mStatsManager;
default:
return super.getSystemService(name);
}
@@ -400,8 +438,9 @@
}).when(mActivityManager).registerUidObserver(any(), anyInt(), anyInt(), any(String.class));
mFutureIntent = newRestrictBackgroundChangedFuture();
+ mDeps = new TestDependencies(mServiceContext);
mService = new NetworkPolicyManagerService(mServiceContext, mActivityManager,
- mNetworkManager, mIpm, mClock, mPolicyDir, true);
+ mNetworkManager, mIpm, mClock, mPolicyDir, true, mDeps);
mService.bindConnectivityManager();
mPolicyListener = new NetworkPolicyListenerAnswer(mService);
@@ -456,6 +495,9 @@
verify(mNetworkManager).registerObserver(networkObserver.capture());
mNetworkObserver = networkObserver.getValue();
+ // Simulate NetworkStatsService broadcast stats updated to signal its readiness.
+ mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_UPDATED));
+
NetworkPolicy defaultPolicy = mService.buildDefaultCarrierPolicy(0, "");
mDefaultWarningBytes = defaultPolicy.warningBytes;
mDefaultLimitBytes = defaultPolicy.limitBytes;
@@ -479,7 +521,6 @@
LocalServices.removeServiceForTest(DeviceIdleInternal.class);
LocalServices.removeServiceForTest(AppStandbyInternal.class);
LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
- LocalServices.removeServiceForTest(NetworkStatsManagerInternal.class);
}
@After
@@ -1108,10 +1149,7 @@
when(mConnManager.getAllNetworkStateSnapshots()).thenReturn(snapshots);
// pretend that 512 bytes total have happened
- stats = new NetworkStats(getElapsedRealtime(), 1)
- .insertEntry(TEST_IFACE, 256L, 2L, 256L, 2L);
- when(mStatsService.getNetworkTotalBytes(sTemplateWifi, CYCLE_START, CYCLE_END))
- .thenReturn(stats.getTotalBytes());
+ mDeps.setMockedTotalBytes(UID_A, 256L, 256L);
mPolicyListener.expect().onMeteredIfacesChanged(any());
setNetworkPolicies(new NetworkPolicy(
@@ -1124,26 +1162,6 @@
@Test
public void testNotificationWarningLimitSnooze() throws Exception {
- // Create a place to store fake usage
- final NetworkStatsHistory history = new NetworkStatsHistory(TimeUnit.HOURS.toMillis(1));
- final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
- when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
- .thenAnswer(new Answer<Long>() {
- @Override
- public Long answer(InvocationOnMock invocation) throws Throwable {
- final NetworkStatsHistory.Entry entry = history.getValues(
- invocation.getArgument(1), invocation.getArgument(2), null);
- return entry.rxBytes + entry.txBytes;
- }
- });
- when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong()))
- .thenAnswer(new Answer<NetworkStats>() {
- @Override
- public NetworkStats answer(InvocationOnMock invocation) throws Throwable {
- return stats;
- }
- });
-
// Get active mobile network in place
expectMobileDefaults();
mService.updateNetworks();
@@ -1161,9 +1179,7 @@
// Normal usage means no notification
{
- history.clear();
- history.recordData(start, end,
- new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(360), 0L, 0L, 0L, 0));
+ mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(360), 0);
reset(mTelephonyManager, mNetworkManager, mNotifManager);
TelephonyManager tmSub = expectMobileDefaults();
@@ -1178,9 +1194,7 @@
// Push over warning
{
- history.clear();
- history.recordData(start, end,
- new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1799), 0L, 0L, 0L, 0));
+ mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(1799), 0);
reset(mTelephonyManager, mNetworkManager, mNotifManager);
TelephonyManager tmSub = expectMobileDefaults();
@@ -1196,9 +1210,7 @@
// Push over warning, but with a config that isn't from an identified carrier
{
- history.clear();
- history.recordData(start, end,
- new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1799), 0L, 0L, 0L, 0));
+ mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(1799), 0);
reset(mTelephonyManager, mNetworkManager, mNotifManager);
TelephonyManager tmSub = expectMobileDefaults();
@@ -1215,9 +1227,7 @@
// Push over limit
{
- history.clear();
- history.recordData(start, end,
- new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1810), 0L, 0L, 0L, 0));
+ mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(1810), 0);
reset(mTelephonyManager, mNetworkManager, mNotifManager);
TelephonyManager tmSub = expectMobileDefaults();
@@ -1248,26 +1258,6 @@
@Test
public void testNotificationRapid() throws Exception {
- // Create a place to store fake usage
- final NetworkStatsHistory history = new NetworkStatsHistory(TimeUnit.HOURS.toMillis(1));
- final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
- when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
- .thenAnswer(new Answer<Long>() {
- @Override
- public Long answer(InvocationOnMock invocation) throws Throwable {
- final NetworkStatsHistory.Entry entry = history.getValues(
- invocation.getArgument(1), invocation.getArgument(2), null);
- return entry.rxBytes + entry.txBytes;
- }
- });
- when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong()))
- .thenAnswer(new Answer<NetworkStats>() {
- @Override
- public NetworkStats answer(InvocationOnMock invocation) throws Throwable {
- return stats;
- }
- });
-
// Get active mobile network in place
expectMobileDefaults();
mService.updateNetworks();
@@ -1285,9 +1275,7 @@
// Using 20% data in 20% time is normal
{
- history.clear();
- history.recordData(start, end,
- new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(360), 0L, 0L, 0L, 0));
+ mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(360), 0);
reset(mNotifManager);
mService.updateNetworks();
@@ -1297,16 +1285,9 @@
// Using 80% data in 20% time is alarming; but spread equally among
// three UIDs means we get generic alert
{
- history.clear();
- history.recordData(start, end,
- new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1440), 0L, 0L, 0L, 0));
- stats.clear();
- stats.insertEntry(IFACE_ALL, UID_A, SET_ALL, TAG_ALL,
- DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
- stats.insertEntry(IFACE_ALL, UID_B, SET_ALL, TAG_ALL,
- DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
- stats.insertEntry(IFACE_ALL, UID_C, SET_ALL, TAG_ALL,
- DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
+ mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(480), 0);
+ mDeps.setMockedTotalBytes(UID_B, DataUnit.MEGABYTES.toBytes(480), 0);
+ mDeps.setMockedTotalBytes(UID_C, DataUnit.MEGABYTES.toBytes(480), 0);
reset(mNotifManager);
mService.updateNetworks();
@@ -1325,14 +1306,9 @@
// Using 80% data in 20% time is alarming; but mostly done by one UID
// means we get specific alert
{
- history.clear();
- history.recordData(start, end,
- new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1440), 0L, 0L, 0L, 0));
- stats.clear();
- stats.insertEntry(IFACE_ALL, UID_A, SET_ALL, TAG_ALL,
- DataUnit.MEGABYTES.toBytes(960), 0, 0, 0, 0);
- stats.insertEntry(IFACE_ALL, UID_B, SET_ALL, TAG_ALL,
- DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
+ mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(960), 0);
+ mDeps.setMockedTotalBytes(UID_B, DataUnit.MEGABYTES.toBytes(480), 0);
+ mDeps.setMockedTotalBytes(UID_C, 0, 0);
reset(mNotifManager);
mService.updateNetworks();
@@ -1362,13 +1338,10 @@
// bring up wifi network with metered policy
snapshots = List.of(buildWifi());
- stats = new NetworkStats(getElapsedRealtime(), 1)
- .insertEntry(TEST_IFACE, 0L, 0L, 0L, 0L);
+ mDeps.setMockedTotalBytes(UID_A, 0L, 0L);
{
when(mConnManager.getAllNetworkStateSnapshots()).thenReturn(snapshots);
- when(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15,
- currentTimeMillis())).thenReturn(stats.getTotalBytes());
mPolicyListener.expect().onMeteredIfacesChanged(any());
setNetworkPolicies(new NetworkPolicy(
@@ -1647,18 +1620,6 @@
final NetworkPolicyManagerInternal internal = LocalServices
.getService(NetworkPolicyManagerInternal.class);
- // Create a place to store fake usage
- final NetworkStatsHistory history = new NetworkStatsHistory(TimeUnit.HOURS.toMillis(1));
- final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
- when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
- .thenAnswer(invocation -> {
- final NetworkStatsHistory.Entry entry = history.getValues(
- invocation.getArgument(1), invocation.getArgument(2), null);
- return entry.rxBytes + entry.txBytes;
- });
- when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong()))
- .thenReturn(stats);
-
// Get active mobile network in place
expectMobileDefaults();
mService.updateNetworks();
@@ -1669,9 +1630,7 @@
setCurrentTimeMillis(end);
// Get some data usage in place
- history.clear();
- history.recordData(start, end,
- new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(360), 0L, 0L, 0L, 0));
+ mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(360), 0);
// No data plan
{
@@ -1786,22 +1745,11 @@
true);
}
- private void increaseMockedTotalBytes(NetworkStats stats, long rxBytes, long txBytes) {
- stats.insertEntry(TEST_IFACE, UID_A, SET_ALL, TAG_NONE,
- rxBytes, 1, txBytes, 1, 0);
- when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
- .thenReturn(stats.getTotalBytes());
- when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong()))
- .thenReturn(stats);
- }
-
private void triggerOnStatsProviderWarningOrLimitReached() throws InterruptedException {
- final NetworkPolicyManagerInternal npmi = LocalServices
- .getService(NetworkPolicyManagerInternal.class);
- npmi.onStatsProviderWarningOrLimitReached("TEST");
+ mService.onStatsProviderWarningOrLimitReached();
// Wait for processing of MSG_STATS_PROVIDER_WARNING_OR_LIMIT_REACHED.
postMsgAndWaitForCompletion();
- verify(mStatsService).forceUpdate();
+ verify(mStatsManager).forceUpdate();
// Wait for processing of MSG_*_INTERFACE_QUOTAS.
postMsgAndWaitForCompletion();
}
@@ -1814,13 +1762,12 @@
public void testStatsProviderWarningAndLimitReached() throws Exception {
final int CYCLE_DAY = 15;
- final NetworkStats stats = new NetworkStats(0L, 1);
- increaseMockedTotalBytes(stats, 2999, 2000);
+ mDeps.setMockedTotalBytes(UID_A, 2999, 2000);
// Get active mobile network in place
expectMobileDefaults();
mService.updateNetworks();
- verify(mStatsService).setStatsProviderWarningAndLimitAsync(TEST_IFACE, Long.MAX_VALUE,
+ verify(mStatsManager).setStatsProviderWarningAndLimitAsync(TEST_IFACE, Long.MAX_VALUE,
Long.MAX_VALUE);
// Set warning to 7KB and limit to 10KB.
@@ -1830,32 +1777,32 @@
postMsgAndWaitForCompletion();
// Verifies that remaining quotas are set to providers.
- verify(mStatsService).setStatsProviderWarningAndLimitAsync(TEST_IFACE, 2001L, 5001L);
- reset(mStatsService);
+ verify(mStatsManager).setStatsProviderWarningAndLimitAsync(TEST_IFACE, 2001L, 5001L);
+ reset(mStatsManager);
// Increase the usage and simulates that limit reached fires earlier by provider,
// but actually the quota is not yet reached. Verifies that the limit reached leads to
// a force update and new quotas should be set.
- increaseMockedTotalBytes(stats, 1000, 999);
+ mDeps.increaseMockedTotalBytes(UID_A, 1000, 999);
triggerOnStatsProviderWarningOrLimitReached();
- verify(mStatsService).setStatsProviderWarningAndLimitAsync(TEST_IFACE, 2L, 3002L);
- reset(mStatsService);
+ verify(mStatsManager).setStatsProviderWarningAndLimitAsync(TEST_IFACE, 2L, 3002L);
+ reset(mStatsManager);
// Increase the usage and simulate warning reached, the new warning should be unlimited
// since service will disable warning quota to stop lower layer from keep triggering
// warning reached event.
- increaseMockedTotalBytes(stats, 1000L, 1000);
+ mDeps.increaseMockedTotalBytes(UID_A, 1000L, 1000);
triggerOnStatsProviderWarningOrLimitReached();
- verify(mStatsService).setStatsProviderWarningAndLimitAsync(
+ verify(mStatsManager).setStatsProviderWarningAndLimitAsync(
TEST_IFACE, Long.MAX_VALUE, 1002L);
- reset(mStatsService);
+ reset(mStatsManager);
// Increase the usage that over the warning and limit, the new limit should set to 1 to
// block the network traffic.
- increaseMockedTotalBytes(stats, 1000L, 1000);
+ mDeps.increaseMockedTotalBytes(UID_A, 1000L, 1000);
triggerOnStatsProviderWarningOrLimitReached();
- verify(mStatsService).setStatsProviderWarningAndLimitAsync(TEST_IFACE, Long.MAX_VALUE, 1L);
- reset(mStatsService);
+ verify(mStatsManager).setStatsProviderWarningAndLimitAsync(TEST_IFACE, Long.MAX_VALUE, 1L);
+ reset(mStatsManager);
}
private void enableRestrictedMode(boolean enable) throws Exception {
@@ -2145,7 +2092,7 @@
}
private void verifyAdvisePersistThreshold() throws Exception {
- verify(mStatsService).advisePersistThreshold(anyLong());
+ verify(mStatsManager).advisePersistThreshold(anyLong());
}
private static class TestAbstractFuture<T> extends AbstractFuture<T> {
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 e4273dc..429445f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -315,8 +315,8 @@
mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_USER, /* value= */ true,
asHandle(currentUser));
try {
- assertThat(mUserManager.removeUserOrSetEphemeral(user1.id,
- /* evenWhenDisallowed= */ false)).isEqualTo(UserManager.REMOVE_RESULT_ERROR);
+ assertThat(mUserManager.removeUserWhenPossible(user1.getUserHandle(),
+ /* overrideDevicePolicy= */ false)).isEqualTo(UserManager.REMOVE_RESULT_ERROR);
} finally {
mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_USER, /* value= */ false,
asHandle(currentUser));
@@ -335,8 +335,8 @@
asHandle(currentUser));
try {
synchronized (mUserRemoveLock) {
- assertThat(mUserManager.removeUserOrSetEphemeral(user1.id,
- /* evenWhenDisallowed= */ true))
+ assertThat(mUserManager.removeUserWhenPossible(user1.getUserHandle(),
+ /* overrideDevicePolicy= */ true))
.isEqualTo(UserManager.REMOVE_RESULT_REMOVED);
waitForUserRemovalLocked(user1.id);
}
@@ -352,8 +352,8 @@
@MediumTest
@Test
public void testRemoveUserOrSetEphemeral_systemUserReturnsError() throws Exception {
- assertThat(mUserManager.removeUserOrSetEphemeral(UserHandle.USER_SYSTEM,
- /* evenWhenDisallowed= */ false)).isEqualTo(UserManager.REMOVE_RESULT_ERROR);
+ assertThat(mUserManager.removeUserWhenPossible(UserHandle.SYSTEM,
+ /* overrideDevicePolicy= */ false)).isEqualTo(UserManager.REMOVE_RESULT_ERROR);
assertThat(hasUser(UserHandle.USER_SYSTEM)).isTrue();
}
@@ -362,8 +362,8 @@
@Test
public void testRemoveUserOrSetEphemeral_invalidUserReturnsError() throws Exception {
assertThat(hasUser(Integer.MAX_VALUE)).isFalse();
- assertThat(mUserManager.removeUserOrSetEphemeral(Integer.MAX_VALUE,
- /* evenWhenDisallowed= */ false)).isEqualTo(UserManager.REMOVE_RESULT_ERROR);
+ assertThat(mUserManager.removeUserWhenPossible(UserHandle.of(Integer.MAX_VALUE),
+ /* overrideDevicePolicy= */ false)).isEqualTo(UserManager.REMOVE_RESULT_ERROR);
}
@MediumTest
@@ -374,8 +374,8 @@
// Switch to the user just created.
switchUser(user1.id, null, /* ignoreHandle= */ true);
- assertThat(mUserManager.removeUserOrSetEphemeral(user1.id, /* evenWhenDisallowed= */ false))
- .isEqualTo(UserManager.REMOVE_RESULT_DEFERRED);
+ assertThat(mUserManager.removeUserWhenPossible(user1.getUserHandle(),
+ /* overrideDevicePolicy= */ false)).isEqualTo(UserManager.REMOVE_RESULT_DEFERRED);
assertThat(hasUser(user1.id)).isTrue();
assertThat(getUser(user1.id).isEphemeral()).isTrue();
@@ -395,8 +395,9 @@
public void testRemoveUserOrSetEphemeral_nonCurrentUserRemoved() throws Exception {
final UserInfo user1 = createUser("User 1", /* flags= */ 0);
synchronized (mUserRemoveLock) {
- assertThat(mUserManager.removeUserOrSetEphemeral(user1.id,
- /* evenWhenDisallowed= */ false)).isEqualTo(UserManager.REMOVE_RESULT_REMOVED);
+ assertThat(mUserManager.removeUserWhenPossible(user1.getUserHandle(),
+ /* overrideDevicePolicy= */ false))
+ .isEqualTo(UserManager.REMOVE_RESULT_REMOVED);
waitForUserRemovalLocked(user1.id);
}
diff --git a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
index 761cea7..90b19a4 100644
--- a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
@@ -160,12 +160,12 @@
}
@Test
- public void create_stateWithCancelStickyRequestFlag() {
+ public void create_stateWithCancelOverrideRequestFlag() {
String configString = "<device-state-config>\n"
+ " <device-state>\n"
+ " <identifier>1</identifier>\n"
+ " <flags>\n"
- + " <flag>FLAG_CANCEL_STICKY_REQUESTS</flag>\n"
+ + " <flag>FLAG_CANCEL_OVERRIDE_REQUESTS</flag>\n"
+ " </flags>\n"
+ " <conditions/>\n"
+ " </device-state>\n"
@@ -183,7 +183,7 @@
verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
final DeviceState[] expectedStates = new DeviceState[]{
- new DeviceState(1, "", DeviceState.FLAG_CANCEL_STICKY_REQUESTS),
+ new DeviceState(1, "", DeviceState.FLAG_CANCEL_OVERRIDE_REQUESTS),
new DeviceState(2, "", 0 /* flags */) };
assertArrayEquals(expectedStates, mDeviceStateArrayCaptor.getValue());
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index d831903..a192bf8 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -2701,6 +2701,8 @@
// should trigger a broadcast
mBinderService.setNotificationsEnabledForPackage(PKG, 0, true);
+ Thread.sleep(500);
+ waitForIdle();
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null));
@@ -2728,6 +2730,8 @@
// should trigger a broadcast
mBinderService.setNotificationsEnabledForPackage(PKG, 0, true);
+ Thread.sleep(500);
+ waitForIdle();
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null));
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
index 1362628..f6400b6 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
@@ -601,6 +601,8 @@
when(mAppOpsManager.checkOpNoThrow(anyInt(), eq(mUid), eq(PKG))).thenReturn(MODE_IGNORED);
mService.mAppOpsCallback.opChanged(0, mUid, PKG);
+ Thread.sleep(500);
+ waitForIdle();
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null));
@@ -616,6 +618,8 @@
when(mAppOpsManager.checkOpNoThrow(anyInt(), eq(mUid), eq(PKG))).thenReturn(MODE_ALLOWED);
mService.mAppOpsCallback.opChanged(0, mUid, PKG);
+ Thread.sleep(500);
+ waitForIdle();
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null));
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index d49cf67..b48c9c3 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -542,6 +542,7 @@
mHelper.setInvalidMessageSent(PKG_P, UID_P);
mHelper.setValidMessageSent(PKG_P, UID_P);
mHelper.setInvalidMsgAppDemoted(PKG_P, UID_P, true);
+ mHelper.setValidBubbleSent(PKG_P, UID_P);
mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_NONE);
@@ -561,6 +562,7 @@
assertFalse(mHelper.hasSentInvalidMsg(PKG_N_MR1, UID_N_MR1));
assertTrue(mHelper.hasSentValidMsg(PKG_P, UID_P));
assertTrue(mHelper.didUserEverDemoteInvalidMsgApp(PKG_P, UID_P));
+ assertTrue(mHelper.hasSentValidBubble(PKG_P, UID_P));
assertEquals(channel1,
mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1.getId(), false));
compareChannels(channel2,
@@ -3174,6 +3176,19 @@
}
@Test
+ public void testIsGroupBlocked_appCannotCreateAsBlocked() throws Exception {
+ NotificationChannelGroup group = new NotificationChannelGroup("id", "name");
+ group.setBlocked(true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group, true);
+ assertFalse(mHelper.isGroupBlocked(PKG_N_MR1, UID_N_MR1, group.getId()));
+
+ NotificationChannelGroup group3 = group.clone();
+ group3.setBlocked(false);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group3, true);
+ assertFalse(mHelper.isGroupBlocked(PKG_N_MR1, UID_N_MR1, group.getId()));
+ }
+
+ @Test
public void testIsGroup_appCannotResetBlock() throws Exception {
NotificationChannelGroup group = new NotificationChannelGroup("id", "name");
mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group, true);
@@ -4706,7 +4721,7 @@
public void testGetConversations_noDisabledGroups() {
NotificationChannelGroup group = new NotificationChannelGroup("a", "a");
group.setBlocked(true);
- mHelper.createNotificationChannelGroup(PKG_O, UID_O, group, true);
+ mHelper.createNotificationChannelGroup(PKG_O, UID_O, group, false);
NotificationChannel parent = new NotificationChannel("parent", "p", 1);
mHelper.createNotificationChannel(PKG_O, UID_O, parent, true, false);
@@ -4927,6 +4942,18 @@
}
@Test
+ public void testValidBubbleSent() {
+ // create package preferences
+ mHelper.canShowBadge(PKG_P, UID_P);
+ // false by default
+ assertFalse(mHelper.hasSentValidBubble(PKG_P, UID_P));
+
+ // set something valid was sent
+ mHelper.setValidBubbleSent(PKG_P, UID_P);
+ assertTrue(mHelper.hasSentValidBubble(PKG_P, UID_P));
+ }
+
+ @Test
public void testPullPackageChannelPreferencesStats() {
String channelId = "parent";
String name = "messages";
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerHelperTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerHelperTests.java
new file mode 100644
index 0000000..6e11d8c
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerHelperTests.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2021 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.wm;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+
+import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import android.view.Display;
+import android.window.DisplayWindowPolicyController;
+
+import androidx.annotation.NonNull;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+/**
+ * Tests for the {@link DisplayWindowPolicyControllerHelper} class.
+ *
+ * Build/Install/Run:
+ * atest WmTests:DisplayWindowPolicyControllerHelperTests
+ */
+@RunWith(WindowTestRunner.class)
+public class DisplayWindowPolicyControllerHelperTests extends WindowTestsBase {
+ private static final int TEST_USER_0_ID = 0;
+ private static final int TEST_USER_1_ID = 10;
+
+ private TestDisplayWindowPolicyController mDwpc = new TestDisplayWindowPolicyController();
+ private DisplayContent mSecondaryDisplay;
+
+ @Before
+ public void setUp() {
+ doReturn(mDwpc).when(mWm.mDisplayManagerInternal)
+ .getDisplayWindowPolicyController(anyInt());
+ mSecondaryDisplay = createNewDisplay();
+ assertNotEquals(Display.DEFAULT_DISPLAY, mSecondaryDisplay.getDisplayId());
+ assertTrue(mSecondaryDisplay.mDwpcHelper.hasController());
+ }
+
+ @Test
+ public void testOnRunningActivityChanged() {
+ final ActivityRecord activity1 = launchActivityOnDisplay(mSecondaryDisplay, TEST_USER_0_ID);
+ verifyTopActivityAndRunningUid(activity1,
+ true /* expectedUid0 */, false /* expectedUid1 */);
+ final ActivityRecord activity2 = launchActivityOnDisplay(mSecondaryDisplay, TEST_USER_1_ID);
+ verifyTopActivityAndRunningUid(activity2,
+ true /* expectedUid0 */, true /* expectedUid1 */);
+ final ActivityRecord activity3 = launchActivityOnDisplay(mSecondaryDisplay, TEST_USER_0_ID);
+ verifyTopActivityAndRunningUid(activity3,
+ true /* expectedUid0 */, true /* expectedUid1 */);
+
+ activity3.finishing = true;
+ verifyTopActivityAndRunningUid(activity2,
+ true /* expectedUid0 */, true /* expectedUid1 */);
+
+ activity2.finishing = true;
+ verifyTopActivityAndRunningUid(activity1,
+ true /* expectedUid0 */, false /* expectedUid1 */);
+
+ activity1.finishing = true;
+ verifyTopActivityAndRunningUid(null /* expectedTopActivity */,
+ false /* expectedUid0 */, false /* expectedUid1 */);
+ }
+
+ private void verifyTopActivityAndRunningUid(ActivityRecord expectedTopActivity,
+ boolean expectedUid0, boolean expectedUid1) {
+ mSecondaryDisplay.onRunningActivityChanged();
+ int uidAmount = (expectedUid0 && expectedUid1) ? 2 : (expectedUid0 || expectedUid1) ? 1 : 0;
+ assertEquals(expectedTopActivity == null ? null :
+ expectedTopActivity.info.getComponentName(), mDwpc.mTopActivity);
+ assertEquals(expectedTopActivity == null ? UserHandle.USER_NULL :
+ expectedTopActivity.info.applicationInfo.uid, mDwpc.mTopActivityUid);
+ assertEquals(uidAmount, mDwpc.mRunningUids.size());
+ assertTrue(mDwpc.mRunningUids.contains(TEST_USER_0_ID) == expectedUid0);
+ assertTrue(mDwpc.mRunningUids.contains(TEST_USER_1_ID) == expectedUid1);
+
+ }
+
+ private ActivityRecord launchActivityOnDisplay(DisplayContent display, int uid) {
+ final Task task = new TaskBuilder(mSupervisor)
+ .setDisplay(display)
+ .setUserId(uid)
+ .build();
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(task)
+ .setUid(uid)
+ .setOnTop(true)
+ .build();
+ return activity;
+ }
+
+ private class TestDisplayWindowPolicyController extends DisplayWindowPolicyController {
+
+ ComponentName mTopActivity = null;
+ int mTopActivityUid = UserHandle.USER_NULL;
+ ArraySet<Integer> mRunningUids = new ArraySet<>();
+
+ @Override
+ public boolean canContainActivities(@NonNull List<ActivityInfo> activities) {
+ return false;
+ }
+
+ @Override
+ public boolean keepActivityOnWindowFlagsChanged(ActivityInfo activityInfo, int windowFlags,
+ int systemWindowFlags) {
+ return false;
+ }
+
+ @Override
+ public void onTopActivityChanged(ComponentName topActivity, int uid) {
+ super.onTopActivityChanged(topActivity, uid);
+ mTopActivity = topActivity;
+ mTopActivityUid = uid;
+ }
+
+ @Override
+ public void onRunningAppsChanged(ArraySet<Integer> runningUids) {
+ super.onRunningAppsChanged(runningUids);
+ mRunningUids.clear();
+ mRunningUids.addAll(runningUids);
+ }
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index b4c449a..632a59d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -117,8 +117,7 @@
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_RECENTS,
mFinishedCallback);
- // Remove the app window so that the animation target can not be created
- activity.removeImmediately();
+ // The activity doesn't contain window so the animation target cannot be created.
mController.startAnimation();
// Verify that the finish callback to reparent the leash is called
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index 9ad8c5b..0debdfa 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -21,14 +21,17 @@
import static com.android.server.wm.testing.Assert.assertThrows;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import android.content.Intent;
@@ -471,6 +474,7 @@
final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
.setParentTask(task)
.setOrganizer(mOrganizer)
+ .setFragmentToken(mFragmentToken)
.build();
// Mock the task to invisible
@@ -485,4 +489,38 @@
// Verifies that event was not sent
verify(mOrganizer, never()).onTaskFragmentInfoChanged(any());
}
+
+ /**
+ * Tests that a task fragment info changed event is still sent if the task is invisible only
+ * when the info changed event is because of the last activity in a task finishing.
+ */
+ @Test
+ public void testLastPendingTaskFragmentInfoChangedEventOfInvisibleTaskSent() {
+ // Create a TaskFragment with an activity, all within a parent task
+ final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
+ .setOrganizer(mOrganizer)
+ .setFragmentToken(mFragmentToken)
+ .setCreateParentTask()
+ .createActivityCount(1)
+ .build();
+ final Task parentTask = taskFragment.getTask();
+ final ActivityRecord activity = taskFragment.getTopNonFinishingActivity();
+ assertTrue(parentTask.shouldBeVisible(null));
+
+ // Dispatch pending info changed event from creating the activity
+ mController.registerOrganizer(mIOrganizer);
+ taskFragment.mTaskFragmentAppearedSent = true;
+ mController.onTaskFragmentInfoChanged(mIOrganizer, taskFragment);
+ mController.dispatchPendingEvents();
+
+ // Finish the activity and verify that the task is invisible
+ activity.finishing = true;
+ assertFalse(parentTask.shouldBeVisible(null));
+
+ // Verify the info changed callback still occurred despite the task being invisible
+ reset(mOrganizer);
+ mController.onTaskFragmentInfoChanged(mIOrganizer, taskFragment);
+ mController.dispatchPendingEvents();
+ verify(mOrganizer).onTaskFragmentInfoChanged(any());
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index bec53d7..8b14e98 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -22,6 +22,7 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
@@ -77,6 +78,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import java.util.ArrayList;
@@ -271,6 +273,22 @@
}
@Test
+ public void testRemoveImmediatelyClearsLeash() {
+ final AnimationAdapter animAdapter = mock(AnimationAdapter.class);
+ final WindowToken token = createTestWindowToken(TYPE_APPLICATION_OVERLAY, mDisplayContent);
+ final SurfaceControl.Transaction t = token.getPendingTransaction();
+ token.startAnimation(t, animAdapter, false /* hidden */,
+ SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION);
+ final ArgumentCaptor<SurfaceControl> leashCaptor =
+ ArgumentCaptor.forClass(SurfaceControl.class);
+ verify(animAdapter).startAnimation(leashCaptor.capture(), eq(t), anyInt(), any());
+ assertTrue(token.mSurfaceAnimator.hasLeash());
+ token.removeImmediately();
+ assertFalse(token.mSurfaceAnimator.hasLeash());
+ verify(t).remove(eq(leashCaptor.getValue()));
+ }
+
+ @Test
public void testAddChildByIndex() {
final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
final TestWindowContainer root = builder.setLayer(0).build();
diff --git a/telephony/java/android/telephony/UiccCardInfo.java b/telephony/java/android/telephony/UiccCardInfo.java
index 464389b..30ca162 100644
--- a/telephony/java/android/telephony/UiccCardInfo.java
+++ b/telephony/java/android/telephony/UiccCardInfo.java
@@ -257,8 +257,6 @@
+ mCardId
+ ", mEid="
+ mEid
- + ", mIccId="
- + SubscriptionInfo.givePrintableIccid(getIccId())
+ ", mPhysicalSlotIndex="
+ mPhysicalSlotIndex
+ ", mIsRemovable="
diff --git a/telephony/java/android/telephony/UiccSlotInfo.java b/telephony/java/android/telephony/UiccSlotInfo.java
index 2b1c8c8..17f34db 100644
--- a/telephony/java/android/telephony/UiccSlotInfo.java
+++ b/telephony/java/android/telephony/UiccSlotInfo.java
@@ -271,16 +271,13 @@
@NonNull
@Override
public String toString() {
- return "UiccSlotInfo (mIsActive="
- + mIsActive
+ return "UiccSlotInfo ("
+ ", mIsEuicc="
+ mIsEuicc
+ ", mCardId="
+ mCardId
+ ", cardState="
+ mCardStateInfo
- + ", phoneId="
- + mLogicalSlotIdx
+ ", mIsExtendedApduSupported="
+ mIsExtendedApduSupported
+ ", mIsRemovable="
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
index 56879c9..c87d8e1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
@@ -31,7 +31,6 @@
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.navBarLayerIsVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsVisible
@@ -39,7 +38,6 @@
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.common.FlickerComponentName
-import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -157,11 +155,7 @@
@FlakyTest(bugId = 206753786)
@Test
- fun statusBarLayerRotatesScales() {
- // This test doesn't work in shell transitions because of b/206753786
- assumeFalse(isShellTransitionsEnabled)
- testSpec.statusBarLayerRotatesScales()
- }
+ fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
@Presubmit
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
index c28466c..f2696d8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
@@ -34,12 +34,10 @@
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.entireScreenCovered
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.common.FlickerComponentName
-import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -117,11 +115,7 @@
@Presubmit
@Test
- fun entireScreenCovered() {
- // This test doesn't work in shell transitions because of b/206086894
- assumeFalse(isShellTransitionsEnabled)
- testSpec.entireScreenCovered()
- }
+ fun entireScreenCovered() = testSpec.entireScreenCovered()
@Presubmit
@Test
@@ -159,11 +153,7 @@
@FlakyTest(bugId = 206753786)
@Test
- fun statusBarLayerRotatesScales() {
- // This test doesn't work in shell transitions because of b/206753786
- assumeFalse(isShellTransitionsEnabled)
- testSpec.statusBarLayerRotatesScales()
- }
+ fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
@Presubmit
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
index c7f1b99..24b1598 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
@@ -32,7 +32,6 @@
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.entireScreenCovered
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.common.FlickerComponentName
@@ -134,11 +133,7 @@
@FlakyTest(bugId = 206753786)
@Test
- fun statusBarLayerRotatesScales() {
- // This test doesn't work in shell transitions because of b/206753786
- assumeFalse(isShellTransitionsEnabled)
- testSpec.statusBarLayerRotatesScales()
- }
+ fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
@Presubmit
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
index 46ed0ad..e5d82a1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
@@ -34,11 +34,9 @@
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.entireScreenCovered
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.common.FlickerComponentName
-import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -126,11 +124,7 @@
@Presubmit
@Test
- fun entireScreenCovered() {
- // This test doesn't work in shell transitions because of b/206086894
- assumeFalse(isShellTransitionsEnabled)
- testSpec.entireScreenCovered()
- }
+ fun entireScreenCovered() = testSpec.entireScreenCovered()
@Presubmit
@Test
@@ -152,11 +146,7 @@
@FlakyTest(bugId = 206753786)
@Test
- fun statusBarLayerRotatesScales() {
- // This test doesn't work in shell transitions because of b/206753786
- assumeFalse(isShellTransitionsEnabled)
- testSpec.statusBarLayerRotatesScales()
- }
+ fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
@Presubmit
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
index ebe4be2..87f8ef2 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
@@ -34,11 +34,9 @@
import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -130,11 +128,7 @@
@FlakyTest(bugId = 206753786)
@Test
- fun statusBarLayerRotatesScales() {
- // This test doesn't work in shell transitions because of b/206753786
- assumeFalse(isShellTransitionsEnabled)
- testSpec.statusBarLayerRotatesScales()
- }
+ fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
@Presubmit
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
index f6e5adc..0ad0a03 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
@@ -101,8 +101,6 @@
@Presubmit
@Test
fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
- // This test doesn't work in shell transitions because of b/204570898
- assumeFalse(isShellTransitionsEnabled)
val component = FlickerComponentName("", "RecentTaskScreenshotSurface")
testSpec.assertWm {
this.visibleWindowsShownMoreThanOneConsecutiveEntry(
@@ -116,8 +114,6 @@
@Presubmit
@Test
fun launcherWindowBecomesInvisible() {
- // This test doesn't work in shell transitions because of b/204574221
- assumeFalse(isShellTransitionsEnabled)
testSpec.assertWm {
this.isAppWindowVisible(LAUNCHER_COMPONENT)
.then()
@@ -127,11 +123,7 @@
@Presubmit
@Test
- fun imeWindowIsAlwaysVisible() {
- // This test doesn't work in shell transitions because of b/204570898
- assumeFalse(isShellTransitionsEnabled)
- testSpec.imeWindowIsAlwaysVisible(!isShellTransitionsEnabled)
- }
+ fun imeWindowIsAlwaysVisible() = testSpec.imeWindowIsAlwaysVisible(!isShellTransitionsEnabled)
@Presubmit
@Test
@@ -202,8 +194,6 @@
@Presubmit
@Test
fun appLayerReplacesLauncher() {
- // This test doesn't work in shell transitions because of b/204574221
- assumeFalse(isShellTransitionsEnabled)
testSpec.assertLayers {
this.isVisible(LAUNCHER_COMPONENT)
.then()
@@ -219,11 +209,7 @@
@FlakyTest(bugId = 206753786)
@Test
- fun statusBarLayerRotatesScales() {
- // This test doesn't work in shell transitions because of b/206753786
- assumeFalse(isShellTransitionsEnabled)
- testSpec.statusBarLayerRotatesScales()
- }
+ fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
@Presubmit
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index 0879b98..3f0de7f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -25,13 +25,11 @@
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.rules.WMFlickerServiceRuleForTestSpec
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.common.FlickerComponentName
-import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Rule
import org.junit.Test
@@ -167,11 +165,7 @@
*/
@FlakyTest(bugId = 206753786)
@Test
- fun statusBarLayerRotatesScales() {
- // This test doesn't work in shell transitions because of b/206753786
- assumeFalse(isShellTransitionsEnabled)
- testSpec.statusBarLayerRotatesScales()
- }
+ fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
/** {@inheritDoc} */
@FlakyTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index e44bee6..3ae484b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -26,10 +26,8 @@
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.SeamlessRotationAppHelper
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.testapp.ActivityOptions
import com.android.server.wm.traces.common.FlickerComponentName
-import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -102,8 +100,6 @@
@Presubmit
@Test
fun appWindowFullScreen() {
- // This test doesn't work in shell transitions because of b/206101151
- assumeFalse(isShellTransitionsEnabled)
testSpec.assertWm {
this.invoke("isFullScreen") {
val appWindow = it.windowState(testApp.`package`)
@@ -139,8 +135,6 @@
@Presubmit
@Test
fun appLayerAlwaysVisible() {
- // This test doesn't work in shell transitions because of b/206101151
- assumeFalse(isShellTransitionsEnabled)
testSpec.assertLayers {
isVisible(testApp.component)
}
@@ -152,8 +146,6 @@
@Presubmit
@Test
fun appLayerRotates() {
- // This test doesn't work in shell transitions because of b/206101151
- assumeFalse(isShellTransitionsEnabled)
testSpec.assertLayers {
this.invoke("entireScreenCovered") { entry ->
entry.entry.displays.map { display ->
@@ -193,8 +185,6 @@
@Presubmit
@Test
fun focusDoesNotChange() {
- // This test doesn't work in shell transitions because of b/206101151
- assumeFalse(isShellTransitionsEnabled)
testSpec.assertEventLog {
this.focusDoesNotChange()
}
diff --git a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java
index 4a724b7..2fbcf9d 100644
--- a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java
@@ -18,25 +18,29 @@
import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
-import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_ANY;
-import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
import org.junit.Test;
import java.util.HashSet;
import java.util.Set;
-public class VcnCellUnderlyingNetworkTemplateTest {
+public class VcnCellUnderlyingNetworkTemplateTest extends VcnUnderlyingNetworkTemplateTestBase {
private static final Set<String> ALLOWED_PLMN_IDS = new HashSet<>();
private static final Set<Integer> ALLOWED_CARRIER_IDS = new HashSet<>();
// Package private for use in VcnGatewayConnectionConfigTest
static VcnCellUnderlyingNetworkTemplate getTestNetworkTemplate() {
return new VcnCellUnderlyingNetworkTemplate.Builder()
- .setNetworkQuality(NETWORK_QUALITY_OK)
.setMetered(MATCH_FORBIDDEN)
+ .setMinUpstreamBandwidthKbps(
+ TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS)
+ .setMinDownstreamBandwidthKbps(
+ TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS)
.setOperatorPlmnIds(ALLOWED_PLMN_IDS)
.setSimSpecificCarrierIds(ALLOWED_CARRIER_IDS)
.setRoaming(MATCH_FORBIDDEN)
@@ -47,8 +51,19 @@
@Test
public void testBuilderAndGetters() {
final VcnCellUnderlyingNetworkTemplate networkPriority = getTestNetworkTemplate();
- assertEquals(NETWORK_QUALITY_OK, networkPriority.getNetworkQuality());
assertEquals(MATCH_FORBIDDEN, networkPriority.getMetered());
+ assertEquals(
+ TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+ networkPriority.getMinEntryUpstreamBandwidthKbps());
+ assertEquals(
+ TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+ networkPriority.getMinExitUpstreamBandwidthKbps());
+ assertEquals(
+ TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+ networkPriority.getMinEntryDownstreamBandwidthKbps());
+ assertEquals(
+ TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+ networkPriority.getMinExitDownstreamBandwidthKbps());
assertEquals(ALLOWED_PLMN_IDS, networkPriority.getOperatorPlmnIds());
assertEquals(ALLOWED_CARRIER_IDS, networkPriority.getSimSpecificCarrierIds());
assertEquals(MATCH_FORBIDDEN, networkPriority.getRoaming());
@@ -59,8 +74,14 @@
public void testBuilderAndGettersForDefaultValues() {
final VcnCellUnderlyingNetworkTemplate networkPriority =
new VcnCellUnderlyingNetworkTemplate.Builder().build();
- assertEquals(NETWORK_QUALITY_ANY, networkPriority.getNetworkQuality());
assertEquals(MATCH_ANY, networkPriority.getMetered());
+
+ // Explicitly expect 0, as documented in Javadoc on setter methods.
+ assertEquals(0, networkPriority.getMinEntryUpstreamBandwidthKbps());
+ assertEquals(0, networkPriority.getMinExitUpstreamBandwidthKbps());
+ assertEquals(0, networkPriority.getMinEntryDownstreamBandwidthKbps());
+ assertEquals(0, networkPriority.getMinExitDownstreamBandwidthKbps());
+
assertEquals(new HashSet<String>(), networkPriority.getOperatorPlmnIds());
assertEquals(new HashSet<Integer>(), networkPriority.getSimSpecificCarrierIds());
assertEquals(MATCH_ANY, networkPriority.getRoaming());
@@ -68,6 +89,29 @@
}
@Test
+ public void testBuilderRequiresStricterEntryCriteria() {
+ try {
+ new VcnCellUnderlyingNetworkTemplate.Builder()
+ .setMinUpstreamBandwidthKbps(
+ TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS);
+
+ fail("Expected IAE for exit threshold > entry threshold");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ new VcnCellUnderlyingNetworkTemplate.Builder()
+ .setMinDownstreamBandwidthKbps(
+ TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS);
+
+ fail("Expected IAE for exit threshold > entry threshold");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
public void testPersistableBundle() {
final VcnCellUnderlyingNetworkTemplate networkPriority = getTestNetworkTemplate();
assertEquals(
diff --git a/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkTemplateTestBase.java b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkTemplateTestBase.java
new file mode 100644
index 0000000..399e136
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkTemplateTestBase.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net.vcn;
+
+public class VcnUnderlyingNetworkTemplateTestBase {
+ // Public for use in NetworkPriorityClassifierTest
+ public static final int TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS = 200;
+ public static final int TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS = 100;
+ public static final int TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS = 400;
+ public static final int TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS = 300;
+}
diff --git a/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java b/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java
index cb5b47b..4063178 100644
--- a/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java
@@ -17,8 +17,6 @@
import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
-import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_ANY;
-import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -28,15 +26,19 @@
import java.util.Set;
-public class VcnWifiUnderlyingNetworkTemplateTest {
+public class VcnWifiUnderlyingNetworkTemplateTest extends VcnUnderlyingNetworkTemplateTestBase {
private static final String SSID = "TestWifi";
- private static final int INVALID_NETWORK_QUALITY = -1;
// Package private for use in VcnGatewayConnectionConfigTest
static VcnWifiUnderlyingNetworkTemplate getTestNetworkTemplate() {
return new VcnWifiUnderlyingNetworkTemplate.Builder()
- .setNetworkQuality(NETWORK_QUALITY_OK)
.setMetered(MATCH_FORBIDDEN)
+ .setMinUpstreamBandwidthKbps(
+ TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS)
+ .setMinDownstreamBandwidthKbps(
+ TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS)
.setSsids(Set.of(SSID))
.build();
}
@@ -44,8 +46,19 @@
@Test
public void testBuilderAndGetters() {
final VcnWifiUnderlyingNetworkTemplate networkPriority = getTestNetworkTemplate();
- assertEquals(NETWORK_QUALITY_OK, networkPriority.getNetworkQuality());
assertEquals(MATCH_FORBIDDEN, networkPriority.getMetered());
+ assertEquals(
+ TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+ networkPriority.getMinEntryUpstreamBandwidthKbps());
+ assertEquals(
+ TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+ networkPriority.getMinExitUpstreamBandwidthKbps());
+ assertEquals(
+ TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+ networkPriority.getMinEntryDownstreamBandwidthKbps());
+ assertEquals(
+ TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+ networkPriority.getMinExitDownstreamBandwidthKbps());
assertEquals(Set.of(SSID), networkPriority.getSsids());
}
@@ -53,18 +66,37 @@
public void testBuilderAndGettersForDefaultValues() {
final VcnWifiUnderlyingNetworkTemplate networkPriority =
new VcnWifiUnderlyingNetworkTemplate.Builder().build();
- assertEquals(NETWORK_QUALITY_ANY, networkPriority.getNetworkQuality());
assertEquals(MATCH_ANY, networkPriority.getMetered());
+
+ // Explicitly expect 0, as documented in Javadoc on setter methods..
+ assertEquals(0, networkPriority.getMinEntryUpstreamBandwidthKbps());
+ assertEquals(0, networkPriority.getMinExitUpstreamBandwidthKbps());
+ assertEquals(0, networkPriority.getMinEntryDownstreamBandwidthKbps());
+ assertEquals(0, networkPriority.getMinExitDownstreamBandwidthKbps());
+
assertTrue(networkPriority.getSsids().isEmpty());
}
@Test
- public void testBuildWithInvalidNetworkQuality() {
+ public void testBuilderRequiresStricterEntryCriteria() {
try {
new VcnWifiUnderlyingNetworkTemplate.Builder()
- .setNetworkQuality(INVALID_NETWORK_QUALITY);
- fail("Expected to fail due to the invalid network quality");
- } catch (Exception expected) {
+ .setMinUpstreamBandwidthKbps(
+ TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS);
+
+ fail("Expected IAE for exit threshold > entry threshold");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ new VcnWifiUnderlyingNetworkTemplate.Builder()
+ .setMinDownstreamBandwidthKbps(
+ TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS);
+
+ fail("Expected IAE for exit threshold > entry threshold");
+ } catch (IllegalArgumentException expected) {
}
}
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
index 4bb7de8..6c849b5 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
@@ -18,7 +18,10 @@
import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
-import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK;
+import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS;
+import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS;
+import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS;
+import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS;
import static com.android.server.vcn.VcnTestUtils.setupSystemService;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.PRIORITY_ANY;
@@ -76,6 +79,12 @@
private static final int CARRIER_ID = 1;
private static final int CARRIER_ID_OTHER = 2;
+ private static final int LINK_UPSTREAM_BANDWIDTH_KBPS = 1024;
+ private static final int LINK_DOWNSTREAM_BANDWIDTH_KBPS = 2048;
+
+ private static final int TEST_MIN_UPSTREAM_BANDWIDTH_KBPS = 100;
+ private static final int TEST_MIN_DOWNSTREAM_BANDWIDTH_KBPS = 200;
+
private static final ParcelUuid SUB_GROUP = new ParcelUuid(new UUID(0, 0));
private static final NetworkCapabilities WIFI_NETWORK_CAPABILITIES =
@@ -83,6 +92,8 @@
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.setSignalStrength(WIFI_RSSI)
.setSsid(SSID)
+ .setLinkUpstreamBandwidthKbps(LINK_UPSTREAM_BANDWIDTH_KBPS)
+ .setLinkDownstreamBandwidthKbps(LINK_DOWNSTREAM_BANDWIDTH_KBPS)
.build();
private static final TelephonyNetworkSpecifier TEL_NETWORK_SPECIFIER =
@@ -93,6 +104,8 @@
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.setSubscriptionIds(Set.of(SUB_ID))
.setNetworkSpecifier(TEL_NETWORK_SPECIFIER)
+ .setLinkUpstreamBandwidthKbps(LINK_UPSTREAM_BANDWIDTH_KBPS)
+ .setLinkDownstreamBandwidthKbps(LINK_DOWNSTREAM_BANDWIDTH_KBPS)
.build();
private static final LinkProperties LINK_PROPERTIES = getLinkPropertiesWithName("test_iface");
@@ -146,7 +159,6 @@
public void testMatchWithoutNotMeteredBit() {
final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
new VcnWifiUnderlyingNetworkTemplate.Builder()
- .setNetworkQuality(NETWORK_QUALITY_OK)
.setMetered(MATCH_FORBIDDEN)
.build();
@@ -161,11 +173,133 @@
null /* carrierConfig */));
}
+ private void verifyMatchesPriorityRuleForUpstreamBandwidth(
+ int entryUpstreamBandwidth,
+ int exitUpstreamBandwidth,
+ UnderlyingNetworkRecord currentlySelected,
+ boolean expectMatch) {
+ final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
+ new VcnWifiUnderlyingNetworkTemplate.Builder()
+ .setMinUpstreamBandwidthKbps(entryUpstreamBandwidth, exitUpstreamBandwidth)
+ .build();
+
+ assertEquals(
+ expectMatch,
+ checkMatchesPriorityRule(
+ mVcnContext,
+ wifiNetworkPriority,
+ mWifiNetworkRecord,
+ SUB_GROUP,
+ mSubscriptionSnapshot,
+ currentlySelected,
+ null /* carrierConfig */));
+ }
+
+ private void verifyMatchesPriorityRuleForDownstreamBandwidth(
+ int entryDownstreamBandwidth,
+ int exitDownstreamBandwidth,
+ UnderlyingNetworkRecord currentlySelected,
+ boolean expectMatch) {
+ final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
+ new VcnWifiUnderlyingNetworkTemplate.Builder()
+ .setMinDownstreamBandwidthKbps(
+ entryDownstreamBandwidth, exitDownstreamBandwidth)
+ .build();
+
+ assertEquals(
+ expectMatch,
+ checkMatchesPriorityRule(
+ mVcnContext,
+ wifiNetworkPriority,
+ mWifiNetworkRecord,
+ SUB_GROUP,
+ mSubscriptionSnapshot,
+ currentlySelected,
+ null /* carrierConfig */));
+ }
+
+ @Test
+ public void testMatchWithEntryUpstreamBandwidthEquals() {
+ verifyMatchesPriorityRuleForUpstreamBandwidth(
+ TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+ null /* currentlySelected */,
+ true);
+ }
+
+ @Test
+ public void testMatchWithEntryUpstreamBandwidthTooLow() {
+ verifyMatchesPriorityRuleForUpstreamBandwidth(
+ LINK_UPSTREAM_BANDWIDTH_KBPS + 1,
+ LINK_UPSTREAM_BANDWIDTH_KBPS + 1,
+ null /* currentlySelected */,
+ false);
+ }
+
+ @Test
+ public void testMatchWithEntryDownstreamBandwidthEquals() {
+ verifyMatchesPriorityRuleForDownstreamBandwidth(
+ TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+ null /* currentlySelected */,
+ true);
+ }
+
+ @Test
+ public void testMatchWithEntryDownstreamBandwidthTooLow() {
+ verifyMatchesPriorityRuleForDownstreamBandwidth(
+ LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1,
+ LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1,
+ null /* currentlySelected */,
+ false);
+ }
+
+ @Test
+ public void testMatchWithExitUpstreamBandwidthEquals() {
+ verifyMatchesPriorityRuleForUpstreamBandwidth(
+ TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+ mWifiNetworkRecord,
+ true);
+ }
+
+ @Test
+ public void testMatchWithExitUpstreamBandwidthTooLow() {
+ verifyMatchesPriorityRuleForUpstreamBandwidth(
+ LINK_UPSTREAM_BANDWIDTH_KBPS + 1,
+ LINK_UPSTREAM_BANDWIDTH_KBPS + 1,
+ mWifiNetworkRecord,
+ false);
+ }
+
+ @Test
+ public void testMatchWithExitDownstreamBandwidthEquals() {
+ verifyMatchesPriorityRuleForDownstreamBandwidth(
+ TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+ mWifiNetworkRecord,
+ true);
+ }
+
+ @Test
+ public void testMatchWithExitDownstreamBandwidthTooLow() {
+ verifyMatchesPriorityRuleForDownstreamBandwidth(
+ LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1,
+ LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1,
+ mWifiNetworkRecord,
+ false);
+ }
+
private void verifyMatchWifi(
boolean isSelectedNetwork, PersistableBundle carrierConfig, boolean expectMatch) {
final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
new VcnWifiUnderlyingNetworkTemplate.Builder()
- .setNetworkQuality(NETWORK_QUALITY_OK)
+ .setMinUpstreamBandwidthKbps(
+ TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS)
+ .setMinDownstreamBandwidthKbps(
+ TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS)
.build();
final UnderlyingNetworkRecord selectedNetworkRecord =
isSelectedNetwork ? mWifiNetworkRecord : null;
@@ -214,7 +348,12 @@
final String nwPrioritySsid = useMatchedSsid ? SSID : SSID_OTHER;
final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
new VcnWifiUnderlyingNetworkTemplate.Builder()
- .setNetworkQuality(NETWORK_QUALITY_OK)
+ .setMinUpstreamBandwidthKbps(
+ TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS)
+ .setMinDownstreamBandwidthKbps(
+ TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS)
.setSsids(Set.of(nwPrioritySsid))
.build();
@@ -238,7 +377,13 @@
}
private static VcnCellUnderlyingNetworkTemplate.Builder getCellNetworkPriorityBuilder() {
- return new VcnCellUnderlyingNetworkTemplate.Builder().setNetworkQuality(NETWORK_QUALITY_OK);
+ return new VcnCellUnderlyingNetworkTemplate.Builder()
+ .setMinUpstreamBandwidthKbps(
+ TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS)
+ .setMinDownstreamBandwidthKbps(
+ TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS);
}
@Test
diff --git a/tools/aosp/aosp_sha.sh b/tools/aosp/aosp_sha.sh
index 36bea57..95b43cd 100755
--- a/tools/aosp/aosp_sha.sh
+++ b/tools/aosp/aosp_sha.sh
@@ -1,7 +1,7 @@
#!/bin/bash
LOCAL_DIR="$( dirname "${BASH_SOURCE}" )"
-if git branch -vv | grep -q -E "^\*[^\[]+\[aosp/"; then
+if git log -n 1 --format='%D' HEAD@{upstream} | grep -q aosp/; then
# Change appears to be in AOSP
exit 0
elif git log -n 1 --format='%B' $1 | grep -q -E "^Ignore-AOSP-First: .+" ; then