Merge "Satellite API changes"
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
index 1022490..bd8d27c 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -27,6 +27,7 @@
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
+import android.app.BackgroundStartPrivileges;
import android.app.UserSwitchObserver;
import android.app.job.JobInfo;
import android.app.job.JobParameters;
@@ -388,6 +389,12 @@
private final ArrayList<ContextAssignment> mRecycledPreferredUidOnly = new ArrayList<>();
private final ArrayList<ContextAssignment> mRecycledStoppable = new ArrayList<>();
private final AssignmentInfo mRecycledAssignmentInfo = new AssignmentInfo();
+ private final SparseIntArray mRecycledPrivilegedState = new SparseIntArray();
+
+ private static final int PRIVILEGED_STATE_UNDEFINED = 0;
+ private static final int PRIVILEGED_STATE_NONE = 1;
+ private static final int PRIVILEGED_STATE_BAL = 2;
+ private static final int PRIVILEGED_STATE_TOP = 3;
private final Pools.Pool<ContextAssignment> mContextAssignmentPool =
new Pools.SimplePool<>(MAX_RETAINED_OBJECTS);
@@ -792,7 +799,7 @@
cleanUpAfterAssignmentChangesLocked(
mRecycledChanged, mRecycledIdle, mRecycledPreferredUidOnly, mRecycledStoppable,
- mRecycledAssignmentInfo);
+ mRecycledAssignmentInfo, mRecycledPrivilegedState);
noteConcurrency();
}
@@ -915,7 +922,8 @@
continue;
}
- final boolean hasImmediacyPrivilege = hasImmediacyPrivilegeLocked(nextPending);
+ final boolean hasImmediacyPrivilege =
+ hasImmediacyPrivilegeLocked(nextPending, mRecycledPrivilegedState);
if (DEBUG && isSimilarJobRunningLocked(nextPending)) {
Slog.w(TAG, "Already running similar job to: " + nextPending);
}
@@ -1183,7 +1191,8 @@
final ArraySet<ContextAssignment> idle,
final List<ContextAssignment> preferredUidOnly,
final List<ContextAssignment> stoppable,
- final AssignmentInfo assignmentInfo) {
+ final AssignmentInfo assignmentInfo,
+ final SparseIntArray privilegedState) {
for (int s = stoppable.size() - 1; s >= 0; --s) {
final ContextAssignment assignment = stoppable.get(s);
assignment.clear();
@@ -1205,20 +1214,58 @@
stoppable.clear();
preferredUidOnly.clear();
assignmentInfo.clear();
+ privilegedState.clear();
mWorkCountTracker.resetStagingCount();
mActivePkgStats.forEach(mPackageStatsStagingCountClearer);
}
@VisibleForTesting
@GuardedBy("mLock")
- boolean hasImmediacyPrivilegeLocked(@NonNull JobStatus job) {
+ boolean hasImmediacyPrivilegeLocked(@NonNull JobStatus job,
+ @NonNull SparseIntArray cachedPrivilegedState) {
+ if (!job.shouldTreatAsExpeditedJob() && !job.shouldTreatAsUserInitiatedJob()) {
+ return false;
+ }
// EJs & user-initiated jobs for the TOP app should run immediately.
// However, even for user-initiated jobs, if the app has not recently been in TOP or BAL
// state, we don't give the immediacy privilege so that we can try and maintain
// reasonably concurrency behavior.
- return job.lastEvaluatedBias == JobInfo.BIAS_TOP_APP
- // TODO(): include BAL state for user-initiated jobs
- && (job.shouldTreatAsExpeditedJob() || job.shouldTreatAsUserInitiatedJob());
+ if (job.lastEvaluatedBias == JobInfo.BIAS_TOP_APP) {
+ return true;
+ }
+ final int uid = job.getSourceUid();
+ final int privilegedState = cachedPrivilegedState.get(uid, PRIVILEGED_STATE_UNDEFINED);
+ switch (privilegedState) {
+ case PRIVILEGED_STATE_TOP:
+ return true;
+ case PRIVILEGED_STATE_BAL:
+ return job.shouldTreatAsUserInitiatedJob();
+ case PRIVILEGED_STATE_NONE:
+ return false;
+ case PRIVILEGED_STATE_UNDEFINED:
+ default:
+ final ActivityManagerInternal activityManagerInternal =
+ LocalServices.getService(ActivityManagerInternal.class);
+ final int procState = activityManagerInternal.getUidProcessState(uid);
+ if (procState == ActivityManager.PROCESS_STATE_TOP) {
+ cachedPrivilegedState.put(uid, PRIVILEGED_STATE_TOP);
+ return true;
+ }
+ if (job.shouldTreatAsExpeditedJob()) {
+ // EJs only get the TOP privilege.
+ return false;
+ }
+
+ final BackgroundStartPrivileges bsp =
+ activityManagerInternal.getBackgroundStartPrivileges(uid);
+ final boolean balAllowed = bsp.allowsBackgroundActivityStarts();
+ if (DEBUG) {
+ Slog.d(TAG, "Job " + job.toShortString() + " bal state: " + bsp);
+ }
+ cachedPrivilegedState.put(uid,
+ balAllowed ? PRIVILEGED_STATE_BAL : PRIVILEGED_STATE_NONE);
+ return balAllowed;
+ }
}
@GuardedBy("mLock")
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 419111a..22fea7f 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -31,6 +31,7 @@
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
+import android.app.BackgroundStartPrivileges;
import android.app.IUidObserver;
import android.app.compat.CompatChanges;
import android.app.job.IJobScheduler;
@@ -3841,6 +3842,25 @@
return callingResult;
}
}
+
+ final int uid = sourceUid != -1 ? sourceUid : callingUid;
+ final int procState = mActivityManagerInternal.getUidProcessState(uid);
+ if (DEBUG) {
+ Slog.d(TAG, "Uid " + uid + " proc state="
+ + ActivityManager.procStateToString(procState));
+ }
+ if (procState != ActivityManager.PROCESS_STATE_TOP) {
+ final BackgroundStartPrivileges bsp =
+ mActivityManagerInternal.getBackgroundStartPrivileges(uid);
+ if (DEBUG) {
+ Slog.d(TAG, "Uid " + uid + ": " + bsp);
+ }
+ if (!bsp.allowsBackgroundActivityStarts()) {
+ Slog.e(TAG,
+ "Uid " + uid + " not in a state to schedule user-initiated jobs");
+ return JobScheduler.RESULT_FAILURE;
+ }
+ }
}
if (jobWorkItem != null) {
jobWorkItem.enforceValidity(rejectNegativeNetworkEstimates);
diff --git a/core/api/current.txt b/core/api/current.txt
index d54290b..7ed066e 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -7782,6 +7782,7 @@
method public int addOverrideApn(@NonNull android.content.ComponentName, @NonNull android.telephony.data.ApnSetting);
method public void addPersistentPreferredActivity(@NonNull android.content.ComponentName, android.content.IntentFilter, @NonNull android.content.ComponentName);
method public void addUserRestriction(@NonNull android.content.ComponentName, String);
+ method public void addUserRestrictionGlobally(@NonNull String);
method public boolean bindDeviceAdminServiceAsUser(@NonNull android.content.ComponentName, android.content.Intent, @NonNull android.content.ServiceConnection, int, @NonNull android.os.UserHandle);
method public boolean canAdminGrantSensorsPermissions();
method public boolean canUsbDataSignalingBeDisabled();
@@ -7884,6 +7885,7 @@
method @Nullable public java.util.List<android.os.PersistableBundle> getTrustAgentConfiguration(@Nullable android.content.ComponentName, @NonNull android.content.ComponentName);
method @NonNull public java.util.List<java.lang.String> getUserControlDisabledPackages(@NonNull android.content.ComponentName);
method @NonNull public android.os.Bundle getUserRestrictions(@NonNull android.content.ComponentName);
+ method @NonNull public android.os.Bundle getUserRestrictionsGlobally();
method @Nullable public String getWifiMacAddress(@NonNull android.content.ComponentName);
method @Nullable public android.app.admin.WifiSsidPolicy getWifiSsidPolicy();
method public boolean grantKeyPairToApp(@Nullable android.content.ComponentName, @NonNull String, @NonNull String);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 67aa074..6426cf0 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1559,6 +1559,13 @@
field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.UnknownAuthority> CREATOR;
}
+ public final class UserRestrictionPolicyKey extends android.app.admin.PolicyKey {
+ method public int describeContents();
+ method @NonNull public String getRestriction();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.UserRestrictionPolicyKey> CREATOR;
+ }
+
}
package android.app.ambientcontext {
@@ -2276,7 +2283,6 @@
method public void notifyEvent(@NonNull android.app.search.Query, @NonNull android.app.search.SearchTargetEvent);
method @Nullable public void query(@NonNull android.app.search.Query, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.List<android.app.search.SearchTarget>>);
method public void registerEmptyQueryResultUpdateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.app.search.SearchSession.Callback);
- method public void requestEmptyQueryResultUpdate();
method public void unregisterEmptyQueryResultUpdateCallback(@NonNull android.app.search.SearchSession.Callback);
}
@@ -12684,7 +12690,6 @@
method @MainThread public abstract void onDestroy(@NonNull android.app.search.SearchSessionId);
method @MainThread public abstract void onNotifyEvent(@NonNull android.app.search.SearchSessionId, @NonNull android.app.search.Query, @NonNull android.app.search.SearchTargetEvent);
method @MainThread public abstract void onQuery(@NonNull android.app.search.SearchSessionId, @NonNull android.app.search.Query, @NonNull java.util.function.Consumer<java.util.List<android.app.search.SearchTarget>>);
- method @MainThread public void onRequestEmptyQueryResultUpdate(@NonNull android.app.search.SearchSessionId);
method public void onSearchSessionCreated(@NonNull android.app.search.SearchContext, @NonNull android.app.search.SearchSessionId);
method @MainThread public void onStartUpdateEmptyQueryResult();
method @MainThread public void onStopUpdateEmptyQueryResult();
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index bea2bfc..bb3ea7b 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1497,10 +1497,13 @@
method @RequiresPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT) public void removeKeyboardLayoutForInputDevice(@NonNull android.hardware.input.InputDeviceIdentifier, @NonNull String);
method public void removeUniqueIdAssociation(@NonNull String);
method @RequiresPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT) public void setCurrentKeyboardLayoutForInputDevice(@NonNull android.hardware.input.InputDeviceIdentifier, @NonNull String);
- method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setMaximumObscuringOpacityForTouch(@FloatRange(from=0, to=1) float);
field public static final long BLOCK_UNTRUSTED_TOUCHES = 158002302L; // 0x96aec7eL
}
+ public class InputSettings {
+ method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static void setMaximumObscuringOpacityForTouch(@NonNull android.content.Context, @FloatRange(from=0, to=1) float);
+ }
+
}
package android.hardware.lights {
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 2ad5c20..aa9212f 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -464,6 +464,11 @@
public abstract boolean isActivityStartsLoggingEnabled();
/** Returns true if the background activity starts is enabled. */
public abstract boolean isBackgroundActivityStartsEnabled();
+ /**
+ * Returns The current {@link BackgroundStartPrivileges} of the UID.
+ */
+ @NonNull
+ public abstract BackgroundStartPrivileges getBackgroundStartPrivileges(int uid);
public abstract void reportCurKeyguardUsageEvent(boolean keyguardShowing);
/** @see com.android.server.am.ActivityManagerService#monitor */
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index ac65a6b..4016e32 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -11269,6 +11269,13 @@
* See the constants in {@link android.os.UserManager} for the list of restrictions that can
* be enforced device-wide.
*
+ * <p>For callers targeting Android {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} or
+ * above, calling this API will result in applying the restriction locally on the calling user,
+ * or locally on the parent profile if called from the
+ * {@link DevicePolicyManager} instance obtained from
+ * {@link #getParentProfileInstance(ComponentName)}. To set a restriction globally, call
+ * {@link #addUserRestrictionGlobally} instead.
+ *
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param key The key of the restriction.
* @throws SecurityException if {@code admin} is not a device or profile owner.
@@ -11277,7 +11284,38 @@
@UserManager.UserRestrictionKey String key) {
if (mService != null) {
try {
- mService.setUserRestriction(admin, key, true, mParentInstance);
+ mService.setUserRestriction(
+ admin, mContext.getPackageName(), key, true, mParentInstance);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Called by a profile or device owner to set a user restriction specified by the provided
+ * {@code key} globally on all users. To clear the restriction use
+ * {@link #clearUserRestriction}.
+ *
+ * <p>For a given user, a restriction will be set if it was applied globally or locally by any
+ * admin.
+ *
+ * <p> The calling device admin must be a profile or device owner; if it is not, a security
+ * exception will be thrown.
+ *
+ * <p> See the constants in {@link android.os.UserManager} for the list of restrictions that can
+ * be enforced device-wide.
+ *
+ * @param key The key of the restriction.
+ * @throws SecurityException if {@code admin} is not a device or profile owner.
+ * @throws IllegalStateException if caller is not targeting Android
+ * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} or above.
+ */
+ public void addUserRestrictionGlobally(@NonNull @UserManager.UserRestrictionKey String key) {
+ throwIfParentInstance("addUserRestrictionGlobally");
+ if (mService != null) {
+ try {
+ mService.setUserRestrictionGlobally(mContext.getPackageName(), key);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -11296,6 +11334,10 @@
* <p>
* See the constants in {@link android.os.UserManager} for the list of restrictions.
*
+ * <p>For callers targeting Android {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} or
+ * above, calling this API will result in clearing any local and global restriction with the
+ * specified key that was previously set by the caller.
+ *
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param key The key of the restriction.
* @throws SecurityException if {@code admin} is not a device or profile owner.
@@ -11304,7 +11346,8 @@
@UserManager.UserRestrictionKey String key) {
if (mService != null) {
try {
- mService.setUserRestriction(admin, key, false, mParentInstance);
+ mService.setUserRestriction(
+ admin, mContext.getPackageName(), key, false, mParentInstance);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -11324,6 +11367,18 @@
* {@link #getParentProfileInstance(ComponentName)}, for retrieving device-wide restrictions
* it previously set with {@link #addUserRestriction(ComponentName, String)}.
*
+ * <p>For callers targeting Android {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} or
+ * above, this API will return the local restrictions set on the calling user, or on the parent
+ * profile if called from the {@link DevicePolicyManager} instance obtained from
+ * {@link #getParentProfileInstance(ComponentName)}. To get global restrictions set by admin,
+ * call {@link #getUserRestrictionsGlobally()} instead.
+ *
+ * <p>Note that this is different that the returned restrictions for callers targeting pre
+ * Android {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, were this API returns
+ * all local/global restrictions set by the admin on the calling user using
+ * {@link #addUserRestriction(ComponentName, String)} or the parent user if called on the
+ * {@link DevicePolicyManager} instance it obtained from {@link #getParentProfileInstance}.
+ *
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @return a {@link Bundle} whose keys are the user restrictions, and the values a
* {@code boolean} indicating whether the restriction is set.
@@ -11333,7 +11388,33 @@
Bundle ret = null;
if (mService != null) {
try {
- ret = mService.getUserRestrictions(admin, mParentInstance);
+ ret = mService.getUserRestrictions(
+ admin, mContext.getPackageName(), mParentInstance);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return ret == null ? new Bundle() : ret;
+ }
+
+ /**
+ * Called by a profile or device owner to get global user restrictions set with
+ * {@link #addUserRestrictionGlobally(String)}.
+ * <p>
+ * To get all the user restrictions currently set for a certain user, use
+ * {@link UserManager#getUserRestrictions()}.
+ * @return a {@link Bundle} whose keys are the user restrictions, and the values a
+ * {@code boolean} indicating whether the restriction is set.
+ * @throws SecurityException if {@code admin} is not a device or profile owner.
+ * @throws IllegalStateException if caller is not targeting Android
+ * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} or above.
+ */
+ public @NonNull Bundle getUserRestrictionsGlobally() {
+ throwIfParentInstance("createAdminSupportIntent");
+ Bundle ret = null;
+ if (mService != null) {
+ try {
+ ret = mService.getUserRestrictionsGlobally(mContext.getPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 3f66b45..5b9e948 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -247,8 +247,11 @@
void setRestrictionsProvider(in ComponentName who, in ComponentName provider);
ComponentName getRestrictionsProvider(int userHandle);
- void setUserRestriction(in ComponentName who, in String key, boolean enable, boolean parent);
- Bundle getUserRestrictions(in ComponentName who, boolean parent);
+ void setUserRestriction(in ComponentName who, in String callerPackage, in String key, boolean enable, boolean parent);
+ void setUserRestrictionGlobally(in String callerPackage, in String key);
+ Bundle getUserRestrictions(in ComponentName who, in String callerPackage, boolean parent);
+ Bundle getUserRestrictionsGlobally(in String callerPackage);
+
void addCrossProfileIntentFilter(in ComponentName admin, in IntentFilter filter, int flags);
void clearCrossProfileIntentFilters(in ComponentName admin);
diff --git a/core/java/android/app/admin/IntentFilterPolicyKey.java b/core/java/android/app/admin/IntentFilterPolicyKey.java
index f2f8aa4..a49152d 100644
--- a/core/java/android/app/admin/IntentFilterPolicyKey.java
+++ b/core/java/android/app/admin/IntentFilterPolicyKey.java
@@ -18,6 +18,7 @@
import static android.app.admin.PolicyUpdatesReceiver.EXTRA_INTENT_FILTER;
import static android.app.admin.PolicyUpdatesReceiver.EXTRA_POLICY_BUNDLE_KEY;
+import static android.app.admin.PolicyUpdatesReceiver.EXTRA_POLICY_KEY;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -100,7 +101,7 @@
*/
@Override
public void writeToBundle(Bundle bundle) {
- super.writeToBundle(bundle);
+ bundle.putString(EXTRA_POLICY_KEY, getIdentifier());
Bundle extraPolicyParams = new Bundle();
extraPolicyParams.putParcelable(EXTRA_INTENT_FILTER, mFilter);
bundle.putBundle(EXTRA_POLICY_BUNDLE_KEY, extraPolicyParams);
diff --git a/core/java/android/app/admin/NoArgsPolicyKey.java b/core/java/android/app/admin/NoArgsPolicyKey.java
index 57b67d5..2c9d2be 100644
--- a/core/java/android/app/admin/NoArgsPolicyKey.java
+++ b/core/java/android/app/admin/NoArgsPolicyKey.java
@@ -16,8 +16,11 @@
package android.app.admin;
+import static android.app.admin.PolicyUpdatesReceiver.EXTRA_POLICY_KEY;
+
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -41,6 +44,14 @@
this(source.readString());
}
+ /**
+ * @hide
+ */
+ @Override
+ public void writeToBundle(Bundle bundle) {
+ bundle.putString(EXTRA_POLICY_KEY, getIdentifier());
+ }
+
@Override
public int describeContents() {
return 0;
diff --git a/core/java/android/app/admin/PackagePermissionPolicyKey.java b/core/java/android/app/admin/PackagePermissionPolicyKey.java
index 4aa2e38..90ca052 100644
--- a/core/java/android/app/admin/PackagePermissionPolicyKey.java
+++ b/core/java/android/app/admin/PackagePermissionPolicyKey.java
@@ -19,6 +19,7 @@
import static android.app.admin.PolicyUpdatesReceiver.EXTRA_PACKAGE_NAME;
import static android.app.admin.PolicyUpdatesReceiver.EXTRA_PERMISSION_NAME;
import static android.app.admin.PolicyUpdatesReceiver.EXTRA_POLICY_BUNDLE_KEY;
+import static android.app.admin.PolicyUpdatesReceiver.EXTRA_POLICY_KEY;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -119,7 +120,7 @@
*/
@Override
public void writeToBundle(Bundle bundle) {
- super.writeToBundle(bundle);
+ bundle.putString(EXTRA_POLICY_KEY, getIdentifier());
Bundle extraPolicyParams = new Bundle();
extraPolicyParams.putString(EXTRA_PACKAGE_NAME, mPackageName);
extraPolicyParams.putString(EXTRA_PERMISSION_NAME, mPermissionName);
diff --git a/core/java/android/app/admin/PackagePolicyKey.java b/core/java/android/app/admin/PackagePolicyKey.java
index 3469970..08b6110 100644
--- a/core/java/android/app/admin/PackagePolicyKey.java
+++ b/core/java/android/app/admin/PackagePolicyKey.java
@@ -18,6 +18,7 @@
import static android.app.admin.PolicyUpdatesReceiver.EXTRA_PACKAGE_NAME;
import static android.app.admin.PolicyUpdatesReceiver.EXTRA_POLICY_BUNDLE_KEY;
+import static android.app.admin.PolicyUpdatesReceiver.EXTRA_POLICY_KEY;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -101,7 +102,7 @@
*/
@Override
public void writeToBundle(Bundle bundle) {
- super.writeToBundle(bundle);
+ bundle.putString(EXTRA_POLICY_KEY, getIdentifier());
Bundle extraPolicyParams = new Bundle();
extraPolicyParams.putString(EXTRA_PACKAGE_NAME, mPackageName);
bundle.putBundle(EXTRA_POLICY_BUNDLE_KEY, extraPolicyParams);
diff --git a/core/java/android/app/admin/PolicyKey.java b/core/java/android/app/admin/PolicyKey.java
index 84cc66b..3544c19 100644
--- a/core/java/android/app/admin/PolicyKey.java
+++ b/core/java/android/app/admin/PolicyKey.java
@@ -16,8 +16,6 @@
package android.app.admin;
-import static android.app.admin.PolicyUpdatesReceiver.EXTRA_POLICY_KEY;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
@@ -103,9 +101,7 @@
/**
* @hide
*/
- public void writeToBundle(Bundle bundle) {
- bundle.putString(EXTRA_POLICY_KEY, mIdentifier);
- }
+ public abstract void writeToBundle(Bundle bundle);
@Override
public boolean equals(@Nullable Object o) {
diff --git a/core/java/android/app/admin/UserRestrictionPolicyKey.java b/core/java/android/app/admin/UserRestrictionPolicyKey.java
new file mode 100644
index 0000000..92014763
--- /dev/null
+++ b/core/java/android/app/admin/UserRestrictionPolicyKey.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.admin;
+
+import static android.app.admin.PolicyUpdatesReceiver.EXTRA_POLICY_KEY;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Bundle;
+import android.os.Parcel;
+
+import java.util.Objects;
+
+/**
+ * Class used to identify a policy that relates to a certain user restriction
+ * (See {@link DevicePolicyManager#addUserRestriction} and
+ * {@link DevicePolicyManager#addUserRestrictionGlobally}).
+ *
+ * @hide
+ */
+@SystemApi
+public final class UserRestrictionPolicyKey extends PolicyKey {
+
+ private final String mRestriction;
+
+ /**
+ * @hide
+ */
+ public UserRestrictionPolicyKey(@NonNull String identifier, @NonNull String restriction) {
+ super(identifier);
+ mRestriction = Objects.requireNonNull(restriction);
+ }
+
+ private UserRestrictionPolicyKey(Parcel source) {
+ this(source.readString(), source.readString());
+ }
+
+ /**
+ * Returns the user restriction associated with this policy.
+ */
+ @NonNull
+ public String getRestriction() {
+ return mRestriction;
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public void writeToBundle(Bundle bundle) {
+ bundle.putString(EXTRA_POLICY_KEY, getIdentifier());
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(getIdentifier());
+ dest.writeString(mRestriction);
+ }
+
+ @NonNull
+ public static final Creator<UserRestrictionPolicyKey> CREATOR =
+ new Creator<UserRestrictionPolicyKey>() {
+ @Override
+ public UserRestrictionPolicyKey createFromParcel(Parcel source) {
+ return new UserRestrictionPolicyKey(source);
+ }
+
+ @Override
+ public UserRestrictionPolicyKey[] newArray(int size) {
+ return new UserRestrictionPolicyKey[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ return "UserRestrictionPolicyKey " + getIdentifier();
+ }
+}
diff --git a/core/java/android/app/search/ISearchUiManager.aidl b/core/java/android/app/search/ISearchUiManager.aidl
index fefbd5a..4cf0b81 100644
--- a/core/java/android/app/search/ISearchUiManager.aidl
+++ b/core/java/android/app/search/ISearchUiManager.aidl
@@ -38,8 +38,6 @@
void registerEmptyQueryResultUpdateCallback(in SearchSessionId sessionId, in ISearchCallback callback);
- void requestEmptyQueryResultUpdate(in SearchSessionId sessionId);
-
void unregisterEmptyQueryResultUpdateCallback(in SearchSessionId sessionId, in ISearchCallback callback);
void destroySearchSession(in SearchSessionId sessionId);
diff --git a/core/java/android/app/search/SearchSession.java b/core/java/android/app/search/SearchSession.java
index 9e0a1d0..0dbd81e 100644
--- a/core/java/android/app/search/SearchSession.java
+++ b/core/java/android/app/search/SearchSession.java
@@ -225,32 +225,6 @@
}
/**
- * Requests the search ui service to dispatch a new set of search targets to the pre-registered
- * callback for zero state. Zero state means when user entered search ui but not issued any
- * query yet. This method can be used for client to sync up with server data if they think data
- * might be out of sync, for example, after restart.
- * Pre-register a callback with
- * {@link SearchSession#registerEmptyQueryResultUpdateCallback(Executor, Callback)}
- * is required before calling this method. Otherwise this is no-op.
- *
- * @see {@link SearchSession#registerEmptyQueryResultUpdateCallback(Executor, Callback)}
- * @see {@link SearchSession.Callback#onTargetsAvailable(List)}.
- */
- public void requestEmptyQueryResultUpdate() {
- if (mIsClosed.get()) {
- throw new IllegalStateException("This client has already been destroyed.");
- }
-
- try {
- mInterface.requestEmptyQueryResultUpdate(mSessionId);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to request empty query result update", e);
- e.rethrowAsRuntimeException();
- }
- }
-
-
- /**
* Destroys the client and unregisters the callback. Any method on this class after this call
* will throw {@link IllegalStateException}.
*
diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
index 709fa60..41c406d 100644
--- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
@@ -614,8 +614,15 @@
boolean status = true;
synchronized (mInterfaceLock) {
try {
- mSessionProcessor.onCaptureSessionStart(mRequestProcessor);
- mInitialized = true;
+ if (mSessionProcessor != null) {
+ mSessionProcessor.onCaptureSessionStart(mRequestProcessor);
+ mInitialized = true;
+ } else {
+ Log.v(TAG, "Failed to start capture session, session released before " +
+ "extension start!");
+ status = false;
+ mCaptureSession.close();
+ }
} catch (RemoteException e) {
Log.e(TAG, "Failed to start capture session,"
+ " extension service does not respond!");
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 657541c..2ea9ea0 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -51,11 +51,9 @@
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
import android.os.SystemClock;
-import android.os.UserHandle;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.os.VibratorManager;
-import android.provider.Settings;
import android.util.Log;
import android.util.SparseArray;
import android.view.Display;
@@ -215,30 +213,6 @@
"android.hardware.input.metadata.KEYBOARD_LAYOUTS";
/**
- * Pointer Speed: The minimum (slowest) pointer speed (-7).
- * @hide
- */
- public static final int MIN_POINTER_SPEED = -7;
-
- /**
- * Pointer Speed: The maximum (fastest) pointer speed (7).
- * @hide
- */
- public static final int MAX_POINTER_SPEED = 7;
-
- /**
- * Pointer Speed: The default pointer speed (0).
- * @hide
- */
- public static final int DEFAULT_POINTER_SPEED = 0;
-
- /**
- * The maximum allowed obscuring opacity by UID to propagate touches (0 <= x <= 1).
- * @hide
- */
- public static final float DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH = .8f;
-
- /**
* Prevent touches from being consumed by apps if these touches passed through a non-trusted
* window from a different UID and are considered unsafe.
*
@@ -1135,58 +1109,18 @@
}
/**
- * Gets the mouse pointer speed.
- * <p>
- * Only returns the permanent mouse pointer speed. Ignores any temporary pointer
- * speed set by {@link #tryPointerSpeed}.
- * </p>
- *
- * @param context The application context.
- * @return The pointer speed as a value between {@link #MIN_POINTER_SPEED} and
- * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}.
- *
- * @hide
- */
- public int getPointerSpeed(Context context) {
- return Settings.System.getInt(context.getContentResolver(),
- Settings.System.POINTER_SPEED, DEFAULT_POINTER_SPEED);
- }
-
- /**
- * Sets the mouse pointer speed.
- * <p>
- * Requires {@link android.Manifest.permission#WRITE_SETTINGS}.
- * </p>
- *
- * @param context The application context.
- * @param speed The pointer speed as a value between {@link #MIN_POINTER_SPEED} and
- * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}.
- *
- * @hide
- */
- @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
- public void setPointerSpeed(Context context, int speed) {
- if (speed < MIN_POINTER_SPEED || speed > MAX_POINTER_SPEED) {
- throw new IllegalArgumentException("speed out of range");
- }
-
- Settings.System.putInt(context.getContentResolver(),
- Settings.System.POINTER_SPEED, speed);
- }
-
- /**
* Changes the mouse pointer speed temporarily, but does not save the setting.
* <p>
* Requires {@link android.Manifest.permission#SET_POINTER_SPEED}.
* </p>
*
- * @param speed The pointer speed as a value between {@link #MIN_POINTER_SPEED} and
- * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}.
+ * @param speed The pointer speed as a value between {@link InputSettings#MIN_POINTER_SPEED} and
+ * {@link InputSettings#MAX_POINTER_SPEED}, or the default value {@link InputSettings#DEFAULT_POINTER_SPEED}.
*
* @hide
*/
public void tryPointerSpeed(int speed) {
- if (speed < MIN_POINTER_SPEED || speed > MAX_POINTER_SPEED) {
+ if (speed < InputSettings.MIN_POINTER_SPEED || speed > InputSettings.MAX_POINTER_SPEED) {
throw new IllegalArgumentException("speed out of range");
}
@@ -1211,44 +1145,8 @@
*/
@FloatRange(from = 0, to = 1)
public float getMaximumObscuringOpacityForTouch() {
- return Settings.Global.getFloat(getContext().getContentResolver(),
- Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH,
- DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH);
- }
-
- /**
- * Sets the maximum allowed obscuring opacity by UID to propagate touches.
- *
- * <p>For certain window types (eg. SAWs), the decision of honoring {@link LayoutParams
- * #FLAG_NOT_TOUCHABLE} or not depends on the combined obscuring opacity of the windows
- * above the touch-consuming window.
- *
- * <p>For a certain UID:
- * <ul>
- * <li>If it's the same as the UID of the touch-consuming window, allow it to propagate
- * the touch.
- * <li>Otherwise take all its windows of eligible window types above the touch-consuming
- * window, compute their combined obscuring opacity considering that {@code
- * opacity(A, B) = 1 - (1 - opacity(A))*(1 - opacity(B))}. If the computed value is
- * lesser than or equal to this setting and there are no other windows preventing the
- * touch, allow the UID to propagate the touch.
- * </ul>
- *
- * <p>This value should be between 0 (inclusive) and 1 (inclusive).
- *
- * @see #getMaximumObscuringOpacityForTouch()
- *
- * @hide
- */
- @TestApi
- @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
- public void setMaximumObscuringOpacityForTouch(@FloatRange(from = 0, to = 1) float opacity) {
- if (opacity < 0 || opacity > 1) {
- throw new IllegalArgumentException(
- "Maximum obscuring opacity for touch should be >= 0 and <= 1");
- }
- Settings.Global.putFloat(getContext().getContentResolver(),
- Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH, opacity);
+ Context context = ActivityThread.currentApplication();
+ return InputSettings.getMaximumObscuringOpacityForTouch(context);
}
/**
@@ -2145,26 +2043,6 @@
}
/**
- * Whether stylus has ever been used on device (false by default).
- * @hide
- */
- public boolean isStylusEverUsed(@NonNull Context context) {
- return Settings.Global.getInt(context.getContentResolver(),
- Settings.Global.STYLUS_EVER_USED, 0) == 1;
- }
-
- /**
- * Set whether stylus has ever been used on device.
- * Should only ever be set to true once after stylus first usage.
- * @hide
- */
- @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
- public void setStylusEverUsed(@NonNull Context context, boolean stylusEverUsed) {
- Settings.Global.putInt(context.getContentResolver(),
- Settings.Global.STYLUS_EVER_USED, stylusEverUsed ? 1 : 0);
- }
-
- /**
* Whether there is a gesture-compatible touchpad connected to the device.
* @hide
*/
@@ -2174,200 +2052,6 @@
}
/**
- * Gets the touchpad pointer speed.
- *
- * The returned value only applies to gesture-compatible touchpads.
- *
- * @param context The application context.
- * @return The pointer speed as a value between {@link #MIN_POINTER_SPEED} and
- * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}.
- *
- * @hide
- */
- public int getTouchpadPointerSpeed(@NonNull Context context) {
- return Settings.System.getIntForUser(context.getContentResolver(),
- Settings.System.TOUCHPAD_POINTER_SPEED, DEFAULT_POINTER_SPEED,
- UserHandle.USER_CURRENT);
- }
-
- /**
- * Sets the touchpad pointer speed, and saves it in the settings.
- *
- * The new speed will only apply to gesture-compatible touchpads.
- *
- * @param context The application context.
- * @param speed The pointer speed as a value between {@link #MIN_POINTER_SPEED} and
- * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}.
- *
- * @hide
- */
- @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
- public void setTouchpadPointerSpeed(@NonNull Context context, int speed) {
- if (speed < MIN_POINTER_SPEED || speed > MAX_POINTER_SPEED) {
- throw new IllegalArgumentException("speed out of range");
- }
-
- Settings.System.putIntForUser(context.getContentResolver(),
- Settings.System.TOUCHPAD_POINTER_SPEED, speed, UserHandle.USER_CURRENT);
- }
-
- /**
- * Returns true if the touchpad should use pointer acceleration.
- *
- * The returned value only applies to gesture-compatible touchpads.
- *
- * @param context The application context.
- * @return Whether the touchpad should use pointer acceleration.
- *
- * @hide
- */
- public boolean useTouchpadPointerAcceleration(@NonNull Context context) {
- // TODO: obtain the actual behavior from the settings
- return true;
- }
-
- /**
- * Sets the pointer acceleration behavior for the touchpad.
- *
- * The new behavior is only applied to gesture-compatible touchpads.
- *
- * @param context The application context.
- * @param enabled Will enable pointer acceleration if true, disable it if false
- *
- * @hide
- */
- public void setTouchpadPointerAcceleration(@NonNull Context context, boolean enabled) {
- // TODO: set the right setting
- }
-
- /**
- * Returns true if moving two fingers upwards on the touchpad should
- * scroll down, which is known as natural scrolling.
- *
- * The returned value only applies to gesture-compatible touchpads.
- *
- * @param context The application context.
- * @return Whether the touchpad should use natural scrolling.
- *
- * @hide
- */
- public boolean useTouchpadNaturalScrolling(@NonNull Context context) {
- return Settings.System.getIntForUser(context.getContentResolver(),
- Settings.System.TOUCHPAD_NATURAL_SCROLLING, 0, UserHandle.USER_CURRENT) == 1;
- }
-
- /**
- * Sets the natural scroll behavior for the touchpad.
- *
- * If natural scrolling is enabled, moving two fingers upwards on the
- * touchpad will scroll down.
- *
- * @param context The application context.
- * @param enabled Will enable natural scroll if true, disable it if false
- *
- * @hide
- */
- @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
- public void setTouchpadNaturalScrolling(@NonNull Context context, boolean enabled) {
- Settings.System.putIntForUser(context.getContentResolver(),
- Settings.System.TOUCHPAD_NATURAL_SCROLLING, enabled ? 1 : 0,
- UserHandle.USER_CURRENT);
- }
-
- /**
- * Returns true if the touchpad should use tap to click.
- *
- * The returned value only applies to gesture-compatible touchpads.
- *
- * @param context The application context.
- * @return Whether the touchpad should use tap to click.
- *
- * @hide
- */
- public boolean useTouchpadTapToClick(@NonNull Context context) {
- return Settings.System.getIntForUser(context.getContentResolver(),
- Settings.System.TOUCHPAD_TAP_TO_CLICK, 0, UserHandle.USER_CURRENT) == 1;
- }
-
- /**
- * Sets the tap to click behavior for the touchpad.
- *
- * The new behavior is only applied to gesture-compatible touchpads.
- *
- * @param context The application context.
- * @param enabled Will enable tap to click if true, disable it if false
- *
- * @hide
- */
- @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
- public void setTouchpadTapToClick(@NonNull Context context, boolean enabled) {
- Settings.System.putIntForUser(context.getContentResolver(),
- Settings.System.TOUCHPAD_TAP_TO_CLICK, enabled ? 1 : 0,
- UserHandle.USER_CURRENT);
- }
-
- /**
- * Returns true if the touchpad should use tap dragging.
- *
- * The returned value only applies to gesture-compatible touchpads.
- *
- * @param context The application context.
- * @return Whether the touchpad should use tap dragging.
- *
- * @hide
- */
- public boolean useTouchpadTapDragging(@NonNull Context context) {
- // TODO: obtain the actual behavior from the settings
- return true;
- }
-
- /**
- * Sets the tap dragging behavior for the touchpad.
- *
- * The new behavior is only applied to gesture-compatible touchpads.
- *
- * @param context The application context.
- * @param enabled Will enable tap dragging if true, disable it if false
- *
- * @hide
- */
- public void setTouchpadTapDragging(@NonNull Context context, boolean enabled) {
- // TODO: set the right setting
- }
-
- /**
- * Returns true if the touchpad should use the right click zone.
- *
- * The returned value only applies to gesture-compatible touchpads.
- *
- * @param context The application context.
- * @return Whether the touchpad should use the right click zone.
- *
- * @hide
- */
- public boolean useTouchpadRightClickZone(@NonNull Context context) {
- return Settings.System.getIntForUser(context.getContentResolver(),
- Settings.System.TOUCHPAD_RIGHT_CLICK_ZONE, 0, UserHandle.USER_CURRENT) == 1;
- }
-
- /**
- * Sets the right click zone behavior for the touchpad.
- *
- * The new behavior is only applied to gesture-compatible touchpads.
- *
- * @param context The application context.
- * @param enabled Will enable the right click zone if true, disable it if false
- *
- * @hide
- */
- @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
- public void setTouchpadRightClickZone(@NonNull Context context, boolean enabled) {
- Settings.System.putIntForUser(context.getContentResolver(),
- Settings.System.TOUCHPAD_RIGHT_CLICK_ZONE, enabled ? 1 : 0,
- UserHandle.USER_CURRENT);
- }
-
- /**
* Registers a Keyboard backlight change listener to be notified about {@link
* KeyboardBacklightState} changes for connected keyboard devices.
*
diff --git a/core/java/android/hardware/input/InputSettings.java b/core/java/android/hardware/input/InputSettings.java
new file mode 100644
index 0000000..cdf9ea5
--- /dev/null
+++ b/core/java/android/hardware/input/InputSettings.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2023 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.hardware.input;
+
+import android.Manifest;
+import android.annotation.FloatRange;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
+import android.annotation.TestApi;
+import android.content.Context;
+import android.os.UserHandle;
+import android.provider.Settings;
+
+/**
+ * InputSettings encapsulates reading and writing settings related to input
+ *
+ * @hide
+ */
+@TestApi
+public class InputSettings {
+ /**
+ * Pointer Speed: The minimum (slowest) pointer speed (-7).
+ * @hide
+ */
+ public static final int MIN_POINTER_SPEED = -7;
+
+ /**
+ * Pointer Speed: The maximum (fastest) pointer speed (7).
+ * @hide
+ */
+ public static final int MAX_POINTER_SPEED = 7;
+
+ /**
+ * Pointer Speed: The default pointer speed (0).
+ * @hide
+ */
+ public static final int DEFAULT_POINTER_SPEED = 0;
+
+ /**
+ * The maximum allowed obscuring opacity by UID to propagate touches (0 <= x <= 1).
+ * @hide
+ */
+ public static final float DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH = .8f;
+
+
+ private InputSettings() {
+ }
+
+ /**
+ * Gets the mouse pointer speed.
+ * <p>
+ * Only returns the permanent mouse pointer speed. Ignores any temporary pointer
+ * speed set by {@link InputManager#tryPointerSpeed}.
+ * </p>
+ *
+ * @param context The application context.
+ * @return The pointer speed as a value between {@link #MIN_POINTER_SPEED} and
+ * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}.
+ *
+ * @hide
+ */
+ @SuppressLint("NonUserGetterCalled")
+ public static int getPointerSpeed(Context context) {
+ return Settings.System.getInt(context.getContentResolver(),
+ Settings.System.POINTER_SPEED, DEFAULT_POINTER_SPEED);
+ }
+
+ /**
+ * Sets the mouse pointer speed.
+ * <p>
+ * Requires {@link android.Manifest.permission#WRITE_SETTINGS}.
+ * </p>
+ *
+ * @param context The application context.
+ * @param speed The pointer speed as a value between {@link #MIN_POINTER_SPEED} and
+ * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
+ public static void setPointerSpeed(Context context, int speed) {
+ if (speed < MIN_POINTER_SPEED || speed > MAX_POINTER_SPEED) {
+ throw new IllegalArgumentException("speed out of range");
+ }
+
+ Settings.System.putInt(context.getContentResolver(),
+ Settings.System.POINTER_SPEED, speed);
+ }
+
+ /**
+ * Returns the maximum allowed obscuring opacity per UID to propagate touches.
+ *
+ * <p>For certain window types (e.g. {@link LayoutParams#TYPE_APPLICATION_OVERLAY}),
+ * the decision of honoring {@link LayoutParams#FLAG_NOT_TOUCHABLE} or not depends on
+ * the combined obscuring opacity of the windows above the touch-consuming window, per
+ * UID. Check documentation of {@link LayoutParams#FLAG_NOT_TOUCHABLE} for more details.
+ *
+ * <p>The value returned is between 0 (inclusive) and 1 (inclusive).
+ *
+ * @see LayoutParams#FLAG_NOT_TOUCHABLE
+ *
+ * @hide
+ */
+ @FloatRange(from = 0, to = 1)
+ public static float getMaximumObscuringOpacityForTouch(Context context) {
+ return Settings.Global.getFloat(context.getContentResolver(),
+ Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH,
+ DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH);
+ }
+
+ /**
+ * Sets the maximum allowed obscuring opacity by UID to propagate touches.
+ *
+ * <p>For certain window types (e.g. SAWs), the decision of honoring {@link LayoutParams
+ * #FLAG_NOT_TOUCHABLE} or not depends on the combined obscuring opacity of the windows
+ * above the touch-consuming window.
+ *
+ * <p>For a certain UID:
+ * <ul>
+ * <li>If it's the same as the UID of the touch-consuming window, allow it to propagate
+ * the touch.
+ * <li>Otherwise take all its windows of eligible window types above the touch-consuming
+ * window, compute their combined obscuring opacity considering that {@code
+ * opacity(A, B) = 1 - (1 - opacity(A))*(1 - opacity(B))}. If the computed value is
+ * less than or equal to this setting and there are no other windows preventing the
+ * touch, allow the UID to propagate the touch.
+ * </ul>
+ *
+ * <p>This value should be between 0 (inclusive) and 1 (inclusive).
+ *
+ * @see #getMaximumObscuringOpacityForTouch(Context)
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ public static void setMaximumObscuringOpacityForTouch(
+ @NonNull Context context,
+ @FloatRange(from = 0, to = 1) float opacity) {
+ if (opacity < 0 || opacity > 1) {
+ throw new IllegalArgumentException(
+ "Maximum obscuring opacity for touch should be >= 0 and <= 1");
+ }
+ Settings.Global.putFloat(context.getContentResolver(),
+ Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH, opacity);
+ }
+
+ /**
+ * Whether stylus has ever been used on device (false by default).
+ * @hide
+ */
+ public static boolean isStylusEverUsed(@NonNull Context context) {
+ return Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.STYLUS_EVER_USED, 0) == 1;
+ }
+
+ /**
+ * Set whether stylus has ever been used on device.
+ * Should only ever be set to true once after stylus first usage.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ public static void setStylusEverUsed(@NonNull Context context, boolean stylusEverUsed) {
+ Settings.Global.putInt(context.getContentResolver(),
+ Settings.Global.STYLUS_EVER_USED, stylusEverUsed ? 1 : 0);
+ }
+
+
+ /**
+ * Gets the touchpad pointer speed.
+ *
+ * The returned value only applies to gesture-compatible touchpads.
+ *
+ * @param context The application context.
+ * @return The pointer speed as a value between {@link #MIN_POINTER_SPEED} and
+ * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}.
+ *
+ * @hide
+ */
+ public static int getTouchpadPointerSpeed(@NonNull Context context) {
+ return Settings.System.getIntForUser(context.getContentResolver(),
+ Settings.System.TOUCHPAD_POINTER_SPEED, DEFAULT_POINTER_SPEED,
+ UserHandle.USER_CURRENT);
+ }
+
+ /**
+ * Sets the touchpad pointer speed, and saves it in the settings.
+ *
+ * The new speed will only apply to gesture-compatible touchpads.
+ *
+ * @param context The application context.
+ * @param speed The pointer speed as a value between {@link #MIN_POINTER_SPEED} and
+ * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
+ public static void setTouchpadPointerSpeed(@NonNull Context context, int speed) {
+ if (speed < MIN_POINTER_SPEED || speed > MAX_POINTER_SPEED) {
+ throw new IllegalArgumentException("speed out of range");
+ }
+
+ Settings.System.putIntForUser(context.getContentResolver(),
+ Settings.System.TOUCHPAD_POINTER_SPEED, speed, UserHandle.USER_CURRENT);
+ }
+
+ /**
+ * Returns true if moving two fingers upwards on the touchpad should
+ * scroll down, which is known as natural scrolling.
+ *
+ * The returned value only applies to gesture-compatible touchpads.
+ *
+ * @param context The application context.
+ * @return Whether the touchpad should use natural scrolling.
+ *
+ * @hide
+ */
+ public static boolean useTouchpadNaturalScrolling(@NonNull Context context) {
+ return Settings.System.getIntForUser(context.getContentResolver(),
+ Settings.System.TOUCHPAD_NATURAL_SCROLLING, 0, UserHandle.USER_CURRENT) == 1;
+ }
+
+ /**
+ * Sets the natural scroll behavior for the touchpad.
+ *
+ * If natural scrolling is enabled, moving two fingers upwards on the
+ * touchpad will scroll down.
+ *
+ * @param context The application context.
+ * @param enabled Will enable natural scroll if true, disable it if false
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
+ public static void setTouchpadNaturalScrolling(@NonNull Context context, boolean enabled) {
+ Settings.System.putIntForUser(context.getContentResolver(),
+ Settings.System.TOUCHPAD_NATURAL_SCROLLING, enabled ? 1 : 0,
+ UserHandle.USER_CURRENT);
+ }
+
+ /**
+ * Returns true if the touchpad should use tap to click.
+ *
+ * The returned value only applies to gesture-compatible touchpads.
+ *
+ * @param context The application context.
+ * @return Whether the touchpad should use tap to click.
+ *
+ * @hide
+ */
+ public static boolean useTouchpadTapToClick(@NonNull Context context) {
+ return Settings.System.getIntForUser(context.getContentResolver(),
+ Settings.System.TOUCHPAD_TAP_TO_CLICK, 0, UserHandle.USER_CURRENT) == 1;
+ }
+
+ /**
+ * Sets the tap to click behavior for the touchpad.
+ *
+ * The new behavior is only applied to gesture-compatible touchpads.
+ *
+ * @param context The application context.
+ * @param enabled Will enable tap to click if true, disable it if false
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
+ public static void setTouchpadTapToClick(@NonNull Context context, boolean enabled) {
+ Settings.System.putIntForUser(context.getContentResolver(),
+ Settings.System.TOUCHPAD_TAP_TO_CLICK, enabled ? 1 : 0,
+ UserHandle.USER_CURRENT);
+ }
+
+ /**
+ * Returns true if the touchpad should use the right click zone.
+ *
+ * The returned value only applies to gesture-compatible touchpads.
+ *
+ * @param context The application context.
+ * @return Whether the touchpad should use the right click zone.
+ *
+ * @hide
+ */
+ public static boolean useTouchpadRightClickZone(@NonNull Context context) {
+ return Settings.System.getIntForUser(context.getContentResolver(),
+ Settings.System.TOUCHPAD_RIGHT_CLICK_ZONE, 0, UserHandle.USER_CURRENT) == 1;
+ }
+
+ /**
+ * Sets the right click zone behavior for the touchpad.
+ *
+ * The new behavior is only applied to gesture-compatible touchpads.
+ *
+ * @param context The application context.
+ * @param enabled Will enable the right click zone if true, disable it if false
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
+ public static void setTouchpadRightClickZone(@NonNull Context context, boolean enabled) {
+ Settings.System.putIntForUser(context.getContentResolver(),
+ Settings.System.TOUCHPAD_RIGHT_CLICK_ZONE, enabled ? 1 : 0,
+ UserHandle.USER_CURRENT);
+ }
+}
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index c021cad..03c32d70 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -2982,6 +2982,12 @@
/**
* Signals that wake-on-lan/wake-on-wlan is allowed in Low Power Standby.
*
+ * <p>If Low Power Standby is enabled ({@link #isLowPowerStandbyEnabled()}),
+ * wake-on-lan/wake-on-wlan may not be available while in standby.
+ * Use {@link #isAllowedInLowPowerStandby(String)} to determine whether the device allows this
+ * feature to be used during Low Power Standby with the currently active Low Power Standby
+ * policy.
+ *
* @see #isAllowedInLowPowerStandby(String)
*/
public static final String LOW_POWER_STANDBY_FEATURE_WAKE_ON_LAN =
diff --git a/core/java/android/service/search/ISearchUiService.aidl b/core/java/android/service/search/ISearchUiService.aidl
index bc6d421..f59347f 100644
--- a/core/java/android/service/search/ISearchUiService.aidl
+++ b/core/java/android/service/search/ISearchUiService.aidl
@@ -39,8 +39,6 @@
void onRegisterEmptyQueryResultUpdateCallback (in SearchSessionId sessionId, in ISearchCallback callback);
- void onRequestEmptyQueryResultUpdate(in SearchSessionId sessionId);
-
void onUnregisterEmptyQueryResultUpdateCallback(in SearchSessionId sessionId, in ISearchCallback callback);
void onDestroy(in SearchSessionId sessionId);
diff --git a/core/java/android/service/search/SearchUiService.java b/core/java/android/service/search/SearchUiService.java
index 55a96fa..8d05b80 100644
--- a/core/java/android/service/search/SearchUiService.java
+++ b/core/java/android/service/search/SearchUiService.java
@@ -112,12 +112,6 @@
}
@Override
- public void onRequestEmptyQueryResultUpdate(SearchSessionId sessionId) {
- mHandler.sendMessage(obtainMessage(SearchUiService::doRequestEmptyQueryResultUpdate,
- SearchUiService.this, sessionId));
- }
-
- @Override
public void onUnregisterEmptyQueryResultUpdateCallback(SearchSessionId sessionId,
ISearchCallback callback) {
mHandler.sendMessage(
@@ -220,24 +214,6 @@
@MainThread
public void onStartUpdateEmptyQueryResult() {}
- private void doRequestEmptyQueryResultUpdate(@NonNull SearchSessionId sessionId) {
- // Just an optimization, if there are no callbacks, then don't bother notifying the service
- final ArrayList<CallbackWrapper> callbacks = mSessionEmptyQueryResultCallbacks.get(
- sessionId);
- if (callbacks != null && !callbacks.isEmpty()) {
- onRequestEmptyQueryResultUpdate(sessionId);
- }
- }
-
- /**
- * Called by a client to request empty query search target result for zero state. This method
- * is only called if there are one or more empty query result update callbacks registered.
- *
- * @see #updateEmptyQueryResult(SearchSessionId, List)
- */
- @MainThread
- public void onRequestEmptyQueryResultUpdate(@NonNull SearchSessionId sessionId) {}
-
private void doUnregisterEmptyQueryResultUpdateCallback(@NonNull SearchSessionId sessionId,
@NonNull ISearchCallback callback) {
final ArrayList<CallbackWrapper> callbacks = mSessionEmptyQueryResultCallbacks.get(
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index d0acedb..4a83bbe 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -1173,7 +1173,8 @@
*
* @param data The payload which includes frame information. Divide nanosecond values by
* {@code 1000000} to convert it to the {@link SystemClock#uptimeMillis()}
- * time base.
+ * time base. {@code data} is not valid outside of {@code onVsync} and should
+ * not be accessed outside the callback.
* @see FrameCallback#doFrame
**/
void onVsync(@NonNull FrameData data);
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index b4599c8..fc26766 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -334,8 +334,6 @@
"libtimeinstate",
"server_configurable_flags",
"libimage_io",
- "libjpegdecoder",
- "libjpegencoder",
"libjpegrecoverymap",
],
export_shared_lib_headers: [
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 111cfd8..f11836e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -650,7 +650,6 @@
}
mPipUiEventLoggerLogger.setTaskInfo(mTaskInfo);
- mPipUiEventLoggerLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_ENTER);
// If the displayId of the task is different than what PipBoundsHandler has, then update
// it. This is possible if we entered PiP on an external display.
@@ -659,6 +658,17 @@
mOnDisplayIdChangeCallback.accept(info.displayId);
}
+ // UiEvent logging.
+ final PipUiEventLogger.PipUiEventEnum uiEventEnum;
+ if (isLaunchIntoPipTask()) {
+ uiEventEnum = PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_ENTER_CONTENT_PIP;
+ } else if (mPipTransitionState.getInSwipePipToHomeTransition()) {
+ uiEventEnum = PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_AUTO_ENTER;
+ } else {
+ uiEventEnum = PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_ENTER;
+ }
+ mPipUiEventLoggerLogger.log(uiEventEnum);
+
if (mPipTransitionState.getInSwipePipToHomeTransition()) {
if (!mWaitForFixedRotation) {
onEndOfSwipePipToHomeTransition();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java
index 513ebba..3e5a19b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java
@@ -78,6 +78,12 @@
@UiEvent(doc = "Activity enters picture-in-picture mode")
PICTURE_IN_PICTURE_ENTER(603),
+ @UiEvent(doc = "Activity enters picture-in-picture mode with auto-enter-pip API")
+ PICTURE_IN_PICTURE_AUTO_ENTER(1313),
+
+ @UiEvent(doc = "Activity enters picture-in-picture mode from content-pip API")
+ PICTURE_IN_PICTURE_ENTER_CONTENT_PIP(1314),
+
@UiEvent(doc = "Expands from picture-in-picture to fullscreen")
PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN(604),
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 9e3f115..03d89cc 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -396,8 +396,6 @@
"libharfbuzz_ng",
"libimage_io",
"libjpeg",
- "libjpegdecoder",
- "libjpegencoder",
"libjpegrecoverymap",
"liblog",
"libminikin",
diff --git a/libs/hwui/hwui/ImageDecoder.cpp b/libs/hwui/hwui/ImageDecoder.cpp
index 5bf53d0..8266beb 100644
--- a/libs/hwui/hwui/ImageDecoder.cpp
+++ b/libs/hwui/hwui/ImageDecoder.cpp
@@ -17,13 +17,25 @@
#include "ImageDecoder.h"
#include <Gainmap.h>
+#include <SkAlphaType.h>
#include <SkAndroidCodec.h>
#include <SkBitmap.h>
#include <SkBlendMode.h>
#include <SkCanvas.h>
+#include <SkCodec.h>
+#include <SkCodecAnimation.h>
+#include <SkColorSpace.h>
+#include <SkColorType.h>
#include <SkEncodedOrigin.h>
+#include <SkImageInfo.h>
#include <SkGainmapInfo.h>
+#include <SkMatrix.h>
#include <SkPaint.h>
+#include <SkPngChunkReader.h>
+#include <SkRect.h>
+#include <SkRefCnt.h>
+#include <SkSamplingOptions.h>
+#include <SkSize.h>
#include <SkStream.h>
#include <hwui/Bitmap.h>
#include <log/log.h>
diff --git a/libs/hwui/jni/ImageDecoder.cpp b/libs/hwui/jni/ImageDecoder.cpp
index add62b1..fda7080 100644
--- a/libs/hwui/jni/ImageDecoder.cpp
+++ b/libs/hwui/jni/ImageDecoder.cpp
@@ -18,11 +18,16 @@
#include <FrontBufferedStream.h>
#include <HardwareBitmapUploader.h>
+#include <SkAlphaType.h>
#include <SkAndroidCodec.h>
#include <SkBitmap.h>
+#include <SkCodec.h>
+#include <SkCodecAnimation.h>
#include <SkColorSpace.h>
+#include <SkColorType.h>
#include <SkImageInfo.h>
#include <SkRect.h>
+#include <SkSize.h>
#include <SkStream.h>
#include <SkString.h>
#include <androidfw/Asset.h>
diff --git a/native/graphics/jni/imagedecoder.cpp b/native/graphics/jni/imagedecoder.cpp
index cd6ed23..e18b4a9 100644
--- a/native/graphics/jni/imagedecoder.cpp
+++ b/native/graphics/jni/imagedecoder.cpp
@@ -25,10 +25,14 @@
#include <hwui/ImageDecoder.h>
#include <log/log.h>
#include <SkAndroidCodec.h>
+#include <SkAlphaType.h>
#include <SkCodec.h>
+#include <SkCodecAnimation.h>
#include <SkColorSpace.h>
+#include <SkColorType.h>
#include <SkImageInfo.h>
#include <SkRect.h>
+#include <SkRefCnt.h>
#include <SkSize.h>
#include <SkStream.h>
#include <utils/Color.h>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 435a866..754433b 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -3029,4 +3029,22 @@
[CHAR LIMIT=32]
-->
<string name="lock_screen_settings">Lock screen settings</string>
+
+ <!-- Content description for Wi-Fi not available icon on dream [CHAR LIMIT=NONE]-->
+ <string name="wifi_unavailable_dream_overlay_content_description">Wi-Fi not available</string>
+
+ <!-- Content description for camera blocked icon on dream [CHAR LIMIT=NONE] -->
+ <string name="camera_blocked_dream_overlay_content_description">Camera blocked</string>
+
+ <!-- Content description for camera and microphone blocked icon on dream [CHAR LIMIT=NONE] -->
+ <string name="camera_and_microphone_blocked_dream_overlay_content_description">Camera and microphone blocked</string>
+
+ <!-- Content description for camera and microphone disabled icon on dream [CHAR LIMIT=NONE] -->
+ <string name="microphone_blocked_dream_overlay_content_description">Microphone blocked</string>
+
+ <!-- Content description for priority mode icon on dream [CHAR LIMIT=NONE] -->
+ <string name="priority_mode_dream_overlay_content_description">Priority mode on</string>
+
+ <!-- Content description for when assistant attention is active [CHAR LIMIT=NONE] -->
+ <string name="assistant_attention_content_description">Assistant attention on</string>
</resources>
diff --git a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
index 54ae84f9..ead1a10 100644
--- a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
@@ -303,9 +303,18 @@
pw.println(" requestActiveUnlockOnWakeup=$requestActiveUnlockOnWakeup")
pw.println(" requestActiveUnlockOnUnlockIntent=$requestActiveUnlockOnUnlockIntent")
pw.println(" requestActiveUnlockOnBioFail=$requestActiveUnlockOnBioFail")
- pw.println(" requestActiveUnlockOnUnlockIntentWhenBiometricEnrolled=${
- onUnlockIntentWhenBiometricEnrolled.map { BiometricType.values()[it] }
- }")
+
+ val onUnlockIntentWhenBiometricEnrolledString =
+ onUnlockIntentWhenBiometricEnrolled.map {
+ for (biometricType in BiometricType.values()) {
+ if (biometricType.intValue == it) {
+ return@map biometricType.name
+ }
+ }
+ return@map "UNKNOWN"
+ }
+ pw.println(" requestActiveUnlockOnUnlockIntentWhenBiometricEnrolled=" +
+ "$onUnlockIntentWhenBiometricEnrolledString")
pw.println(" requestActiveUnlockOnFaceError=$faceErrorsToTriggerBiometricFailOn")
pw.println(" requestActiveUnlockOnFaceAcquireInfo=" +
"$faceAcquireInfoToTriggerBiometricFailOn")
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index 27641fe..a14aa52 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -269,6 +269,8 @@
*/
private void addOverlayWindowLocked(WindowManager.LayoutParams layoutParams) {
mWindow = new PhoneWindow(mContext);
+ // Default to SystemUI name for TalkBack.
+ mWindow.setTitle("");
mWindow.setAttributes(layoutParams);
mWindow.setWindowManager(null, layoutParams.token, "DreamOverlay", true);
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
index 2221a04..74a49a8 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
@@ -259,7 +259,8 @@
mConnectivityManager.getActiveNetwork());
final boolean available = capabilities != null
&& capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI);
- showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, !available);
+ showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, !available,
+ R.string.wifi_unavailable_dream_overlay_content_description);
}
private void updateAlarmStatusIcon() {
@@ -274,7 +275,8 @@
private void updateAssistantAttentionIcon() {
showIcon(DreamOverlayStatusBarView.STATUS_ICON_ASSISTANT_ATTENTION_ACTIVE,
- mDreamOverlayStateController.hasAssistantAttention());
+ mDreamOverlayStateController.hasAssistantAttention(),
+ R.string.assistant_attention_content_description);
}
private void updateVisibility() {
@@ -304,13 +306,16 @@
@DreamOverlayStatusBarView.StatusIconType int iconType = Resources.ID_NULL;
showIcon(
DreamOverlayStatusBarView.STATUS_ICON_CAMERA_DISABLED,
- !micBlocked && cameraBlocked);
+ !micBlocked && cameraBlocked,
+ R.string.camera_blocked_dream_overlay_content_description);
showIcon(
DreamOverlayStatusBarView.STATUS_ICON_MIC_DISABLED,
- micBlocked && !cameraBlocked);
+ micBlocked && !cameraBlocked,
+ R.string.microphone_blocked_dream_overlay_content_description);
showIcon(
DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED,
- micBlocked && cameraBlocked);
+ micBlocked && cameraBlocked,
+ R.string.camera_and_microphone_blocked_dream_overlay_content_description);
}
private String buildNotificationsContentDescription(int notificationCount) {
@@ -323,11 +328,13 @@
private void updatePriorityModeStatusIcon() {
showIcon(
DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON,
- mZenModeController.getZen() != Settings.Global.ZEN_MODE_OFF);
+ mZenModeController.getZen() != Settings.Global.ZEN_MODE_OFF,
+ R.string.priority_mode_dream_overlay_content_description);
}
- private void showIcon(@DreamOverlayStatusBarView.StatusIconType int iconType, boolean show) {
- showIcon(iconType, show, null);
+ private void showIcon(@DreamOverlayStatusBarView.StatusIconType int iconType, boolean show,
+ int contentDescriptionResId) {
+ showIcon(iconType, show, mResources.getString(contentDescriptionResId));
}
private void showIcon(
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index b75b6d8..85e050c 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -566,6 +566,12 @@
val CONTROLS_MANAGEMENT_NEW_FLOWS =
releasedFlag(2002, "controls_management_new_flows", teamfood = true)
+ // Enables removing app from Home control panel as a part of a new flow
+ // TODO(b/269132640): Tracking Bug
+ @JvmField
+ val APP_PANELS_REMOVE_APPS_ALLOWED =
+ unreleasedFlag(2003, "app_panels_remove_apps_allowed", teamfood = false)
+
// 2100 - Falsing Manager
@JvmField val FALSING_FOR_LONG_TAPS = releasedFlag(2100, "falsing_for_long_taps")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
index 5a9f775..c9f645d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
@@ -18,6 +18,7 @@
package com.android.systemui.keyguard.data.quickaffordance
import android.app.StatusBarManager
+import android.app.admin.DevicePolicyManager
import android.content.Context
import android.content.pm.PackageManager
import com.android.systemui.R
@@ -27,10 +28,14 @@
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.settings.UserTracker
import dagger.Lazy
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.withContext
@SysUISingleton
class CameraQuickAffordanceConfig
@@ -39,6 +44,9 @@
@Application private val context: Context,
private val packageManager: PackageManager,
private val cameraGestureHelper: Lazy<CameraGestureHelper>,
+ private val userTracker: UserTracker,
+ private val devicePolicyManager: DevicePolicyManager,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
) : KeyguardQuickAffordanceConfig {
override val key: String
@@ -79,7 +87,12 @@
return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
}
- private fun isLaunchable(): Boolean {
- return packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)
+ private suspend fun isLaunchable(): Boolean {
+ return packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY) &&
+ withContext(backgroundDispatcher) {
+ !devicePolicyManager.getCameraDisabled(null, userTracker.userId) &&
+ devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId) and
+ DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA == 0
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfig.kt
index d9ec3b1..6f821a2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfig.kt
@@ -18,6 +18,7 @@
package com.android.systemui.keyguard.data.quickaffordance
import android.app.StatusBarManager
+import android.app.admin.DevicePolicyManager
import android.content.Context
import android.content.Intent
import com.android.systemui.ActivityIntentHelper
@@ -29,10 +30,13 @@
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.settings.UserTracker
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.withContext
@SysUISingleton
class VideoCameraQuickAffordanceConfig
@@ -42,6 +46,8 @@
private val cameraIntents: CameraIntentsWrapper,
private val activityIntentHelper: ActivityIntentHelper,
private val userTracker: UserTracker,
+ private val devicePolicyManager: DevicePolicyManager,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
) : KeyguardQuickAffordanceConfig {
private val intent: Intent by lazy {
@@ -63,8 +69,8 @@
get() = R.drawable.ic_videocam
override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState>
- get() =
- flowOf(
+ get() = flow {
+ emit(
if (isLaunchable()) {
KeyguardQuickAffordanceConfig.LockScreenState.Visible(
icon =
@@ -77,6 +83,7 @@
KeyguardQuickAffordanceConfig.LockScreenState.Hidden
}
)
+ }
override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState {
return if (isLaunchable()) {
@@ -95,11 +102,14 @@
)
}
- private fun isLaunchable(): Boolean {
+ private suspend fun isLaunchable(): Boolean {
return activityIntentHelper.getTargetActivityInfo(
intent,
userTracker.userId,
true,
- ) != null
+ ) != null &&
+ withContext(backgroundDispatcher) {
+ !devicePolicyManager.getCameraDisabled(null, userTracker.userId)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
index b72923a..68d2c5c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
@@ -164,13 +164,13 @@
mediaCarouselScrollHandler.scrollToStart()
}
}
- private var currentlyExpanded = true
+
+ @VisibleForTesting
+ var currentlyExpanded = true
set(value) {
if (field != value) {
field = value
- for (player in MediaPlayerData.players()) {
- player.setListening(field)
- }
+ updateSeekbarListening(mediaCarouselScrollHandler.visibleToUser)
}
}
@@ -259,6 +259,7 @@
executor,
this::onSwipeToDismiss,
this::updatePageIndicatorLocation,
+ this::updateSeekbarListening,
this::closeGuts,
falsingCollector,
falsingManager,
@@ -590,6 +591,17 @@
?: mediaCarouselScrollHandler.scrollToPlayer(destIndex = mediaIndex)
}
}
+ // Check postcondition: mediaContent should have the same number of children as there
+ // are
+ // elements in mediaPlayers.
+ if (MediaPlayerData.players().size != mediaContent.childCount) {
+ Log.e(
+ TAG,
+ "Size of players list and number of views in carousel are out of sync. " +
+ "Players size is ${MediaPlayerData.players().size}. " +
+ "View count is ${mediaContent.childCount}."
+ )
+ }
}
// Returns true if new player is added
@@ -618,7 +630,9 @@
)
newPlayer.mediaViewHolder?.player?.setLayoutParams(lp)
newPlayer.bindPlayer(data, key)
- newPlayer.setListening(currentlyExpanded)
+ newPlayer.setListening(
+ mediaCarouselScrollHandler.visibleToUser && currentlyExpanded
+ )
MediaPlayerData.addMediaPlayer(
key,
data,
@@ -665,17 +679,6 @@
updatePageIndicator()
mediaCarouselScrollHandler.onPlayersChanged()
mediaFrame.requiresRemeasuring = true
- // Check postcondition: mediaContent should have the same number of children as there
- // are
- // elements in mediaPlayers.
- if (MediaPlayerData.players().size != mediaContent.childCount) {
- Log.e(
- TAG,
- "Size of players list and number of views in carousel are out of sync. " +
- "Players size is ${MediaPlayerData.players().size}. " +
- "View count is ${mediaContent.childCount}."
- )
- }
return existingPlayer == null
}
@@ -914,6 +917,13 @@
.toFloat()
}
+ /** Update listening to seekbar. */
+ private fun updateSeekbarListening(visibleToUser: Boolean) {
+ for (player in MediaPlayerData.players()) {
+ player.setListening(visibleToUser && currentlyExpanded)
+ }
+ }
+
/** Update the dimension of this carousel. */
private fun updateCarouselDimensions() {
var width = 0
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt
index 36b2eda..1ace316 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt
@@ -57,6 +57,7 @@
private val mainExecutor: DelayableExecutor,
val dismissCallback: () -> Unit,
private var translationChangedListener: () -> Unit,
+ private var seekBarUpdateListener: (visibleToUser: Boolean) -> Unit,
private val closeGuts: (immediate: Boolean) -> Unit,
private val falsingCollector: FalsingCollector,
private val falsingManager: FalsingManager,
@@ -177,6 +178,12 @@
/** Whether the media card is visible to user if any */
var visibleToUser: Boolean = false
+ set(value) {
+ if (field != value) {
+ field = value
+ seekBarUpdateListener.invoke(field)
+ }
+ }
/** Whether the quick setting is expanded or not */
var qsExpanded: Boolean = false
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
index 6076e58..288266a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
@@ -347,6 +347,11 @@
mSeekBarViewModel.setListening(listening);
}
+ @VisibleForTesting
+ public boolean getListening() {
+ return mSeekBarViewModel.getListening();
+ }
+
/** Sets whether the user is touching the seek bar to change the track position. */
private void setIsScrubbing(boolean isScrubbing) {
if (mMediaData == null || mMediaData.getSemanticActions() == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
index 7a62bae..fc94aed 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
@@ -93,7 +93,13 @@
@UiEvent(doc = "User has discarded the result of a long screenshot")
SCREENSHOT_LONG_SCREENSHOT_EXIT(911),
@UiEvent(doc = "A screenshot has been taken and saved to work profile")
- SCREENSHOT_SAVED_TO_WORK_PROFILE(1240);
+ SCREENSHOT_SAVED_TO_WORK_PROFILE(1240),
+ @UiEvent(doc = "Notes application triggered the screenshot for notes")
+ SCREENSHOT_FOR_NOTE_TRIGGERED(1308),
+ @UiEvent(doc = "User accepted the screenshot to be sent to the notes app")
+ SCREENSHOT_FOR_NOTE_ACCEPTED(1309),
+ @UiEvent(doc = "User cancelled the screenshot for notes app flow")
+ SCREENSHOT_FOR_NOTE_CANCELLED(1310);
private final int mId;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java
index f8d86a0..3133924 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java
@@ -17,15 +17,21 @@
package com.android.systemui.screenshot;
import static com.android.systemui.screenshot.AppClipsTrampolineActivity.ACTION_FINISH_FROM_TRAMPOLINE;
+import static com.android.systemui.screenshot.AppClipsTrampolineActivity.EXTRA_CALLING_PACKAGE_NAME;
import static com.android.systemui.screenshot.AppClipsTrampolineActivity.EXTRA_RESULT_RECEIVER;
import static com.android.systemui.screenshot.AppClipsTrampolineActivity.EXTRA_SCREENSHOT_URI;
import static com.android.systemui.screenshot.AppClipsTrampolineActivity.PERMISSION_SELF;
+import static com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_FOR_NOTE_ACCEPTED;
+import static com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_FOR_NOTE_CANCELLED;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.ApplicationInfoFlags;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
@@ -33,15 +39,20 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.ResultReceiver;
+import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import androidx.activity.ComponentActivity;
+import androidx.annotation.Nullable;
import androidx.lifecycle.ViewModelProvider;
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.UiEventLogger.UiEventEnum;
import com.android.settingslib.Utils;
import com.android.systemui.R;
+import com.android.systemui.settings.UserTracker;
import javax.inject.Inject;
@@ -64,9 +75,15 @@
*
* TODO(b/267309532): Polish UI and animations.
*/
-public final class AppClipsActivity extends ComponentActivity {
+public class AppClipsActivity extends ComponentActivity {
+
+ private static final String TAG = AppClipsActivity.class.getSimpleName();
+ private static final ApplicationInfoFlags APPLICATION_INFO_FLAGS = ApplicationInfoFlags.of(0);
private final AppClipsViewModel.Factory mViewModelFactory;
+ private final PackageManager mPackageManager;
+ private final UserTracker mUserTracker;
+ private final UiEventLogger mUiEventLogger;
private final BroadcastReceiver mBroadcastReceiver;
private final IntentFilter mIntentFilter;
@@ -80,10 +97,17 @@
private AppClipsViewModel mViewModel;
private ResultReceiver mResultReceiver;
+ @Nullable
+ private String mCallingPackageName;
+ private int mCallingPackageUid;
@Inject
- public AppClipsActivity(AppClipsViewModel.Factory viewModelFactory) {
+ public AppClipsActivity(AppClipsViewModel.Factory viewModelFactory,
+ PackageManager packageManager, UserTracker userTracker, UiEventLogger uiEventLogger) {
mViewModelFactory = viewModelFactory;
+ mPackageManager = packageManager;
+ mUserTracker = userTracker;
+ mUiEventLogger = uiEventLogger;
mBroadcastReceiver = new BroadcastReceiver() {
@Override
@@ -113,6 +137,7 @@
RECEIVER_NOT_EXPORTED);
Intent intent = getIntent();
+ setUpUiLogging(intent);
mResultReceiver = intent.getParcelableExtra(EXTRA_RESULT_RECEIVER, ResultReceiver.class);
if (mResultReceiver == null) {
setErrorThenFinish(Intent.CAPTURE_CONTENT_FOR_NOTE_FAILED);
@@ -169,6 +194,17 @@
}
}
+ private void setUpUiLogging(Intent intent) {
+ mCallingPackageName = intent.getStringExtra(EXTRA_CALLING_PACKAGE_NAME);
+ mCallingPackageUid = 0;
+ try {
+ mCallingPackageUid = mPackageManager.getApplicationInfoAsUser(mCallingPackageName,
+ APPLICATION_INFO_FLAGS, mUserTracker.getUserId()).uid;
+ } catch (NameNotFoundException e) {
+ Log.d(TAG, "Couldn't find notes app UID " + e);
+ }
+ }
+
private void setScreenshot(Bitmap screenshot) {
// Set background, status and navigation bar colors as the activity is no longer
// translucent.
@@ -228,6 +264,7 @@
data.putParcelable(EXTRA_SCREENSHOT_URI, uri);
try {
mResultReceiver.send(Activity.RESULT_OK, data);
+ logUiEvent(SCREENSHOT_FOR_NOTE_ACCEPTED);
} catch (Exception e) {
// Do nothing.
}
@@ -251,6 +288,9 @@
data.putInt(Intent.EXTRA_CAPTURE_CONTENT_FOR_NOTE_STATUS_CODE, errorCode);
try {
mResultReceiver.send(RESULT_OK, data);
+ if (errorCode == Intent.CAPTURE_CONTENT_FOR_NOTE_USER_CANCELED) {
+ logUiEvent(SCREENSHOT_FOR_NOTE_CANCELLED);
+ }
} catch (Exception e) {
// Do nothing.
}
@@ -259,6 +299,10 @@
mResultReceiver = null;
}
+ private void logUiEvent(UiEventEnum uiEvent) {
+ mUiEventLogger.log(uiEvent, mCallingPackageUid, mCallingPackageName);
+ }
+
private void updateImageDimensions() {
Drawable drawable = mPreview.getDrawable();
if (drawable == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivity.java
index 4759cc6..1946b8e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivity.java
@@ -24,26 +24,33 @@
import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
import static com.android.systemui.flags.Flags.SCREENSHOT_APP_CLIPS;
+import static com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_FOR_NOTE_TRIGGERED;
import android.app.Activity;
import android.app.admin.DevicePolicyManager;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.ApplicationInfoFlags;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Parcel;
import android.os.ResultReceiver;
+import android.util.Log;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.notetask.NoteTaskController;
+import com.android.systemui.settings.UserTracker;
import com.android.wm.shell.bubbles.Bubbles;
import java.util.Optional;
@@ -70,15 +77,23 @@
public class AppClipsTrampolineActivity extends Activity {
private static final String TAG = AppClipsTrampolineActivity.class.getSimpleName();
- public static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
+ static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
+ @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
public static final String EXTRA_SCREENSHOT_URI = TAG + "SCREENSHOT_URI";
- public static final String ACTION_FINISH_FROM_TRAMPOLINE = TAG + "FINISH_FROM_TRAMPOLINE";
- static final String EXTRA_RESULT_RECEIVER = TAG + "RESULT_RECEIVER";
+ static final String ACTION_FINISH_FROM_TRAMPOLINE = TAG + "FINISH_FROM_TRAMPOLINE";
+ @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+ public static final String EXTRA_RESULT_RECEIVER = TAG + "RESULT_RECEIVER";
+ @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+ public static final String EXTRA_CALLING_PACKAGE_NAME = TAG + "CALLING_PACKAGE_NAME";
+ private static final ApplicationInfoFlags APPLICATION_INFO_FLAGS = ApplicationInfoFlags.of(0);
private final DevicePolicyManager mDevicePolicyManager;
private final FeatureFlags mFeatureFlags;
private final Optional<Bubbles> mOptionalBubbles;
private final NoteTaskController mNoteTaskController;
+ private final PackageManager mPackageManager;
+ private final UserTracker mUserTracker;
+ private final UiEventLogger mUiEventLogger;
private final ResultReceiver mResultReceiver;
private Intent mKillAppClipsBroadcastIntent;
@@ -86,11 +101,15 @@
@Inject
public AppClipsTrampolineActivity(DevicePolicyManager devicePolicyManager, FeatureFlags flags,
Optional<Bubbles> optionalBubbles, NoteTaskController noteTaskController,
+ PackageManager packageManager, UserTracker userTracker, UiEventLogger uiEventLogger,
@Main Handler mainHandler) {
mDevicePolicyManager = devicePolicyManager;
mFeatureFlags = flags;
mOptionalBubbles = optionalBubbles;
mNoteTaskController = noteTaskController;
+ mPackageManager = packageManager;
+ mUserTracker = userTracker;
+ mUiEventLogger = uiEventLogger;
mResultReceiver = createResultReceiver(mainHandler);
}
@@ -138,8 +157,12 @@
return;
}
- Intent intent = new Intent().setComponent(componentName).addFlags(
- Intent.FLAG_ACTIVITY_NEW_TASK).putExtra(EXTRA_RESULT_RECEIVER, mResultReceiver);
+ String callingPackageName = getCallingPackage();
+ Intent intent = new Intent().setComponent(componentName)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ .putExtra(EXTRA_RESULT_RECEIVER, mResultReceiver)
+ .putExtra(EXTRA_CALLING_PACKAGE_NAME, callingPackageName);
+
try {
// Start the App Clips activity.
startActivity(intent);
@@ -150,6 +173,9 @@
new Intent(ACTION_FINISH_FROM_TRAMPOLINE)
.setComponent(componentName)
.setPackage(componentName.getPackageName());
+
+ // Log successful triggering of screenshot for notes.
+ logScreenshotTriggeredUiEvent(callingPackageName);
} catch (ActivityNotFoundException e) {
setErrorResultAndFinish(CAPTURE_CONTENT_FOR_NOTE_FAILED);
}
@@ -170,6 +196,18 @@
finish();
}
+ private void logScreenshotTriggeredUiEvent(@Nullable String callingPackageName) {
+ int callingPackageUid = 0;
+ try {
+ callingPackageUid = mPackageManager.getApplicationInfoAsUser(callingPackageName,
+ APPLICATION_INFO_FLAGS, mUserTracker.getUserId()).uid;
+ } catch (NameNotFoundException e) {
+ Log.d(TAG, "Couldn't find notes app UID " + e);
+ }
+
+ mUiEventLogger.log(SCREENSHOT_FOR_NOTE_TRIGGERED, callingPackageUid, callingPackageName);
+ }
+
private class AppClipsResultReceiver extends ResultReceiver {
AppClipsResultReceiver(Handler handler) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java
index 5a7b5f9..b2910fd 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java
@@ -18,6 +18,8 @@
import static android.content.Intent.CAPTURE_CONTENT_FOR_NOTE_FAILED;
+import static androidx.annotation.VisibleForTesting.PACKAGE_PRIVATE;
+
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.HardwareRenderer;
@@ -29,6 +31,7 @@
import android.os.Process;
import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
@@ -49,7 +52,8 @@
import javax.inject.Inject;
/** A {@link ViewModel} to help with the App Clips screenshot flow. */
-final class AppClipsViewModel extends ViewModel {
+@VisibleForTesting(otherwise = PACKAGE_PRIVATE)
+public final class AppClipsViewModel extends ViewModel {
private final AppClipsCrossProcessHelper mAppClipsCrossProcessHelper;
private final ImageExporter mImageExporter;
@@ -76,7 +80,8 @@
}
/** Grabs a screenshot and updates the {@link Bitmap} set in screenshot {@link LiveData}. */
- void performScreenshot() {
+ @VisibleForTesting(otherwise = PACKAGE_PRIVATE)
+ public void performScreenshot() {
mBgExecutor.execute(() -> {
Bitmap screenshot = mAppClipsCrossProcessHelper.takeScreenshot();
mMainExecutor.execute(() -> {
@@ -90,12 +95,14 @@
}
/** Returns a {@link LiveData} that holds the captured screenshot. */
- LiveData<Bitmap> getScreenshot() {
+ @VisibleForTesting(otherwise = PACKAGE_PRIVATE)
+ public LiveData<Bitmap> getScreenshot() {
return mScreenshotLiveData;
}
/** Returns a {@link LiveData} that holds the {@link Uri} where screenshot is saved. */
- LiveData<Uri> getResultLiveData() {
+ @VisibleForTesting(otherwise = PACKAGE_PRIVATE)
+ public LiveData<Uri> getResultLiveData() {
return mResultLiveData;
}
@@ -103,7 +110,8 @@
* Returns a {@link LiveData} that holds the error codes for
* {@link Intent#EXTRA_CAPTURE_CONTENT_FOR_NOTE_STATUS_CODE}.
*/
- LiveData<Integer> getErrorLiveData() {
+ @VisibleForTesting(otherwise = PACKAGE_PRIVATE)
+ public LiveData<Integer> getErrorLiveData() {
return mErrorLiveData;
}
@@ -111,7 +119,8 @@
* Saves the provided {@link Drawable} to storage then informs the result {@link Uri} to
* {@link LiveData}.
*/
- void saveScreenshotThenFinish(Drawable screenshotDrawable, Rect bounds) {
+ @VisibleForTesting(otherwise = PACKAGE_PRIVATE)
+ public void saveScreenshotThenFinish(Drawable screenshotDrawable, Rect bounds) {
mBgExecutor.execute(() -> {
// Render the screenshot bitmap in background.
Bitmap screenshotBitmap = renderBitmap(screenshotDrawable, bounds);
@@ -151,7 +160,8 @@
}
/** Helper factory to help with injecting {@link AppClipsViewModel}. */
- static final class Factory implements ViewModelProvider.Factory {
+ @VisibleForTesting(otherwise = PACKAGE_PRIVATE)
+ public static final class Factory implements ViewModelProvider.Factory {
private final AppClipsCrossProcessHelper mAppClipsCrossProcessHelper;
private final ImageExporter mImageExporter;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SubscriptionModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SubscriptionModel.kt
index 2f34516..16c4027 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SubscriptionModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SubscriptionModel.kt
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.pipeline.mobile.data.model
+import android.os.ParcelUuid
+
/**
* SystemUI representation of [SubscriptionInfo]. Currently we only use two fields on the
* subscriptions themselves: subscriptionId and isOpportunistic. Any new fields that we need can be
@@ -29,4 +31,7 @@
* filtering in certain cases. See [MobileIconsInteractor] for the filtering logic
*/
val isOpportunistic: Boolean = false,
+
+ /** Subscriptions in the same group may be filtered or treated as a single subscription */
+ val groupUuid: ParcelUuid? = null,
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
index c9049d8..e77266f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
@@ -376,6 +376,7 @@
SubscriptionModel(
subscriptionId = subscriptionId,
isOpportunistic = isOpportunistic,
+ groupUuid = groupUuid,
)
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
index 72d5113..5a2e11e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
@@ -150,6 +150,12 @@
val info1 = unfilteredSubs[0]
val info2 = unfilteredSubs[1]
+
+ // Filtering only applies to subscriptions in the same group
+ if (info1.groupUuid == null || info1.groupUuid != info2.groupUuid) {
+ return@combine unfilteredSubs
+ }
+
// If both subscriptions are primary, show both
if (!info1.isOpportunistic && !info2.isOpportunistic) {
return@combine unfilteredSubs
@@ -186,7 +192,7 @@
* validated bit from the old active network (A) while data is changing to the new one (B).
*
* This condition only applies if
- * 1. A and B are in the same subscription group (e.c. for CBRS data switching) and
+ * 1. A and B are in the same subscription group (e.g. for CBRS data switching) and
* 2. A was validated before the switch
*
* The goal of this is to minimize the flickering in the UI of the cellular indicator
diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt
index dee4a6f..c0cbd62 100644
--- a/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt
@@ -21,6 +21,7 @@
import android.content.Context
import android.hardware.BatteryState
import android.hardware.input.InputManager
+import android.hardware.input.InputSettings
import android.os.Handler
import android.util.ArrayMap
import android.util.Log
@@ -235,9 +236,9 @@
*/
private fun onStylusUsed() {
if (!featureFlags.isEnabled(Flags.TRACK_STYLUS_EVER_USED)) return
- if (inputManager.isStylusEverUsed(context)) return
+ if (InputSettings.isStylusEverUsed(context)) return
- inputManager.setStylusEverUsed(context, true)
+ InputSettings.setStylusEverUsed(context, true)
executeStylusCallbacks { cb -> cb.onStylusFirstUsed() }
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/condition/ConditionalCoreStartable.java b/packages/SystemUI/src/com/android/systemui/util/condition/ConditionalCoreStartable.java
index b41bca0..8d32a48 100644
--- a/packages/SystemUI/src/com/android/systemui/util/condition/ConditionalCoreStartable.java
+++ b/packages/SystemUI/src/com/android/systemui/util/condition/ConditionalCoreStartable.java
@@ -43,11 +43,6 @@
@Override
public final void start() {
- if (mConditionSet == null || mConditionSet.isEmpty()) {
- onStart();
- return;
- }
-
mStartToken = mMonitor.addSubscription(
new Monitor.Subscription.Builder(allConditionsMet -> {
if (allConditionsMet) {
@@ -63,11 +58,6 @@
@Override
public final void onBootCompleted() {
- if (mConditionSet == null || mConditionSet.isEmpty()) {
- bootCompleted();
- return;
- }
-
mBootCompletedToken = mMonitor.addSubscription(
new Monitor.Subscription.Builder(allConditionsMet -> {
if (allConditionsMet) {
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index ed2772a..2ef3511 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -165,6 +165,12 @@
android:exported="false"
android:permission="com.android.systemui.permission.SELF"
android:excludeFromRecents="true" />
+
+ <activity
+ android:name="com.android.systemui.screenshot.appclips.AppClipsActivityTest$AppClipsActivityTestable"
+ android:exported="false"
+ android:permission="com.android.systemui.permission.SELF"
+ android:excludeFromRecents="true" />
</application>
<instrumentation android:name="android.testing.TestableInstrumentation"
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
index e8d50ca..badeb27 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
@@ -24,13 +24,19 @@
import android.os.PowerManager
import android.os.PowerManager.WAKE_REASON_BIOMETRIC
import android.os.UserHandle
-import android.provider.Settings
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_WAKE
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.settings.SecureSettings
+import com.android.systemui.util.settings.FakeSettings
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
@@ -41,20 +47,11 @@
import org.mockito.Mockito.`when`
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import java.io.PrintWriter
@SmallTest
class ActiveUnlockConfigTest : SysuiTestCase() {
- private val fakeWakeUri = Uri.Builder().appendPath("wake").build()
- private val fakeUnlockIntentUri = Uri.Builder().appendPath("unlock-intent").build()
- private val fakeBioFailUri = Uri.Builder().appendPath("bio-fail").build()
- private val fakeFaceErrorsUri = Uri.Builder().appendPath("face-errors").build()
- private val fakeFaceAcquiredUri = Uri.Builder().appendPath("face-acquired").build()
- private val fakeUnlockIntentBioEnroll = Uri.Builder().appendPath("unlock-intent-bio").build()
- private val fakeWakeupsConsideredUnlockIntents =
- Uri.Builder().appendPath("wakeups-considered-unlock-intent").build()
-
- @Mock
- private lateinit var secureSettings: SecureSettings
+ private lateinit var secureSettings: FakeSettings
@Mock
private lateinit var contentResolver: ContentResolver
@Mock
@@ -63,33 +60,20 @@
private lateinit var dumpManager: DumpManager
@Mock
private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+ @Mock private lateinit var mockPrintWriter: PrintWriter
@Captor
private lateinit var settingsObserverCaptor: ArgumentCaptor<ContentObserver>
private lateinit var activeUnlockConfig: ActiveUnlockConfig
+ private var currentUser: Int = 0
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- `when`(secureSettings.getUriFor(Settings.Secure.ACTIVE_UNLOCK_ON_WAKE))
- .thenReturn(fakeWakeUri)
- `when`(secureSettings.getUriFor(Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT))
- .thenReturn(fakeUnlockIntentUri)
- `when`(secureSettings.getUriFor(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
- .thenReturn(fakeBioFailUri)
- `when`(secureSettings.getUriFor(Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS))
- .thenReturn(fakeFaceErrorsUri)
- `when`(secureSettings.getUriFor(Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO))
- .thenReturn(fakeFaceAcquiredUri)
- `when`(secureSettings.getUriFor(
- Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED))
- .thenReturn(fakeUnlockIntentBioEnroll)
- `when`(secureSettings.getUriFor(
- Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS))
- .thenReturn(fakeWakeupsConsideredUnlockIntents)
-
+ currentUser = KeyguardUpdateMonitor.getCurrentUser()
+ secureSettings = FakeSettings()
activeUnlockConfig = ActiveUnlockConfig(
handler,
secureSettings,
@@ -105,8 +89,6 @@
@Test
fun onWakeupSettingChanged() {
- verifyRegisterSettingObserver()
-
// GIVEN no active unlock settings enabled
assertFalse(
activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -114,9 +96,8 @@
)
// WHEN unlock on wake is allowed
- `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_WAKE,
- 0, 0)).thenReturn(1)
- updateSetting(fakeWakeUri)
+ secureSettings.putIntForUser(ACTIVE_UNLOCK_ON_WAKE, 1, currentUser)
+ updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_WAKE))
// THEN active unlock triggers allowed on: wake, unlock-intent, and biometric failure
assertTrue(
@@ -135,8 +116,6 @@
@Test
fun onUnlockIntentSettingChanged() {
- verifyRegisterSettingObserver()
-
// GIVEN no active unlock settings enabled
assertFalse(
activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -144,9 +123,8 @@
)
// WHEN unlock on biometric failed is allowed
- `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT,
- 0, 0)).thenReturn(1)
- updateSetting(fakeUnlockIntentUri)
+ secureSettings.putIntForUser(ACTIVE_UNLOCK_ON_UNLOCK_INTENT, 1, currentUser)
+ updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT))
// THEN active unlock triggers allowed on: biometric failure ONLY
assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -159,21 +137,19 @@
@Test
fun onBioFailSettingChanged() {
- verifyRegisterSettingObserver()
-
// GIVEN no active unlock settings enabled and triggering unlock intent on biometric
// enrollment setting is disabled (empty string is disabled, null would use the default)
- `when`(secureSettings.getStringForUser(
- Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
- 0)).thenReturn("")
- updateSetting(fakeUnlockIntentBioEnroll)
+ secureSettings.putStringForUser(
+ ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED, "", currentUser)
+ updateSetting(secureSettings.getUriFor(
+ ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
+ ))
assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL))
// WHEN unlock on biometric failed is allowed
- `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
- 0, 0)).thenReturn(1)
- updateSetting(fakeBioFailUri)
+ secureSettings.putIntForUser(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, 1, currentUser)
+ updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
// THEN active unlock triggers allowed on: biometric failure ONLY
assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -186,17 +162,14 @@
@Test
fun faceErrorSettingsChanged() {
- verifyRegisterSettingObserver()
-
// GIVEN unlock on biometric fail
- `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
- 0, 0)).thenReturn(1)
- updateSetting(fakeBioFailUri)
+ secureSettings.putIntForUser(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, 1, currentUser)
+ updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
// WHEN face error timeout (3), allow trigger active unlock
- `when`(secureSettings.getStringForUser(Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS,
- 0)).thenReturn("3")
- updateSetting(fakeFaceAcquiredUri)
+ secureSettings.putStringForUser(
+ ACTIVE_UNLOCK_ON_FACE_ERRORS, "3", currentUser)
+ updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ERRORS))
// THEN active unlock triggers allowed on error TIMEOUT
assertTrue(activeUnlockConfig.shouldRequestActiveUnlockOnFaceError(
@@ -208,19 +181,17 @@
@Test
fun faceAcquiredSettingsChanged() {
- verifyRegisterSettingObserver()
-
// GIVEN unlock on biometric fail
- `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
- 0, 0)).thenReturn(1)
- updateSetting(fakeBioFailUri)
+ secureSettings.putStringForUser(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, "1", currentUser)
+ updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
// WHEN face acquiredMsg DARK_GLASSESand MOUTH_COVERING are allowed to trigger
- `when`(secureSettings.getStringForUser(Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO,
- 0)).thenReturn(
+ secureSettings.putStringForUser(
+ ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO,
"${BiometricFaceConstants.FACE_ACQUIRED_MOUTH_COVERING_DETECTED}" +
- "|${BiometricFaceConstants.FACE_ACQUIRED_DARK_GLASSES_DETECTED}")
- updateSetting(fakeFaceAcquiredUri)
+ "|${BiometricFaceConstants.FACE_ACQUIRED_DARK_GLASSES_DETECTED}",
+ currentUser)
+ updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO))
// THEN active unlock triggers allowed on acquired messages DARK_GLASSES & MOUTH_COVERING
assertTrue(activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo(
@@ -236,23 +207,23 @@
@Test
fun triggerOnUnlockIntentWhenBiometricEnrolledNone() {
- verifyRegisterSettingObserver()
-
// GIVEN unlock on biometric fail
- `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
- 0, 0)).thenReturn(1)
- updateSetting(fakeBioFailUri)
+ secureSettings.putIntForUser(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, 1, currentUser)
+ updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
// GIVEN fingerprint and face are NOT enrolled
activeUnlockConfig.keyguardUpdateMonitor = keyguardUpdateMonitor
- `when`(keyguardUpdateMonitor.isFaceEnrolled()).thenReturn(false)
+ `when`(keyguardUpdateMonitor.isFaceEnrolled).thenReturn(false)
`when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(false)
// WHEN unlock intent is allowed when NO biometrics are enrolled (0)
- `when`(secureSettings.getStringForUser(
- Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
- 0)).thenReturn("${ActiveUnlockConfig.BiometricType.NONE.intValue}")
- updateSetting(fakeUnlockIntentBioEnroll)
+
+ secureSettings.putStringForUser(
+ ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
+ "${ActiveUnlockConfig.BiometricType.NONE.intValue}", currentUser)
+ updateSetting(secureSettings.getUriFor(
+ ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
+ ))
// THEN active unlock triggers allowed on unlock intent
assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -261,12 +232,9 @@
@Test
fun triggerOnUnlockIntentWhenBiometricEnrolledFingerprintOrFaceOnly() {
- verifyRegisterSettingObserver()
-
// GIVEN unlock on biometric fail
- `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
- 0, 0)).thenReturn(1)
- updateSetting(fakeBioFailUri)
+ secureSettings.putIntForUser(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, 1, currentUser)
+ updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
// GIVEN fingerprint and face are both enrolled
activeUnlockConfig.keyguardUpdateMonitor = keyguardUpdateMonitor
@@ -275,12 +243,14 @@
// WHEN unlock intent is allowed when ONLY fingerprint is enrolled or NO biometircs
// are enrolled
- `when`(secureSettings.getStringForUser(
- Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
- 0)).thenReturn(
+ secureSettings.putStringForUser(
+ ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
"${ActiveUnlockConfig.BiometricType.ANY_FACE.intValue}" +
- "|${ActiveUnlockConfig.BiometricType.ANY_FINGERPRINT.intValue}")
- updateSetting(fakeUnlockIntentBioEnroll)
+ "|${ActiveUnlockConfig.BiometricType.ANY_FINGERPRINT.intValue}",
+ currentUser)
+ updateSetting(secureSettings.getUriFor(
+ ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
+ ))
// THEN active unlock triggers NOT allowed on unlock intent
assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -305,13 +275,12 @@
@Test
fun isWakeupConsideredUnlockIntent_singleValue() {
- verifyRegisterSettingObserver()
-
// GIVEN lift is considered an unlock intent
- `when`(secureSettings.getStringForUser(
- Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS,
- 0)).thenReturn(PowerManager.WAKE_REASON_LIFT.toString())
- updateSetting(fakeWakeupsConsideredUnlockIntents)
+ secureSettings.putIntForUser(
+ ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS,
+ PowerManager.WAKE_REASON_LIFT,
+ currentUser)
+ updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS))
// THEN only WAKE_REASON_LIFT is considered an unlock intent
for (wakeReason in 0..WAKE_REASON_BIOMETRIC) {
@@ -325,17 +294,15 @@
@Test
fun isWakeupConsideredUnlockIntent_multiValue() {
- verifyRegisterSettingObserver()
-
// GIVEN lift and tap are considered an unlock intent
- `when`(secureSettings.getStringForUser(
- Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS,
- 0)).thenReturn(
+ secureSettings.putStringForUser(
+ ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS,
PowerManager.WAKE_REASON_LIFT.toString() +
"|" +
- PowerManager.WAKE_REASON_TAP.toString()
+ PowerManager.WAKE_REASON_TAP.toString(),
+ currentUser
)
- updateSetting(fakeWakeupsConsideredUnlockIntents)
+ updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS))
// THEN WAKE_REASON_LIFT and WAKE_REASON TAP are considered an unlock intent
for (wakeReason in 0..WAKE_REASON_BIOMETRIC) {
@@ -354,13 +321,10 @@
@Test
fun isWakeupConsideredUnlockIntent_emptyValues() {
- verifyRegisterSettingObserver()
-
// GIVEN lift and tap are considered an unlock intent
- `when`(secureSettings.getStringForUser(
- Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS,
- 0)).thenReturn(" ")
- updateSetting(fakeWakeupsConsideredUnlockIntents)
+ secureSettings.putStringForUser(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS, " ",
+ currentUser)
+ updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS))
// THEN no wake up gestures are considered an unlock intent
for (wakeReason in 0..WAKE_REASON_BIOMETRIC) {
@@ -373,7 +337,23 @@
PowerManager.WAKE_REASON_UNFOLD_DEVICE))
}
+ @Test
+ fun dump_onUnlockIntentWhenBiometricEnrolled_invalidNum_noArrayOutOfBoundsException() {
+ // GIVEN an invalid input (-1)
+ secureSettings.putStringForUser(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
+ "-1", currentUser)
+
+ // WHEN the setting updates
+ updateSetting(secureSettings.getUriFor(
+ ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
+ ))
+
+ // THEN no exception thrown
+ activeUnlockConfig.dump(mockPrintWriter, emptyArray())
+ }
+
private fun updateSetting(uri: Uri) {
+ verifyRegisterSettingObserver()
settingsObserverCaptor.value.onChange(
false,
listOf(uri),
@@ -383,13 +363,17 @@
}
private fun verifyRegisterSettingObserver() {
- verifyRegisterSettingObserver(fakeWakeUri)
- verifyRegisterSettingObserver(fakeUnlockIntentUri)
- verifyRegisterSettingObserver(fakeBioFailUri)
- verifyRegisterSettingObserver(fakeFaceErrorsUri)
- verifyRegisterSettingObserver(fakeFaceAcquiredUri)
- verifyRegisterSettingObserver(fakeUnlockIntentBioEnroll)
- verifyRegisterSettingObserver(fakeWakeupsConsideredUnlockIntents)
+ verifyRegisterSettingObserver(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_WAKE))
+ verifyRegisterSettingObserver(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT))
+ verifyRegisterSettingObserver(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
+ verifyRegisterSettingObserver(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ERRORS))
+ verifyRegisterSettingObserver(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO))
+ verifyRegisterSettingObserver(secureSettings.getUriFor(
+ ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
+ ))
+ verifyRegisterSettingObserver(secureSettings.getUriFor(
+ ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS
+ ))
}
private fun verifyRegisterSettingObserver(uri: Uri) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java
index b9db9c4..b3329eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java
@@ -32,6 +32,7 @@
import com.android.settingslib.dream.DreamBackend;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.condition.SelfExecutingMonitor;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.shared.condition.Monitor;
import com.android.systemui.util.concurrency.FakeExecutor;
@@ -67,7 +68,6 @@
private ComplicationTypesUpdater mController;
- @Mock
private Monitor mMonitor;
@Before
@@ -75,6 +75,7 @@
MockitoAnnotations.initMocks(this);
when(mDreamBackend.getEnabledComplications()).thenReturn(new HashSet<>());
+ mMonitor = SelfExecutingMonitor.createInstance();
mController = new ComplicationTypesUpdater(mDreamBackend, mExecutor,
mSecureSettings, mDreamOverlayStateController, mMonitor);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java
index 52aaea1..f6662d0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java
@@ -29,6 +29,7 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.condition.SelfExecutingMonitor;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.shared.condition.Monitor;
@@ -70,13 +71,13 @@
@Mock
private ComplicationLayoutParams mLayoutParams;
- @Mock
private Monitor mMonitor;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
when(mDreamClockTimeViewHolderProvider.get()).thenReturn(mDreamClockTimeViewHolder);
+ mMonitor = SelfExecutingMonitor.createInstance();
}
/**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java
index 8534d4f..3312c43 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java
@@ -38,6 +38,7 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.view.LaunchableImageView;
+import com.android.systemui.condition.SelfExecutingMonitor;
import com.android.systemui.controls.ControlsServiceInfo;
import com.android.systemui.controls.controller.ControlsController;
import com.android.systemui.controls.controller.StructureInfo;
@@ -102,7 +103,6 @@
@Captor
private ArgumentCaptor<DreamOverlayStateController.Callback> mStateCallbackCaptor;
- @Mock
private Monitor mMonitor;
@Before
@@ -116,6 +116,8 @@
Optional.of(mControlsListingController));
when(mControlsComponent.getVisibility()).thenReturn(AVAILABLE);
when(mView.findViewById(R.id.home_controls_chip)).thenReturn(mHomeControlsView);
+
+ mMonitor = SelfExecutingMonitor.createInstance();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/SmartSpaceComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/SmartSpaceComplicationTest.java
index 77ca958..ef62abf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/SmartSpaceComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/SmartSpaceComplicationTest.java
@@ -30,6 +30,7 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.condition.SelfExecutingMonitor;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.dreams.smartspace.DreamSmartspaceController;
import com.android.systemui.plugins.BcSmartspaceDataPlugin;
@@ -64,7 +65,6 @@
@Mock
private View mBcSmartspaceView;
- @Mock
private Monitor mMonitor;
private final Set<Condition> mPreconditions = new HashSet<>();
@@ -72,6 +72,7 @@
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
+ mMonitor = SelfExecutingMonitor.createInstance();
}
/**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
index db18ba6..5bb8367 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
@@ -18,15 +18,19 @@
package com.android.systemui.keyguard.data.quickaffordance
import android.app.StatusBarManager
+import android.app.admin.DevicePolicyManager
import android.content.Context
import android.content.pm.PackageManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.camera.CameraGestureHelper
+import com.android.systemui.settings.UserTracker
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Before
@@ -44,21 +48,28 @@
@Mock private lateinit var cameraGestureHelper: CameraGestureHelper
@Mock private lateinit var context: Context
@Mock private lateinit var packageManager: PackageManager
+ @Mock private lateinit var userTracker: UserTracker
+ @Mock private lateinit var devicePolicyManager: DevicePolicyManager
private lateinit var underTest: CameraQuickAffordanceConfig
+ private lateinit var testScope: TestScope
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- setLaunchable(true)
+ setLaunchable()
+ val testDispatcher = StandardTestDispatcher()
+ testScope = TestScope(testDispatcher)
underTest =
CameraQuickAffordanceConfig(
context,
packageManager,
- ) {
- cameraGestureHelper
- }
+ { cameraGestureHelper },
+ userTracker,
+ devicePolicyManager,
+ testDispatcher,
+ )
}
@Test
@@ -73,23 +84,57 @@
}
@Test
- fun `getPickerScreenState - default when launchable`() = runTest {
- setLaunchable(true)
+ fun `getPickerScreenState - default when launchable`() =
+ testScope.runTest {
+ setLaunchable(true)
- Truth.assertThat(underTest.getPickerScreenState())
- .isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Default::class.java)
- }
+ Truth.assertThat(underTest.getPickerScreenState())
+ .isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Default::class.java)
+ }
@Test
- fun `getPickerScreenState - unavailable when not launchable`() = runTest {
- setLaunchable(false)
+ fun `getPickerScreenState - unavailable when camera app not installed`() =
+ testScope.runTest {
+ setLaunchable(isCameraAppInstalled = false)
- Truth.assertThat(underTest.getPickerScreenState())
- .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
- }
+ Truth.assertThat(underTest.getPickerScreenState())
+ .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
+ }
- private fun setLaunchable(isLaunchable: Boolean) {
+ @Test
+ fun `getPickerScreenState - unavailable when camera disabled by admin`() =
+ testScope.runTest {
+ setLaunchable(isCameraDisabledByDeviceAdmin = true)
+
+ Truth.assertThat(underTest.getPickerScreenState())
+ .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
+ }
+
+ @Test
+ fun `getPickerScreenState - unavailable when secure camera disabled by admin`() =
+ testScope.runTest {
+ setLaunchable(isSecureCameraDisabledByDeviceAdmin = true)
+
+ Truth.assertThat(underTest.getPickerScreenState())
+ .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
+ }
+
+ private fun setLaunchable(
+ isCameraAppInstalled: Boolean = true,
+ isCameraDisabledByDeviceAdmin: Boolean = false,
+ isSecureCameraDisabledByDeviceAdmin: Boolean = false,
+ ) {
whenever(packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY))
- .thenReturn(isLaunchable)
+ .thenReturn(isCameraAppInstalled)
+ whenever(devicePolicyManager.getCameraDisabled(null, userTracker.userId))
+ .thenReturn(isCameraDisabledByDeviceAdmin)
+ whenever(devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId))
+ .thenReturn(
+ if (isSecureCameraDisabledByDeviceAdmin) {
+ DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA
+ } else {
+ 0
+ }
+ )
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt
index 5bd86bd..f1b9c5f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.data.quickaffordance
+import android.app.admin.DevicePolicyManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.ActivityIntentHelper
@@ -24,11 +25,14 @@
import com.android.systemui.camera.CameraIntentsWrapper
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.settings.FakeUserTracker
+import com.android.systemui.settings.UserTracker
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -44,59 +48,94 @@
class VideoCameraQuickAffordanceConfigTest : SysuiTestCase() {
@Mock private lateinit var activityIntentHelper: ActivityIntentHelper
+ @Mock private lateinit var devicePolicyManager: DevicePolicyManager
private lateinit var underTest: VideoCameraQuickAffordanceConfig
+ private lateinit var userTracker: UserTracker
+ private lateinit var testScope: TestScope
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+ val testDispatcher = StandardTestDispatcher()
+ testScope = TestScope(testDispatcher)
+ userTracker = FakeUserTracker()
underTest =
VideoCameraQuickAffordanceConfig(
context = context,
cameraIntents = CameraIntentsWrapper(context),
activityIntentHelper = activityIntentHelper,
- userTracker = FakeUserTracker(),
+ userTracker = userTracker,
+ devicePolicyManager = devicePolicyManager,
+ backgroundDispatcher = testDispatcher,
)
}
@Test
- fun `lockScreenState - visible when launchable`() = runTest {
- setLaunchable(true)
+ fun `lockScreenState - visible when launchable`() =
+ testScope.runTest {
+ setLaunchable()
- val lockScreenState = collectLastValue(underTest.lockScreenState)
+ val lockScreenState = collectLastValue(underTest.lockScreenState)
- assertThat(lockScreenState())
- .isInstanceOf(KeyguardQuickAffordanceConfig.LockScreenState.Visible::class.java)
- }
+ assertThat(lockScreenState())
+ .isInstanceOf(KeyguardQuickAffordanceConfig.LockScreenState.Visible::class.java)
+ }
@Test
- fun `lockScreenState - hidden when not launchable`() = runTest {
- setLaunchable(false)
+ fun `lockScreenState - hidden when app not installed on device`() =
+ testScope.runTest {
+ setLaunchable(isVideoCameraAppInstalled = false)
- val lockScreenState = collectLastValue(underTest.lockScreenState)
+ val lockScreenState = collectLastValue(underTest.lockScreenState)
- assertThat(lockScreenState())
- .isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
- }
+ assertThat(lockScreenState())
+ .isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
+ }
@Test
- fun `getPickerScreenState - default when launchable`() = runTest {
- setLaunchable(true)
+ fun `lockScreenState - hidden when camera disabled by admin`() =
+ testScope.runTest {
+ setLaunchable(isCameraDisabledByAdmin = true)
- assertThat(underTest.getPickerScreenState())
- .isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Default::class.java)
- }
+ val lockScreenState = collectLastValue(underTest.lockScreenState)
+
+ assertThat(lockScreenState())
+ .isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
+ }
@Test
- fun `getPickerScreenState - unavailable when not launchable`() = runTest {
- setLaunchable(false)
+ fun `getPickerScreenState - default when launchable`() =
+ testScope.runTest {
+ setLaunchable()
- assertThat(underTest.getPickerScreenState())
- .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
- }
+ assertThat(underTest.getPickerScreenState())
+ .isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Default::class.java)
+ }
- private fun setLaunchable(isLaunchable: Boolean) {
+ @Test
+ fun `getPickerScreenState - unavailable when app not installed on device`() =
+ testScope.runTest {
+ setLaunchable(isVideoCameraAppInstalled = false)
+
+ assertThat(underTest.getPickerScreenState())
+ .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
+ }
+
+ @Test
+ fun `getPickerScreenState - unavailable when camera disabled by admin`() =
+ testScope.runTest {
+ setLaunchable(isCameraDisabledByAdmin = true)
+
+ assertThat(underTest.getPickerScreenState())
+ .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
+ }
+
+ private fun setLaunchable(
+ isVideoCameraAppInstalled: Boolean = true,
+ isCameraDisabledByAdmin: Boolean = false,
+ ) {
whenever(
activityIntentHelper.getTargetActivityInfo(
any(),
@@ -105,11 +144,13 @@
)
)
.thenReturn(
- if (isLaunchable) {
+ if (isVideoCameraAppInstalled) {
mock()
} else {
null
}
)
+ whenever(devicePolicyManager.getCameraDisabled(null, userTracker.userId))
+ .thenReturn(isCameraDisabledByAdmin)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
index 997198e..a72634b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
@@ -61,7 +61,6 @@
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Before
-import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
@@ -69,6 +68,8 @@
import org.mockito.Mock
import org.mockito.Mockito.floatThat
import org.mockito.Mockito.mock
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@@ -107,7 +108,6 @@
@Captor lateinit var listener: ArgumentCaptor<MediaDataManager.Listener>
@Captor
lateinit var configListener: ArgumentCaptor<ConfigurationController.ConfigurationListener>
- @Captor lateinit var newConfig: ArgumentCaptor<Configuration>
@Captor lateinit var visualStabilityCallback: ArgumentCaptor<OnReorderingAllowedListener>
@Captor lateinit var keyguardCallback: ArgumentCaptor<KeyguardUpdateMonitorCallback>
@@ -150,7 +150,6 @@
MediaPlayerData.clear()
}
- @Ignore("b/253229241")
@Test
fun testPlayerOrdering() {
// Test values: key, data, last active time
@@ -327,7 +326,6 @@
}
}
- @Ignore("b/253229241")
@Test
fun testOrderWithSmartspace_prioritized() {
testPlayerOrdering()
@@ -335,7 +333,7 @@
// If smartspace is prioritized
MediaPlayerData.addMediaRecommendation(
SMARTSPACE_KEY,
- EMPTY_SMARTSPACE_MEDIA_DATA,
+ EMPTY_SMARTSPACE_MEDIA_DATA.copy(isActive = true),
panel,
true,
clock
@@ -345,7 +343,6 @@
assertTrue(MediaPlayerData.playerKeys().elementAt(2).isSsMediaRec)
}
- @Ignore("b/253229241")
@Test
fun testOrderWithSmartspace_prioritized_updatingVisibleMediaPlayers() {
testPlayerOrdering()
@@ -362,7 +359,6 @@
assertTrue(MediaPlayerData.visiblePlayerKeys().elementAt(2).isSsMediaRec)
}
- @Ignore("b/253229241")
@Test
fun testOrderWithSmartspace_notPrioritized() {
testPlayerOrdering()
@@ -370,7 +366,7 @@
// If smartspace is not prioritized
MediaPlayerData.addMediaRecommendation(
SMARTSPACE_KEY,
- EMPTY_SMARTSPACE_MEDIA_DATA,
+ EMPTY_SMARTSPACE_MEDIA_DATA.copy(isActive = true),
panel,
false,
clock
@@ -381,7 +377,6 @@
assertTrue(MediaPlayerData.playerKeys().elementAt(idx).isSsMediaRec)
}
- @Ignore("b/253229241")
@Test
fun testPlayingExistingMediaPlayerFromCarousel_visibleMediaPlayersNotUpdated() {
testPlayerOrdering()
@@ -419,7 +414,6 @@
)
}
- @Ignore("b/253229241")
@Test
fun testSwipeDismiss_logged() {
mediaCarouselController.mediaCarouselScrollHandler.dismissCallback.invoke()
@@ -427,7 +421,6 @@
verify(logger).logSwipeDismiss()
}
- @Ignore("b/253229241")
@Test
fun testSettingsButton_logged() {
mediaCarouselController.settingsButton.callOnClick()
@@ -435,18 +428,16 @@
verify(logger).logCarouselSettings()
}
- @Ignore("b/253229241")
@Test
fun testLocationChangeQs_logged() {
mediaCarouselController.onDesiredLocationChanged(
- MediaHierarchyManager.LOCATION_QS,
+ LOCATION_QS,
mediaHostState,
animate = false
)
- verify(logger).logCarouselPosition(MediaHierarchyManager.LOCATION_QS)
+ verify(logger).logCarouselPosition(LOCATION_QS)
}
- @Ignore("b/253229241")
@Test
fun testLocationChangeQqs_logged() {
mediaCarouselController.onDesiredLocationChanged(
@@ -457,7 +448,6 @@
verify(logger).logCarouselPosition(MediaHierarchyManager.LOCATION_QQS)
}
- @Ignore("b/253229241")
@Test
fun testLocationChangeLockscreen_logged() {
mediaCarouselController.onDesiredLocationChanged(
@@ -468,7 +458,6 @@
verify(logger).logCarouselPosition(MediaHierarchyManager.LOCATION_LOCKSCREEN)
}
- @Ignore("b/253229241")
@Test
fun testLocationChangeDream_logged() {
mediaCarouselController.onDesiredLocationChanged(
@@ -479,7 +468,6 @@
verify(logger).logCarouselPosition(MediaHierarchyManager.LOCATION_DREAM_OVERLAY)
}
- @Ignore("b/253229241")
@Test
fun testRecommendationRemoved_logged() {
val packageName = "smartspace package"
@@ -493,7 +481,6 @@
verify(logger).logRecommendationRemoved(eq(packageName), eq(instanceId!!))
}
- @Ignore("b/253229241")
@Test
fun testMediaLoaded_ScrollToActivePlayer() {
listener.value.onMediaDataLoaded(
@@ -551,7 +538,6 @@
)
}
- @Ignore("b/253229241")
@Test
fun testMediaLoadedFromRecommendationCard_ScrollToActivePlayer() {
listener.value.onSmartspaceMediaDataLoaded(
@@ -595,7 +581,6 @@
assertEquals(playerIndex, 0)
}
- @Ignore("b/253229241")
@Test
fun testRecommendationRemovedWhileNotVisible_updateHostVisibility() {
var result = false
@@ -607,7 +592,6 @@
assertEquals(true, result)
}
- @Ignore("b/253229241")
@Test
fun testRecommendationRemovedWhileVisible_thenReorders_updateHostVisibility() {
var result = false
@@ -621,7 +605,6 @@
assertEquals(true, result)
}
- @Ignore("b/253229241")
@Test
fun testGetCurrentVisibleMediaContentIntent() {
val clickIntent1 = mock(PendingIntent::class.java)
@@ -668,7 +651,6 @@
assertEquals(mediaCarouselController.getCurrentVisibleMediaContentIntent(), clickIntent2)
}
- @Ignore("b/253229241")
@Test
fun testSetCurrentState_UpdatePageIndicatorAlphaWhenSquish() {
val delta = 0.0001F
@@ -690,7 +672,6 @@
verify(pageIndicator).alpha = floatThat { abs(it - 1.0F) < delta }
}
- @Ignore("b/253229241")
@Test
fun testOnConfigChanged_playersAreAddedBack() {
listener.value.onMediaDataLoaded(
@@ -716,7 +697,7 @@
val playersSize = MediaPlayerData.players().size
- configListener.value.onConfigChanged(capture(newConfig))
+ configListener.value.onConfigChanged(Configuration())
assertEquals(playersSize, MediaPlayerData.players().size)
assertEquals(
@@ -796,4 +777,59 @@
job.cancel()
}
+
+ @Test
+ fun testInvisibleToUserAndExpanded_playersNotListening() {
+ // Add players to carousel.
+ testPlayerOrdering()
+
+ // Make the carousel visible to user in expanded layout.
+ mediaCarouselController.currentlyExpanded = true
+ mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = true
+
+ // panel is the player for each MediaPlayerData.
+ // Verify that seekbar listening attribute in media control panel is set to true.
+ verify(panel, times(MediaPlayerData.players().size)).listening = true
+
+ // Make the carousel invisible to user.
+ mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = false
+
+ // panel is the player for each MediaPlayerData.
+ // Verify that seekbar listening attribute in media control panel is set to false.
+ verify(panel, times(MediaPlayerData.players().size)).listening = false
+ }
+
+ @Test
+ fun testVisibleToUserAndExpanded_playersListening() {
+ // Add players to carousel.
+ testPlayerOrdering()
+
+ // Make the carousel visible to user in expanded layout.
+ mediaCarouselController.currentlyExpanded = true
+ mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = true
+
+ // panel is the player for each MediaPlayerData.
+ // Verify that seekbar listening attribute in media control panel is set to true.
+ verify(panel, times(MediaPlayerData.players().size)).listening = true
+ }
+
+ @Test
+ fun testUMOCollapsed_playersNotListening() {
+ // Add players to carousel.
+ testPlayerOrdering()
+
+ // Make the carousel in collapsed layout.
+ mediaCarouselController.currentlyExpanded = false
+
+ // panel is the player for each MediaPlayerData.
+ // Verify that seekbar listening attribute in media control panel is set to false.
+ verify(panel, times(MediaPlayerData.players().size)).listening = false
+
+ // Make the carousel visible to user.
+ reset(panel)
+ mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = true
+
+ // Verify that seekbar listening attribute in media control panel is set to false.
+ verify(panel, times(MediaPlayerData.players().size)).listening = false
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java
new file mode 100644
index 0000000..d828e51
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2023 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.screenshot.appclips;
+
+import static android.app.Activity.RESULT_OK;
+
+import static com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_FOR_NOTE_ACCEPTED;
+import static com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_FOR_NOTE_CANCELLED;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.ApplicationInfoFlags;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.ResultReceiver;
+import android.testing.AndroidTestingRunner;
+import android.widget.ImageView;
+
+import androidx.lifecycle.MutableLiveData;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.intercepting.SingleActivityFactory;
+
+import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.screenshot.AppClipsActivity;
+import com.android.systemui.screenshot.AppClipsTrampolineActivity;
+import com.android.systemui.screenshot.AppClipsViewModel;
+import com.android.systemui.settings.UserTracker;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.function.BiConsumer;
+
+
+@RunWith(AndroidTestingRunner.class)
+public final class AppClipsActivityTest extends SysuiTestCase {
+
+ private static final int TEST_UID = 42;
+ private static final int TEST_USER_ID = 43;
+ private static final Bitmap TEST_BITMAP = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
+ private static final String TEST_URI_STRING = "www.test-uri.com";
+ private static final Uri TEST_URI = Uri.parse(TEST_URI_STRING);
+ private static final BiConsumer<Integer, Bundle> FAKE_CONSUMER = (unUsed1, unUsed2) -> {};
+ private static final String TEST_CALLING_PACKAGE = "test-calling-package";
+
+ @Mock
+ private AppClipsViewModel.Factory mViewModelFactory;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private UserTracker mUserTracker;
+ @Mock
+ private UiEventLogger mUiEventLogger;
+ @Mock
+ private AppClipsViewModel mViewModel;
+
+ private MutableLiveData<Bitmap> mScreenshotLiveData;
+ private MutableLiveData<Uri> mResultLiveData;
+ private AppClipsActivity mActivity;
+
+ // Using the deprecated ActivityTestRule and SingleActivityFactory to help with injecting mocks.
+ private final SingleActivityFactory<AppClipsActivityTestable> mFactory =
+ new SingleActivityFactory<>(AppClipsActivityTestable.class) {
+ @Override
+ protected AppClipsActivityTestable create(Intent unUsed) {
+ return new AppClipsActivityTestable(mViewModelFactory, mPackageManager,
+ mUserTracker, mUiEventLogger);
+ }
+ };
+
+ @Rule
+ public final ActivityTestRule<AppClipsActivityTestable> mActivityRule =
+ new ActivityTestRule<>(mFactory, false, false);
+
+ @Before
+ public void setUp() throws PackageManager.NameNotFoundException {
+ MockitoAnnotations.initMocks(this);
+
+ mScreenshotLiveData = new MutableLiveData<>();
+ mResultLiveData = new MutableLiveData<>();
+ MutableLiveData<Integer> errorLiveData = new MutableLiveData<>();
+
+ when(mViewModelFactory.create(any(Class.class))).thenReturn(mViewModel);
+ when(mViewModel.getScreenshot()).thenReturn(mScreenshotLiveData);
+ when(mViewModel.getResultLiveData()).thenReturn(mResultLiveData);
+ when(mViewModel.getErrorLiveData()).thenReturn(errorLiveData);
+ when(mUserTracker.getUserId()).thenReturn(TEST_USER_ID);
+
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.uid = TEST_UID;
+ when(mPackageManager.getApplicationInfoAsUser(eq(TEST_CALLING_PACKAGE),
+ any(ApplicationInfoFlags.class), eq(TEST_USER_ID))).thenReturn(applicationInfo);
+
+ doAnswer(invocation -> {
+ runOnMainThread(() -> mScreenshotLiveData.setValue(TEST_BITMAP));
+ return null;
+ }).when(mViewModel).performScreenshot();
+ doAnswer(invocation -> {
+ runOnMainThread(() -> mResultLiveData.setValue(TEST_URI));
+ return null;
+ }).when(mViewModel).saveScreenshotThenFinish(any(Drawable.class), any(Rect.class));
+ }
+
+ @After
+ public void tearDown() {
+ mActivityRule.finishActivity();
+ }
+
+ @Test
+ public void appClipsLaunched_screenshotDisplayed() {
+ launchActivity();
+
+ assertThat(((ImageView) mActivity.findViewById(R.id.preview)).getDrawable()).isNotNull();
+ }
+
+ @Test
+ public void screenshotDisplayed_userConsented_screenshotExportedSuccessfully() {
+ ResultReceiver resultReceiver = createResultReceiver((resultCode, data) -> {
+ assertThat(resultCode).isEqualTo(RESULT_OK);
+ assertThat(
+ data.getParcelable(AppClipsTrampolineActivity.EXTRA_SCREENSHOT_URI, Uri.class))
+ .isEqualTo(TEST_URI);
+ assertThat(data.getInt(Intent.EXTRA_CAPTURE_CONTENT_FOR_NOTE_STATUS_CODE))
+ .isEqualTo(Intent.CAPTURE_CONTENT_FOR_NOTE_SUCCESS);
+ });
+
+ launchActivity(resultReceiver);
+ runOnMainThread(() -> mActivity.findViewById(R.id.save).performClick());
+ waitForIdleSync();
+
+ assertThat(mActivity.isFinishing()).isTrue();
+ verify(mUiEventLogger).log(SCREENSHOT_FOR_NOTE_ACCEPTED, TEST_UID, TEST_CALLING_PACKAGE);
+ }
+
+ @Test
+ public void screenshotDisplayed_userDeclined() {
+ ResultReceiver resultReceiver = createResultReceiver((resultCode, data) -> {
+ assertThat(resultCode).isEqualTo(RESULT_OK);
+ assertThat(data.getInt(Intent.EXTRA_CAPTURE_CONTENT_FOR_NOTE_STATUS_CODE))
+ .isEqualTo(Intent.CAPTURE_CONTENT_FOR_NOTE_USER_CANCELED);
+ assertThat(data.keySet().contains(AppClipsTrampolineActivity.EXTRA_SCREENSHOT_URI))
+ .isFalse();
+ });
+
+ launchActivity(resultReceiver);
+ runOnMainThread(() -> mActivity.findViewById(R.id.cancel).performClick());
+ waitForIdleSync();
+
+ assertThat(mActivity.isFinishing()).isTrue();
+ verify(mUiEventLogger).log(SCREENSHOT_FOR_NOTE_CANCELLED, TEST_UID, TEST_CALLING_PACKAGE);
+ }
+
+ private void launchActivity() {
+ launchActivity(createResultReceiver(FAKE_CONSUMER));
+ }
+
+ private void launchActivity(ResultReceiver resultReceiver) {
+ Intent intent = new Intent()
+ .putExtra(AppClipsTrampolineActivity.EXTRA_RESULT_RECEIVER, resultReceiver)
+ .putExtra(AppClipsTrampolineActivity.EXTRA_CALLING_PACKAGE_NAME,
+ TEST_CALLING_PACKAGE);
+
+ mActivity = mActivityRule.launchActivity(intent);
+ waitForIdleSync();
+ }
+
+ private ResultReceiver createResultReceiver(
+ BiConsumer<Integer, Bundle> resultReceiverConsumer) {
+ ResultReceiver testReceiver = new ResultReceiver(mContext.getMainThreadHandler()) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ resultReceiverConsumer.accept(resultCode, resultData);
+ }
+ };
+
+ Parcel parcel = Parcel.obtain();
+ testReceiver.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ testReceiver = ResultReceiver.CREATOR.createFromParcel(parcel);
+ parcel.recycle();
+ return testReceiver;
+ }
+
+ private void runOnMainThread(Runnable runnable) {
+ mContext.getMainExecutor().execute(runnable);
+ }
+
+ public static class AppClipsActivityTestable extends AppClipsActivity {
+
+ public AppClipsActivityTestable(AppClipsViewModel.Factory viewModelFactory,
+ PackageManager packageManager,
+ UserTracker userTracker,
+ UiEventLogger uiEventLogger) {
+ super(viewModelFactory, packageManager, userTracker, uiEventLogger);
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperServiceTest.java
index 6e8f5fe..ab321f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperServiceTest.java
@@ -76,7 +76,7 @@
}
@Test
- public void bubblesPresent_screenshotFailed_ShouldReturnNull() throws RemoteException {
+ public void bubblesPresent_screenshotFailed_shouldReturnNull() throws RemoteException {
when(mBubblesOptional.isEmpty()).thenReturn(false);
when(mBubblesOptional.get()).thenReturn(mBubbles);
when(mBubbles.getScreenshotExcludingBubble(DEFAULT_DISPLAY)).thenReturn(mScreenshotSync);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivityTest.java
index 295d127..e40c49b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivityTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivityTest.java
@@ -25,23 +25,25 @@
import static android.content.Intent.EXTRA_CAPTURE_CONTENT_FOR_NOTE_STATUS_CODE;
import static com.android.systemui.flags.Flags.SCREENSHOT_APP_CLIPS;
-import static com.android.systemui.screenshot.AppClipsTrampolineActivity.ACTION_FINISH_FROM_TRAMPOLINE;
import static com.android.systemui.screenshot.AppClipsTrampolineActivity.EXTRA_SCREENSHOT_URI;
-import static com.android.systemui.screenshot.AppClipsTrampolineActivity.PERMISSION_SELF;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Activity;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
-import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.ApplicationInfoFlags;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Bundle;
@@ -51,12 +53,15 @@
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.intercepting.SingleActivityFactory;
+import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.notetask.NoteTaskController;
import com.android.systemui.screenshot.AppClipsTrampolineActivity;
+import com.android.systemui.screenshot.ScreenshotEvent;
+import com.android.systemui.settings.UserTracker;
import com.android.wm.shell.bubbles.Bubbles;
import org.junit.After;
@@ -74,7 +79,9 @@
private static final String TEST_URI_STRING = "www.test-uri.com";
private static final Uri TEST_URI = Uri.parse(TEST_URI_STRING);
- private static final int TIME_OUT = 5000;
+ private static final int TEST_UID = 42;
+ private static final int TEST_USER_ID = 43;
+ private static final String TEST_CALLING_PACKAGE = "test-calling-package";
@Mock
private DevicePolicyManager mDevicePolicyManager;
@@ -86,6 +93,12 @@
private Bubbles mBubbles;
@Mock
private NoteTaskController mNoteTaskController;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private UserTracker mUserTracker;
+ @Mock
+ private UiEventLogger mUiEventLogger;
@Main
private Handler mMainHandler;
@@ -96,7 +109,8 @@
@Override
protected AppClipsTrampolineActivityTestable create(Intent unUsed) {
return new AppClipsTrampolineActivityTestable(mDevicePolicyManager,
- mFeatureFlags, mOptionalBubbles, mNoteTaskController, mMainHandler);
+ mFeatureFlags, mOptionalBubbles, mNoteTaskController, mPackageManager,
+ mUserTracker, mUiEventLogger, mMainHandler);
}
};
@@ -104,41 +118,36 @@
public final ActivityTestRule<AppClipsTrampolineActivityTestable> mActivityRule =
new ActivityTestRule<>(mFactory, false, false);
- private Context mContext;
private Intent mActivityIntent;
private ComponentName mExpectedComponentName;
- private Intent mKillAppClipsActivityBroadcast;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mContext = getContext();
mMainHandler = mContext.getMainThreadHandler();
mActivityIntent = new Intent(mContext, AppClipsTrampolineActivityTestable.class);
mExpectedComponentName = ComponentName.unflattenFromString(
mContext.getString(
R.string.config_screenshotAppClipsActivityComponent));
- mKillAppClipsActivityBroadcast = new Intent(ACTION_FINISH_FROM_TRAMPOLINE)
- .setComponent(mExpectedComponentName)
- .setPackage(mExpectedComponentName.getPackageName());
}
@After
public void tearDown() {
- mContext.sendBroadcast(mKillAppClipsActivityBroadcast, PERMISSION_SELF);
mActivityRule.finishActivity();
}
@Test
- public void configComponentName_shouldResolve() {
+ public void appClipsActivityConfig_shouldBeConfigured() {
// Verify component name is setup - has package and class name.
assertThat(mExpectedComponentName).isNotNull();
assertThat(mExpectedComponentName.getPackageName()).isNotEmpty();
assertThat(mExpectedComponentName.getClassName()).isNotEmpty();
+ }
- // Verify an intent when launched with above component resolves to the same component to
- // confirm that component from above is available in framework.
+ @Test
+ public void configComponentName_shouldResolve() {
+ // Verify an intent when launched with configured component resolves to activity.
Intent appClipsActivityIntent = new Intent();
appClipsActivityIntent.setComponent(mExpectedComponentName);
ResolveInfo resolveInfo = getContext().getPackageManager().resolveActivity(
@@ -205,7 +214,8 @@
}
@Test
- public void startAppClipsActivity_userCanceled_shouldReturnUserCanceled() {
+ public void startAppClipsActivity_userCanceled_shouldReturnUserCanceled()
+ throws NameNotFoundException {
mockToSatisfyAllPrerequisites();
AppClipsTrampolineActivityTestable activity = mActivityRule.launchActivity(mActivityIntent);
@@ -224,7 +234,8 @@
}
@Test
- public void startAppClipsActivity_shouldReturnSuccess() {
+ public void startAppClipsActivity_shouldReturnSuccess()
+ throws NameNotFoundException {
mockToSatisfyAllPrerequisites();
AppClipsTrampolineActivityTestable activity = mActivityRule.launchActivity(mActivityIntent);
@@ -243,12 +254,31 @@
assertThat(actualResult.getResultData().getData()).isEqualTo(TEST_URI);
}
- private void mockToSatisfyAllPrerequisites() {
+ @Test
+ public void startAppClipsActivity_shouldLogUiEvent()
+ throws NameNotFoundException {
+ mockToSatisfyAllPrerequisites();
+
+ mActivityRule.launchActivity(mActivityIntent);
+ waitForIdleSync();
+
+ verify(mUiEventLogger).log(ScreenshotEvent.SCREENSHOT_FOR_NOTE_TRIGGERED, TEST_UID,
+ TEST_CALLING_PACKAGE);
+ }
+
+ private void mockToSatisfyAllPrerequisites() throws NameNotFoundException {
when(mFeatureFlags.isEnabled(SCREENSHOT_APP_CLIPS)).thenReturn(true);
when(mOptionalBubbles.isEmpty()).thenReturn(false);
when(mOptionalBubbles.get()).thenReturn(mBubbles);
when(mBubbles.isAppBubbleTaskId(anyInt())).thenReturn(true);
when(mDevicePolicyManager.getScreenCaptureDisabled(eq(null))).thenReturn(false);
+ when(mUserTracker.getUserId()).thenReturn(TEST_USER_ID);
+
+ ApplicationInfo testApplicationInfo = new ApplicationInfo();
+ testApplicationInfo.uid = TEST_UID;
+ when(mPackageManager.getApplicationInfoAsUser(eq(TEST_CALLING_PACKAGE),
+ any(ApplicationInfoFlags.class),
+ eq(TEST_USER_ID))).thenReturn(testApplicationInfo);
}
public static final class AppClipsTrampolineActivityTestable extends
@@ -258,8 +288,22 @@
FeatureFlags flags,
Optional<Bubbles> optionalBubbles,
NoteTaskController noteTaskController,
+ PackageManager packageManager,
+ UserTracker userTracker,
+ UiEventLogger uiEventLogger,
@Main Handler mainHandler) {
- super(devicePolicyManager, flags, optionalBubbles, noteTaskController, mainHandler);
+ super(devicePolicyManager, flags, optionalBubbles, noteTaskController, packageManager,
+ userTracker, uiEventLogger, mainHandler);
+ }
+
+ @Override
+ public String getCallingPackage() {
+ return TEST_CALLING_PACKAGE;
+ }
+
+ @Override
+ public void startActivity(Intent unUsed) {
+ // Ignore this intent to avoid App Clips screenshot editing activity from starting.
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java
index 9eccbb6..aa1636d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java
@@ -249,6 +249,21 @@
}
@Test
+ public void addCallback_preCondition_noConditions_reportAllConditionsMet() {
+ final Monitor
+ monitor = new Monitor(mExecutor, new HashSet<>(Arrays.asList(mCondition1)));
+ final Monitor.Callback callback = mock(
+ Monitor.Callback.class);
+
+ monitor.addSubscription(new Monitor.Subscription.Builder(callback).build());
+ mExecutor.runAllReady();
+ verify(callback, never()).onConditionsChanged(true);
+ mCondition1.fakeUpdateCondition(true);
+ mExecutor.runAllReady();
+ verify(callback).onConditionsChanged(true);
+ }
+
+ @Test
public void removeCallback_noFailureOnDoubleRemove() {
final Condition condition = mock(
Condition.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
index 673e559..3853b99 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
@@ -896,21 +896,31 @@
// Subscription 1
private const val SUB_1_ID = 1
+ private val GROUP_1 = ParcelUuid(UUID.randomUUID())
private val SUB_1 =
mock<SubscriptionInfo>().also {
whenever(it.subscriptionId).thenReturn(SUB_1_ID)
- whenever(it.groupUuid).thenReturn(ParcelUuid(UUID.randomUUID()))
+ whenever(it.groupUuid).thenReturn(GROUP_1)
}
- private val MODEL_1 = SubscriptionModel(subscriptionId = SUB_1_ID)
+ private val MODEL_1 =
+ SubscriptionModel(
+ subscriptionId = SUB_1_ID,
+ groupUuid = GROUP_1,
+ )
// Subscription 2
private const val SUB_2_ID = 2
+ private val GROUP_2 = ParcelUuid(UUID.randomUUID())
private val SUB_2 =
mock<SubscriptionInfo>().also {
whenever(it.subscriptionId).thenReturn(SUB_2_ID)
- whenever(it.groupUuid).thenReturn(ParcelUuid(UUID.randomUUID()))
+ whenever(it.groupUuid).thenReturn(GROUP_2)
}
- private val MODEL_2 = SubscriptionModel(subscriptionId = SUB_2_ID)
+ private val MODEL_2 =
+ SubscriptionModel(
+ subscriptionId = SUB_2_ID,
+ groupUuid = GROUP_2,
+ )
// Subs 3 and 4 are considered to be in the same group ------------------------------------
private val GROUP_ID_3_4 = ParcelUuid(UUID.randomUUID())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
index f8a9783..bbca001 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.pipeline.mobile.domain.interactor
+import android.os.ParcelUuid
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import androidx.test.filters.SmallTest
import com.android.settingslib.mobile.MobileMappings
@@ -34,6 +35,7 @@
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
+import java.util.UUID
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -104,6 +106,21 @@
job.cancel()
}
+ // Based on the logic from the old pipeline, we'll never filter subs when there are more than 2
+ @Test
+ fun filteredSubscriptions_moreThanTwo_doesNotFilter() =
+ testScope.runTest {
+ connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_3_OPP, SUB_4_OPP))
+ connectionsRepository.setActiveMobileDataSubscriptionId(SUB_4_ID)
+
+ var latest: List<SubscriptionModel>? = null
+ val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
+
+ assertThat(latest).isEqualTo(listOf(SUB_1, SUB_3_OPP, SUB_4_OPP))
+
+ job.cancel()
+ }
+
@Test
fun filteredSubscriptions_nonOpportunistic_updatesWithMultipleSubs() =
testScope.runTest {
@@ -118,10 +135,50 @@
}
@Test
- fun filteredSubscriptions_bothOpportunistic_configFalse_showsActive_3() =
+ fun filteredSubscriptions_opportunistic_differentGroups_doesNotFilter() =
testScope.runTest {
connectionsRepository.setSubscriptions(listOf(SUB_3_OPP, SUB_4_OPP))
connectionsRepository.setActiveMobileDataSubscriptionId(SUB_3_ID)
+
+ var latest: List<SubscriptionModel>? = null
+ val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
+
+ assertThat(latest).isEqualTo(listOf(SUB_3_OPP, SUB_4_OPP))
+
+ job.cancel()
+ }
+
+ @Test
+ fun filteredSubscriptions_opportunistic_nonGrouped_doesNotFilter() =
+ testScope.runTest {
+ val (sub1, sub2) =
+ createSubscriptionPair(
+ subscriptionIds = Pair(SUB_1_ID, SUB_2_ID),
+ opportunistic = Pair(true, true),
+ grouped = false,
+ )
+ connectionsRepository.setSubscriptions(listOf(sub1, sub2))
+ connectionsRepository.setActiveMobileDataSubscriptionId(SUB_1_ID)
+
+ var latest: List<SubscriptionModel>? = null
+ val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
+
+ assertThat(latest).isEqualTo(listOf(sub1, sub2))
+
+ job.cancel()
+ }
+
+ @Test
+ fun filteredSubscriptions_opportunistic_grouped_configFalse_showsActive_3() =
+ testScope.runTest {
+ val (sub3, sub4) =
+ createSubscriptionPair(
+ subscriptionIds = Pair(SUB_3_ID, SUB_4_ID),
+ opportunistic = Pair(true, true),
+ grouped = true,
+ )
+ connectionsRepository.setSubscriptions(listOf(sub3, sub4))
+ connectionsRepository.setActiveMobileDataSubscriptionId(SUB_3_ID)
whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
.thenReturn(false)
@@ -129,15 +186,21 @@
val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
// Filtered subscriptions should show the active one when the config is false
- assertThat(latest).isEqualTo(listOf(SUB_3_OPP))
+ assertThat(latest).isEqualTo(listOf(sub3))
job.cancel()
}
@Test
- fun filteredSubscriptions_bothOpportunistic_configFalse_showsActive_4() =
+ fun filteredSubscriptions_opportunistic_grouped_configFalse_showsActive_4() =
testScope.runTest {
- connectionsRepository.setSubscriptions(listOf(SUB_3_OPP, SUB_4_OPP))
+ val (sub3, sub4) =
+ createSubscriptionPair(
+ subscriptionIds = Pair(SUB_3_ID, SUB_4_ID),
+ opportunistic = Pair(true, true),
+ grouped = true,
+ )
+ connectionsRepository.setSubscriptions(listOf(sub3, sub4))
connectionsRepository.setActiveMobileDataSubscriptionId(SUB_4_ID)
whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
.thenReturn(false)
@@ -146,15 +209,21 @@
val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
// Filtered subscriptions should show the active one when the config is false
- assertThat(latest).isEqualTo(listOf(SUB_4_OPP))
+ assertThat(latest).isEqualTo(listOf(sub4))
job.cancel()
}
@Test
- fun filteredSubscriptions_oneOpportunistic_configTrue_showsPrimary_active_1() =
+ fun filteredSubscriptions_oneOpportunistic_grouped_configTrue_showsPrimary_active_1() =
testScope.runTest {
- connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_3_OPP))
+ val (sub1, sub3) =
+ createSubscriptionPair(
+ subscriptionIds = Pair(SUB_1_ID, SUB_3_ID),
+ opportunistic = Pair(false, true),
+ grouped = true,
+ )
+ connectionsRepository.setSubscriptions(listOf(sub1, sub3))
connectionsRepository.setActiveMobileDataSubscriptionId(SUB_1_ID)
whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
.thenReturn(true)
@@ -164,15 +233,21 @@
// Filtered subscriptions should show the primary (non-opportunistic) if the config is
// true
- assertThat(latest).isEqualTo(listOf(SUB_1))
+ assertThat(latest).isEqualTo(listOf(sub1))
job.cancel()
}
@Test
- fun filteredSubscriptions_oneOpportunistic_configTrue_showsPrimary_nonActive_1() =
+ fun filteredSubscriptions_oneOpportunistic_grouped_configTrue_showsPrimary_nonActive_1() =
testScope.runTest {
- connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_3_OPP))
+ val (sub1, sub3) =
+ createSubscriptionPair(
+ subscriptionIds = Pair(SUB_1_ID, SUB_3_ID),
+ opportunistic = Pair(false, true),
+ grouped = true,
+ )
+ connectionsRepository.setSubscriptions(listOf(sub1, sub3))
connectionsRepository.setActiveMobileDataSubscriptionId(SUB_3_ID)
whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
.thenReturn(true)
@@ -182,7 +257,7 @@
// Filtered subscriptions should show the primary (non-opportunistic) if the config is
// true
- assertThat(latest).isEqualTo(listOf(SUB_1))
+ assertThat(latest).isEqualTo(listOf(sub1))
job.cancel()
}
@@ -642,6 +717,33 @@
job.cancel()
}
+ /**
+ * Convenience method for creating a pair of subscriptions to test the filteredSubscriptions
+ * flow.
+ */
+ private fun createSubscriptionPair(
+ subscriptionIds: Pair<Int, Int>,
+ opportunistic: Pair<Boolean, Boolean> = Pair(false, false),
+ grouped: Boolean = false,
+ ): Pair<SubscriptionModel, SubscriptionModel> {
+ val groupUuid = if (grouped) ParcelUuid(UUID.randomUUID()) else null
+ val sub1 =
+ SubscriptionModel(
+ subscriptionId = subscriptionIds.first,
+ isOpportunistic = opportunistic.first,
+ groupUuid = groupUuid,
+ )
+
+ val sub2 =
+ SubscriptionModel(
+ subscriptionId = subscriptionIds.second,
+ isOpportunistic = opportunistic.second,
+ groupUuid = groupUuid,
+ )
+
+ return Pair(sub1, sub2)
+ }
+
companion object {
private val tableLogBuffer =
TableLogBuffer(8, "MobileIconsInteractorTest", FakeSystemClock())
@@ -655,11 +757,21 @@
private val CONNECTION_2 = FakeMobileConnectionRepository(SUB_2_ID, tableLogBuffer)
private const val SUB_3_ID = 3
- private val SUB_3_OPP = SubscriptionModel(subscriptionId = SUB_3_ID, isOpportunistic = true)
+ private val SUB_3_OPP =
+ SubscriptionModel(
+ subscriptionId = SUB_3_ID,
+ isOpportunistic = true,
+ groupUuid = ParcelUuid(UUID.randomUUID()),
+ )
private val CONNECTION_3 = FakeMobileConnectionRepository(SUB_3_ID, tableLogBuffer)
private const val SUB_4_ID = 4
- private val SUB_4_OPP = SubscriptionModel(subscriptionId = SUB_4_ID, isOpportunistic = true)
+ private val SUB_4_OPP =
+ SubscriptionModel(
+ subscriptionId = SUB_4_ID,
+ isOpportunistic = true,
+ groupUuid = ParcelUuid(UUID.randomUUID()),
+ )
private val CONNECTION_4 = FakeMobileConnectionRepository(SUB_4_ID, tableLogBuffer)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusManagerTest.kt
index 6d7941f6..48a2e09 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusManagerTest.kt
@@ -19,10 +19,16 @@
import android.bluetooth.BluetoothDevice
import android.hardware.BatteryState
import android.hardware.input.InputManager
+import android.hardware.input.InputSettings
import android.os.Handler
import android.testing.AndroidTestingRunner
import android.view.InputDevice
import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
+import com.android.dx.mockito.inline.extended.ExtendedMockito.never
+import com.android.dx.mockito.inline.extended.ExtendedMockito.times
+import com.android.dx.mockito.inline.extended.ExtendedMockito.verify
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
import com.android.internal.logging.UiEventLogger
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FeatureFlags
@@ -30,18 +36,17 @@
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.whenever
import java.util.concurrent.Executor
+import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.inOrder
-import org.mockito.Mockito.never
-import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.Mockito.verifyZeroInteractions
import org.mockito.MockitoAnnotations
+import org.mockito.quality.Strictness
@RunWith(AndroidTestingRunner::class)
@SmallTest
@@ -67,11 +72,17 @@
@Mock lateinit var otherStylusBatteryCallback: StylusManager.StylusBatteryCallback
+ private lateinit var mockitoSession: StaticMockitoSession
private lateinit var stylusManager: StylusManager
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+ mockitoSession =
+ mockitoSession()
+ .mockStatic(InputSettings::class.java)
+ .strictness(Strictness.LENIENT)
+ .startMocking()
whenever(handler.post(any())).thenAnswer {
(it.arguments[0] as Runnable).run()
@@ -103,19 +114,25 @@
whenever(inputManager.getInputDevice(STYLUS_DEVICE_ID)).thenReturn(stylusDevice)
whenever(inputManager.getInputDevice(BT_STYLUS_DEVICE_ID)).thenReturn(btStylusDevice)
whenever(inputManager.inputDeviceIds).thenReturn(intArrayOf(STYLUS_DEVICE_ID))
- whenever(inputManager.isStylusEverUsed(mContext)).thenReturn(false)
whenever(bluetoothAdapter.getRemoteDevice(STYLUS_BT_ADDRESS)).thenReturn(bluetoothDevice)
whenever(bluetoothDevice.address).thenReturn(STYLUS_BT_ADDRESS)
whenever(featureFlags.isEnabled(Flags.TRACK_STYLUS_EVER_USED)).thenReturn(true)
+ whenever(InputSettings.isStylusEverUsed(mContext)).thenReturn(false)
+
stylusManager.startListener()
stylusManager.registerCallback(stylusCallback)
stylusManager.registerBatteryCallback(stylusBatteryCallback)
clearInvocations(inputManager)
}
+ @After
+ fun tearDown() {
+ mockitoSession.finishMocking()
+ }
+
@Test
fun startListener_hasNotStarted_registersInputDeviceListener() {
stylusManager =
@@ -209,8 +226,7 @@
@Test
fun onInputDeviceAdded_btStylus_firstUsed_setsFlag() {
stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID)
-
- verify(inputManager, times(1)).setStylusEverUsed(mContext, true)
+ verify({ InputSettings.setStylusEverUsed(mContext, true) }, times(1))
}
@Test
@@ -484,7 +500,7 @@
stylusManager.onBatteryStateChanged(STYLUS_DEVICE_ID, 1, batteryState)
- verify(inputManager).setStylusEverUsed(mContext, true)
+ verify({ InputSettings.setStylusEverUsed(mContext, true) }, times(1))
}
@Test
@@ -539,12 +555,13 @@
@Test
fun onBatteryStateChanged_batteryPresent_stylusUsed_doesNotUpdateEverUsedFlag() {
- whenever(inputManager.isStylusEverUsed(mContext)).thenReturn(true)
+ whenever(InputSettings.isStylusEverUsed(mContext)).thenReturn(true)
+
whenever(batteryState.isPresent).thenReturn(true)
stylusManager.onBatteryStateChanged(STYLUS_DEVICE_ID, 1, batteryState)
- verify(inputManager, never()).setStylusEverUsed(mContext, true)
+ verify({ InputSettings.setStylusEverUsed(mContext, true) }, never())
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java
index 5ef62c1..b367a60 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java
@@ -60,6 +60,11 @@
mCallback = callback;
}
+ public FakeConditionalCoreStartable(Monitor monitor, Callback callback) {
+ super(monitor);
+ mCallback = callback;
+ }
+
@Override
protected void onStart() {
mCallback.onStart();
@@ -122,6 +127,31 @@
verify(mMonitor).removeSubscription(mSubscriptionToken);
}
+ @Test
+ public void testOnStartCallbackWithNoConditions() {
+ final CoreStartable coreStartable =
+ new FakeConditionalCoreStartable(mMonitor,
+ mCallback);
+
+ when(mMonitor.addSubscription(any())).thenReturn(mSubscriptionToken);
+ coreStartable.start();
+
+ final ArgumentCaptor<Monitor.Subscription> subscriptionCaptor = ArgumentCaptor.forClass(
+ Monitor.Subscription.class);
+ verify(mMonitor).addSubscription(subscriptionCaptor.capture());
+
+ final Monitor.Subscription subscription = subscriptionCaptor.getValue();
+
+ assertThat(subscription.getConditions()).isEmpty();
+
+ verify(mCallback, never()).onStart();
+
+ subscription.getCallback().onConditionsChanged(true);
+
+ verify(mCallback).onStart();
+ verify(mMonitor).removeSubscription(mSubscriptionToken);
+ }
+
/**
* Verifies that {@link ConditionalCoreStartable#bootCompleted()} ()} is predicated on
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/condition/SelfExecutingMonitor.java b/packages/SystemUI/tests/utils/src/com/android/systemui/condition/SelfExecutingMonitor.java
new file mode 100644
index 0000000..7ee05d0
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/condition/SelfExecutingMonitor.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2023 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.condition;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.shared.condition.Monitor;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+
+/**
+ * {@link SelfExecutingMonitor} creates a monitor that independently executes its logic through
+ * a {@link FakeExecutor}, which is ran at when a subscription is added and removed.
+ */
+public class SelfExecutingMonitor extends Monitor {
+ private final FakeExecutor mExecutor;
+
+ /**
+ * Default constructor that allows specifying the FakeExecutor to use.
+ */
+ public SelfExecutingMonitor(FakeExecutor executor) {
+ super(executor);
+ mExecutor = executor;
+ }
+
+ @Override
+ public Subscription.Token addSubscription(@NonNull Subscription subscription) {
+ final Subscription.Token result = super.addSubscription(subscription);
+ mExecutor.runAllReady();
+ return result;
+ }
+
+ @Override
+ public void removeSubscription(@NonNull Subscription.Token token) {
+ super.removeSubscription(token);
+ mExecutor.runNextReady();
+ }
+
+ /**
+ * Creates a {@link SelfExecutingMonitor} with a self-managed {@link FakeExecutor}. Use only
+ * for cases where condition state only will be set at when a subscription is added.
+ */
+ public static SelfExecutingMonitor createInstance() {
+ final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
+ final FakeExecutor mExecutor = new FakeExecutor(mFakeSystemClock);
+ return new SelfExecutingMonitor(mExecutor);
+ }
+}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 669be1a..4d5baaf 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -487,6 +487,7 @@
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.BiFunction;
+import java.util.function.Consumer;
import java.util.function.Supplier;
public class ActivityManagerService extends IActivityManager.Stub
@@ -6452,6 +6453,44 @@
return entry == null ? null : entry.second;
}
+ private static class GetBackgroundStartPrivilegesFunctor implements Consumer<ProcessRecord> {
+ private BackgroundStartPrivileges mBackgroundStartPrivileges =
+ BackgroundStartPrivileges.NONE;
+ private int mUid;
+
+ void prepare(int uid) {
+ mUid = uid;
+ mBackgroundStartPrivileges = BackgroundStartPrivileges.NONE;
+ }
+
+ @NonNull
+ BackgroundStartPrivileges getResult() {
+ return mBackgroundStartPrivileges;
+ }
+
+ public void accept(ProcessRecord pr) {
+ if (pr.uid == mUid) {
+ mBackgroundStartPrivileges =
+ mBackgroundStartPrivileges.merge(pr.getBackgroundStartPrivileges());
+ }
+ }
+ }
+
+ private final GetBackgroundStartPrivilegesFunctor mGetBackgroundStartPrivilegesFunctor =
+ new GetBackgroundStartPrivilegesFunctor();
+
+ /**
+ * Returns the current complete {@link BackgroundStartPrivileges} of the UID.
+ */
+ @NonNull
+ private BackgroundStartPrivileges getBackgroundStartPrivileges(int uid) {
+ synchronized (mProcLock) {
+ mGetBackgroundStartPrivilegesFunctor.prepare(uid);
+ mProcessList.forEachLruProcessesLOSP(false, mGetBackgroundStartPrivilegesFunctor);
+ return mGetBackgroundStartPrivilegesFunctor.getResult();
+ }
+ }
+
/**
* @return allowlist tag for a uid from mPendingTempAllowlist, null if not currently on
* the allowlist
@@ -17851,6 +17890,11 @@
return mConstants.mFlagBackgroundActivityStartsEnabled;
}
+ @Override
+ public BackgroundStartPrivileges getBackgroundStartPrivileges(int uid) {
+ return ActivityManagerService.this.getBackgroundStartPrivileges(uid);
+ }
+
public void reportCurKeyguardUsageEvent(boolean keyguardShowing) {
ActivityManagerService.this.reportGlobalUsageEvent(keyguardShowing
? UsageEvents.Event.KEYGUARD_SHOWN
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index a707202..fa3f684 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -20,6 +20,7 @@
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.ActivityManagerService.MY_PID;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ApplicationExitInfo;
@@ -341,6 +342,24 @@
private boolean mInFullBackup;
/**
+ * A set of tokens that currently contribute to this process being temporarily allowed
+ * to start certain components (eg. activities or foreground services) even if it's not
+ * in the foreground.
+ */
+ @GuardedBy("mBackgroundStartPrivileges")
+ private final ArrayMap<Binder, BackgroundStartPrivileges> mBackgroundStartPrivileges =
+ new ArrayMap<>();
+
+ /**
+ * The merged BackgroundStartPrivileges based on what's in {@link #mBackgroundStartPrivileges}.
+ * This is lazily generated using {@link #getBackgroundStartPrivileges()}.
+ */
+ @Nullable
+ @GuardedBy("mBackgroundStartPrivileges")
+ private BackgroundStartPrivileges mBackgroundStartPrivilegesMerged =
+ BackgroundStartPrivileges.NONE;
+
+ /**
* Controller for driving the process state on the window manager side.
*/
private final WindowProcessController mWindowProcessController;
@@ -1325,11 +1344,50 @@
Objects.requireNonNull(entity);
mWindowProcessController.addOrUpdateBackgroundStartPrivileges(entity,
backgroundStartPrivileges);
+ setBackgroundStartPrivileges(entity, backgroundStartPrivileges);
}
void removeBackgroundStartPrivileges(Binder entity) {
Objects.requireNonNull(entity);
mWindowProcessController.removeBackgroundStartPrivileges(entity);
+ setBackgroundStartPrivileges(entity, null);
+ }
+
+ @NonNull
+ BackgroundStartPrivileges getBackgroundStartPrivileges() {
+ synchronized (mBackgroundStartPrivileges) {
+ if (mBackgroundStartPrivilegesMerged == null) {
+ // Lazily generate the merged version when it's actually needed.
+ mBackgroundStartPrivilegesMerged = BackgroundStartPrivileges.NONE;
+ for (int i = mBackgroundStartPrivileges.size() - 1; i >= 0; --i) {
+ mBackgroundStartPrivilegesMerged =
+ mBackgroundStartPrivilegesMerged.merge(
+ mBackgroundStartPrivileges.valueAt(i));
+ }
+ }
+ return mBackgroundStartPrivilegesMerged;
+ }
+ }
+
+ private void setBackgroundStartPrivileges(@NonNull Binder entity,
+ @Nullable BackgroundStartPrivileges backgroundStartPrivileges) {
+ synchronized (mBackgroundStartPrivileges) {
+ final boolean changed;
+ if (backgroundStartPrivileges == null) {
+ changed = mBackgroundStartPrivileges.remove(entity) != null;
+ } else {
+ final BackgroundStartPrivileges oldBsp =
+ mBackgroundStartPrivileges.put(entity, backgroundStartPrivileges);
+ // BackgroundStartPrivileges tries to reuse the same object and avoid creating
+ // additional objects. For now, we just compare the reference to see if something
+ // has changed.
+ // TODO: actually compare the individual values to see if there's a change
+ changed = backgroundStartPrivileges != oldBsp;
+ }
+ if (changed) {
+ mBackgroundStartPrivilegesMerged = null;
+ }
+ }
}
@Override
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 172aa20..b2b22a0 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -50,6 +50,7 @@
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.InputManager;
import android.hardware.input.InputSensorInfo;
+import android.hardware.input.InputSettings;
import android.hardware.input.KeyboardLayout;
import android.hardware.input.TouchCalibration;
import android.hardware.lights.Light;
@@ -1309,7 +1310,7 @@
throw new SecurityException("Requires SET_POINTER_SPEED permission");
}
- if (speed < InputManager.MIN_POINTER_SPEED || speed > InputManager.MAX_POINTER_SPEED) {
+ if (speed < InputSettings.MIN_POINTER_SPEED || speed > InputSettings.MAX_POINTER_SPEED) {
throw new IllegalArgumentException("speed out of range");
}
@@ -1317,8 +1318,8 @@
}
private void setPointerSpeedUnchecked(int speed) {
- speed = Math.min(Math.max(speed, InputManager.MIN_POINTER_SPEED),
- InputManager.MAX_POINTER_SPEED);
+ speed = Math.min(Math.max(speed, InputSettings.MIN_POINTER_SPEED),
+ InputSettings.MAX_POINTER_SPEED);
mNative.setPointerSpeed(speed);
}
diff --git a/services/core/java/com/android/server/input/InputSettingsObserver.java b/services/core/java/com/android/server/input/InputSettingsObserver.java
index a113d01..5b21669 100644
--- a/services/core/java/com/android/server/input/InputSettingsObserver.java
+++ b/services/core/java/com/android/server/input/InputSettingsObserver.java
@@ -21,7 +21,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
-import android.hardware.input.InputManager;
+import android.hardware.input.InputSettings;
import android.net.Uri;
import android.os.Handler;
import android.os.UserHandle;
@@ -32,7 +32,6 @@
import android.view.ViewConfiguration;
import java.util.Map;
-import java.util.Objects;
import java.util.function.Consumer;
/** Observes settings changes and propagates them to the native side. */
@@ -111,9 +110,9 @@
private int getPointerSpeedValue(String settingName) {
int speed = Settings.System.getIntForUser(mContext.getContentResolver(),
- settingName, InputManager.DEFAULT_POINTER_SPEED, UserHandle.USER_CURRENT);
- return Math.min(Math.max(speed, InputManager.MIN_POINTER_SPEED),
- InputManager.MAX_POINTER_SPEED);
+ settingName, InputSettings.DEFAULT_POINTER_SPEED, UserHandle.USER_CURRENT);
+ return Math.min(Math.max(speed, InputSettings.MIN_POINTER_SPEED),
+ InputSettings.MAX_POINTER_SPEED);
}
private void updateMousePointerSpeed() {
@@ -170,8 +169,7 @@
}
private void updateMaximumObscuringOpacityForTouch() {
- InputManager im = Objects.requireNonNull(mContext.getSystemService(InputManager.class));
- final float opacity = im.getMaximumObscuringOpacityForTouch();
+ final float opacity = InputSettings.getMaximumObscuringOpacityForTouch(mContext);
if (opacity < 0 || opacity > 1) {
Log.e(TAG, "Invalid maximum obscuring opacity " + opacity
+ ", it should be >= 0 and <= 1, rejecting update.");
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
index 3799193..84ea077 100644
--- a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
@@ -320,12 +320,26 @@
MOBILE_NETWORK_SETTINGS);
}
+ /** Call intent with tel scheme */
+ private static final DefaultCrossProfileIntentFilter CALL =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PROFILE,
+ SKIP_CURRENT_PROFILE,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_CALL)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addDataScheme("tel")
+ .build();
+
/**
* Returns default telephony related intent filters for managed profile.
*/
public static List<DefaultCrossProfileIntentFilter> getDefaultManagedProfileTelephonyFilters() {
return Arrays.asList(
DIAL_DATA,
+ DIAL_MIME,
+ DIAL_RAW,
+ CALL,
SMS_MMS);
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 7f1679a..449801d 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3501,8 +3501,8 @@
}
@Override
- public void onKeyguardOccludedChangedLw(boolean occluded) {
- if (mKeyguardDelegate != null && mKeyguardDelegate.isShowing()) {
+ public void onKeyguardOccludedChangedLw(boolean occluded, boolean waitAppTransition) {
+ if (mKeyguardDelegate != null && waitAppTransition) {
mPendingKeyguardOccluded = occluded;
mKeyguardOccludedChanged = true;
} else {
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index ca5fa5f..3c4dbf2 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -164,9 +164,10 @@
/**
* Called when the Keyguard occluded state changed.
+ *
* @param occluded Whether Keyguard is currently occluded or not.
*/
- void onKeyguardOccludedChangedLw(boolean occluded);
+ void onKeyguardOccludedChangedLw(boolean occluded, boolean waitAppTransition);
/**
* @param notify {@code true} if the status change should be immediately notified via
diff --git a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
index 5fd70a8..fc659c5 100644
--- a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
+++ b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
@@ -104,10 +104,10 @@
}
/**
- * Returns {@code true} if location time zone detection should run all the time on supported
- * devices, even when the user has not enabled it explicitly in settings. Enabled for internal
- * testing only. See {@link #isGeoDetectionExecutionEnabled()} and {@link #getDetectionMode()}
- * for details.
+ * Returns {@code true} if location time zone detection should run when auto time zone detection
+ * is enabled on supported devices, even when the user has not enabled the algorithm explicitly
+ * in settings. Enabled for internal testing only. See {@link #isGeoDetectionExecutionEnabled()}
+ * and {@link #getDetectionMode()} for details.
*/
boolean getGeoDetectionRunInBackgroundEnabledSetting() {
return mGeoDetectionRunInBackgroundEnabled;
@@ -219,6 +219,7 @@
private boolean getGeoDetectionRunInBackgroundEnabledBehavior() {
return isGeoDetectionSupported()
&& getLocationEnabledSetting()
+ && getAutoDetectionEnabledSetting()
&& getGeoDetectionRunInBackgroundEnabledSetting();
}
@@ -433,9 +434,9 @@
}
/**
- * Sets whether location time zone detection should run all the time on supported devices,
- * even when the user has not enabled it explicitly in settings. Enabled for internal
- * testing only.
+ * Sets whether location time zone detection should run when auto time zone detection is
+ * enabled on supported devices, even when the user has not enabled the algorithm explicitly
+ * in settings. Enabled for internal testing only.
*/
public Builder setGeoDetectionRunInBackgroundEnabled(boolean enabled) {
mGeoDetectionRunInBackgroundEnabled = enabled;
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index e9badef..32b3ccf 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -401,8 +401,10 @@
return;
}
- mWindowManager.mPolicy.onKeyguardOccludedChangedLw(isDisplayOccluded(DEFAULT_DISPLAY));
- if (isKeyguardLocked(displayId)) {
+ final boolean waitAppTransition = isKeyguardLocked(displayId);
+ mWindowManager.mPolicy.onKeyguardOccludedChangedLw(isDisplayOccluded(DEFAULT_DISPLAY),
+ waitAppTransition);
+ if (waitAppTransition) {
mService.deferWindowLayout();
try {
mRootWindowContainer.getDefaultDisplay()
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a63f9a3..ae7dc1e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -193,6 +193,7 @@
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.input.InputManager;
+import android.hardware.input.InputSettings;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -744,7 +745,7 @@
private final DisplayHashController mDisplayHashController;
volatile float mMaximumObscuringOpacityForTouch =
- InputManager.DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH;
+ InputSettings.DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH;
@VisibleForTesting
final WindowContextListenerController mWindowContextListenerController =
@@ -886,11 +887,11 @@
ContentResolver resolver = mContext.getContentResolver();
mMaximumObscuringOpacityForTouch = Settings.Global.getFloat(resolver,
Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH,
- InputManager.DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH);
+ InputSettings.DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH);
if (mMaximumObscuringOpacityForTouch < 0.0f
|| mMaximumObscuringOpacityForTouch > 1.0f) {
mMaximumObscuringOpacityForTouch =
- InputManager.DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH;
+ InputSettings.DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH;
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index 8f16737..5013fb0 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -33,6 +33,7 @@
import android.app.admin.PolicyUpdatesReceiver;
import android.app.admin.PolicyValue;
import android.app.admin.TargetUser;
+import android.app.admin.UserRestrictionPolicyKey;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -614,6 +615,47 @@
}
}
+ /**
+ * Returns all user restriction policies set by the given admin.
+ *
+ * <p>Pass in {@link UserHandle#USER_ALL} for {@code userId} to get global restrictions set by
+ * the admin
+ */
+ @NonNull
+ Set<UserRestrictionPolicyKey> getUserRestrictionPolicyKeysForAdmin(
+ @NonNull EnforcingAdmin admin,
+ int userId) {
+ Objects.requireNonNull(admin);
+ synchronized (mLock) {
+ if (userId == UserHandle.USER_ALL) {
+ return getUserRestrictionPolicyKeysForAdminLocked(mGlobalPolicies, admin);
+ }
+ if (!mLocalPolicies.contains(userId)) {
+ return Set.of();
+ }
+ return getUserRestrictionPolicyKeysForAdminLocked(mLocalPolicies.get(userId), admin);
+ }
+ }
+
+ private Set<UserRestrictionPolicyKey> getUserRestrictionPolicyKeysForAdminLocked(
+ Map<PolicyKey, PolicyState<?>> policies,
+ EnforcingAdmin admin) {
+ Set<UserRestrictionPolicyKey> keys = new HashSet<>();
+ for (PolicyKey key : policies.keySet()) {
+ if (!policies.get(key).getPolicyDefinition().isUserRestrictionPolicy()) {
+ continue;
+ }
+ // User restriction policies are always boolean
+ PolicyValue<Boolean> value = (PolicyValue<Boolean>) policies.get(key)
+ .getPoliciesSetByAdmins().get(admin);
+ if (value == null || !value.getValue()) {
+ continue;
+ }
+ keys.add((UserRestrictionPolicyKey) key);
+ }
+ return keys;
+ }
+
private <V> boolean hasLocalPolicyLocked(PolicyDefinition<V> policyDefinition, int userId) {
if (policyDefinition.isGlobalOnlyPolicy()) {
return false;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c847aa2..61ee0e7 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -264,6 +264,7 @@
import android.app.admin.SystemUpdateInfo;
import android.app.admin.SystemUpdatePolicy;
import android.app.admin.UnsafeStateException;
+import android.app.admin.UserRestrictionPolicyKey;
import android.app.admin.WifiSsidPolicy;
import android.app.backup.IBackupManager;
import android.app.compat.CompatChanges;
@@ -1351,7 +1352,7 @@
if (shouldMigrateToDevicePolicyEngine()) {
migratePoliciesToDevicePolicyEngine();
}
- if (isDevicePolicyEngineFlagEnabled()) {
+ if (isDevicePolicyEngineEnabled()) {
mDevicePolicyEngine.handlePackageChanged(packageName, userHandle);
}
// Persist updates if the removed package was an admin or delegate.
@@ -2044,7 +2045,7 @@
mUserManagerInternal.addUserLifecycleListener(new UserLifecycleListener());
mDeviceManagementResourcesProvider.load();
- if (isDevicePolicyEngineFlagEnabled()) {
+ if (isDevicePolicyEngineEnabled()) {
mDevicePolicyEngine.load();
}
@@ -2506,8 +2507,7 @@
if (profileOwner == null || !mUserManager.isManagedProfile(userId)) {
continue;
}
- maybeSetDefaultRestrictionsForAdminLocked(userId, profileOwner,
- UserRestrictionsUtils.getDefaultEnabledForManagedProfiles());
+ maybeSetDefaultRestrictionsForAdminLocked(userId, profileOwner);
ensureUnknownSourcesRestrictionForProfileOwnerLocked(
userId, profileOwner, false /* newOwner */);
}
@@ -2522,9 +2522,20 @@
ActiveAdmin profileOwner, boolean newOwner) {
if (newOwner || mInjector.settingsSecureGetIntForUser(
Settings.Secure.UNKNOWN_SOURCES_DEFAULT_REVERSED, 0, userId) != 0) {
- profileOwner.ensureUserRestrictions().putBoolean(
- UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, true);
- saveUserRestrictionsLocked(userId);
+ if (isDevicePolicyEngineEnabled()) {
+ mDevicePolicyEngine.setLocalPolicy(
+ PolicyDefinition.getPolicyDefinitionForUserRestriction(
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES),
+ EnforcingAdmin.createEnterpriseEnforcingAdmin(
+ profileOwner.info.getComponent(),
+ profileOwner.getUserHandle().getIdentifier()),
+ new BooleanPolicyValue(true),
+ userId);
+ } else {
+ profileOwner.ensureUserRestrictions().putBoolean(
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, true);
+ saveUserRestrictionsLocked(userId);
+ }
mInjector.settingsSecurePutIntForUser(
Settings.Secure.UNKNOWN_SOURCES_DEFAULT_REVERSED, 0, userId);
}
@@ -2533,11 +2544,28 @@
/**
* Apply default restrictions that haven't been applied to a given admin yet.
*/
- private void maybeSetDefaultRestrictionsForAdminLocked(
- int userId, ActiveAdmin admin, Set<String> defaultRestrictions) {
+ private void maybeSetDefaultRestrictionsForAdminLocked(int userId, ActiveAdmin admin) {
+ Set<String> defaultRestrictions =
+ UserRestrictionsUtils.getDefaultEnabledForManagedProfiles();
if (defaultRestrictions.equals(admin.defaultEnabledRestrictionsAlreadySet)) {
return; // The same set of default restrictions has been already applied.
}
+ if (isDevicePolicyEngineEnabled()) {
+ for (String restriction : defaultRestrictions) {
+ mDevicePolicyEngine.setLocalPolicy(
+ PolicyDefinition.getPolicyDefinitionForUserRestriction(restriction),
+ EnforcingAdmin.createEnterpriseEnforcingAdmin(
+ admin.info.getComponent(),
+ admin.getUserHandle().getIdentifier()),
+ new BooleanPolicyValue(true),
+ userId);
+ }
+ admin.defaultEnabledRestrictionsAlreadySet.addAll(defaultRestrictions);
+ Slogf.i(LOG_TAG, "Enabled the following restrictions by default: " +
+ defaultRestrictions);
+ return;
+ }
+
Slogf.i(LOG_TAG, "New user restrictions need to be set by default for user " + userId);
if (VERBOSE_LOG) {
@@ -3388,7 +3416,7 @@
updateNetworkPreferenceForUser(userId, preferentialNetworkServiceConfigs);
startOwnerService(userId, "start-user");
- if (isDevicePolicyEngineFlagEnabled()) {
+ if (isDevicePolicyEngineEnabled()) {
mDevicePolicyEngine.handleStartUser(userId);
}
}
@@ -3413,7 +3441,7 @@
void handleUnlockUser(int userId) {
startOwnerService(userId, "unlock-user");
- if (isDevicePolicyEngineFlagEnabled()) {
+ if (isDevicePolicyEngineEnabled()) {
mDevicePolicyEngine.handleUnlockUser(userId);
}
}
@@ -3425,7 +3453,7 @@
void handleStopUser(int userId) {
updateNetworkPreferenceForUser(userId, List.of(PreferentialNetworkServiceConfig.DEFAULT));
mDeviceAdminServiceController.stopServicesForUser(userId, /* actionForLog= */ "stop-user");
- if (isDevicePolicyEngineFlagEnabled()) {
+ if (isDevicePolicyEngineEnabled()) {
mDevicePolicyEngine.handleStopUser(userId);
}
}
@@ -9358,8 +9386,7 @@
mInjector.binderWithCleanCallingIdentity(() -> {
if (mUserManager.isManagedProfile(userHandle)) {
- maybeSetDefaultRestrictionsForAdminLocked(userHandle, admin,
- UserRestrictionsUtils.getDefaultEnabledForManagedProfiles());
+ maybeSetDefaultRestrictionsForAdminLocked(userHandle, admin);
ensureUnknownSourcesRestrictionForProfileOwnerLocked(userHandle, admin,
true /* newOwner */);
}
@@ -11503,7 +11530,7 @@
final int userId = user.id;
- if (isDevicePolicyEngineFlagEnabled()) {
+ if (isDevicePolicyEngineEnabled()) {
mDevicePolicyEngine.handleUserCreated(user);
}
@@ -12074,7 +12101,8 @@
}
@Override
- public void setUserRestriction(ComponentName who, String key, boolean enabledFromThisOwner,
+ public void setUserRestriction(
+ ComponentName who, String callerPackage, String key, boolean enabledFromThisOwner,
boolean parent) {
Objects.requireNonNull(who, "ComponentName is null");
@@ -12083,18 +12111,13 @@
if (!UserRestrictionsUtils.isValidRestriction(key)) {
return;
}
-
if (parent) {
Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller));
} else {
Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
}
-
int userId = caller.getUserId();
synchronized (getLockObject()) {
- final ActiveAdmin activeAdmin = getParentOfAdminIfRequired(
- getProfileOwnerOrDeviceOwnerLocked(userId), parent);
-
if (isDefaultDeviceOwner(caller)) {
if (!UserRestrictionsUtils.canDeviceOwnerChange(key)) {
throw new SecurityException("Device owner cannot set user restriction " + key);
@@ -12112,27 +12135,119 @@
boolean profileOwnerCanChangeOnItself = !parent
&& UserRestrictionsUtils.canProfileOwnerChange(
key, userId == getMainUserId());
- boolean orgOwnedProfileOwnerCanChangesGlobally = parent
+ boolean orgOwnedProfileOwnerCanChangeGlobally = parent
&& isProfileOwnerOfOrganizationOwnedDevice(caller)
&& UserRestrictionsUtils.canProfileOwnerOfOrganizationOwnedDeviceChange(
key);
- if (!profileOwnerCanChangeOnItself && !orgOwnedProfileOwnerCanChangesGlobally) {
+ if (!profileOwnerCanChangeOnItself && !orgOwnedProfileOwnerCanChangeGlobally) {
throw new SecurityException("Profile owner cannot set user restriction " + key);
}
}
- checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_USER_RESTRICTION);
-
- // Save the restriction to ActiveAdmin.
- final Bundle restrictions = activeAdmin.ensureUserRestrictions();
- if (enabledFromThisOwner) {
- restrictions.putBoolean(key, true);
- } else {
- restrictions.remove(key);
- }
- saveUserRestrictionsLocked(userId);
}
- final int eventId = enabledFromThisOwner
+ checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_USER_RESTRICTION);
+
+ if (useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
+ EnforcingAdmin admin = EnforcingAdmin.createEnterpriseEnforcingAdmin(
+ who, caller.getUserId());
+ PolicyDefinition<Boolean> policyDefinition =
+ PolicyDefinition.getPolicyDefinitionForUserRestriction(key);
+ if (enabledFromThisOwner) {
+ mDevicePolicyEngine.setLocalPolicy(
+ policyDefinition,
+ admin,
+ new BooleanPolicyValue(true),
+ parent ? getProfileParentId(userId) : userId);
+ } else {
+ // Remove any local and global policy that was set by the admin
+ if (!policyDefinition.isLocalOnlyPolicy()) {
+ mDevicePolicyEngine.removeGlobalPolicy(
+ policyDefinition,
+ admin);
+ }
+ if (!policyDefinition.isGlobalOnlyPolicy()) {
+ mDevicePolicyEngine.removeLocalPolicy(
+ policyDefinition,
+ admin,
+ userId);
+
+ int parentUserId = getProfileParentId(userId);
+ if (parentUserId != userId) {
+ mDevicePolicyEngine.removeLocalPolicy(
+ policyDefinition,
+ admin,
+ parentUserId);
+ }
+ }
+ }
+ } else {
+ synchronized (getLockObject()) {
+ final ActiveAdmin activeAdmin = getParentOfAdminIfRequired(
+ getProfileOwnerOrDeviceOwnerLocked(userId), parent);
+ // Save the restriction to ActiveAdmin.
+ final Bundle restrictions = activeAdmin.ensureUserRestrictions();
+ if (enabledFromThisOwner) {
+ restrictions.putBoolean(key, true);
+ } else {
+ restrictions.remove(key);
+ }
+ saveUserRestrictionsLocked(userId);
+ }
+ }
+ logUserRestrictionCall(key, enabledFromThisOwner, parent, caller);
+ }
+
+ @Override
+ public void setUserRestrictionGlobally(String callerPackage, String key) {
+ final CallerIdentity caller = getCallerIdentity(callerPackage);
+ // TODO: Replace with new permission checks, for now copying this over from
+ // setUserRestriction
+ if (!UserRestrictionsUtils.isValidRestriction(key)) {
+ return;
+ }
+ Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+
+ int userHandle = caller.getUserId();
+ if (isDefaultDeviceOwner(caller)) {
+ if (!UserRestrictionsUtils.canDeviceOwnerChange(key)) {
+ throw new SecurityException("Device owner cannot set user restriction " + key);
+ }
+ } else if (isFinancedDeviceOwner(caller)) {
+ if (!UserRestrictionsUtils.canFinancedDeviceOwnerChange(key)) {
+ throw new SecurityException("Cannot set user restriction " + key
+ + " when managing a financed device");
+ }
+ } else {
+ boolean profileOwnerCanChangeOnItself =
+ UserRestrictionsUtils.canProfileOwnerChange(key, userHandle == getMainUserId());
+ boolean orgOwnedProfileOwnerCanChangeGlobally =
+ isProfileOwnerOfOrganizationOwnedDevice(caller)
+ && UserRestrictionsUtils.canProfileOwnerOfOrganizationOwnedDeviceChange(
+ key);
+
+ if (!profileOwnerCanChangeOnItself && !orgOwnedProfileOwnerCanChangeGlobally) {
+ throw new SecurityException("Profile owner cannot set user restriction " + key);
+ }
+ }
+ checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_USER_RESTRICTION);
+
+ if (!useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
+ throw new IllegalStateException("One or more admins are not targeting Android 14.");
+ }
+ EnforcingAdmin admin = EnforcingAdmin.createEnterpriseEnforcingAdmin(
+ caller.getPackageName(), caller.getUserId());
+
+ mDevicePolicyEngine.setGlobalPolicy(
+ PolicyDefinition.getPolicyDefinitionForUserRestriction(key),
+ admin,
+ new BooleanPolicyValue(true));
+
+ logUserRestrictionCall(key, /* enabled= */ true, /* parent= */ false, caller);
+ }
+
+ private void logUserRestrictionCall(
+ String key, boolean enabled, boolean parent, CallerIdentity caller) {
+ final int eventId = enabled
? DevicePolicyEnums.ADD_USER_RESTRICTION
: DevicePolicyEnums.REMOVE_USER_RESTRICTION;
DevicePolicyEventLogger
@@ -12141,14 +12256,18 @@
.setStrings(key, parent ? CALLED_FROM_PARENT : NOT_CALLED_FROM_PARENT)
.write();
if (SecurityLog.isLoggingEnabled()) {
- final int eventTag = enabledFromThisOwner
+ final int eventTag = enabled
? SecurityLog.TAG_USER_RESTRICTION_ADDED
: SecurityLog.TAG_USER_RESTRICTION_REMOVED;
- SecurityLog.writeEvent(eventTag, who.getPackageName(), userId, key);
+ SecurityLog.writeEvent(eventTag, caller.getPackageName(), caller.getUserId(), key);
}
}
private void saveUserRestrictionsLocked(int userId) {
+ if (isDevicePolicyEngineEnabled()) {
+ // User restrictions are handled in the policy engine
+ return;
+ }
saveSettingsLocked(userId);
pushUserRestrictions(userId);
sendChangedNotification(userId);
@@ -12210,7 +12329,7 @@
}
@Override
- public Bundle getUserRestrictions(ComponentName who, boolean parent) {
+ public Bundle getUserRestrictions(ComponentName who, String callerPackage, boolean parent) {
if (!mHasFeature) {
return null;
}
@@ -12222,14 +12341,59 @@
|| isProfileOwner(caller)
|| (parent && isProfileOwnerOfOrganizationOwnedDevice(caller)));
- synchronized (getLockObject()) {
- final ActiveAdmin activeAdmin = getParentOfAdminIfRequired(
- getProfileOwnerOrDeviceOwnerLocked(caller.getUserId()), parent);
- return activeAdmin.userRestrictions;
+ if (useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
+ return getUserRestrictionsFromPolicyEngine(
+ EnforcingAdmin.createEnterpriseEnforcingAdmin(who, caller.getUserId()),
+ parent ? getProfileParentId(caller.getUserId()) : caller.getUserId());
+ } else {
+ synchronized (getLockObject()) {
+ final ActiveAdmin activeAdmin = getParentOfAdminIfRequired(
+ getProfileOwnerOrDeviceOwnerLocked(caller.getUserId()), parent);
+ return activeAdmin.userRestrictions;
+ }
}
}
@Override
+ public Bundle getUserRestrictionsGlobally(String callerPackage) {
+ if (!mHasFeature) {
+ return null;
+ }
+ final CallerIdentity caller = getCallerIdentity(callerPackage);
+ if (!useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
+ throw new IllegalStateException("One or more admins are not targeting Android 14.");
+ }
+ // TODO: Replace with new permission checks, for now copying this over from
+ // setUserRestriction
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
+ || isFinancedDeviceOwner(caller)
+ || isProfileOwner(caller));
+
+ return getUserRestrictionsFromPolicyEngine(
+ EnforcingAdmin.createEnterpriseEnforcingAdmin(
+ caller.getPackageName(), caller.getUserId()),
+ UserHandle.USER_ALL);
+ }
+
+ /**
+ * Returns user restrictions set by the given admin for the provided {@code userId}.
+ *
+ * <p>Pass in {@link UserHandle#USER_ALL} for {@code userId} to get global restrictions set by
+ * the admin
+ */
+ private Bundle getUserRestrictionsFromPolicyEngine(EnforcingAdmin admin, int userId) {
+ Set<UserRestrictionPolicyKey> restrictionKeys =
+ mDevicePolicyEngine.getUserRestrictionPolicyKeysForAdmin(
+ admin,
+ userId);
+ Bundle restrictions = new Bundle();
+ for (UserRestrictionPolicyKey key : restrictionKeys) {
+ restrictions.putBoolean(key.getRestriction(), true);
+ }
+ return restrictions;
+ }
+
+ @Override
public boolean setApplicationHidden(ComponentName who, String callerPackage, String packageName,
boolean hidden, boolean parent) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
@@ -13639,7 +13803,8 @@
+ " should be used instead.");
} else {
try {
- setUserRestriction(who, UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
+ setUserRestriction(who, who.getPackageName(),
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
(Integer.parseInt(value) == 0) ? true : false, /* parent */ false);
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_SECURE_SETTING)
@@ -13696,7 +13861,8 @@
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_MASTER_VOLUME_MUTED);
synchronized (getLockObject()) {
- setUserRestriction(who, UserManager.DISALLOW_UNMUTE_DEVICE, on, /* parent */ false);
+ setUserRestriction(who, who.getPackageName(), UserManager.DISALLOW_UNMUTE_DEVICE, on,
+ /* parent */ false);
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_MASTER_VOLUME_MUTED)
.setAdmin(who)
@@ -18308,6 +18474,15 @@
}
});
}
+ String[] appOpExemptions = new String[exemptions.length];
+ for (int i = 0; i < exemptions.length; i++) {
+ appOpExemptions[i] = APPLICATION_EXEMPTION_CONSTANTS_TO_APP_OPS.get(exemptions[i]);
+ }
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_APPLICATION_EXEMPTIONS)
+ .setAdmin(caller.getPackageName())
+ .setStrings(packageName, appOpExemptions)
+ .write();
}
@Override
@@ -21294,10 +21469,14 @@
}
return true;
} else {
- return isDevicePolicyEngineFlagEnabled() && !hasDPCsNotSupportingCoexistence();
+ return isDevicePolicyEngineEnabled();
}
}
+ private boolean isDevicePolicyEngineEnabled() {
+ return isDevicePolicyEngineFlagEnabled() && !hasDPCsNotSupportingCoexistence();
+ }
+
private boolean isDevicePolicyEngineFlagEnabled() {
return DeviceConfig.getBoolean(
NAMESPACE_DEVICE_POLICY_MANAGER,
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java
index a303fde..7ed148b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java
@@ -93,6 +93,15 @@
activeAdmin);
}
+
+ static EnforcingAdmin createEnterpriseEnforcingAdmin(
+ @NonNull String packageName, int userId) {
+ Objects.requireNonNull(packageName);
+ return new EnforcingAdmin(
+ packageName, /* componentName= */ null, Set.of(DPC_AUTHORITY), userId,
+ /* activeAdmin= */ null);
+ }
+
static EnforcingAdmin createDeviceAdminEnforcingAdmin(ComponentName componentName, int userId) {
Objects.requireNonNull(componentName);
return new EnforcingAdmin(
@@ -130,10 +139,9 @@
}
private EnforcingAdmin(
- String packageName, ComponentName componentName, Set<String> authorities, int userId,
- ActiveAdmin activeAdmin) {
+ String packageName, @Nullable ComponentName componentName, Set<String> authorities,
+ int userId, @Nullable ActiveAdmin activeAdmin) {
Objects.requireNonNull(packageName);
- Objects.requireNonNull(componentName);
Objects.requireNonNull(authorities);
// Role authorities should not be using this constructor
@@ -262,10 +270,12 @@
@Override
public int hashCode() {
if (mIsRoleAuthority) {
- // TODO(b/256854977): should we add UserId?
- return Objects.hash(mPackageName);
+ return Objects.hash(mPackageName, mUserId);
} else {
- return Objects.hash(mComponentName, getAuthorities());
+ return Objects.hash(
+ mComponentName == null ? mPackageName : mComponentName,
+ mUserId,
+ getAuthorities());
}
}
@@ -274,8 +284,10 @@
serializer.attributeBoolean(/* namespace= */ null, ATTR_IS_ROLE, mIsRoleAuthority);
serializer.attributeInt(/* namespace= */ null, ATTR_USER_ID, mUserId);
if (!mIsRoleAuthority) {
- serializer.attribute(
- /* namespace= */ null, ATTR_CLASS_NAME, mComponentName.getClassName());
+ if (mComponentName != null) {
+ serializer.attribute(
+ /* namespace= */ null, ATTR_CLASS_NAME, mComponentName.getClassName());
+ }
// Role authorities get recomputed on load so no need to save them.
serializer.attribute(
/* namespace= */ null,
@@ -295,7 +307,8 @@
return new EnforcingAdmin(packageName, userId, null);
} else {
String className = parser.getAttributeValue(/* namespace= */ null, ATTR_CLASS_NAME);
- ComponentName componentName = new ComponentName(packageName, className);
+ ComponentName componentName = className == null
+ ? null : new ComponentName(packageName, className);
Set<String> authorities = Set.of(authoritiesStr.split(ATTR_AUTHORITIES_SEPARATOR));
return new EnforcingAdmin(packageName, componentName, authorities, userId, null);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
index 21c9434..a08c2054 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
@@ -29,10 +29,12 @@
import android.app.admin.PackagePolicyKey;
import android.app.admin.PolicyKey;
import android.app.admin.PolicyValue;
+import android.app.admin.UserRestrictionPolicyKey;
import android.content.ComponentName;
import android.content.Context;
import android.content.IntentFilter;
import android.os.Bundle;
+import android.os.UserManager;
import com.android.internal.util.function.QuadFunction;
import com.android.modules.utils.TypedXmlPullParser;
@@ -41,6 +43,7 @@
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -67,6 +70,11 @@
// global policy please add support.
private static final int POLICY_FLAG_NON_COEXISTABLE_POLICY = 1 << 3;
+ // Add this flag to any policy that is a user restriction, the reason for this is that there
+ // are some special APIs to handle user restriction policies and this is the way we can identify
+ // them.
+ private static final int POLICY_FLAG_USER_RESTRICTION_POLICY = 1 << 4;
+
private static final MostRestrictive<Boolean> FALSE_MORE_RESTRICTIVE = new MostRestrictive<>(
List.of(new BooleanPolicyValue(false), new BooleanPolicyValue(true)));
@@ -238,21 +246,124 @@
(Long value, Context context, Integer userId, PolicyKey policyKey) -> true,
new LongPolicySerializer());
- private static final Map<String, PolicyDefinition<?>> sPolicyDefinitions = Map.of(
- DevicePolicyIdentifiers.AUTO_TIMEZONE_POLICY, AUTO_TIMEZONE,
- DevicePolicyIdentifiers.PERMISSION_GRANT_POLICY, GENERIC_PERMISSION_GRANT,
- DevicePolicyIdentifiers.LOCK_TASK_POLICY, LOCK_TASK,
- DevicePolicyIdentifiers.USER_CONTROL_DISABLED_PACKAGES_POLICY,
- USER_CONTROLLED_DISABLED_PACKAGES,
- DevicePolicyIdentifiers.PERSISTENT_PREFERRED_ACTIVITY_POLICY,
- GENERIC_PERSISTENT_PREFERRED_ACTIVITY,
- DevicePolicyIdentifiers.PACKAGE_UNINSTALL_BLOCKED_POLICY,
- GENERIC_PACKAGE_UNINSTALL_BLOCKED,
- DevicePolicyIdentifiers.APPLICATION_RESTRICTIONS_POLICY,
- GENERIC_APPLICATION_RESTRICTIONS,
- DevicePolicyIdentifiers.RESET_PASSWORD_TOKEN_POLICY,
- RESET_PASSWORD_TOKEN
- );
+ private static final Map<String, PolicyDefinition<?>> POLICY_DEFINITIONS = new HashMap<>();
+ private static Map<String, Integer> USER_RESTRICTION_FLAGS = new HashMap<>();
+
+ static {
+ POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.AUTO_TIMEZONE_POLICY, AUTO_TIMEZONE);
+ POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.PERMISSION_GRANT_POLICY,
+ GENERIC_PERMISSION_GRANT);
+ POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.LOCK_TASK_POLICY, LOCK_TASK);
+ POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.USER_CONTROL_DISABLED_PACKAGES_POLICY,
+ USER_CONTROLLED_DISABLED_PACKAGES);
+ POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.PERSISTENT_PREFERRED_ACTIVITY_POLICY,
+ GENERIC_PERSISTENT_PREFERRED_ACTIVITY);
+ POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.PACKAGE_UNINSTALL_BLOCKED_POLICY,
+ GENERIC_PACKAGE_UNINSTALL_BLOCKED);
+ POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.APPLICATION_RESTRICTIONS_POLICY,
+ GENERIC_APPLICATION_RESTRICTIONS);
+ POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.RESET_PASSWORD_TOKEN_POLICY,
+ RESET_PASSWORD_TOKEN);
+
+ // User Restriction Policies
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_MODIFY_ACCOUNTS, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CONFIG_WIFI, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(
+ UserManager.DISALLOW_CHANGE_WIFI_STATE, POLICY_FLAG_GLOBAL_ONLY_POLICY);
+ USER_RESTRICTION_FLAGS.put(
+ UserManager.DISALLOW_WIFI_TETHERING, POLICY_FLAG_GLOBAL_ONLY_POLICY);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_GRANT_ADMIN, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(
+ UserManager.DISALLOW_SHARING_ADMIN_CONFIGURED_WIFI, POLICY_FLAG_GLOBAL_ONLY_POLICY);
+ USER_RESTRICTION_FLAGS.put(
+ UserManager.DISALLOW_WIFI_DIRECT, POLICY_FLAG_GLOBAL_ONLY_POLICY);
+ USER_RESTRICTION_FLAGS.put(
+ UserManager.DISALLOW_ADD_WIFI_CONFIG, POLICY_FLAG_GLOBAL_ONLY_POLICY);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CONFIG_LOCALE, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_INSTALL_APPS, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_UNINSTALL_APPS, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_SHARE_LOCATION, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(
+ UserManager.DISALLOW_AIRPLANE_MODE, POLICY_FLAG_GLOBAL_ONLY_POLICY);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CONFIG_BRIGHTNESS, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_AMBIENT_DISPLAY, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CONFIG_SCREEN_TIMEOUT, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY,
+ POLICY_FLAG_GLOBAL_ONLY_POLICY);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CONFIG_BLUETOOTH, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_BLUETOOTH, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_BLUETOOTH_SHARING, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(
+ UserManager.DISALLOW_USB_FILE_TRANSFER, POLICY_FLAG_GLOBAL_ONLY_POLICY);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CONFIG_CREDENTIALS, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_REMOVE_USER, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_DEBUGGING_FEATURES, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CONFIG_VPN, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CONFIG_LOCATION, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CONFIG_DATE_TIME, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(
+ UserManager.DISALLOW_CONFIG_TETHERING, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(
+ UserManager.DISALLOW_NETWORK_RESET, POLICY_FLAG_GLOBAL_ONLY_POLICY);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_FACTORY_RESET, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_ADD_USER, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_ADD_MANAGED_PROFILE, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_ADD_CLONE_PROFILE, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.ENSURE_VERIFY_APPS, POLICY_FLAG_GLOBAL_ONLY_POLICY);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CONFIG_CELL_BROADCASTS, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_APPS_CONTROL, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_UNMUTE_MICROPHONE, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_ADJUST_VOLUME, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_OUTGOING_CALLS, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_SMS, /* flags= */ 0);
+ // TODO: check if its global only
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_FUN, /* flags= */ 0);
+ // TODO: check if its global only
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CREATE_WINDOWS, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE, /* flags= */ 0);
+ // TODO: check if its global only
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_OUTGOING_BEAM, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_WALLPAPER, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_SET_WALLPAPER, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_SAFE_BOOT, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_RECORD_AUDIO, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_RUN_IN_BACKGROUND, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CAMERA, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_UNMUTE_DEVICE, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_DATA_ROAMING, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_SET_USER_ICON, /* flags= */ 0);
+ // TODO: double check flags
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_OEM_UNLOCK, POLICY_FLAG_GLOBAL_ONLY_POLICY);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_UNIFIED_PASSWORD, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.ALLOW_PARENT_PROFILE_APP_LINKING, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_AUTOFILL, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CONTENT_CAPTURE, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CONTENT_SUGGESTIONS, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(
+ UserManager.DISALLOW_USER_SWITCH, POLICY_FLAG_GLOBAL_ONLY_POLICY);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_PRINTING, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(
+ UserManager.DISALLOW_CONFIG_PRIVATE_DNS, POLICY_FLAG_GLOBAL_ONLY_POLICY);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_MICROPHONE_TOGGLE, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CAMERA_TOGGLE, /* flags= */ 0);
+ // TODO: check if its global only
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_BIOMETRIC, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CONFIG_DEFAULT_APPS, /* flags= */ 0);
+ USER_RESTRICTION_FLAGS.put(
+ UserManager.DISALLOW_CELLULAR_2G, POLICY_FLAG_GLOBAL_ONLY_POLICY);
+ USER_RESTRICTION_FLAGS.put(
+ UserManager.DISALLOW_ULTRA_WIDEBAND_RADIO, POLICY_FLAG_GLOBAL_ONLY_POLICY);
+
+ for (String key : USER_RESTRICTION_FLAGS.keySet()) {
+ createAndAddUserRestrictionPolicyDefinition(key, USER_RESTRICTION_FLAGS.get(key));
+ }
+ }
private final PolicyKey mPolicyKey;
private final ResolutionMechanism<V> mResolutionMechanism;
@@ -267,6 +378,17 @@
mPolicyEnforcerCallback, mPolicySerializer);
}
+ static PolicyDefinition<Boolean> getPolicyDefinitionForUserRestriction(
+ @UserManager.UserRestrictionKey String restriction) {
+ String key = DevicePolicyIdentifiers.getIdentifierForUserRestriction(restriction);
+
+ if (!POLICY_DEFINITIONS.containsKey(key)) {
+ throw new IllegalArgumentException("Unsupported user restriction " + restriction);
+ }
+ // All user restrictions are of type boolean
+ return (PolicyDefinition<Boolean>) POLICY_DEFINITIONS.get(key);
+ }
+
@NonNull
PolicyKey getPolicyKey() {
return mPolicyKey;
@@ -305,6 +427,10 @@
return (mPolicyFlags & POLICY_FLAG_NON_COEXISTABLE_POLICY) != 0;
}
+ boolean isUserRestrictionPolicy() {
+ return (mPolicyFlags & POLICY_FLAG_USER_RESTRICTION_POLICY) != 0;
+ }
+
@Nullable
PolicyValue<V> resolvePolicy(LinkedHashMap<EnforcingAdmin, PolicyValue<V>> adminsPolicy) {
return mResolutionMechanism.resolve(adminsPolicy);
@@ -314,6 +440,21 @@
return mPolicyEnforcerCallback.apply(value, context, userId, mPolicyKey);
}
+ private static void createAndAddUserRestrictionPolicyDefinition(
+ String restriction, int flags) {
+ String identifier = DevicePolicyIdentifiers.getIdentifierForUserRestriction(restriction);
+ UserRestrictionPolicyKey key = new UserRestrictionPolicyKey(identifier, restriction);
+ flags |= POLICY_FLAG_USER_RESTRICTION_POLICY;
+ PolicyDefinition<Boolean> definition = new PolicyDefinition<>(
+ key,
+ TRUE_MORE_RESTRICTIVE,
+ flags,
+ PolicyEnforcerCallbacks::setUserRestriction,
+ new BooleanPolicySerializer());
+ POLICY_DEFINITIONS.put(key.getIdentifier(), definition);
+ }
+
+
/**
* Callers must ensure that {@code policyType} have implemented an appropriate
* {@link Object#equals} implementation.
@@ -360,7 +501,7 @@
// TODO: can we avoid casting?
PolicyKey policyKey = readPolicyKeyFromXml(parser);
PolicyDefinition<V> genericPolicyDefinition =
- (PolicyDefinition<V>) sPolicyDefinitions.get(policyKey.getIdentifier());
+ (PolicyDefinition<V>) POLICY_DEFINITIONS.get(policyKey.getIdentifier());
return genericPolicyDefinition.createPolicyDefinition(policyKey);
}
@@ -369,7 +510,7 @@
// TODO: can we avoid casting?
PolicyKey policyKey = PolicyKey.readGenericPolicyKeyFromXml(parser);
PolicyDefinition<PolicyValue<V>> genericPolicyDefinition =
- (PolicyDefinition<PolicyValue<V>>) sPolicyDefinitions.get(
+ (PolicyDefinition<PolicyValue<V>>) POLICY_DEFINITIONS.get(
policyKey.getIdentifier());
return genericPolicyDefinition.mPolicyKey.readFromXml(parser);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
index 4ae7ca6..daa8a26 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
@@ -25,6 +25,7 @@
import android.app.admin.PackagePermissionPolicyKey;
import android.app.admin.PackagePolicyKey;
import android.app.admin.PolicyKey;
+import android.app.admin.UserRestrictionPolicyKey;
import android.content.ComponentName;
import android.content.Context;
import android.content.IntentFilter;
@@ -40,6 +41,7 @@
import android.util.Slog;
import com.android.server.LocalServices;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.utils.Slogf;
import java.util.Collections;
@@ -202,4 +204,22 @@
return true;
}));
}
+
+ static boolean setUserRestriction(
+ @Nullable Boolean enabled, @NonNull Context context, int userId,
+ @NonNull PolicyKey policyKey) {
+ return Boolean.TRUE.equals(Binder.withCleanCallingIdentity(() -> {
+ if (!(policyKey instanceof UserRestrictionPolicyKey)) {
+ throw new IllegalArgumentException("policyKey is not of type "
+ + "UserRestrictionPolicyKey");
+ }
+ UserRestrictionPolicyKey parsedKey =
+ (UserRestrictionPolicyKey) policyKey;
+ // TODO: call into new UserManager API when merged
+ UserManagerInternal userManager = LocalServices.getService(UserManagerInternal.class);
+// userManager.setUserRestriction(
+// userId, parsedKey.getRestriction(), enabled != null && enabled);
+ return true;
+ }));
+ }
}
diff --git a/services/searchui/java/com/android/server/searchui/SearchUiManagerService.java b/services/searchui/java/com/android/server/searchui/SearchUiManagerService.java
index 29ad537..c259701 100644
--- a/services/searchui/java/com/android/server/searchui/SearchUiManagerService.java
+++ b/services/searchui/java/com/android/server/searchui/SearchUiManagerService.java
@@ -140,12 +140,6 @@
}
@Override
- public void requestEmptyQueryResultUpdate(@NonNull SearchSessionId sessionId) {
- runForUserLocked("requestEmptyQueryResultUpdate", sessionId,
- (service) -> service.requestEmptyQueryResultUpdateLocked(sessionId));
- }
-
- @Override
public void destroySearchSession(@NonNull SearchSessionId sessionId) {
runForUserLocked("destroySearchSession", sessionId,
(service) -> service.onDestroyLocked(sessionId));
diff --git a/services/searchui/java/com/android/server/searchui/SearchUiPerUserService.java b/services/searchui/java/com/android/server/searchui/SearchUiPerUserService.java
index 0d70fff..dc150cf 100644
--- a/services/searchui/java/com/android/server/searchui/SearchUiPerUserService.java
+++ b/services/searchui/java/com/android/server/searchui/SearchUiPerUserService.java
@@ -181,16 +181,6 @@
}
/**
- * Requests a new set of search targets for empty query result used for zero state.
- */
- @GuardedBy("mLock")
- public void requestEmptyQueryResultUpdateLocked(@NonNull SearchSessionId sessionId) {
- final SearchSessionInfo sessionInfo = mSessionInfos.get(sessionId);
- if (sessionInfo == null) return;
- resolveService(sessionId, s->s.onRequestEmptyQueryResultUpdate(sessionId));
- }
-
- /**
* Notifies the service of the end of an existing search session.
*/
@GuardedBy("mLock")
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java
index 3750aa0..2fbbf0d 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java
@@ -49,6 +49,7 @@
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
+import android.app.BackgroundStartPrivileges;
import android.app.IActivityManager;
import android.app.job.JobInfo;
import android.content.ComponentName;
@@ -63,6 +64,7 @@
import android.provider.DeviceConfig;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.SparseIntArray;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -178,6 +180,8 @@
mGracePeriodObserver = mock(GracePeriodObserver.class);
mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
+ doReturn(BackgroundStartPrivileges.NONE)
+ .when(mActivityManagerInternal).getBackgroundStartPrivileges(anyInt());
mDefaultUserId = mNextUserId;
createCurrentUser(true);
mNextUserId = 10;
@@ -601,34 +605,75 @@
@Test
public void testHasImmediacyPrivilege() {
- JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE, 0);
+ final int uid = mDefaultUserId * UserHandle.PER_USER_RANGE;
+ JobStatus job = createJob(uid, 0);
spyOn(job);
- assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job));
+ doReturn(BackgroundStartPrivileges.NONE)
+ .when(mActivityManagerInternal).getBackgroundStartPrivileges(uid);
+
+ assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job, new SparseIntArray()));
doReturn(false).when(job).shouldTreatAsExpeditedJob();
doReturn(false).when(job).shouldTreatAsUserInitiatedJob();
job.lastEvaluatedBias = JobInfo.BIAS_TOP_APP;
- assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job));
+ assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job, new SparseIntArray()));
doReturn(true).when(job).shouldTreatAsExpeditedJob();
doReturn(false).when(job).shouldTreatAsUserInitiatedJob();
job.lastEvaluatedBias = JobInfo.BIAS_DEFAULT;
- assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job));
+ assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job, new SparseIntArray()));
doReturn(false).when(job).shouldTreatAsExpeditedJob();
doReturn(true).when(job).shouldTreatAsUserInitiatedJob();
job.lastEvaluatedBias = JobInfo.BIAS_DEFAULT;
- assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job));
+ assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job, new SparseIntArray()));
doReturn(false).when(job).shouldTreatAsExpeditedJob();
doReturn(true).when(job).shouldTreatAsUserInitiatedJob();
job.lastEvaluatedBias = JobInfo.BIAS_TOP_APP;
- assertTrue(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job));
+ assertTrue(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job, new SparseIntArray()));
doReturn(true).when(job).shouldTreatAsExpeditedJob();
doReturn(false).when(job).shouldTreatAsUserInitiatedJob();
job.lastEvaluatedBias = JobInfo.BIAS_TOP_APP;
- assertTrue(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job));
+ assertTrue(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job,
+ new SparseIntArray()));
+
+ doReturn(BackgroundStartPrivileges.ALLOW_FGS)
+ .when(mActivityManagerInternal).getBackgroundStartPrivileges(uid);
+
+ doReturn(false).when(job).shouldTreatAsExpeditedJob();
+ doReturn(false).when(job).shouldTreatAsUserInitiatedJob();
+ job.lastEvaluatedBias = JobInfo.BIAS_DEFAULT;
+ assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job, new SparseIntArray()));
+
+ doReturn(true).when(job).shouldTreatAsExpeditedJob();
+ doReturn(false).when(job).shouldTreatAsUserInitiatedJob();
+ job.lastEvaluatedBias = JobInfo.BIAS_DEFAULT;
+ assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job, new SparseIntArray()));
+
+ doReturn(false).when(job).shouldTreatAsExpeditedJob();
+ doReturn(true).when(job).shouldTreatAsUserInitiatedJob();
+ job.lastEvaluatedBias = JobInfo.BIAS_DEFAULT;
+ assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job, new SparseIntArray()));
+
+ doReturn(BackgroundStartPrivileges.ALLOW_BAL)
+ .when(mActivityManagerInternal).getBackgroundStartPrivileges(uid);
+
+ doReturn(false).when(job).shouldTreatAsExpeditedJob();
+ doReturn(false).when(job).shouldTreatAsUserInitiatedJob();
+ job.lastEvaluatedBias = JobInfo.BIAS_DEFAULT;
+ assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job, new SparseIntArray()));
+
+ doReturn(true).when(job).shouldTreatAsExpeditedJob();
+ doReturn(false).when(job).shouldTreatAsUserInitiatedJob();
+ job.lastEvaluatedBias = JobInfo.BIAS_DEFAULT;
+ assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job, new SparseIntArray()));
+
+ doReturn(false).when(job).shouldTreatAsExpeditedJob();
+ doReturn(true).when(job).shouldTreatAsUserInitiatedJob();
+ job.lastEvaluatedBias = JobInfo.BIAS_DEFAULT;
+ assertTrue(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job, new SparseIntArray()));
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
index b5c5582..120ddf6 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
@@ -49,6 +49,7 @@
import com.android.server.SystemService;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -156,6 +157,7 @@
@SmallTest
@Test
+ @Ignore("b/269249457")
public void testCompMigrationAffiliated() throws Exception {
prepareAdmin1AsDo();
prepareAdmin1AsPo(COPE_PROFILE_USER_ID, Build.VERSION_CODES.R);
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
index 0d6bb8a..566c6b0 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
@@ -570,7 +570,7 @@
.setAutoDetectionEnabledSetting(false)
.build();
assertFalse(config.getAutoDetectionEnabledBehavior());
- assertTrue(config.isGeoDetectionExecutionEnabled());
+ assertFalse(config.isGeoDetectionExecutionEnabled());
assertEquals(DETECTION_MODE_MANUAL, config.getDetectionMode());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerTest.java
index 7b1db95..aeb8ec8 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerTest.java
@@ -24,6 +24,7 @@
import static android.service.timezone.TimeZoneProviderStatus.OPERATION_STATUS_UNKNOWN;
import static com.android.server.timezonedetector.ConfigurationInternal.DETECTION_MODE_MANUAL;
+import static com.android.server.timezonedetector.ConfigurationInternal.DETECTION_MODE_TELEPHONY;
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
@@ -1385,6 +1386,37 @@
}
/**
+ * A controller-state-only test to prove that "run in background" doesn't enable the
+ * location-based time zone detection algorithm to run when auto detection is disabled.
+ */
+ @Test
+ public void geoDetectionRunInBackground_obeysAutoDetectionDisabled() {
+ LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController(
+ mTestThreadingDomain, mTestMetricsLogger, mTestPrimaryLocationTimeZoneProvider,
+ mTestSecondaryLocationTimeZoneProvider, false /* recordStateChanges */);
+
+ // A configuration where the user has auto detection disabled.
+ ConfigurationInternal autoTimeZoneDisabledConfig =
+ new ConfigurationInternal.Builder(USER1_CONFIG_GEO_DETECTION_DISABLED)
+ .setLocationEnabledSetting(true)
+ .setAutoDetectionEnabledSetting(false)
+ .setGeoDetectionEnabledSetting(true)
+ .setGeoDetectionRunInBackgroundEnabled(true)
+ .build();
+ assertEquals(DETECTION_MODE_MANUAL, autoTimeZoneDisabledConfig.getDetectionMode());
+ assertFalse(autoTimeZoneDisabledConfig.isGeoDetectionExecutionEnabled());
+
+ TestEnvironment testEnvironment = new TestEnvironment(
+ mTestThreadingDomain, controller, autoTimeZoneDisabledConfig);
+
+ // Initialize and check initial state.
+ controller.initialize(testEnvironment, mTestCallback);
+
+ assertControllerState(controller, STATE_STOPPED);
+ mTestMetricsLogger.assertStateChangesAndCommit(STATE_PROVIDERS_INITIALIZING, STATE_STOPPED);
+ }
+
+ /**
* A controller-state-only test to prove that "run in background" configuration behaves as
* intended. Provider states are well covered by other "enabled" tests.
*/
@@ -1398,7 +1430,7 @@
ConfigurationInternal runInBackgroundDisabledConfig =
new ConfigurationInternal.Builder(USER1_CONFIG_GEO_DETECTION_DISABLED)
.setLocationEnabledSetting(true)
- .setAutoDetectionEnabledSetting(false)
+ .setAutoDetectionEnabledSetting(true)
.setGeoDetectionEnabledSetting(false)
.setGeoDetectionRunInBackgroundEnabled(false)
.build();
@@ -1408,7 +1440,7 @@
new ConfigurationInternal.Builder(runInBackgroundDisabledConfig)
.setGeoDetectionRunInBackgroundEnabled(true)
.build();
- assertEquals(DETECTION_MODE_MANUAL, runInBackgroundEnabledConfig.getDetectionMode());
+ assertEquals(DETECTION_MODE_TELEPHONY, runInBackgroundEnabledConfig.getDetectionMode());
assertTrue(runInBackgroundEnabledConfig.isGeoDetectionExecutionEnabled());
TestEnvironment testEnvironment = new TestEnvironment(
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 3344bdb..1d0715a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -222,7 +222,7 @@
}
@Override
- public void onKeyguardOccludedChangedLw(boolean occluded) {
+ public void onKeyguardOccludedChangedLw(boolean occluded, boolean waitAppTransition) {
}
public void setSafeMode(boolean safeMode) {