summaryrefslogtreecommitdiff
path: root/services
diff options
context:
space:
mode:
Diffstat (limited to 'services')
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerService.java29
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java128
-rw-r--r--services/autofill/java/com/android/server/autofill/RemoteFieldClassificationService.java177
-rw-r--r--services/autofill/java/com/android/server/autofill/RemoteFillService.java3
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java485
-rw-r--r--services/core/java/com/android/server/IntentResolver.java78
-rw-r--r--services/core/java/com/android/server/am/ReceiverList.java4
-rw-r--r--services/core/java/com/android/server/am/UserController.java4
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceConfig.java17
-rw-r--r--services/core/java/com/android/server/display/DisplayModeDirector.java96
-rw-r--r--services/core/java/com/android/server/hdmi/Constants.java7
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java20
-rw-r--r--services/core/java/com/android/server/pm/DexOptHelper.java12
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java3
-rw-r--r--services/core/java/com/android/server/pm/Settings.java3
-rw-r--r--services/core/java/com/android/server/vibrator/AbstractVibratorStep.java6
-rw-r--r--services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java2
-rw-r--r--services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java2
-rw-r--r--services/core/java/com/android/server/vibrator/FinishSequentialEffectStep.java6
-rw-r--r--services/core/java/com/android/server/vibrator/HalVibration.java40
-rw-r--r--services/core/java/com/android/server/vibrator/InputDeviceDelegate.java7
-rw-r--r--services/core/java/com/android/server/vibrator/PerformPrebakedVibratorStep.java2
-rw-r--r--services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java2
-rw-r--r--services/core/java/com/android/server/vibrator/StartSequentialEffectStep.java7
-rw-r--r--services/core/java/com/android/server/vibrator/Vibration.java135
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationSettings.java25
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationStats.java15
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationStepConductor.java2
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationThread.java8
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorManagerService.java159
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java5
-rw-r--r--services/core/java/com/android/server/wm/Transition.java8
-rw-r--r--services/core/java/com/android/server/wm/WallpaperWindowToken.java25
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerInternal.java11
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java13
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java4
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java8
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/BooleanPolicySerializer.java10
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/BundlePolicySerializer.java260
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/ComponentNamePolicySerializer.java11
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DefaultPolicyKey.java42
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java331
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java830
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java63
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/FlagUnion.java (renamed from services/devicepolicy/java/com/android/server/devicepolicy/IntegerUnion.java)18
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/IntegerPolicySerializer.java11
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/LockTaskPolicy.java139
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/LockTaskPolicySerializer.java77
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/MostRecent.java (renamed from services/devicepolicy/java/com/android/server/devicepolicy/SetPolicySerializer.java)31
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/MostRestrictive.java17
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/Owners.java13
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java13
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/PackageSpecificPolicyKey.java101
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/PermissionGrantStatePolicyKey.java113
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/PersistentPreferredActivityPolicyKey.java99
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java146
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java25
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/PolicyKey.java81
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/PolicySerializer.java7
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/PolicyState.java66
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/ResolutionMechanism.java4
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/StringSetPolicySerializer.java61
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/StringSetUnion.java (renamed from services/devicepolicy/java/com/android/server/devicepolicy/SetUnion.java)21
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/TopPriority.java11
-rw-r--r--services/incremental/IncrementalService.cpp19
-rw-r--r--services/java/com/android/server/SystemServer.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java14
-rw-r--r--services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java29
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java62
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java228
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java4
73 files changed, 3140 insertions, 1397 deletions
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index b991a026faa1..5a7fbc57ba11 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -150,6 +150,12 @@ public final class AutofillManagerService
@NonNull
final FrameworkResourcesServiceNameResolver mAugmentedAutofillResolver;
+ /**
+ * Object used to set the name of the field classification service.
+ */
+ @NonNull
+ final FrameworkResourcesServiceNameResolver mFieldClassificationResolver;
+
private final AutoFillUI mUi;
private final LocalLog mRequestsHistory = new LocalLog(20);
@@ -245,6 +251,15 @@ public final class AutofillManagerService
mAugmentedAutofillResolver.setOnTemporaryServiceNameChangedCallback(
(u, s, t) -> onAugmentedServiceNameChanged(u, s, t));
+ mFieldClassificationResolver = new FrameworkResourcesServiceNameResolver(getContext(),
+ com.android.internal.R.string.config_defaultFieldClassificationService);
+ if (sVerbose) {
+ Slog.v(TAG, "Resolving FieldClassificationService to serviceName: "
+ + mFieldClassificationResolver.readServiceName(0));
+ }
+ mFieldClassificationResolver.setOnTemporaryServiceNameChangedCallback(
+ (u, s, t) -> onFieldClassificationServiceNameChanged(u, s, t));
+
if (mSupportedSmartSuggestionModes != AutofillManager.FLAG_SMART_SUGGESTION_OFF) {
final List<UserInfo> users = getSupportedUsers();
for (int i = 0; i < users.size(); i++) {
@@ -358,6 +373,20 @@ public final class AutofillManagerService
}
}
+ private void onFieldClassificationServiceNameChanged(
+ @UserIdInt int userId, @Nullable String serviceName, boolean isTemporary) {
+ synchronized (mLock) {
+ final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
+ if (service == null) {
+ // If we cannot get the service from the services cache, it will call
+ // updateRemoteAugmentedAutofillService() finally. Skip call this update again.
+ getServiceForUserLocked(userId);
+ } else {
+ service.updateRemoteFieldClassificationService();
+ }
+ }
+ }
+
@Override // from AbstractMasterSystemService
protected AutofillManagerServiceImpl newServiceLocked(@UserIdInt int resolvedUserId,
boolean disabled) {
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index cc29109d85c5..76e6974e14a6 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -162,6 +162,17 @@ final class AutofillManagerServiceImpl
private long mLastPrune = 0;
/**
+ * Reference to the {@link RemoteFieldClassificationService}, is set on demand.
+ */
+ @GuardedBy("mLock")
+ @Nullable
+ private RemoteFieldClassificationService mRemoteFieldClassificationService;
+
+ @GuardedBy("mLock")
+ @Nullable
+ private ServiceInfo mRemoteFieldClassificationServiceInfo;
+
+ /**
* Reference to the {@link RemoteAugmentedAutofillService}, is set on demand.
*/
@GuardedBy("mLock")
@@ -1051,10 +1062,11 @@ final class AutofillManagerServiceImpl
}
pw.print(prefix); pw.print("Default component: "); pw.println(getContext()
.getString(R.string.config_defaultAutofillService));
+ pw.println();
- pw.print(prefix); pw.println("mAugmentedAutofillNamer: ");
- pw.print(prefix2); mMaster.mAugmentedAutofillResolver.dumpShort(pw, mUserId); pw.println();
-
+ pw.print(prefix); pw.println("mAugmentedAutofillName: ");
+ pw.print(prefix2); mMaster.mAugmentedAutofillResolver.dumpShort(pw, mUserId);
+ pw.println();
if (mRemoteAugmentedAutofillService != null) {
pw.print(prefix); pw.println("RemoteAugmentedAutofillService: ");
mRemoteAugmentedAutofillService.dump(prefix2, pw);
@@ -1063,6 +1075,27 @@ final class AutofillManagerServiceImpl
pw.print(prefix); pw.print("RemoteAugmentedAutofillServiceInfo: ");
pw.println(mRemoteAugmentedAutofillServiceInfo);
}
+ pw.println();
+
+ pw.print(prefix); pw.println("mFieldClassificationService for system detection");
+ pw.print(prefix2); pw.print("Default component: "); pw.println(getContext()
+ .getString(R.string.config_defaultFieldClassificationService));
+ pw.print(prefix2); mMaster.mFieldClassificationResolver.dumpShort(pw, mUserId);
+ pw.println();
+
+ if (mRemoteFieldClassificationService != null) {
+ pw.print(prefix); pw.println("RemoteFieldClassificationService: ");
+ mRemoteFieldClassificationService.dump(prefix2, pw);
+ } else {
+ pw.print(prefix); pw.println("mRemoteFieldClassificationService: null");
+ }
+ if (mRemoteFieldClassificationServiceInfo != null) {
+ pw.print(prefix); pw.print("RemoteFieldClassificationServiceInfo: ");
+ pw.println(mRemoteFieldClassificationServiceInfo);
+ } else {
+ pw.print(prefix); pw.println("mRemoteFieldClassificationServiceInfo: null");
+ }
+ pw.println();
pw.print(prefix); pw.print("Field classification enabled: ");
pw.println(isFieldClassificationEnabledLocked());
@@ -1629,6 +1662,95 @@ final class AutofillManagerServiceImpl
}
}
+ @GuardedBy("mLock")
+ @Nullable RemoteFieldClassificationService getRemoteFieldClassificationServiceLocked() {
+ if (mRemoteFieldClassificationService == null) {
+ final String serviceName = mMaster.mFieldClassificationResolver.getServiceName(mUserId);
+ if (serviceName == null) {
+ if (mMaster.verbose) {
+ Slog.v(TAG, "getRemoteFieldClassificationServiceLocked(): not set");
+ }
+ return null;
+ }
+ if (sVerbose) {
+ Slog.v(TAG, "getRemoteFieldClassificationServiceLocked serviceName: "
+ + serviceName);
+ }
+ boolean sTemporaryFieldDetectionService =
+ mMaster.mFieldClassificationResolver.isTemporary(mUserId);
+ final Pair<ServiceInfo, ComponentName> pair = RemoteFieldClassificationService
+ .getComponentName(serviceName, mUserId, sTemporaryFieldDetectionService);
+ if (pair == null) {
+ Slog.w(TAG, "RemoteFieldClassificationService.getComponentName returned null "
+ + "with serviceName: " + serviceName);
+ return null;
+ }
+
+ mRemoteFieldClassificationServiceInfo = pair.first;
+ final ComponentName componentName = pair.second;
+ if (sVerbose) {
+ Slog.v(TAG, "getRemoteFieldClassificationServiceLocked(): " + componentName);
+ }
+ final int serviceUid = mRemoteFieldClassificationServiceInfo.applicationInfo.uid;
+ mRemoteFieldClassificationService = new RemoteFieldClassificationService(getContext(),
+ componentName, serviceUid, mUserId);
+ }
+
+ return mRemoteFieldClassificationService;
+ }
+
+ @GuardedBy("mLock")
+ @Nullable RemoteFieldClassificationService
+ getRemoteFieldClassificationServiceIfCreatedLocked() {
+ return mRemoteFieldClassificationService;
+ }
+
+ /**
+ * Called when the {@link AutofillManagerService#mAugmentedAutofillResolver}
+ * changed (among other places).
+ */
+ void updateRemoteFieldClassificationService() {
+ synchronized (mLock) {
+ if (mRemoteFieldClassificationService != null) {
+ if (sVerbose) {
+ Slog.v(TAG, "updateRemoteFieldClassificationService(): "
+ + "destroying old remote service");
+ }
+ mRemoteFieldClassificationService.unbind();
+
+ mRemoteFieldClassificationService = null;
+ mRemoteFieldClassificationServiceInfo = null;
+ }
+
+ final boolean available = isFieldClassificationServiceAvailableLocked();
+ if (sVerbose) Slog.v(TAG, "updateRemoteFieldClassificationService(): " + available);
+
+ if (available) {
+ mRemoteFieldClassificationService = getRemoteFieldClassificationServiceLocked();
+ }
+ }
+ }
+
+ private boolean isFieldClassificationServiceAvailableLocked() {
+ if (mMaster.verbose) {
+ Slog.v(TAG, "isAugmentedAutofillService(): "
+ + "setupCompleted=" + isSetupCompletedLocked()
+ + ", disabled=" + isDisabledByUserRestrictionsLocked()
+ + ", augmentedService="
+ + mMaster.mAugmentedAutofillResolver.getServiceName(mUserId));
+ }
+ if (!isSetupCompletedLocked() || isDisabledByUserRestrictionsLocked()
+ || mMaster.mAugmentedAutofillResolver.getServiceName(mUserId) == null) {
+ return false;
+ }
+ return true;
+ }
+
+ boolean isRemoteClassificationServiceForUserLocked(int callingUid) {
+ return mRemoteFieldClassificationServiceInfo != null
+ && mRemoteFieldClassificationServiceInfo.applicationInfo.uid == callingUid;
+ }
+
@Override
public String toString() {
return "AutofillManagerServiceImpl: [userId=" + mUserId
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFieldClassificationService.java b/services/autofill/java/com/android/server/autofill/RemoteFieldClassificationService.java
new file mode 100644
index 000000000000..99a2291016e9
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/RemoteFieldClassificationService.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.autofill;
+
+import static com.android.server.autofill.Helper.sDebug;
+import static com.android.server.autofill.Helper.sVerbose;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.AppGlobals;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.os.ICancellationSignal;
+import android.os.RemoteException;
+import android.service.assist.classification.FieldClassificationRequest;
+import android.service.assist.classification.FieldClassificationResponse;
+import android.service.assist.classification.FieldClassificationService;
+import android.service.assist.classification.IFieldClassificationCallback;
+import android.service.assist.classification.IFieldClassificationService;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Slog;
+
+import com.android.internal.infra.AbstractRemoteService;
+import com.android.internal.infra.ServiceConnector;
+
+/**
+ * Class responsible for connection with the Remote {@link FieldClassificationService}.
+ * This class is instantiated when {@link AutofillManagerServiceImpl} is established.
+ * The connection is supposed to be bounded forever, as such, this class persists beyond
+ * Autofill {@link Session}'s lifecycle. As such, it can't contain information relevant to Session.
+ * This design is completely different from {@link RemoteFillService}.
+ */
+final class RemoteFieldClassificationService
+ extends ServiceConnector.Impl<IFieldClassificationService> {
+
+ private static final String TAG =
+ "Autofill" + RemoteFieldClassificationService.class.getSimpleName();
+
+ // Bind forever.
+ private static final long TIMEOUT_IDLE_UNBIND_MS =
+ AbstractRemoteService.PERMANENT_BOUND_TIMEOUT_MS;
+ private final ComponentName mComponentName;
+
+ public interface FieldClassificationServiceCallbacks {
+ void onClassificationRequestSuccess(@NonNull FieldClassificationResponse response);
+ void onClassificationRequestFailure(int requestId, @Nullable CharSequence message);
+ void onClassificationRequestTimeout(int requestId);
+ void onServiceDied(@NonNull RemoteFieldClassificationService service);
+ }
+
+ RemoteFieldClassificationService(Context context, ComponentName serviceName,
+ int serviceUid, int userId) {
+ super(context,
+ // TODO(b/266379948): Update service
+ new Intent(FieldClassificationService.SERVICE_INTERFACE).setComponent(serviceName),
+ /* bindingFlags= */ 0, userId, IFieldClassificationService.Stub::asInterface);
+ mComponentName = serviceName;
+ if (sDebug) {
+ Slog.d(TAG, "About to connect to serviceName: " + serviceName);
+ }
+ // Bind right away.
+ connect();
+ }
+
+ @Nullable
+ static Pair<ServiceInfo, ComponentName> getComponentName(@NonNull String serviceName,
+ @UserIdInt int userId, boolean isTemporary) {
+ int flags = PackageManager.GET_META_DATA;
+ if (!isTemporary) {
+ flags |= PackageManager.MATCH_SYSTEM_ONLY;
+ }
+
+ final ComponentName serviceComponent;
+ ServiceInfo serviceInfo = null;
+ try {
+ serviceComponent = ComponentName.unflattenFromString(serviceName);
+ serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent, flags,
+ userId);
+ if (serviceInfo == null) {
+ Slog.e(TAG, "Bad service name for flags " + flags + ": " + serviceName);
+ return null;
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, "Error getting service info for '" + serviceName + "': " + e);
+ return null;
+ }
+ return new Pair<>(serviceInfo, serviceComponent);
+ }
+
+ public ComponentName getComponentName() {
+ return mComponentName;
+ }
+
+ @Override // from ServiceConnector.Impl
+ protected void onServiceConnectionStatusChanged(IFieldClassificationService service,
+ boolean connected) {
+ try {
+ if (connected) {
+ service.onConnected(false, false);
+ } else {
+ service.onDisconnected();
+ }
+ } catch (Exception e) {
+ Slog.w(TAG,
+ "Exception calling onServiceConnectionStatusChanged(" + connected + "): ", e);
+ }
+ }
+
+ @Override // from AbstractRemoteService
+ protected long getAutoDisconnectTimeoutMs() {
+ return TIMEOUT_IDLE_UNBIND_MS;
+ }
+
+ public void onFieldClassificationRequest(@NonNull FieldClassificationRequest request,
+ FieldClassificationServiceCallbacks fieldClassificationServiceCallbacks) {
+
+ if (sVerbose) {
+ Slog.v(TAG, "onFieldClassificationRequest request:" + request);
+ }
+
+ run(
+ (s) ->
+ s.onFieldClassificationRequest(
+ request,
+ new IFieldClassificationCallback.Stub() {
+ @Override
+ public void onCancellable(ICancellationSignal cancellation) {
+ if (sDebug) {
+ Log.d(TAG, "onCancellable");
+ }
+ }
+
+ @Override
+ public void onSuccess(FieldClassificationResponse response) {
+ if (sDebug) {
+ Log.d(TAG, "onSuccess Response: " + response);
+ }
+ fieldClassificationServiceCallbacks
+ .onClassificationRequestSuccess(response);
+ }
+
+ @Override
+ public void onFailure() {
+ if (sDebug) {
+ Log.d(TAG, "onFailure");
+ }
+ }
+
+ @Override
+ public boolean isCompleted() throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public void cancel() throws RemoteException {}
+ }));
+ }
+}
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index 94872b09cd36..4688658bf1c3 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -135,6 +135,9 @@ final class RemoteFillService extends ServiceConnector.Impl<IAutoFillService> {
}
public void onFillRequest(@NonNull FillRequest request) {
+ if (sVerbose) {
+ Slog.v(TAG, "onFillRequest:" + request);
+ }
AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>();
AtomicReference<CompletableFuture<FillResponse>> futureRef = new AtomicReference<>();
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index c5c92885406f..b55f16bc9ae4 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -88,6 +88,8 @@ import android.os.Process;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.service.assist.classification.FieldClassificationRequest;
+import android.service.assist.classification.FieldClassificationResponse;
import android.service.autofill.AutofillFieldClassificationService.Scores;
import android.service.autofill.AutofillService;
import android.service.autofill.CompositeUserData;
@@ -125,6 +127,7 @@ import android.view.autofill.AutofillValue;
import android.view.autofill.IAutoFillManagerClient;
import android.view.autofill.IAutofillWindowPresenter;
import android.view.inputmethod.InlineSuggestionsRequest;
+import android.widget.RemoteViews;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
@@ -146,6 +149,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
+import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -163,7 +167,8 @@ import java.util.function.Function;
* until the user authenticates or it times out.
*/
final class Session implements RemoteFillService.FillServiceCallbacks, ViewState.Listener,
- AutoFillUI.AutoFillUiCallback, ValueFinder {
+ AutoFillUI.AutoFillUiCallback, ValueFinder,
+ RemoteFieldClassificationService.FieldClassificationServiceCallbacks {
private static final String TAG = "AutofillSession";
private static final String ACTION_DELAYED_FILL =
@@ -185,6 +190,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
private static AtomicInteger sIdCounter = new AtomicInteger(2);
+ private static AtomicInteger sIdCounterForPcc = new AtomicInteger(2);
+
@GuardedBy("mLock")
private @SessionState int mSessionState = STATE_UNKNOWN;
@@ -395,6 +402,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@Nullable
private ClientSuggestionsSession mClientSuggestionsSession;
+ private final ClassificationState mClassificationState = new ClassificationState();
+
// TODO(b/216576510): Share one BroadcastReceiver between all Sessions instead of creating a
// new one per Session.
private final BroadcastReceiver mDelayedFillBroadcastReceiver =
@@ -729,24 +738,21 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
* Assist Data Receiver for PCC
*/
private final class PccAssistDataReceiverImpl extends IAssistDataReceiver.Stub {
- // TODO: Uncomment lines below after field classification service definition merged
- // @GuardedBy("mLock")
- // private FieldClassificationRequest mPendingFieldClassifitacionRequest;
- // @GuardedBy("mLock")
- // private FieldClassificationRequest mLastFieldClassifitacionRequest;
@GuardedBy("mLock")
void maybeRequestFieldClassificationFromServiceLocked() {
- // TODO: Uncomment lines below after field classification service definition merged
- // if (mPendingFieldClassifitacionRequest == null) {
- // return;
- // }
- // mLastFieldClassifitacionRequest = mPendingFieldClassifitacionRequest;
- //
- // mRemoteFieldClassificationService.onFieldClassificationRequest(
- // mPendingFieldClassifitacionRequest);
- //
- // mPendingFieldClassifitacionRequest = null;
+ if (mClassificationState.mPendingFieldClassificationRequest == null) {
+ Log.w(TAG, "Received AssistData without pending classification request");
+ return;
+ }
+
+ RemoteFieldClassificationService remoteFieldClassificationService =
+ mService.getRemoteFieldClassificationServiceLocked();
+ if (remoteFieldClassificationService != null) {
+ remoteFieldClassificationService.onFieldClassificationRequest(
+ mClassificationState.mPendingFieldClassificationRequest, Session.this);
+ }
+ mClassificationState.onFieldClassificationRequestSent();
}
@Override
@@ -793,12 +799,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
ids.get(i).setSessionId(Session.this.id);
}
- // TODO: Uncomment lines below after field classification service definition merged
- // FieldClassificationRequest request = new FieldClassificationRequest(structure);
- //
- // mPendingFieldClassifitacionRequest = request;
- //
- // maybeRequestFieldClassificationFromServiceLocked();
+ mClassificationState.onAssistStructureReceived(structure);
+
+ maybeRequestFieldClassificationFromServiceLocked();
}
}
@@ -1177,9 +1180,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
int requestId;
// TODO(b/158623971): Update this to prevent possible overflow
do {
- requestId = sIdCounter.getAndIncrement();
+ requestId = sIdCounterForPcc.getAndIncrement();
} while (requestId == INVALID_REQUEST_ID);
+ if (sVerbose) {
+ Slog.v(TAG, "request id is " + requestId + ", requesting assist structure for pcc");
+ }
// Call requestAutofilLData
try {
final Bundle receiverExtras = new Bundle();
@@ -1187,8 +1193,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final long identity = Binder.clearCallingIdentity();
try {
if (!ActivityTaskManager.getService().requestAutofillData(mPccAssistReceiver,
- receiverExtras, mActivityToken, flags)) {
- Slog.w(TAG, "failed to request autofill data for pcc: " + mActivityToken);
+ receiverExtras, mActivityToken, flags)) {
+ Slog.w(TAG, "failed to request autofill data for " + mActivityToken);
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -1386,6 +1392,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return;
}
+ // TODO: Check if this is required. We can still present datasets to the user even if
+ // traditional field classification is disabled.
fieldClassificationIds = response.getFieldClassificationIds();
if (!mSessionFlags.mClientSuggestionsEnabled && fieldClassificationIds != null
&& !mService.isFieldClassificationEnabledLocked()) {
@@ -1470,11 +1478,231 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
+ // TODO(b/266379948): Ideally wait for PCC request to finish for a while more
+ // (say 100ms) before proceeding further on.
+
synchronized (mLock) {
+ response = getEffectiveFillResponse(response);
processResponseLocked(response, null, requestFlags);
}
}
+ private FillResponse getEffectiveFillResponse(FillResponse response) {
+ // TODO(b/266379948): label dataset source
+ if (!mService.getMaster().isPccClassificationEnabled()) return response;
+ synchronized (mLock) {
+ if (mClassificationState.mState != ClassificationState.STATE_RESPONSE
+ || mClassificationState.mLastFieldClassificationResponse == null) {
+ return response;
+ }
+ if (!mClassificationState.processResponse()) return response;
+ }
+ boolean preferAutofillProvider = mService.getMaster().preferProviderOverPcc();
+ boolean shouldUseFallback = mService.getMaster().shouldUsePccFallback();
+ if (preferAutofillProvider && !shouldUseFallback) {
+ return response;
+ }
+
+ DatasetComputationContainer autofillProviderContainer = new DatasetComputationContainer();
+ DatasetComputationContainer detectionPccContainer = new DatasetComputationContainer();
+
+ computeDatasetsForProviderAndUpdateContainer(response, autofillProviderContainer);
+ computeDatasetsForPccAndUpdateContainer(response, detectionPccContainer);
+
+ DatasetComputationContainer resultContainer;
+ if (preferAutofillProvider) {
+ resultContainer = autofillProviderContainer;
+ if (shouldUseFallback) {
+ // add PCC datasets that are not detected by provider.
+ addFallbackDatasets(autofillProviderContainer, detectionPccContainer);
+ }
+ } else {
+ resultContainer = detectionPccContainer;
+ if (shouldUseFallback) {
+ // add Provider's datasets that are not detected by PCC.
+ addFallbackDatasets(detectionPccContainer, autofillProviderContainer);
+ }
+ }
+ // Create FillResponse with effectiveDatasets, and all the rest value from the original
+ // response.
+ return FillResponse.shallowCopy(response, new ArrayList<>(resultContainer.mDatasets));
+ }
+
+ /**
+ * A private class to hold & compute datasets to be shown
+ */
+ private static class DatasetComputationContainer {
+ // List of all autofill ids that have a corresponding datasets
+ Set<AutofillId> mAutofillIds = new ArraySet<>();
+ // Set of datasets. Kept separately, to be able to be used directly for composing
+ // FillResponse.
+ Set<Dataset> mDatasets = new ArraySet<>();
+ ArrayMap<AutofillId, Set<Dataset>> mAutofillIdToDatasetMap = new ArrayMap<>();
+ }
+
+ // Adds fallback datasets to the first container.
+ // This function will destruct and modify c2 container.
+ private void addFallbackDatasets(
+ DatasetComputationContainer c1, DatasetComputationContainer c2) {
+ for (AutofillId id : c2.mAutofillIds) {
+ if (!c1.mAutofillIds.contains(id)) {
+
+ // Since c2 could be modified in a previous iteration, it's possible that all
+ // datasets corresponding to it have been evaluated, and it's map no longer has
+ // any more datasets left. Early return in this case.
+ if (c2.mAutofillIdToDatasetMap.get(id).isEmpty()) return;
+
+ // For AutofillId id, do the following
+ // 1. Add all the datasets corresponding to it to c1's dataset, and update c1
+ // properly.
+ // 2. All the datasets that were added should be removed from the other autofill
+ // ids that were in this dataset. This prevents us from revisiting those datasets.
+ // Although we are using Sets, and that'd avoid re-adding them, using this logic
+ // for now to keep safe. TODO(b/266379948): Revisit this logic.
+
+ Set<Dataset> datasets = c2.mAutofillIdToDatasetMap.get(id);
+ Set<Dataset> copyDatasets = new ArraySet<>(datasets);
+ c1.mAutofillIds.add(id);
+ c1.mAutofillIdToDatasetMap.put(id, copyDatasets);
+ c1.mDatasets.addAll(copyDatasets);
+
+ for (Dataset dataset : datasets) {
+ for (AutofillId currentId : dataset.getFieldIds()) {
+ if (currentId.equals(id)) continue;
+ // For this id, we need to remove the dataset from it's map.
+ c2.mAutofillIdToDatasetMap.get(currentId).remove(dataset);
+ }
+ }
+ }
+ }
+ }
+
+ private void computeDatasetsForProviderAndUpdateContainer(
+ FillResponse response, DatasetComputationContainer container) {
+ List<Dataset> datasets = response.getDatasets();
+ if (datasets == null) return;
+ ArrayMap<AutofillId, Set<Dataset>> autofillIdToDatasetMap = new ArrayMap<>();
+ Set<Dataset> eligibleDatasets = new ArraySet<>();
+ Set<AutofillId> eligibleAutofillIds = new ArraySet<>();
+ for (Dataset dataset : response.getDatasets()) {
+ if (dataset.getFieldIds() == null) continue;
+ if (dataset.getAutofillDatatypes() != null
+ && dataset.getAutofillDatatypes().size() > 0) {
+ continue;
+ }
+ eligibleDatasets.add(dataset);
+ for (AutofillId id : dataset.getFieldIds()) {
+ eligibleAutofillIds.add(id);
+ Set<Dataset> datasetForIds = autofillIdToDatasetMap.get(id);
+ if (datasetForIds == null) {
+ datasetForIds = new ArraySet<>();
+ }
+ datasetForIds.add(dataset);
+ autofillIdToDatasetMap.put(id, datasetForIds);
+ }
+ }
+ container.mAutofillIdToDatasetMap = autofillIdToDatasetMap;
+ container.mDatasets = eligibleDatasets;
+ container.mAutofillIds = eligibleAutofillIds;
+ }
+
+ private void computeDatasetsForPccAndUpdateContainer(
+ FillResponse response, DatasetComputationContainer container) {
+ List<Dataset> datasets = response.getDatasets();
+ if (datasets == null) return;
+
+ synchronized (mLock) {
+ ArrayMap<String, Set<AutofillId>> hintsToAutofillIdMap =
+ mClassificationState.mHintsToAutofillIdMap;
+
+ ArrayMap<String, Set<AutofillId>> groupHintsToAutofillIdMap =
+ mClassificationState.mGroupHintsToAutofillIdMap;
+
+ ArrayMap<AutofillId, Set<Dataset>> map = new ArrayMap<>();
+
+ Set<Dataset> eligibleDatasets = new ArraySet<>();
+ Set<AutofillId> eligibleAutofillIds = new ArraySet<>();
+
+ for (int i = 0; i < datasets.size(); i++) {
+ Dataset dataset = datasets.get(i);
+ if (dataset.getAutofillDatatypes() == null) continue;
+ if (dataset.getFieldIds() != null && dataset.getFieldIds().size() > 0) continue;
+
+ ArrayList<AutofillId> fieldIds = new ArrayList<>();
+ ArrayList<AutofillValue> fieldValues = new ArrayList<>();
+ ArrayList<RemoteViews> fieldPresentations = new ArrayList<>();
+ ArrayList<RemoteViews> fieldDialogPresentations = new ArrayList<>();
+ ArrayList<InlinePresentation> fieldInlinePresentations = new ArrayList<>();
+ ArrayList<InlinePresentation> fieldInlineTooltipPresentations = new ArrayList<>();
+ ArrayList<Dataset.DatasetFieldFilter> fieldFilters = new ArrayList<>();
+
+ for (int j = 0; j < dataset.getAutofillDatatypes().size(); j++) {
+ String hint = dataset.getAutofillDatatypes().get(j);
+
+ if (hintsToAutofillIdMap.containsKey(hint)) {
+ ArrayList<AutofillId> tempIds =
+ new ArrayList<>(hintsToAutofillIdMap.get(hint));
+
+ for (AutofillId autofillId : tempIds) {
+ eligibleAutofillIds.add(autofillId);
+ // For each of the field, copy over values.
+ fieldIds.add(autofillId);
+ fieldValues.add(dataset.getFieldValues().get(j));
+ // TODO(b/266379948): might need to make it more efficient by not
+ // copying over value if it didn't exist. This would require creating
+ // a getter for the presentations arraylist.
+ fieldPresentations.add(dataset.getFieldPresentation(j));
+ fieldDialogPresentations.add(dataset.getFieldDialogPresentation(j));
+ fieldInlinePresentations.add(dataset.getFieldInlinePresentation(j));
+ fieldInlineTooltipPresentations.add(
+ dataset.getFieldInlineTooltipPresentation(j));
+ fieldFilters.add(dataset.getFilter(j));
+ }
+
+ Dataset newDataset =
+ new Dataset(
+ fieldIds,
+ fieldValues,
+ fieldPresentations,
+ fieldDialogPresentations,
+ fieldInlinePresentations,
+ fieldInlineTooltipPresentations,
+ fieldFilters,
+ new ArrayList<>(),
+ dataset.getFieldContent(),
+ null,
+ null,
+ null,
+ null,
+ dataset.getId(),
+ dataset.getAuthentication());
+ eligibleDatasets.add(newDataset);
+
+ // Associate this dataset with all the ids that are represented with it.
+ Set<Dataset> newDatasets;
+ for (AutofillId autofillId : tempIds) {
+ if (map.containsKey(autofillId)) {
+ newDatasets = map.get(autofillId);
+ } else {
+ newDatasets = new ArraySet<>();
+ }
+ newDatasets.add(newDataset);
+ map.put(autofillId, newDatasets);
+ }
+ }
+ // TODO(b/266379948): handle the case:
+ // groupHintsToAutofillIdMap.containsKey(hint))
+ // but the autofill id not being applicable to other hints.
+ // TODO(b/266379948): also handle the case where there could be more types in
+ // the dataset, provided by the provider, however, they aren't applicable.
+ }
+ }
+ container.mAutofillIds = eligibleAutofillIds;
+ container.mDatasets = eligibleDatasets;
+ container.mAutofillIdToDatasetMap = map;
+ }
+ }
+
@GuardedBy("mLock")
private void processNullResponseOrFallbackLocked(int requestId, int flags) {
if (!mSessionFlags.mClientSuggestionsEnabled) {
@@ -4579,6 +4807,189 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
+ /**
+ * Class maintaining the state of the requests to
+ * {@link android.service.assist.classification.FieldClassificationService}.
+ */
+ private static final class ClassificationState {
+
+ /**
+ * Initial state indicating that the request for classification hasn't been triggered yet.
+ */
+ private static final int STATE_INITIAL = 1;
+ /**
+ * Assist request has been triggered, but awaiting response.
+ */
+ private static final int STATE_PENDING_ASSIST_REQUEST = 2;
+ /**
+ * Classification request has been triggered, but awaiting response.
+ */
+ private static final int STATE_PENDING_REQUEST = 3;
+ /**
+ * Classification response has been received.
+ */
+ private static final int STATE_RESPONSE = 4;
+ /**
+ * Classification state has been invalidated, and the last response may no longer be valid.
+ * This could occur due to various reasons like views changing their layouts, becoming
+ * visible or invisible, thereby rendering previous response potentially inaccurate or
+ * incomplete.
+ */
+ private static final int STATE_INVALIDATED = 5;
+
+ @IntDef(prefix = { "STATE_" }, value = {
+ STATE_INITIAL,
+ STATE_PENDING_ASSIST_REQUEST,
+ STATE_PENDING_REQUEST,
+ STATE_RESPONSE,
+ STATE_INVALIDATED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface ClassificationRequestState{}
+
+ @GuardedBy("mLock")
+ private @ClassificationRequestState int mState = STATE_INITIAL;
+
+ @GuardedBy("mLock")
+ private FieldClassificationRequest mPendingFieldClassificationRequest;
+
+ @GuardedBy("mLock")
+ private FieldClassificationResponse mLastFieldClassificationResponse;
+
+ @GuardedBy("mLock")
+ private ArrayMap<AutofillId, Set<String>> mClassificationHintsMap;
+
+ @GuardedBy("mLock")
+ private ArrayMap<AutofillId, Set<String>> mClassificationGroupHintsMap;
+
+ @GuardedBy("mLock")
+ private ArrayMap<AutofillId, Set<String>> mClassificationCombinedHintsMap;
+
+ /**
+ * Typically, there would be a 1:1 mapping. However, in certain cases, we may have a hint
+ * being applicable to many types. An example of this being new/change password forms,
+ * where you need to confirm the passward twice.
+ */
+ @GuardedBy("mLock")
+ private ArrayMap<String, Set<AutofillId>> mHintsToAutofillIdMap;
+
+ /**
+ * Group hints are expected to have a 1:many mapping. For example, different credit card
+ * fields (creditCardNumber, expiry, cvv) will all map to the same group hints.
+ */
+ @GuardedBy("mLock")
+ private ArrayMap<String, Set<AutofillId>> mGroupHintsToAutofillIdMap;
+
+ @GuardedBy("mLock")
+ private String stateToString() {
+ switch (mState) {
+ case STATE_INITIAL:
+ return "STATE_INITIAL";
+ case STATE_PENDING_ASSIST_REQUEST:
+ return "STATE_PENDING_ASSIST_REQUEST";
+ case STATE_PENDING_REQUEST:
+ return "STATE_PENDING_REQUEST";
+ case STATE_RESPONSE:
+ return "STATE_RESPONSE";
+ case STATE_INVALIDATED:
+ return "STATE_INVALIDATED";
+ default:
+ return "UNKNOWN_CLASSIFICATION_STATE_" + mState;
+ }
+ }
+
+ /**
+ * Process the response received.
+ * @return true if the response was processed, false otherwise. If there wasn't any
+ * response, yet this function was called, it would return false.
+ */
+ @GuardedBy("mLock")
+ private boolean processResponse() {
+ if (mClassificationHintsMap != null && !mClassificationHintsMap.isEmpty()) {
+ // Already processed, so return
+ return true;
+ }
+
+ FieldClassificationResponse response = mLastFieldClassificationResponse;
+ if (response == null) return false;
+
+ mClassificationHintsMap = new ArrayMap<>();
+ mClassificationGroupHintsMap = new ArrayMap<>();
+ mHintsToAutofillIdMap = new ArrayMap<>();
+ mGroupHintsToAutofillIdMap = new ArrayMap<>();
+ Set<android.service.assist.classification.FieldClassification> classifications =
+ response.getClassifications();
+
+ for (android.service.assist.classification.FieldClassification classification :
+ classifications) {
+ AutofillId id = classification.getAutofillId();
+ Set<String> hintDetections = classification.getHints();
+ Set<String> groupHintsDetections = classification.getGroupHints();
+ ArraySet<String> combinedHints = new ArraySet<>(hintDetections);
+ mClassificationHintsMap.put(id, hintDetections);
+ if (groupHintsDetections != null) {
+ mClassificationGroupHintsMap.put(id, groupHintsDetections);
+ combinedHints.addAll(groupHintsDetections);
+ }
+ mClassificationCombinedHintsMap.put(id, combinedHints);
+
+ processDetections(hintDetections, id, mHintsToAutofillIdMap);
+ processDetections(groupHintsDetections, id, mGroupHintsToAutofillIdMap);
+ }
+ return true;
+ }
+
+ @GuardedBy("mLock")
+ private static void processDetections(Set<String> detections, AutofillId id,
+ ArrayMap<String, Set<AutofillId>> currentMap) {
+ for (String detection : detections) {
+ Set<AutofillId> autofillIds;
+ if (currentMap.containsKey(detection)) {
+ autofillIds = currentMap.get(detection);
+ } else {
+ autofillIds = new ArraySet<>();
+ }
+ autofillIds.add(id);
+ currentMap.put(detection, autofillIds);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void invalidateState() {
+ mState = STATE_INVALIDATED;
+ }
+
+ @GuardedBy("mLock")
+ private void updatePendingAssistData() {
+ mState = STATE_PENDING_ASSIST_REQUEST;
+ }
+
+ @GuardedBy("mLock")
+ private void updatePendingRequest() {
+ mState = STATE_PENDING_REQUEST;
+ }
+
+ @GuardedBy("mLock")
+ private void updateResponseReceived(FieldClassificationResponse response) {
+ mState = STATE_RESPONSE;
+ mLastFieldClassificationResponse = response;
+ mPendingFieldClassificationRequest = null;
+ processResponse();
+ }
+
+ @GuardedBy("mLock")
+ private void onAssistStructureReceived(AssistStructure structure) {
+ mState = STATE_PENDING_REQUEST;
+ mPendingFieldClassificationRequest = new FieldClassificationRequest(structure);
+ }
+
+ @GuardedBy("mLock")
+ private void onFieldClassificationRequestSent() {
+ mState = STATE_PENDING_REQUEST;
+ mPendingFieldClassificationRequest = null;
+ }
+ }
+
@Override
public String toString() {
return "Session: [id=" + id + ", component=" + mComponentName
@@ -5088,4 +5499,28 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
ServiceInfo serviceInfo = mService.getServiceInfo();
return serviceInfo == null ? Process.INVALID_UID : serviceInfo.applicationInfo.uid;
}
+
+ // DetectionServiceCallbacks
+ public void onClassificationRequestSuccess(@Nullable FieldClassificationResponse response) {
+ mClassificationState.updateResponseReceived(response);
+ }
+
+ public void onClassificationRequestFailure(int requestId, @Nullable CharSequence message) {
+
+ }
+
+ public void onClassificationRequestTimeout(int requestId) {
+
+ }
+
+ @Override
+ public void onServiceDied(@NonNull RemoteFieldClassificationService service) {
+ Slog.w(TAG, "removing session because service died");
+ synchronized (mLock) {
+ // TODO(b/266379948)
+ // forceRemoveFromServiceLocked();
+ }
+ }
+ // DetectionServiceCallbacks end
+
}
diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java
index a1adc6f4aff3..2a46d862b991 100644
--- a/services/core/java/com/android/server/IntentResolver.java
+++ b/services/core/java/com/android/server/IntentResolver.java
@@ -77,80 +77,6 @@ public abstract class IntentResolver<F, R extends Object> {
}
}
- public static boolean filterEquals(IntentFilter f1, IntentFilter f2) {
- int s1 = f1.countActions();
- int s2 = f2.countActions();
- if (s1 != s2) {
- return false;
- }
- for (int i=0; i<s1; i++) {
- if (!f2.hasAction(f1.getAction(i))) {
- return false;
- }
- }
- s1 = f1.countCategories();
- s2 = f2.countCategories();
- if (s1 != s2) {
- return false;
- }
- for (int i=0; i<s1; i++) {
- if (!f2.hasCategory(f1.getCategory(i))) {
- return false;
- }
- }
- s1 = f1.countDataTypes();
- s2 = f2.countDataTypes();
- if (s1 != s2) {
- return false;
- }
- for (int i=0; i<s1; i++) {
- if (!f2.hasExactDataType(f1.getDataType(i))) {
- return false;
- }
- }
- s1 = f1.countDataSchemes();
- s2 = f2.countDataSchemes();
- if (s1 != s2) {
- return false;
- }
- for (int i=0; i<s1; i++) {
- if (!f2.hasDataScheme(f1.getDataScheme(i))) {
- return false;
- }
- }
- s1 = f1.countDataAuthorities();
- s2 = f2.countDataAuthorities();
- if (s1 != s2) {
- return false;
- }
- for (int i=0; i<s1; i++) {
- if (!f2.hasDataAuthority(f1.getDataAuthority(i))) {
- return false;
- }
- }
- s1 = f1.countDataPaths();
- s2 = f2.countDataPaths();
- if (s1 != s2) {
- return false;
- }
- for (int i=0; i<s1; i++) {
- if (!f2.hasDataPath(f1.getDataPath(i))) {
- return false;
- }
- }
- s1 = f1.countDataSchemeSpecificParts();
- s2 = f2.countDataSchemeSpecificParts();
- if (s1 != s2) {
- return false;
- }
- for (int i=0; i<s1; i++) {
- if (!f2.hasDataSchemeSpecificPart(f1.getDataSchemeSpecificPart(i))) {
- return false;
- }
- }
- return true;
- }
-
/**
* Returns whether an intent matches the IntentFilter with a pre-resolved type.
*/
@@ -200,7 +126,7 @@ public abstract class IntentResolver<F, R extends Object> {
if (cur == null) {
break;
}
- if (filterEquals(getIntentFilter(cur), matching)) {
+ if (IntentFilter.filterEquals(getIntentFilter(cur), matching)) {
if (res == null) {
res = new ArrayList<>();
}
@@ -225,7 +151,7 @@ public abstract class IntentResolver<F, R extends Object> {
} else {
ArrayList<F> res = null;
for (F cur : mFilters) {
- if (filterEquals(getIntentFilter(cur), matching)) {
+ if (IntentFilter.filterEquals(getIntentFilter(cur), matching)) {
if (res == null) {
res = new ArrayList<>();
}
diff --git a/services/core/java/com/android/server/am/ReceiverList.java b/services/core/java/com/android/server/am/ReceiverList.java
index f3d8ba1564fa..7d2dab65958f 100644
--- a/services/core/java/com/android/server/am/ReceiverList.java
+++ b/services/core/java/com/android/server/am/ReceiverList.java
@@ -25,8 +25,6 @@ import android.util.PrintWriterPrinter;
import android.util.Printer;
import android.util.proto.ProtoOutputStream;
-import com.android.server.IntentResolver;
-
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -74,7 +72,7 @@ final class ReceiverList extends ArrayList<BroadcastFilter>
final int N = size();
for (int i = 0; i < N; i++) {
final BroadcastFilter f = get(i);
- if (IntentResolver.filterEquals(f, filter)) {
+ if (IntentFilter.filterEquals(f, filter)) {
return true;
}
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index c5b611db48e1..b0a14ab142c7 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1986,10 +1986,6 @@ class UserController implements Handler.Callback {
Slogf.w(TAG, "Cannot switch to User #" + targetUserId + ": not supported");
return false;
}
- if (targetUserInfo.isProfile()) {
- Slogf.w(TAG, "Cannot switch to User #" + targetUserId + ": not a full user");
- return false;
- }
if (FactoryResetter.isFactoryResetting()) {
Slogf.w(TAG, "Cannot switch to User #" + targetUserId + ": factory reset in progress");
return false;
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index a107f33b5244..ac9400d0d96f 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -1345,6 +1345,23 @@ public class DisplayDeviceConfig {
}
/**
+ * @return Default refresh rate while the device has high brightness mode enabled for HDR.
+ */
+ public int getDefaultRefreshRateInHbmHdr() {
+ return mContext.getResources().getInteger(
+ R.integer.config_defaultRefreshRateInHbmHdr);
+ }
+
+ /**
+ * @return Default refresh rate while the device has high brightness mode enabled because of
+ * high lux.
+ */
+ public int getDefaultRefreshRateInHbmSunlight() {
+ return mContext.getResources().getInteger(
+ R.integer.config_defaultRefreshRateInHbmSunlight);
+ }
+
+ /**
* @return Default refresh rate in the higher blocking zone of the associated display
*/
public int getDefaultHighBlockingZoneRefreshRate() {
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 91ef167dfc3a..0a4b5b0e5c94 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -123,6 +123,10 @@ public class DisplayModeDirector {
private final DeviceConfigInterface mDeviceConfig;
private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
+ @GuardedBy("mLock")
+ @Nullable
+ private DisplayDeviceConfig mDefaultDisplayDeviceConfig;
+
// A map from the display ID to the collection of votes and their priority. The latter takes
// the form of another map from the priority to the vote itself so that each priority is
// guaranteed to have exactly one vote, which is also easily and efficiently replaceable.
@@ -163,6 +167,7 @@ public class DisplayModeDirector {
mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
mSettingsObserver = new SettingsObserver(context, handler);
mBrightnessObserver = new BrightnessObserver(context, handler, injector);
+ mDefaultDisplayDeviceConfig = null;
mUdfpsObserver = new UdfpsObserver();
final BallotBox ballotBox = (displayId, priority, vote) -> {
synchronized (mLock) {
@@ -701,11 +706,15 @@ public class DisplayModeDirector {
* @param displayDeviceConfig configurations relating to the underlying display device.
*/
public void defaultDisplayDeviceUpdated(DisplayDeviceConfig displayDeviceConfig) {
- mSettingsObserver.setRefreshRates(displayDeviceConfig,
- /* attemptLoadingFromDeviceConfig= */ true);
- mBrightnessObserver.updateBlockingZoneThresholds(displayDeviceConfig,
- /* attemptLoadingFromDeviceConfig= */ true);
- mBrightnessObserver.reloadLightSensor(displayDeviceConfig);
+ synchronized (mLock) {
+ mDefaultDisplayDeviceConfig = displayDeviceConfig;
+ mSettingsObserver.setRefreshRates(displayDeviceConfig,
+ /* attemptLoadingFromDeviceConfig= */ true);
+ mBrightnessObserver.updateBlockingZoneThresholds(displayDeviceConfig,
+ /* attemptLoadingFromDeviceConfig= */ true);
+ mBrightnessObserver.reloadLightSensor(displayDeviceConfig);
+ mHbmObserver.setupHdrRefreshRates(displayDeviceConfig);
+ }
}
/**
@@ -2766,7 +2775,7 @@ public class DisplayModeDirector {
* HBM that are associated with that display. Restrictions are retrieved from
* DisplayManagerInternal but originate in the display-device-config file.
*/
- public static class HbmObserver implements DisplayManager.DisplayListener {
+ public class HbmObserver implements DisplayManager.DisplayListener {
private final BallotBox mBallotBox;
private final Handler mHandler;
private final SparseIntArray mHbmMode = new SparseIntArray();
@@ -2786,10 +2795,24 @@ public class DisplayModeDirector {
mDeviceConfigDisplaySettings = displaySettings;
}
- public void observe() {
- mRefreshRateInHbmSunlight = mDeviceConfigDisplaySettings.getRefreshRateInHbmSunlight();
- mRefreshRateInHbmHdr = mDeviceConfigDisplaySettings.getRefreshRateInHbmHdr();
+ /**
+ * Sets up the refresh rate to be used when HDR is enabled
+ */
+ public void setupHdrRefreshRates(DisplayDeviceConfig displayDeviceConfig) {
+ mRefreshRateInHbmHdr = mDeviceConfigDisplaySettings
+ .getRefreshRateInHbmHdr(displayDeviceConfig);
+ mRefreshRateInHbmSunlight = mDeviceConfigDisplaySettings
+ .getRefreshRateInHbmSunlight(displayDeviceConfig);
+ }
+ /**
+ * Sets up the HDR refresh rates, and starts observing for the changes in the display that
+ * might impact it
+ */
+ public void observe() {
+ synchronized (mLock) {
+ setupHdrRefreshRates(mDefaultDisplayDeviceConfig);
+ }
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
mInjector.registerDisplayListener(this, mHandler,
DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
@@ -3021,26 +3044,33 @@ public class DisplayModeDirector {
-1);
}
- public int getRefreshRateInHbmSunlight() {
- final int defaultRefreshRateInHbmSunlight =
- mContext.getResources().getInteger(
- R.integer.config_defaultRefreshRateInHbmSunlight);
-
- final int refreshRate = mDeviceConfig.getInt(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_SUNLIGHT,
- defaultRefreshRateInHbmSunlight);
-
+ public int getRefreshRateInHbmHdr(DisplayDeviceConfig displayDeviceConfig) {
+ int refreshRate =
+ (displayDeviceConfig == null) ? mContext.getResources().getInteger(
+ R.integer.config_defaultRefreshRateInHbmHdr)
+ : displayDeviceConfig.getDefaultRefreshRateInHbmHdr();
+ try {
+ refreshRate = mDeviceConfig.getInt(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_HDR,
+ refreshRate);
+ } catch (NullPointerException e) {
+ // Do Nothing
+ }
return refreshRate;
}
- public int getRefreshRateInHbmHdr() {
- final int defaultRefreshRateInHbmHdr =
- mContext.getResources().getInteger(R.integer.config_defaultRefreshRateInHbmHdr);
-
- final int refreshRate = mDeviceConfig.getInt(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_HDR,
- defaultRefreshRateInHbmHdr);
-
+ public int getRefreshRateInHbmSunlight(DisplayDeviceConfig displayDeviceConfig) {
+ int refreshRate =
+ (displayDeviceConfig == null) ? mContext.getResources()
+ .getInteger(R.integer.config_defaultRefreshRateInHbmSunlight)
+ : displayDeviceConfig.getDefaultRefreshRateInHbmSunlight();
+ try {
+ refreshRate = mDeviceConfig.getInt(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_SUNLIGHT,
+ refreshRate);
+ } catch (NullPointerException e) {
+ // Do Nothing
+ }
return refreshRate;
}
@@ -3090,14 +3120,18 @@ public class DisplayModeDirector {
0).sendToTarget();
}
- final int refreshRateInHbmSunlight = getRefreshRateInHbmSunlight();
- mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HBM_SUNLIGHT_CHANGED,
- refreshRateInHbmSunlight, 0)
+ synchronized (mLock) {
+ final int refreshRateInHbmSunlight =
+ getRefreshRateInHbmSunlight(mDefaultDisplayDeviceConfig);
+ mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HBM_SUNLIGHT_CHANGED,
+ refreshRateInHbmSunlight, 0)
.sendToTarget();
- final int refreshRateInHbmHdr = getRefreshRateInHbmHdr();
- mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HBM_HDR_CHANGED, refreshRateInHbmHdr, 0)
+ final int refreshRateInHbmHdr =
+ getRefreshRateInHbmHdr(mDefaultDisplayDeviceConfig);
+ mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HBM_HDR_CHANGED, refreshRateInHbmHdr, 0)
.sendToTarget();
+ }
}
private int[] getIntArrayProperty(String prop) {
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index 6d1af3bf96f6..e21ec7243958 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -627,11 +627,14 @@ final class Constants {
})
@interface HpdSignalType {}
- static final String DEVICE_CONFIG_FEATURE_FLAG_SOUNDBAR_MODE = "soundbar_mode";
+ static final String DEVICE_CONFIG_FEATURE_FLAG_SOUNDBAR_MODE = "enable_soundbar_mode";
static final String DEVICE_CONFIG_FEATURE_FLAG_ENABLE_EARC_TX = "enable_earc_tx";
+ static final String DEVICE_CONFIG_FEATURE_FLAG_TRANSITION_ARC_TO_EARC_TX =
+ "transition_arc_to_earc_tx";
@StringDef({
DEVICE_CONFIG_FEATURE_FLAG_SOUNDBAR_MODE,
- DEVICE_CONFIG_FEATURE_FLAG_ENABLE_EARC_TX
+ DEVICE_CONFIG_FEATURE_FLAG_ENABLE_EARC_TX,
+ DEVICE_CONFIG_FEATURE_FLAG_TRANSITION_ARC_TO_EARC_TX
})
@interface FeatureFlag {}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 740d2a3c2b6e..3563938a9c3c 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -458,6 +458,9 @@ public class HdmiControlService extends SystemService {
private boolean mEarcTxFeatureFlagEnabled = false;
@ServiceThreadOnly
+ private boolean mTransitionFromArcToEarcTxEnabled = false;
+
+ @ServiceThreadOnly
private int mActivePortId = Constants.INVALID_PORT_ID;
// Set to true while the input change by MHL is allowed.
@@ -675,6 +678,8 @@ public class HdmiControlService extends SystemService {
Constants.DEVICE_CONFIG_FEATURE_FLAG_SOUNDBAR_MODE, false);
mEarcTxFeatureFlagEnabled = mDeviceConfig.getBoolean(
Constants.DEVICE_CONFIG_FEATURE_FLAG_ENABLE_EARC_TX, false);
+ mTransitionFromArcToEarcTxEnabled = mDeviceConfig.getBoolean(
+ Constants.DEVICE_CONFIG_FEATURE_FLAG_TRANSITION_ARC_TO_EARC_TX, false);
synchronized (mLock) {
mEarcEnabled = (mHdmiCecConfig.getIntValue(
@@ -886,6 +891,16 @@ public class HdmiControlService extends SystemService {
? SOUNDBAR_MODE_ENABLED : SOUNDBAR_MODE_DISABLED);
}
}, mServiceThreadExecutor);
+
+ mDeviceConfig.addOnPropertiesChangedListener(getContext().getMainExecutor(),
+ new DeviceConfig.OnPropertiesChangedListener() {
+ @Override
+ public void onPropertiesChanged(DeviceConfig.Properties properties) {
+ mTransitionFromArcToEarcTxEnabled = properties.getBoolean(
+ Constants.DEVICE_CONFIG_FEATURE_FLAG_TRANSITION_ARC_TO_EARC_TX,
+ false);
+ }
+ });
}
/** Returns true if the device screen is off */
boolean isScreenOff() {
@@ -1618,6 +1633,11 @@ public class HdmiControlService extends SystemService {
}
void enableAudioReturnChannel(int portId, boolean enabled) {
+ if (!mTransitionFromArcToEarcTxEnabled && enabled && mEarcController != null) {
+ // If the feature flag is set to false, prevent eARC from establishing if ARC is already
+ // established.
+ setEarcEnabledInHal(false, false);
+ }
mCecController.enableAudioReturnChannel(portId, enabled);
}
diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java
index de37080d0255..d8bfa592ea95 100644
--- a/services/core/java/com/android/server/pm/DexOptHelper.java
+++ b/services/core/java/com/android/server/pm/DexOptHelper.java
@@ -40,8 +40,10 @@ import static dalvik.system.DexFile.isProfileGuidedCompilerFilter;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppGlobals;
+import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.ResolveInfo;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.dex.ArtManager;
@@ -1019,7 +1021,15 @@ public final class DexOptHelper {
pm.getDexOptHelper().new DexoptDoneHandler());
LocalManagerRegistry.addManager(ArtManagerLocal.class, artManager);
- artManager.scheduleBackgroundDexoptJob();
+ // Schedule the background job when boot is complete. This decouples us from when
+ // JobSchedulerService is initialized.
+ systemContext.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ context.unregisterReceiver(this);
+ artManager.scheduleBackgroundDexoptJob();
+ }
+ }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
}
/**
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 97ee3c5b3f74..382be45fd9df 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -189,7 +189,6 @@ import com.android.modules.utils.TypedXmlSerializer;
import com.android.permission.persistence.RuntimePermissionsPersistence;
import com.android.server.EventLogTags;
import com.android.server.FgThread;
-import com.android.server.IntentResolver;
import com.android.server.LocalManagerRegistry;
import com.android.server.LocalServices;
import com.android.server.LockGuard;
@@ -4775,7 +4774,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService
ArraySet<CrossProfileIntentFilter> set =
new ArraySet<>(resolver.filterSet());
for (CrossProfileIntentFilter filter : set) {
- if (IntentResolver.filterEquals(filter.mFilter, intentFilter)
+ if (IntentFilter.filterEquals(filter.mFilter, intentFilter)
&& filter.getOwnerPackage().equals(ownerPackage)
&& filter.getTargetUserId() == targetUserId
&& filter.getFlags() == flags) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 7e7205d84493..6541b40cc724 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -98,7 +98,6 @@ import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
import com.android.permission.persistence.RuntimePermissionsPersistence;
import com.android.permission.persistence.RuntimePermissionsState;
-import com.android.server.IntentResolver;
import com.android.server.LocalServices;
import com.android.server.backup.PreferredActivityBackupHelper;
import com.android.server.pm.Installer.InstallerException;
@@ -6307,7 +6306,7 @@ public final class Settings implements Watchable, Snappable {
boolean changed = false;
while (it.hasNext()) {
PersistentPreferredActivity ppa = it.next();
- if (IntentResolver.filterEquals(ppa.getIntentFilter(), filter)) {
+ if (IntentFilter.filterEquals(ppa.getIntentFilter(), filter)) {
ppir.removeFilter(ppa);
changed = true;
break;
diff --git a/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java b/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java
index be9053055fe3..90b6f95f8740 100644
--- a/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java
@@ -126,7 +126,7 @@ abstract class AbstractVibratorStep extends Step {
"Turning off vibrator " + getVibratorId());
}
controller.off();
- getVibration().stats().reportVibratorOff();
+ getVibration().stats.reportVibratorOff();
mPendingVibratorOffDeadline = 0;
}
@@ -136,7 +136,7 @@ abstract class AbstractVibratorStep extends Step {
"Amplitude changed on vibrator " + getVibratorId() + " to " + amplitude);
}
controller.setAmplitude(amplitude);
- getVibration().stats().reportSetAmplitude();
+ getVibration().stats.reportSetAmplitude();
}
/**
@@ -170,7 +170,7 @@ abstract class AbstractVibratorStep extends Step {
// Count the loops that were played.
int loopSize = effectSize - repeatIndex;
int loopSegmentsPlayed = nextSegmentIndex - repeatIndex;
- getVibration().stats().reportRepetition(loopSegmentsPlayed / loopSize);
+ getVibration().stats.reportRepetition(loopSegmentsPlayed / loopSize);
nextSegmentIndex = repeatIndex + ((nextSegmentIndex - effectSize) % loopSize);
}
Step nextStep = conductor.nextVibrateStep(nextStartTime, controller, effect,
diff --git a/services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java b/services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java
index 545ec5bcff03..940bd08eee4b 100644
--- a/services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java
@@ -73,7 +73,7 @@ final class ComposePrimitivesVibratorStep extends AbstractVibratorStep {
primitives.toArray(new PrimitiveSegment[primitives.size()]);
long vibratorOnResult = controller.on(primitivesArray, getVibration().id);
handleVibratorOnResult(vibratorOnResult);
- getVibration().stats().reportComposePrimitives(vibratorOnResult, primitivesArray);
+ getVibration().stats.reportComposePrimitives(vibratorOnResult, primitivesArray);
// The next start and off times will be calculated from mVibratorOnResult.
return nextSteps(/* segmentsPlayed= */ primitives.size());
diff --git a/services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java b/services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java
index 8bfa2c3cd082..7100ffd54175 100644
--- a/services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java
@@ -72,7 +72,7 @@ final class ComposePwleVibratorStep extends AbstractVibratorStep {
RampSegment[] pwlesArray = pwles.toArray(new RampSegment[pwles.size()]);
long vibratorOnResult = controller.on(pwlesArray, getVibration().id);
handleVibratorOnResult(vibratorOnResult);
- getVibration().stats().reportComposePwle(vibratorOnResult, pwlesArray);
+ getVibration().stats.reportComposePwle(vibratorOnResult, pwlesArray);
// The next start and off times will be calculated from mVibratorOnResult.
return nextSteps(/* segmentsPlayed= */ pwles.size());
diff --git a/services/core/java/com/android/server/vibrator/FinishSequentialEffectStep.java b/services/core/java/com/android/server/vibrator/FinishSequentialEffectStep.java
index bbbca024214f..c9683d9f69ed 100644
--- a/services/core/java/com/android/server/vibrator/FinishSequentialEffectStep.java
+++ b/services/core/java/com/android/server/vibrator/FinishSequentialEffectStep.java
@@ -51,7 +51,8 @@ final class FinishSequentialEffectStep extends Step {
Slog.d(VibrationThread.TAG,
"FinishSequentialEffectStep for effect #" + startedStep.currentIndex);
}
- conductor.vibratorManagerHooks.noteVibratorOff(conductor.getVibration().uid);
+ conductor.vibratorManagerHooks.noteVibratorOff(
+ conductor.getVibration().callerInfo.uid);
Step nextStep = startedStep.nextStep();
return nextStep == null ? VibrationStepConductor.EMPTY_STEP_LIST
: Arrays.asList(nextStep);
@@ -68,6 +69,7 @@ final class FinishSequentialEffectStep extends Step {
@Override
public void cancelImmediately() {
- conductor.vibratorManagerHooks.noteVibratorOff(conductor.getVibration().uid);
+ conductor.vibratorManagerHooks.noteVibratorOff(
+ conductor.getVibration().callerInfo.uid);
}
}
diff --git a/services/core/java/com/android/server/vibrator/HalVibration.java b/services/core/java/com/android/server/vibrator/HalVibration.java
index 53f04d7209ce..87f189aed592 100644
--- a/services/core/java/com/android/server/vibrator/HalVibration.java
+++ b/services/core/java/com/android/server/vibrator/HalVibration.java
@@ -16,6 +16,7 @@
package com.android.server.vibrator;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.CombinedVibration;
import android.os.IBinder;
@@ -35,13 +36,6 @@ import java.util.function.Function;
*/
final class HalVibration extends Vibration {
- public final VibrationAttributes attrs;
- public final long id;
- public final int uid;
- public final int displayId;
- public final String opPkg;
- public final String reason;
- public final IBinder token;
public final SparseArray<VibrationEffect> mFallbacks = new SparseArray<>();
/** The actual effect to be played. */
@@ -58,29 +52,15 @@ final class HalVibration extends Vibration {
/** Vibration status. */
private Vibration.Status mStatus;
- /** Vibration runtime stats. */
- private final VibrationStats mStats = new VibrationStats();
-
/** A {@link CountDownLatch} to enable waiting for completion. */
private final CountDownLatch mCompletionLatch = new CountDownLatch(1);
- HalVibration(IBinder token, int id, CombinedVibration effect,
- VibrationAttributes attrs, int uid, int displayId, String opPkg, String reason) {
- this.token = token;
+ HalVibration(@NonNull IBinder token, CombinedVibration effect, @NonNull CallerInfo callerInfo) {
+ super(token, callerInfo);
this.mEffect = effect;
- this.id = id;
- this.attrs = attrs;
- this.uid = uid;
- this.displayId = displayId;
- this.opPkg = opPkg;
- this.reason = reason;
mStatus = Vibration.Status.RUNNING;
}
- VibrationStats stats() {
- return mStats;
- }
-
/**
* Set the {@link Status} of this vibration and reports the current system time as this
* vibration end time, for debugging purposes.
@@ -94,7 +74,7 @@ final class HalVibration extends Vibration {
return;
}
mStatus = info.status;
- mStats.reportEnded(info.endedByUid, info.endedByUsage);
+ stats.reportEnded(info.endedBy);
mCompletionLatch.countDown();
}
@@ -190,8 +170,8 @@ final class HalVibration extends Vibration {
* Return {@link Vibration.DebugInfo} with read-only debug information about this vibration.
*/
public Vibration.DebugInfo getDebugInfo() {
- return new Vibration.DebugInfo(mStatus, mStats, mEffect, mOriginalEffect, /* scale= */ 0,
- attrs, uid, displayId, opPkg, reason);
+ return new Vibration.DebugInfo(mStatus, stats, mEffect, mOriginalEffect, /* scale= */ 0,
+ callerInfo);
}
/** Return {@link VibrationStats.StatsInfo} with read-only metrics about this vibration. */
@@ -200,7 +180,8 @@ final class HalVibration extends Vibration {
? FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__REPEATED
: FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__SINGLE;
return new VibrationStats.StatsInfo(
- uid, vibrationType, attrs.getUsage(), mStatus, mStats, completionUptimeMillis);
+ callerInfo.uid, vibrationType, callerInfo.attrs.getUsage(), mStatus,
+ stats, completionUptimeMillis);
}
/**
@@ -212,8 +193,9 @@ final class HalVibration extends Vibration {
* pipeline very short vibrations together, regardless of the flag.
*/
public boolean canPipelineWith(HalVibration vib) {
- return uid == vib.uid && attrs.isFlagSet(VibrationAttributes.FLAG_PIPELINED_EFFECT)
- && vib.attrs.isFlagSet(VibrationAttributes.FLAG_PIPELINED_EFFECT)
+ return callerInfo.uid == vib.callerInfo.uid && callerInfo.attrs.isFlagSet(
+ VibrationAttributes.FLAG_PIPELINED_EFFECT)
+ && vib.callerInfo.attrs.isFlagSet(VibrationAttributes.FLAG_PIPELINED_EFFECT)
&& !isRepeating();
}
}
diff --git a/services/core/java/com/android/server/vibrator/InputDeviceDelegate.java b/services/core/java/com/android/server/vibrator/InputDeviceDelegate.java
index 21ba87445478..4e58b9a35243 100644
--- a/services/core/java/com/android/server/vibrator/InputDeviceDelegate.java
+++ b/services/core/java/com/android/server/vibrator/InputDeviceDelegate.java
@@ -21,7 +21,6 @@ import android.content.Context;
import android.hardware.input.InputManager;
import android.os.CombinedVibration;
import android.os.Handler;
-import android.os.VibrationAttributes;
import android.os.VibratorManager;
import android.util.SparseArray;
import android.view.InputDevice;
@@ -94,11 +93,11 @@ final class InputDeviceDelegate implements InputManager.InputDeviceListener {
*
* @return {@link #isAvailable()}
*/
- public boolean vibrateIfAvailable(int uid, String opPkg, CombinedVibration effect,
- String reason, VibrationAttributes attrs) {
+ public boolean vibrateIfAvailable(Vibration.CallerInfo callerInfo, CombinedVibration effect) {
synchronized (mLock) {
for (int i = 0; i < mInputDeviceVibrators.size(); i++) {
- mInputDeviceVibrators.valueAt(i).vibrate(uid, opPkg, effect, reason, attrs);
+ mInputDeviceVibrators.valueAt(i).vibrate(callerInfo.uid, callerInfo.opPkg, effect,
+ callerInfo.reason, callerInfo.attrs);
}
return mInputDeviceVibrators.size() > 0;
}
diff --git a/services/core/java/com/android/server/vibrator/PerformPrebakedVibratorStep.java b/services/core/java/com/android/server/vibrator/PerformPrebakedVibratorStep.java
index d91bafa7c8c2..8094e7c5c58e 100644
--- a/services/core/java/com/android/server/vibrator/PerformPrebakedVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/PerformPrebakedVibratorStep.java
@@ -64,7 +64,7 @@ final class PerformPrebakedVibratorStep extends AbstractVibratorStep {
VibrationEffect fallback = getVibration().getFallback(prebaked.getEffectId());
long vibratorOnResult = controller.on(prebaked, getVibration().id);
handleVibratorOnResult(vibratorOnResult);
- getVibration().stats().reportPerformEffect(vibratorOnResult, prebaked);
+ getVibration().stats.reportPerformEffect(vibratorOnResult, prebaked);
if (vibratorOnResult == 0 && prebaked.shouldFallback()
&& (fallback instanceof VibrationEffect.Composed)) {
diff --git a/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java b/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java
index 1672470f1f1a..cce1ef4db381 100644
--- a/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java
@@ -161,7 +161,7 @@ final class SetAmplitudeVibratorStep extends AbstractVibratorStep {
}
long vibratorOnResult = controller.on(duration, getVibration().id);
handleVibratorOnResult(vibratorOnResult);
- getVibration().stats().reportVibratorOn(vibratorOnResult);
+ getVibration().stats.reportVibratorOn(vibratorOnResult);
return vibratorOnResult;
}
diff --git a/services/core/java/com/android/server/vibrator/StartSequentialEffectStep.java b/services/core/java/com/android/server/vibrator/StartSequentialEffectStep.java
index fd1a3acd4cd7..15c60a3ded62 100644
--- a/services/core/java/com/android/server/vibrator/StartSequentialEffectStep.java
+++ b/services/core/java/com/android/server/vibrator/StartSequentialEffectStep.java
@@ -93,8 +93,8 @@ final class StartSequentialEffectStep extends Step {
}
mVibratorsOnMaxDuration = startVibrating(effectMapping, nextSteps);
- conductor.vibratorManagerHooks.noteVibratorOn(conductor.getVibration().uid,
- mVibratorsOnMaxDuration);
+ conductor.vibratorManagerHooks.noteVibratorOn(
+ conductor.getVibration().callerInfo.uid, mVibratorsOnMaxDuration);
} finally {
if (mVibratorsOnMaxDuration >= 0) {
// It least one vibrator was started then add a finish step to wait for all
@@ -211,7 +211,8 @@ final class StartSequentialEffectStep extends Step {
// Check if sync was prepared and if any step was accepted by a vibrator,
// otherwise there is nothing to trigger here.
if (hasPrepared && !hasFailed && maxDuration > 0) {
- hasTriggered = conductor.vibratorManagerHooks.triggerSyncedVibration(getVibration().id);
+ hasTriggered = conductor.vibratorManagerHooks.triggerSyncedVibration(
+ getVibration().id);
hasFailed &= hasTriggered;
}
diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java
index 5667c72db5dd..1cc0a4ff4b75 100644
--- a/services/core/java/com/android/server/vibrator/Vibration.java
+++ b/services/core/java/com/android/server/vibrator/Vibration.java
@@ -19,6 +19,7 @@ package com.android.server.vibrator;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.CombinedVibration;
+import android.os.IBinder;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.vibrator.PrebakedSegment;
@@ -31,6 +32,7 @@ import android.util.proto.ProtoOutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Objects;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* The base class for all vibrations.
@@ -38,6 +40,13 @@ import java.util.Objects;
class Vibration {
private static final SimpleDateFormat DEBUG_DATE_FORMAT =
new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
+ // Used to generate globally unique vibration ids.
+ private static final AtomicInteger sNextVibrationId = new AtomicInteger(1); // 0 = no callback
+
+ public final long id;
+ public final CallerInfo callerInfo;
+ public final VibrationStats stats = new VibrationStats();
+ public final IBinder callerToken;
/** Vibration status with reference to values from vibratormanagerservice.proto for logging. */
enum Status {
@@ -80,24 +89,82 @@ class Vibration {
}
}
+ Vibration(@NonNull IBinder token, @NonNull CallerInfo callerInfo) {
+ Objects.requireNonNull(token);
+ Objects.requireNonNull(callerInfo);
+ this.id = sNextVibrationId.getAndIncrement();
+ this.callerToken = token;
+ this.callerInfo = callerInfo;
+ }
+
+ /**
+ * Holds lightweight immutable info on the process that triggered the vibration. This data
+ * could potentially be kept in memory for a long time for bugreport dumpsys operations.
+ *
+ * Since CallerInfo can be kept in memory for a long time, it shouldn't hold any references to
+ * potentially expensive or resource-linked objects, such as {@link IBinder}.
+ */
+ static final class CallerInfo {
+ public final VibrationAttributes attrs;
+ public final int uid;
+ public final int displayId;
+ public final String opPkg;
+ public final String reason;
+
+ CallerInfo(@NonNull VibrationAttributes attrs, int uid, int displayId,
+ String opPkg, String reason) {
+ Objects.requireNonNull(attrs);
+ this.attrs = attrs;
+ this.uid = uid;
+ this.displayId = displayId;
+ this.opPkg = opPkg;
+ this.reason = reason;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof CallerInfo)) return false;
+ CallerInfo that = (CallerInfo) o;
+ return Objects.equals(attrs, that.attrs)
+ && uid == that.uid
+ && displayId == that.displayId
+ && Objects.equals(opPkg, that.opPkg)
+ && Objects.equals(reason, that.reason);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(attrs, uid, displayId, opPkg, reason);
+ }
+
+ @Override
+ public String toString() {
+ return "CallerInfo{"
+ + " attrs=" + attrs
+ + ", uid=" + uid
+ + ", displayId=" + displayId
+ + ", opPkg=" + opPkg
+ + ", reason=" + reason
+ + '}';
+ }
+ }
+
/** Immutable info passed as a signal to end a vibration. */
static final class EndInfo {
/** The {@link Status} to be set to the vibration when it ends with this info. */
@NonNull
public final Status status;
- /** The UID that triggered the vibration that ended this, or -1 if undefined. */
- public final int endedByUid;
- /** The VibrationAttributes.USAGE_* of the vibration that ended this, or -1 if undefined. */
- public final int endedByUsage;
+ /** Info about the process that ended the vibration. */
+ public final CallerInfo endedBy;
EndInfo(@NonNull Vibration.Status status) {
- this(status, /* endedByUid= */ -1, /* endedByUsage= */ -1);
+ this(status, null);
}
- EndInfo(@NonNull Vibration.Status status, int endedByUid, int endedByUsage) {
+ EndInfo(@NonNull Vibration.Status status, @Nullable CallerInfo endedBy) {
this.status = status;
- this.endedByUid = endedByUid;
- this.endedByUsage = endedByUsage;
+ this.endedBy = endedBy;
}
@Override
@@ -105,27 +172,31 @@ class Vibration {
if (this == o) return true;
if (!(o instanceof EndInfo)) return false;
EndInfo that = (EndInfo) o;
- return endedByUid == that.endedByUid
- && endedByUsage == that.endedByUsage
+ return Objects.equals(endedBy, that.endedBy)
&& status == that.status;
}
@Override
public int hashCode() {
- return Objects.hash(status, endedByUid, endedByUsage);
+ return Objects.hash(status, endedBy);
}
@Override
public String toString() {
return "EndInfo{"
+ "status=" + status
- + ", endedByUid=" + endedByUid
- + ", endedByUsage=" + endedByUsage
+ + ", endedBy=" + endedBy
+ '}';
}
}
- /** Debug information about vibrations. */
+ /**
+ * Holds lightweight debug information about the vibration that could potentially be kept in
+ * memory for a long time for bugreport dumpsys operations.
+ *
+ * Since DebugInfo can be kept in memory for a long time, it shouldn't hold any references to
+ * potentially expensive or resource-linked objects, such as {@link IBinder}.
+ */
static final class DebugInfo {
private final long mCreateTime;
private final long mStartTime;
@@ -134,16 +205,13 @@ class Vibration {
private final CombinedVibration mEffect;
private final CombinedVibration mOriginalEffect;
private final float mScale;
- private final VibrationAttributes mAttrs;
- private final int mUid;
- private final int mDisplayId;
- private final String mOpPkg;
- private final String mReason;
+ private final CallerInfo mCallerInfo;
private final Status mStatus;
DebugInfo(Status status, VibrationStats stats, @Nullable CombinedVibration effect,
- @Nullable CombinedVibration originalEffect, float scale, VibrationAttributes attrs,
- int uid, int displayId, String opPkg, String reason) {
+ @Nullable CombinedVibration originalEffect, float scale,
+ @NonNull CallerInfo callerInfo) {
+ Objects.requireNonNull(callerInfo);
mCreateTime = stats.getCreateTimeDebug();
mStartTime = stats.getStartTimeDebug();
mEndTime = stats.getEndTimeDebug();
@@ -151,11 +219,7 @@ class Vibration {
mEffect = effect;
mOriginalEffect = originalEffect;
mScale = scale;
- mAttrs = attrs;
- mUid = uid;
- mDisplayId = displayId;
- mOpPkg = opPkg;
- mReason = reason;
+ mCallerInfo = callerInfo;
mStatus = status;
}
@@ -179,16 +243,8 @@ class Vibration {
.append(mOriginalEffect)
.append(", scale: ")
.append(String.format("%.2f", mScale))
- .append(", attrs: ")
- .append(mAttrs)
- .append(", uid: ")
- .append(mUid)
- .append(", displayId: ")
- .append(mDisplayId)
- .append(", opPkg: ")
- .append(mOpPkg)
- .append(", reason: ")
- .append(mReason)
+ .append(", callerInfo: ")
+ .append(mCallerInfo)
.toString();
}
@@ -201,9 +257,10 @@ class Vibration {
proto.write(VibrationProto.STATUS, mStatus.ordinal());
final long attrsToken = proto.start(VibrationProto.ATTRIBUTES);
- proto.write(VibrationAttributesProto.USAGE, mAttrs.getUsage());
- proto.write(VibrationAttributesProto.AUDIO_USAGE, mAttrs.getAudioUsage());
- proto.write(VibrationAttributesProto.FLAGS, mAttrs.getFlags());
+ final VibrationAttributes attrs = mCallerInfo.attrs;
+ proto.write(VibrationAttributesProto.USAGE, attrs.getUsage());
+ proto.write(VibrationAttributesProto.AUDIO_USAGE, attrs.getAudioUsage());
+ proto.write(VibrationAttributesProto.FLAGS, attrs.getFlags());
proto.end(attrsToken);
if (mEffect != null) {
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index d944a3b913d6..8a7d607c1e60 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -27,6 +27,7 @@ import static android.os.VibrationAttributes.USAGE_RINGTONE;
import static android.os.VibrationAttributes.USAGE_TOUCH;
import static android.os.VibrationAttributes.USAGE_UNKNOWN;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.IUidObserver;
@@ -375,15 +376,15 @@ final class VibrationSettings {
* null otherwise.
*/
@Nullable
- public Vibration.Status shouldIgnoreVibration(int uid, int displayId,
- VibrationAttributes attrs) {
- final int usage = attrs.getUsage();
+ public Vibration.Status shouldIgnoreVibration(@NonNull Vibration.CallerInfo callerInfo) {
+ final int usage = callerInfo.attrs.getUsage();
synchronized (mLock) {
- if (!mUidObserver.isUidForeground(uid)
+ if (!mUidObserver.isUidForeground(callerInfo.uid)
&& !BACKGROUND_PROCESS_USAGE_ALLOWLIST.contains(usage)) {
return Vibration.Status.IGNORED_BACKGROUND;
}
- if (mVirtualDeviceListener.isAppOrDisplayOnAnyVirtualDevice(uid, displayId)) {
+ if (mVirtualDeviceListener.isAppOrDisplayOnAnyVirtualDevice(callerInfo.uid,
+ callerInfo.displayId)) {
return Vibration.Status.IGNORED_FROM_VIRTUAL_DEVICE;
}
@@ -391,7 +392,8 @@ final class VibrationSettings {
return Vibration.Status.IGNORED_FOR_POWER;
}
- if (!attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)) {
+ if (!callerInfo.attrs.isFlagSet(
+ VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)) {
if (!mVibrateOn && (VIBRATE_ON_DISABLED_USAGE_ALLOWED != usage)) {
return Vibration.Status.IGNORED_FOR_SETTINGS;
}
@@ -401,7 +403,7 @@ final class VibrationSettings {
}
}
- if (!attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY)) {
+ if (!callerInfo.attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY)) {
if (!shouldVibrateForRingerModeLocked(usage)) {
return Vibration.Status.IGNORED_FOR_RINGER_MODE;
}
@@ -420,8 +422,8 @@ final class VibrationSettings {
*
* @return true if the vibration should be cancelled when the screen goes off, false otherwise.
*/
- public boolean shouldCancelVibrationOnScreenOff(int uid, String opPkg,
- @VibrationAttributes.Usage int usage, long vibrationStartUptimeMillis) {
+ public boolean shouldCancelVibrationOnScreenOff(@NonNull Vibration.CallerInfo callerInfo,
+ long vibrationStartUptimeMillis) {
PowerManagerInternal pm;
synchronized (mLock) {
pm = mPowerManagerInternal;
@@ -442,12 +444,13 @@ final class VibrationSettings {
return false;
}
}
- if (!SYSTEM_VIBRATION_SCREEN_OFF_USAGE_ALLOWLIST.contains(usage)) {
+ if (!SYSTEM_VIBRATION_SCREEN_OFF_USAGE_ALLOWLIST.contains(callerInfo.attrs.getUsage())) {
// Usages not allowed even for system vibrations should always be cancelled.
return true;
}
// Only allow vibrations from System packages to continue vibrating when the screen goes off
- return uid != Process.SYSTEM_UID && uid != 0 && !mSystemUiPackage.equals(opPkg);
+ return callerInfo.uid != Process.SYSTEM_UID && callerInfo.uid != 0
+ && !mSystemUiPackage.equals(callerInfo.opPkg);
}
/**
diff --git a/services/core/java/com/android/server/vibrator/VibrationStats.java b/services/core/java/com/android/server/vibrator/VibrationStats.java
index 931be1d5d711..2d003513bee1 100644
--- a/services/core/java/com/android/server/vibrator/VibrationStats.java
+++ b/services/core/java/com/android/server/vibrator/VibrationStats.java
@@ -16,6 +16,8 @@
package com.android.server.vibrator;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.SystemClock;
import android.os.vibrator.PrebakedSegment;
import android.os.vibrator.PrimitiveSegment;
@@ -159,15 +161,18 @@ final class VibrationStats {
* @return true if the status was accepted. This method will only accept given values if
* the end timestamp was never set.
*/
- boolean reportEnded(int endedByUid, int endedByUsage) {
+ boolean reportEnded(@Nullable Vibration.CallerInfo endedBy) {
if (hasEnded()) {
// Vibration already ended, keep first ending stats set and ignore this one.
return false;
}
- mEndedByUid = endedByUid;
- mEndedByUsage = endedByUsage;
+ if (endedBy != null) {
+ mEndedByUid = endedBy.uid;
+ mEndedByUsage = endedBy.attrs.getUsage();
+ }
mEndUptimeMillis = SystemClock.uptimeMillis();
mEndTimeDebug = System.currentTimeMillis();
+
return true;
}
@@ -177,9 +182,9 @@ final class VibrationStats {
* <p>This method will only accept the first value as the one that was interrupted by this
* vibration, and will ignore all successive calls.
*/
- void reportInterruptedAnotherVibration(int interruptedUsage) {
+ void reportInterruptedAnotherVibration(@NonNull Vibration.CallerInfo callerInfo) {
if (mInterruptedUsage < 0) {
- mInterruptedUsage = interruptedUsage;
+ mInterruptedUsage = callerInfo.attrs.getUsage();
}
}
diff --git a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
index 11cab4b83fb8..4202afb738d0 100644
--- a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
+++ b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
@@ -157,7 +157,7 @@ final class VibrationStepConductor implements IBinder.DeathRecipient {
mNextSteps.offer(new StartSequentialEffectStep(this, sequentialEffect));
// Vibration will start playing in the Vibrator, following the effect timings and delays.
// Report current time as the vibration start time, for debugging.
- mVibration.stats().reportStarted();
+ mVibration.stats.reportStarted();
}
public HalVibration getVibration() {
diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java
index d2dc43c68e5d..cfb4c74fcbfc 100644
--- a/services/core/java/com/android/server/vibrator/VibrationThread.java
+++ b/services/core/java/com/android/server/vibrator/VibrationThread.java
@@ -161,7 +161,8 @@ final class VibrationThread extends Thread {
// for this thread.
// No point doing this in finally, as if there's an exception, this thread will die
// and be unusable anyway.
- mVibratorManagerHooks.onVibrationThreadReleased(mExecutingConductor.getVibration().id);
+ mVibratorManagerHooks.onVibrationThreadReleased(
+ mExecutingConductor.getVibration().id);
synchronized (mLock) {
mLock.notifyAll();
}
@@ -230,7 +231,8 @@ final class VibrationThread extends Thread {
/** Runs the VibrationThread ensuring that the wake lock is acquired and released. */
private void runCurrentVibrationWithWakeLock() {
- WorkSource workSource = new WorkSource(mExecutingConductor.getVibration().uid);
+ WorkSource workSource = new WorkSource(
+ mExecutingConductor.getVibration().callerInfo.uid);
mWakeLock.setWorkSource(workSource);
mWakeLock.acquire();
try {
@@ -251,7 +253,7 @@ final class VibrationThread extends Thread {
* Called from within runWithWakeLock.
*/
private void runCurrentVibrationWithWakeLockAndDeathLink() {
- IBinder vibrationBinderToken = mExecutingConductor.getVibration().token;
+ IBinder vibrationBinderToken = mExecutingConductor.getVibration().callerToken;
try {
vibrationBinderToken.linkToDeath(mExecutingConductor, 0);
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index c87332d2ff36..5756414a303d 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -77,7 +77,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -123,9 +122,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
}
- // Used to generate globally unique vibration ids.
- private final AtomicInteger mNextVibrationId = new AtomicInteger(1); // 0 = no callback
-
private final Object mLock = new Object();
private final Context mContext;
private final PowerManager.WakeLock mWakeLock;
@@ -367,8 +363,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
// missing on individual vibrators.
return false;
}
- AlwaysOnVibration alwaysOnVibration = new AlwaysOnVibration(
- alwaysOnId, uid, opPkg, attrs, effects);
+ AlwaysOnVibration alwaysOnVibration = new AlwaysOnVibration(alwaysOnId,
+ new Vibration.CallerInfo(attrs, uid, Display.DEFAULT_DISPLAY, opPkg,
+ null), effects);
mAlwaysOnEffects.put(alwaysOnId, alwaysOnVibration);
updateAlwaysOnLocked(alwaysOnVibration);
}
@@ -408,8 +405,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
attrs = fixupVibrationAttributes(attrs, effect);
// Create Vibration.Stats as close to the received request as possible, for tracking.
- HalVibration vib = new HalVibration(token, mNextVibrationId.getAndIncrement(), effect,
- attrs, uid, displayId, opPkg, reason);
+ HalVibration vib = new HalVibration(token, effect,
+ new Vibration.CallerInfo(attrs, uid, displayId, opPkg, reason));
fillVibrationFallbacks(vib, effect);
if (attrs.isFlagSet(VibrationAttributes.FLAG_INVALIDATE_SETTINGS_CACHE)) {
@@ -422,27 +419,23 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
if (DEBUG) {
Slog.d(TAG, "Starting vibrate for vibration " + vib.id);
}
- int ignoredByUid = -1;
- int ignoredByUsage = -1;
- Vibration.Status status = null;
+ Vibration.CallerInfo ignoredByCallerInfo = null;
+ Vibration.Status status;
// Check if user settings or DnD is set to ignore this vibration.
- status = shouldIgnoreVibrationLocked(vib.uid, vib.displayId, vib.opPkg, vib.attrs);
+ status = shouldIgnoreVibrationLocked(vib.callerInfo);
// Check if something has external control, assume it's more important.
if ((status == null) && (mCurrentExternalVibration != null)) {
status = Vibration.Status.IGNORED_FOR_EXTERNAL;
- ignoredByUid = mCurrentExternalVibration.externalVibration.getUid();
- ignoredByUsage = mCurrentExternalVibration.externalVibration
- .getVibrationAttributes().getUsage();
+ ignoredByCallerInfo = mCurrentExternalVibration.callerInfo;
}
// Check if ongoing vibration is more important than this vibration.
if (status == null) {
status = shouldIgnoreVibrationForOngoingLocked(vib);
if (status != null) {
- ignoredByUid = mCurrentVibration.getVibration().uid;
- ignoredByUsage = mCurrentVibration.getVibration().attrs.getUsage();
+ ignoredByCallerInfo = mCurrentVibration.getVibration().callerInfo;
}
}
@@ -460,12 +453,11 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
Slog.d(TAG, "Pipelining vibration " + vib.id);
}
} else {
- vib.stats().reportInterruptedAnotherVibration(
- mCurrentVibration.getVibration().attrs.getUsage());
+ vib.stats.reportInterruptedAnotherVibration(
+ mCurrentVibration.getVibration().callerInfo);
mCurrentVibration.notifyCancelled(
- new Vibration.EndInfo(
- Vibration.Status.CANCELLED_SUPERSEDED, vib.uid,
- vib.attrs.getUsage()),
+ new Vibration.EndInfo(Vibration.Status.CANCELLED_SUPERSEDED,
+ vib.callerInfo),
/* immediate= */ false);
}
}
@@ -478,7 +470,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
// Ignored or failed to start the vibration, end it and report metrics right away.
if (status != Vibration.Status.RUNNING) {
endVibrationLocked(vib,
- new Vibration.EndInfo(status, ignoredByUid, ignoredByUsage),
+ new Vibration.EndInfo(status, ignoredByCallerInfo),
/* shouldWriteStats= */ true);
}
return vib;
@@ -641,8 +633,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
HalVibration vib = mCurrentVibration.getVibration();
- Vibration.Status ignoreStatus = shouldIgnoreVibrationLocked(
- vib.uid, vib.displayId, vib.opPkg, vib.attrs);
+ Vibration.Status ignoreStatus = shouldIgnoreVibrationLocked(vib.callerInfo);
if (inputDevicesChanged || (ignoreStatus != null)) {
if (DEBUG) {
@@ -671,10 +662,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
if (vibrator == null) {
continue;
}
- Vibration.Status ignoreStatus = shouldIgnoreVibrationLocked(
- vib.uid, Display.DEFAULT_DISPLAY, vib.opPkg, vib.attrs);
+ Vibration.Status ignoreStatus = shouldIgnoreVibrationLocked(vib.callerInfo);
if (ignoreStatus == null) {
- effect = mVibrationScaler.scale(effect, vib.attrs.getUsage());
+ effect = mVibrationScaler.scale(effect, vib.callerInfo.attrs.getUsage());
} else {
// Vibration should not run, use null effect to remove registered effect.
effect = null;
@@ -687,9 +677,10 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
private Vibration.Status startVibrationLocked(HalVibration vib) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationLocked");
try {
- vib.updateEffects(effect -> mVibrationScaler.scale(effect, vib.attrs.getUsage()));
+ vib.updateEffects(
+ effect -> mVibrationScaler.scale(effect, vib.callerInfo.attrs.getUsage()));
boolean inputDevicesAvailable = mInputDeviceDelegate.vibrateIfAvailable(
- vib.uid, vib.opPkg, vib.getEffect(), vib.reason, vib.attrs);
+ vib.callerInfo, vib.getEffect());
if (inputDevicesAvailable) {
return Vibration.Status.FORWARDED_TO_INPUT_DEVICES;
}
@@ -704,8 +695,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
// Note that we don't consider pipelining here, because new pipelined ones should
// replace pending non-executing pipelined ones anyway.
clearNextVibrationLocked(
- new Vibration.EndInfo(Vibration.Status.IGNORED_SUPERSEDED,
- vib.uid, vib.attrs.getUsage()));
+ new Vibration.EndInfo(Vibration.Status.IGNORED_SUPERSEDED, vib.callerInfo));
mNextVibration = conductor;
return Vibration.Status.RUNNING;
} finally {
@@ -718,7 +708,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationThreadLocked");
try {
HalVibration vib = conductor.getVibration();
- int mode = startAppOpModeLocked(vib.uid, vib.opPkg, vib.attrs);
+ int mode = startAppOpModeLocked(vib.callerInfo);
switch (mode) {
case AppOpsManager.MODE_ALLOWED:
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
@@ -731,7 +721,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
return Vibration.Status.RUNNING;
case AppOpsManager.MODE_ERRORED:
- Slog.w(TAG, "Start AppOpsManager operation errored for uid " + vib.uid);
+ Slog.w(TAG, "Start AppOpsManager operation errored for uid "
+ + vib.callerInfo.uid);
return Vibration.Status.IGNORED_ERROR_APP_OPS;
default:
return Vibration.Status.IGNORED_APP_OPS;
@@ -745,7 +736,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
private void endVibrationLocked(HalVibration vib, Vibration.EndInfo vibrationEndInfo,
boolean shouldWriteStats) {
vib.end(vibrationEndInfo);
- logVibrationStatus(vib.uid, vib.attrs, vibrationEndInfo.status);
+ logVibrationStatus(vib.callerInfo.uid, vib.callerInfo.attrs,
+ vibrationEndInfo.status);
mVibratorManagerRecords.record(vib);
if (shouldWriteStats) {
mFrameworkStatsLogger.writeVibrationReportedAsync(
@@ -817,12 +809,13 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
try {
HalVibration vib = mCurrentVibration.getVibration();
if (DEBUG) {
- Slog.d(TAG, "Reporting vibration " + vib.id + " finished with " + vibrationEndInfo);
+ Slog.d(TAG, "Reporting vibration " + vib.id + " finished with "
+ + vibrationEndInfo);
}
// DO NOT write metrics at this point, wait for the VibrationThread to report the
// vibration was released, after all cleanup. The metrics will be reported then.
endVibrationLocked(vib, vibrationEndInfo, /* shouldWriteStats= */ false);
- finishAppOpModeLocked(vib.uid, vib.opPkg);
+ finishAppOpModeLocked(vib.callerInfo);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
@@ -830,7 +823,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
private void onSyncedVibrationComplete(long vibrationId) {
synchronized (mLock) {
- if (mCurrentVibration != null && mCurrentVibration.getVibration().id == vibrationId) {
+ if (mCurrentVibration != null
+ && mCurrentVibration.getVibration().id == vibrationId) {
if (DEBUG) {
Slog.d(TAG, "Synced vibration " + vibrationId + " complete, notifying thread");
}
@@ -841,7 +835,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
private void onVibrationComplete(int vibratorId, long vibrationId) {
synchronized (mLock) {
- if (mCurrentVibration != null && mCurrentVibration.getVibration().id == vibrationId) {
+ if (mCurrentVibration != null
+ && mCurrentVibration.getVibration().id == vibrationId) {
if (DEBUG) {
Slog.d(TAG, "Vibration " + vibrationId + " on vibrator " + vibratorId
+ " complete, notifying thread");
@@ -871,8 +866,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
return null;
}
- int currentUsage = currentVibration.attrs.getUsage();
- int newUsage = vib.attrs.getUsage();
+ int currentUsage = currentVibration.callerInfo.attrs.getUsage();
+ int newUsage = vib.callerInfo.attrs.getUsage();
if (getVibrationImportance(currentUsage) > getVibrationImportance(newUsage)) {
// Current vibration has higher importance than this one and should not be cancelled.
return Vibration.Status.IGNORED_FOR_HIGHER_IMPORTANCE;
@@ -916,15 +911,13 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
*/
@GuardedBy("mLock")
@Nullable
- private Vibration.Status shouldIgnoreVibrationLocked(int uid, int displayId, String opPkg,
- VibrationAttributes attrs) {
- Vibration.Status statusFromSettings = mVibrationSettings.shouldIgnoreVibration(uid,
- displayId, attrs);
+ private Vibration.Status shouldIgnoreVibrationLocked(Vibration.CallerInfo callerInfo) {
+ Vibration.Status statusFromSettings = mVibrationSettings.shouldIgnoreVibration(callerInfo);
if (statusFromSettings != null) {
return statusFromSettings;
}
- int mode = checkAppOpModeLocked(uid, opPkg, attrs);
+ int mode = checkAppOpModeLocked(callerInfo);
if (mode != AppOpsManager.MODE_ALLOWED) {
if (mode == AppOpsManager.MODE_ERRORED) {
// We might be getting calls from within system_server, so we don't actually
@@ -948,7 +941,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
* started with the same token can be cancelled with it.
*/
private boolean shouldCancelVibration(HalVibration vib, int usageFilter, IBinder token) {
- return (vib.token == token) && shouldCancelVibration(vib.attrs, usageFilter);
+ return (vib.callerToken == token) && shouldCancelVibration(vib.callerInfo.attrs,
+ usageFilter);
}
/**
@@ -973,24 +967,25 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
* {@code attrs}. This will return one of the AppOpsManager.MODE_*.
*/
@GuardedBy("mLock")
- private int checkAppOpModeLocked(int uid, String opPkg, VibrationAttributes attrs) {
+ private int checkAppOpModeLocked(Vibration.CallerInfo callerInfo) {
int mode = mAppOps.checkAudioOpNoThrow(AppOpsManager.OP_VIBRATE,
- attrs.getAudioUsage(), uid, opPkg);
- int fixedMode = fixupAppOpModeLocked(mode, attrs);
+ callerInfo.attrs.getAudioUsage(), callerInfo.uid, callerInfo.opPkg);
+ int fixedMode = fixupAppOpModeLocked(mode, callerInfo.attrs);
if (mode != fixedMode && fixedMode == AppOpsManager.MODE_ALLOWED) {
// If we're just ignoring the vibration op then this is set by DND and we should ignore
// if we're asked to bypass. AppOps won't be able to record this operation, so make
// sure we at least note it in the logs for debugging.
- Slog.d(TAG, "Bypassing DND for vibrate from uid " + uid);
+ Slog.d(TAG, "Bypassing DND for vibrate from uid " + callerInfo.uid);
}
return fixedMode;
}
/** Start an operation in {@link AppOpsManager}, if allowed. */
@GuardedBy("mLock")
- private int startAppOpModeLocked(int uid, String opPkg, VibrationAttributes attrs) {
+ private int startAppOpModeLocked(Vibration.CallerInfo callerInfo) {
return fixupAppOpModeLocked(
- mAppOps.startOpNoThrow(AppOpsManager.OP_VIBRATE, uid, opPkg), attrs);
+ mAppOps.startOpNoThrow(AppOpsManager.OP_VIBRATE, callerInfo.uid, callerInfo.opPkg),
+ callerInfo.attrs);
}
/**
@@ -998,8 +993,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
* operation with same uid was previously started.
*/
@GuardedBy("mLock")
- private void finishAppOpModeLocked(int uid, String opPkg) {
- mAppOps.finishOp(AppOpsManager.OP_VIBRATE, uid, opPkg);
+ private void finishAppOpModeLocked(Vibration.CallerInfo callerInfo) {
+ mAppOps.finishOp(AppOpsManager.OP_VIBRATE, callerInfo.uid, callerInfo.opPkg);
}
/**
@@ -1186,8 +1181,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
return false;
}
HalVibration vib = conductor.getVibration();
- return mVibrationSettings.shouldCancelVibrationOnScreenOff(
- vib.uid, vib.opPkg, vib.attrs.getUsage(), vib.stats().getCreateUptimeMillis());
+ return mVibrationSettings.shouldCancelVibrationOnScreenOff(vib.callerInfo,
+ vib.stats.getCreateUptimeMillis());
}
@GuardedBy("mLock")
@@ -1385,31 +1380,33 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
*/
private static final class AlwaysOnVibration {
public final int alwaysOnId;
- public final int uid;
- public final String opPkg;
- public final VibrationAttributes attrs;
+ public final Vibration.CallerInfo callerInfo;
public final SparseArray<PrebakedSegment> effects;
- AlwaysOnVibration(int alwaysOnId, int uid, String opPkg, VibrationAttributes attrs,
+ AlwaysOnVibration(int alwaysOnId, Vibration.CallerInfo callerInfo,
SparseArray<PrebakedSegment> effects) {
this.alwaysOnId = alwaysOnId;
- this.uid = uid;
- this.opPkg = opPkg;
- this.attrs = attrs;
+ this.callerInfo = callerInfo;
this.effects = effects;
}
}
/** Holder for a {@link ExternalVibration}. */
- private final class ExternalVibrationHolder implements IBinder.DeathRecipient {
+ private final class ExternalVibrationHolder extends Vibration implements
+ IBinder.DeathRecipient {
public final ExternalVibration externalVibration;
- public final VibrationStats stats = new VibrationStats();
public int scale;
private Vibration.Status mStatus;
private ExternalVibrationHolder(ExternalVibration externalVibration) {
+ super(externalVibration.getToken(), new Vibration.CallerInfo(
+ externalVibration.getVibrationAttributes(), externalVibration.getUid(),
+ // TODO(b/243604888): propagating displayID from IExternalVibration instead of
+ // using INVALID_DISPLAY for all external vibrations.
+ Display.INVALID_DISPLAY,
+ externalVibration.getPackage(), null));
this.externalVibration = externalVibration;
this.scale = IExternalVibratorService.SCALE_NONE;
mStatus = Vibration.Status.RUNNING;
@@ -1437,14 +1434,15 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
return;
}
mStatus = info.status;
- stats.reportEnded(info.endedByUid, info.endedByUsage);
+ stats.reportEnded(info.endedBy);
if (stats.hasStarted()) {
// External vibration doesn't have feedback from total time the vibrator was playing
// with non-zero amplitude, so we use the duration between start and end times of
// the vibration as the time the vibrator was ON, since the haptic channels are
// open for this duration and can receive vibration waveform data.
- stats.reportVibratorOn(stats.getEndUptimeMillis() - stats.getStartUptimeMillis());
+ stats.reportVibratorOn(
+ stats.getEndUptimeMillis() - stats.getStartUptimeMillis());
}
}
@@ -1462,13 +1460,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
public Vibration.DebugInfo getDebugInfo() {
- return new Vibration.DebugInfo(
- mStatus, stats, /* effect= */ null, /* originalEffect= */ null, scale,
- externalVibration.getVibrationAttributes(), externalVibration.getUid(),
- // TODO(b/243604888): propagating displayID from IExternalVibration instead of
- // using INVALID_DISPLAY for all external vibrations.
- Display.INVALID_DISPLAY,
- externalVibration.getPackage(), /* reason= */ null);
+ return new Vibration.DebugInfo(mStatus, stats, /* effect= */ null,
+ /* originalEffect= */ null, scale, callerInfo);
}
public VibrationStats.StatsInfo getStatsInfo(long completionUptimeMillis) {
@@ -1538,7 +1531,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
synchronized void record(HalVibration vib) {
- int usage = vib.attrs.getUsage();
+ int usage = vib.callerInfo.attrs.getUsage();
if (!mPreviousVibrations.contains(usage)) {
mPreviousVibrations.put(usage, new LinkedList<>());
}
@@ -1679,8 +1672,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
synchronized (mLock) {
// TODO(b/243604888): propagating displayID from IExternalVibration instead of
// using INVALID_DISPLAY for all external vibrations.
- Vibration.Status ignoreStatus = shouldIgnoreVibrationLocked(
- vib.getUid(), Display.INVALID_DISPLAY, vib.getPackage(), attrs);
+ Vibration.Status ignoreStatus = shouldIgnoreVibrationLocked(vibHolder.callerInfo);
if (ignoreStatus != null) {
vibHolder.scale = IExternalVibratorService.SCALE_MUTE;
// Failed to start the vibration, end it and report metrics right away.
@@ -1698,13 +1690,13 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
// vibration that may be playing and ready the vibrator for external control.
if (mCurrentVibration != null) {
vibHolder.stats.reportInterruptedAnotherVibration(
- mCurrentVibration.getVibration().attrs.getUsage());
+ mCurrentVibration.getVibration().callerInfo);
clearNextVibrationLocked(
new Vibration.EndInfo(Vibration.Status.IGNORED_FOR_EXTERNAL,
- vib.getUid(), attrs.getUsage()));
+ vibHolder.callerInfo));
mCurrentVibration.notifyCancelled(
new Vibration.EndInfo(Vibration.Status.CANCELLED_SUPERSEDED,
- vib.getUid(), attrs.getUsage()),
+ vibHolder.callerInfo),
/* immediate= */ true);
waitForCompletion = true;
}
@@ -1720,11 +1712,10 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
alreadyUnderExternalControl = true;
mCurrentExternalVibration.mute();
vibHolder.stats.reportInterruptedAnotherVibration(
- mCurrentExternalVibration.externalVibration
- .getVibrationAttributes().getUsage());
+ mCurrentExternalVibration.callerInfo);
endExternalVibrateLocked(
new Vibration.EndInfo(Vibration.Status.CANCELLED_SUPERSEDED,
- vib.getUid(), attrs.getUsage()),
+ vibHolder.callerInfo),
/* continueExternalControl= */ true);
}
mCurrentExternalVibration = vibHolder;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 2b9364c2f692..f092172b24ea 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -5115,6 +5115,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return mDeferHidingClient;
}
+ boolean canAffectSystemUiFlags() {
+ return task != null && task.canAffectSystemUiFlags() && isVisible()
+ && !inPinnedWindowingMode();
+ }
+
@Override
boolean isVisible() {
// If the activity isn't hidden then it is considered visible and there is no need to check
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 71bb99cbf515..59bba2331c6e 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -2010,17 +2010,15 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
return mainWin.getAttrs().rotationAnimation;
}
- /** Applies the new configuration and returns {@code true} if there is a display change. */
- boolean applyDisplayChangeIfNeeded() {
- boolean changed = false;
+ /** Applies the new configuration for the changed displays. */
+ void applyDisplayChangeIfNeeded() {
for (int i = mParticipants.size() - 1; i >= 0; --i) {
final WindowContainer<?> wc = mParticipants.valueAt(i);
final DisplayContent dc = wc.asDisplayContent();
if (dc == null || !mChanges.get(dc).hasChanged()) continue;
dc.sendNewConfiguration();
- changed = true;
+ setReady(dc, true);
}
- return changed;
}
boolean getLegacyIsReady() {
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index 6b2bf599d5ea..8708f73980c6 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -41,6 +41,8 @@ class WallpaperWindowToken extends WindowToken {
private static final String TAG = TAG_WITH_CLASS_NAME ? "WallpaperWindowToken" : TAG_WM;
+ private boolean mShowWhenLocked = false;
+
WallpaperWindowToken(WindowManagerService service, IBinder token, boolean explicit,
DisplayContent dc, boolean ownerCanManageAppTokens) {
this(service, token, explicit, dc, ownerCanManageAppTokens, null /* options */);
@@ -65,6 +67,29 @@ class WallpaperWindowToken extends WindowToken {
mDisplayContent.mWallpaperController.removeWallpaperToken(this);
}
+ /**
+ * Controls whether this wallpaper shows underneath the keyguard or is hidden and only
+ * revealed once keyguard is dismissed.
+ */
+ void setShowWhenLocked(boolean showWhenLocked) {
+ if (showWhenLocked == mShowWhenLocked) {
+ return;
+ }
+ mShowWhenLocked = showWhenLocked;
+
+ // Move the window token to the front (private) or back (showWhenLocked). This is possible
+ // because the DisplayArea underneath TaskDisplayArea only contains TYPE_WALLPAPER windows.
+ final int position = showWhenLocked ? POSITION_BOTTOM : POSITION_TOP;
+
+ // Note: Moving all the way to the front or back breaks ordering based on addition times.
+ // We should never have more than one non-animating token of each type.
+ getParent().positionChildAt(position, this /* child */, false /*includingParents */);
+ }
+
+ boolean canShowWhenLocked() {
+ return mShowWhenLocked;
+ }
+
void sendWindowWallpaperCommand(
String action, int x, int y, int z, Bundle extras, boolean sync) {
for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 1282acbc9e5a..2f3a70eb0e2d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -657,6 +657,17 @@ public abstract class WindowManagerInternal {
public abstract int getWindowOwnerUserId(IBinder windowToken);
/**
+ * Control visilibility of a {@link WallpaperWindowToken} {@code} binder on the lock screen.
+ *
+ * <p>This will also affect its Z-ordering as {@code showWhenLocked} wallpaper tokens are
+ * arranged underneath non-{@code showWhenLocked} wallpaper tokens.
+ *
+ * @param windowToken wallpaper token previously added via {@link #addWindowToken}
+ * @param showWhenLocked whether {@param token} can continue to be shown on the lock screen.
+ */
+ public abstract void setWallpaperShowWhenLocked(IBinder windowToken, boolean showWhenLocked);
+
+ /**
* Returns {@code true} if a Window owned by {@code uid} has focus.
*/
public abstract boolean isUidFocused(int uid);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index ad819820ceec..22fddec71326 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8045,6 +8045,19 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
+ public void setWallpaperShowWhenLocked(IBinder binder, boolean showWhenLocked) {
+ synchronized (mGlobalLock) {
+ final WindowToken token = mRoot.getWindowToken(binder);
+ if (token == null || token.asWallpaperToken() == null) {
+ ProtoLog.w(WM_ERROR,
+ "setWallpaperShowWhenLocked: non-existent wallpaper token: %s", binder);
+ return;
+ }
+ token.asWallpaperToken().setShowWhenLocked(showWhenLocked);
+ }
+ }
+
+ @Override
public boolean isUidFocused(int uid) {
synchronized (mGlobalLock) {
for (int i = mRoot.getChildCount() - 1; i >= 0; i--) {
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 3faf9e005c5e..957d99adea3e 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -494,8 +494,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
mService.deferWindowLayout();
mService.mTaskSupervisor.setDeferRootVisibilityUpdate(true /* deferUpdate */);
try {
- if (transition != null && transition.applyDisplayChangeIfNeeded()) {
- effects |= TRANSACT_EFFECTS_LIFECYCLE;
+ if (transition != null) {
+ transition.applyDisplayChangeIfNeeded();
}
final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();
final int hopSize = hops.size();
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index e429db924df5..69e84c76ac3b 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2067,12 +2067,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
final boolean exiting = mAnimatingExit || mDestroying;
return shown && !exiting;
} else {
- final Task task = getTask();
- final boolean canFromTask = task != null && task.canAffectSystemUiFlags();
- return canFromTask && mActivityRecord.isVisible()
- // Do not let snapshot window control the bar
+ return mActivityRecord.canAffectSystemUiFlags()
+ // Do not let snapshot window control the bar
&& (mAttrs.type != TYPE_APPLICATION_STARTING
- || !(mStartingData instanceof SnapshotStartingData));
+ || !(mStartingData instanceof SnapshotStartingData));
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BooleanPolicySerializer.java b/services/devicepolicy/java/com/android/server/devicepolicy/BooleanPolicySerializer.java
index 9cb7533e43ac..474df98cb1e6 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BooleanPolicySerializer.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BooleanPolicySerializer.java
@@ -18,6 +18,8 @@ package com.android.server.devicepolicy;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.admin.BooleanPolicyValue;
+import android.app.admin.PolicyKey;
import android.util.Log;
import com.android.modules.utils.TypedXmlPullParser;
@@ -31,7 +33,8 @@ import java.util.Objects;
final class BooleanPolicySerializer extends PolicySerializer<Boolean> {
@Override
- void saveToXml(TypedXmlSerializer serializer, String attributeName, @NonNull Boolean value)
+ void saveToXml(PolicyKey policyKey, TypedXmlSerializer serializer, String attributeName,
+ @NonNull Boolean value)
throws IOException {
Objects.requireNonNull(value);
serializer.attributeBoolean(/* namespace= */ null, attributeName, value);
@@ -39,9 +42,10 @@ final class BooleanPolicySerializer extends PolicySerializer<Boolean> {
@Nullable
@Override
- Boolean readFromXml(TypedXmlPullParser parser, String attributeName) {
+ BooleanPolicyValue readFromXml(TypedXmlPullParser parser, String attributeName) {
try {
- return parser.getAttributeBoolean(/* namespace= */ null, attributeName);
+ return new BooleanPolicyValue(
+ parser.getAttributeBoolean(/* namespace= */ null, attributeName));
} catch (XmlPullParserException e) {
Log.e(DevicePolicyEngine.TAG, "Error parsing Boolean policy value", e);
return null;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BundlePolicySerializer.java b/services/devicepolicy/java/com/android/server/devicepolicy/BundlePolicySerializer.java
new file mode 100644
index 000000000000..c79aac722bd7
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BundlePolicySerializer.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.devicepolicy;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.admin.BundlePolicyValue;
+import android.app.admin.PackagePolicyKey;
+import android.app.admin.PolicyKey;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Parcelable;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.XmlUtils;
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Objects;
+
+// TODO(b/266704763): clean this up and stop creating separate files for each value, the code here
+// is copied from UserManagerService, however it doesn't currently handle setting different
+// restrictions for the same package in different users, it also will not remove the files for
+// outdated restrictions, this will all get fixed when we save it as part of the policies file
+// rather than in its own files.
+final class BundlePolicySerializer extends PolicySerializer<Bundle> {
+
+ private static final String RESTRICTIONS_FILE_PREFIX = "AppRestrictions_";
+ private static final String XML_SUFFIX = ".xml";
+
+ private static final String TAG_RESTRICTIONS = "restrictions";
+ private static final String TAG_ENTRY = "entry";
+ private static final String TAG_VALUE = "value";
+ private static final String ATTR_KEY = "key";
+ private static final String ATTR_VALUE_TYPE = "type";
+ private static final String ATTR_MULTIPLE = "m";
+
+ private static final String ATTR_TYPE_STRING_ARRAY = "sa";
+ private static final String ATTR_TYPE_STRING = "s";
+ private static final String ATTR_TYPE_BOOLEAN = "b";
+ private static final String ATTR_TYPE_INTEGER = "i";
+ private static final String ATTR_TYPE_BUNDLE = "B";
+ private static final String ATTR_TYPE_BUNDLE_ARRAY = "BA";
+
+ @Override
+ void saveToXml(@NonNull PolicyKey policyKey, TypedXmlSerializer serializer,
+ String attributeName, @NonNull Bundle value) throws IOException {
+ Objects.requireNonNull(value);
+ Objects.requireNonNull(policyKey);
+ if (!(policyKey instanceof PackagePolicyKey)) {
+ throw new IllegalArgumentException("policyKey is not of type "
+ + "PackagePolicyKey");
+ }
+ String packageName = ((PackagePolicyKey) policyKey).getPackageName();
+ String fileName = packageToRestrictionsFileName(packageName, value);
+ writeApplicationRestrictionsLAr(fileName, value);
+ serializer.attribute(/* namespace= */ null, attributeName, fileName);
+ }
+
+ @Nullable
+ @Override
+ BundlePolicyValue readFromXml(TypedXmlPullParser parser, String attributeName) {
+ String fileName = parser.getAttributeValue(/* namespace= */ null, attributeName);
+
+ return new BundlePolicyValue(readApplicationRestrictions(fileName));
+ }
+
+ private static String packageToRestrictionsFileName(String packageName, Bundle restrictions) {
+ return RESTRICTIONS_FILE_PREFIX + packageName + Objects.hash(restrictions) + XML_SUFFIX;
+ }
+
+ @GuardedBy("mAppRestrictionsLock")
+ private static Bundle readApplicationRestrictions(String fileName) {
+ AtomicFile restrictionsFile =
+ new AtomicFile(new File(Environment.getDataSystemDirectory(), fileName));
+ return readApplicationRestrictions(restrictionsFile);
+ }
+
+ @VisibleForTesting
+ @GuardedBy("mAppRestrictionsLock")
+ static Bundle readApplicationRestrictions(AtomicFile restrictionsFile) {
+ final Bundle restrictions = new Bundle();
+ final ArrayList<String> values = new ArrayList<>();
+ if (!restrictionsFile.getBaseFile().exists()) {
+ return restrictions;
+ }
+
+ FileInputStream fis = null;
+ try {
+ fis = restrictionsFile.openRead();
+ final TypedXmlPullParser parser = Xml.resolvePullParser(fis);
+ XmlUtils.nextElement(parser);
+ if (parser.getEventType() != XmlPullParser.START_TAG) {
+ Slog.e(DevicePolicyEngine.TAG, "Unable to read restrictions file "
+ + restrictionsFile.getBaseFile());
+ return restrictions;
+ }
+ while (parser.next() != XmlPullParser.END_DOCUMENT) {
+ readEntry(restrictions, values, parser);
+ }
+ } catch (IOException | XmlPullParserException e) {
+ Slog.w(DevicePolicyEngine.TAG, "Error parsing " + restrictionsFile.getBaseFile(), e);
+ } finally {
+ IoUtils.closeQuietly(fis);
+ }
+ return restrictions;
+ }
+
+ private static void readEntry(Bundle restrictions, ArrayList<String> values,
+ TypedXmlPullParser parser) throws XmlPullParserException, IOException {
+ int type = parser.getEventType();
+ if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_ENTRY)) {
+ String key = parser.getAttributeValue(null, ATTR_KEY);
+ String valType = parser.getAttributeValue(null, ATTR_VALUE_TYPE);
+ int count = parser.getAttributeInt(null, ATTR_MULTIPLE, -1);
+ if (count != -1) {
+ values.clear();
+ while (count > 0 && (type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ if (type == XmlPullParser.START_TAG
+ && parser.getName().equals(TAG_VALUE)) {
+ values.add(parser.nextText().trim());
+ count--;
+ }
+ }
+ String [] valueStrings = new String[values.size()];
+ values.toArray(valueStrings);
+ restrictions.putStringArray(key, valueStrings);
+ } else if (ATTR_TYPE_BUNDLE.equals(valType)) {
+ restrictions.putBundle(key, readBundleEntry(parser, values));
+ } else if (ATTR_TYPE_BUNDLE_ARRAY.equals(valType)) {
+ final int outerDepth = parser.getDepth();
+ ArrayList<Bundle> bundleList = new ArrayList<>();
+ while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+ Bundle childBundle = readBundleEntry(parser, values);
+ bundleList.add(childBundle);
+ }
+ restrictions.putParcelableArray(key,
+ bundleList.toArray(new Bundle[bundleList.size()]));
+ } else {
+ String value = parser.nextText().trim();
+ if (ATTR_TYPE_BOOLEAN.equals(valType)) {
+ restrictions.putBoolean(key, Boolean.parseBoolean(value));
+ } else if (ATTR_TYPE_INTEGER.equals(valType)) {
+ restrictions.putInt(key, Integer.parseInt(value));
+ } else {
+ restrictions.putString(key, value);
+ }
+ }
+ }
+ }
+
+ private static Bundle readBundleEntry(TypedXmlPullParser parser, ArrayList<String> values)
+ throws IOException, XmlPullParserException {
+ Bundle childBundle = new Bundle();
+ int outerDepth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+ readEntry(childBundle, values, parser);
+ }
+ return childBundle;
+ }
+
+ private static void writeApplicationRestrictionsLAr(String fileName, Bundle restrictions) {
+ AtomicFile restrictionsFile = new AtomicFile(
+ new File(Environment.getDataSystemDirectory(), fileName));
+ writeApplicationRestrictionsLAr(restrictions, restrictionsFile);
+ }
+
+ static void writeApplicationRestrictionsLAr(Bundle restrictions, AtomicFile restrictionsFile) {
+ FileOutputStream fos = null;
+ try {
+ fos = restrictionsFile.startWrite();
+ final TypedXmlSerializer serializer = Xml.resolveSerializer(fos);
+ serializer.startDocument(null, true);
+ serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+
+ serializer.startTag(null, TAG_RESTRICTIONS);
+ writeBundle(restrictions, serializer);
+ serializer.endTag(null, TAG_RESTRICTIONS);
+
+ serializer.endDocument();
+ restrictionsFile.finishWrite(fos);
+ } catch (Exception e) {
+ restrictionsFile.failWrite(fos);
+ Slog.e(DevicePolicyEngine.TAG, "Error writing application restrictions list", e);
+ }
+ }
+
+ private static void writeBundle(Bundle restrictions, TypedXmlSerializer serializer)
+ throws IOException {
+ for (String key : restrictions.keySet()) {
+ Object value = restrictions.get(key);
+ serializer.startTag(null, TAG_ENTRY);
+ serializer.attribute(null, ATTR_KEY, key);
+
+ if (value instanceof Boolean) {
+ serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_BOOLEAN);
+ serializer.text(value.toString());
+ } else if (value instanceof Integer) {
+ serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_INTEGER);
+ serializer.text(value.toString());
+ } else if (value == null || value instanceof String) {
+ serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_STRING);
+ serializer.text(value != null ? (String) value : "");
+ } else if (value instanceof Bundle) {
+ serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_BUNDLE);
+ writeBundle((Bundle) value, serializer);
+ } else if (value instanceof Parcelable[]) {
+ serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_BUNDLE_ARRAY);
+ Parcelable[] array = (Parcelable[]) value;
+ for (Parcelable parcelable : array) {
+ if (!(parcelable instanceof Bundle)) {
+ throw new IllegalArgumentException("bundle-array can only hold Bundles");
+ }
+ serializer.startTag(null, TAG_ENTRY);
+ serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_BUNDLE);
+ writeBundle((Bundle) parcelable, serializer);
+ serializer.endTag(null, TAG_ENTRY);
+ }
+ } else {
+ serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_STRING_ARRAY);
+ String[] values = (String[]) value;
+ serializer.attributeInt(null, ATTR_MULTIPLE, values.length);
+ for (String choice : values) {
+ serializer.startTag(null, TAG_VALUE);
+ serializer.text(choice != null ? choice : "");
+ serializer.endTag(null, TAG_VALUE);
+ }
+ }
+ serializer.endTag(null, TAG_ENTRY);
+ }
+ }
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ComponentNamePolicySerializer.java b/services/devicepolicy/java/com/android/server/devicepolicy/ComponentNamePolicySerializer.java
index d40000094297..d1c6bcb8b48c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ComponentNamePolicySerializer.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ComponentNamePolicySerializer.java
@@ -18,6 +18,8 @@ package com.android.server.devicepolicy;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.admin.ComponentNamePolicyValue;
+import android.app.admin.PolicyKey;
import android.content.ComponentName;
import android.util.Log;
@@ -32,9 +34,8 @@ final class ComponentNamePolicySerializer extends PolicySerializer<ComponentName
private static final String ATTR_CLASS_NAME = ":class-name";
@Override
- void saveToXml(
- TypedXmlSerializer serializer, String attributeNamePrefix, @NonNull ComponentName value)
- throws IOException {
+ void saveToXml(PolicyKey policyKey, TypedXmlSerializer serializer, String attributeNamePrefix,
+ @NonNull ComponentName value) throws IOException {
Objects.requireNonNull(value);
serializer.attribute(
/* namespace= */ null,
@@ -46,7 +47,7 @@ final class ComponentNamePolicySerializer extends PolicySerializer<ComponentName
@Nullable
@Override
- ComponentName readFromXml(TypedXmlPullParser parser, String attributeNamePrefix) {
+ ComponentNamePolicyValue readFromXml(TypedXmlPullParser parser, String attributeNamePrefix) {
String packageName = parser.getAttributeValue(
/* namespace= */ null, attributeNamePrefix + ATTR_PACKAGE_NAME);
String className = parser.getAttributeValue(
@@ -55,6 +56,6 @@ final class ComponentNamePolicySerializer extends PolicySerializer<ComponentName
Log.e(DevicePolicyEngine.TAG, "Error parsing ComponentName policy.");
return null;
}
- return new ComponentName(packageName, className);
+ return new ComponentNamePolicyValue(new ComponentName(packageName, className));
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DefaultPolicyKey.java b/services/devicepolicy/java/com/android/server/devicepolicy/DefaultPolicyKey.java
deleted file mode 100644
index ab0fc992db8a..000000000000
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DefaultPolicyKey.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.devicepolicy;
-
-import com.android.modules.utils.TypedXmlPullParser;
-
-/**
- * Default implementation for {@link PolicyKey} used to identify a policy that doesn't require any
- * additional arguments to be represented in the policy engine's data structure.
- */
-final class DefaultPolicyKey extends PolicyKey {
- private static final String ATTR_GENERIC_POLICY_KEY = "generic-policy-key";
-
- DefaultPolicyKey(String policyKey) {
- super(policyKey);
- }
-
- String getKey() {
- return mKey;
- }
-
- static DefaultPolicyKey readGenericPolicyKeyFromXml(TypedXmlPullParser parser) {
- String genericPolicyKey = parser.getAttributeValue(
- /* namespace= */ null, ATTR_GENERIC_POLICY_KEY);
- return new DefaultPolicyKey(genericPolicyKey);
- }
-
-}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index 27371c0f1a52..71764dc45462 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -17,16 +17,21 @@
package com.android.server.devicepolicy;
import static android.app.admin.PolicyUpdateResult.RESULT_FAILURE_CONFLICTING_ADMIN_POLICY;
+import static android.app.admin.PolicyUpdateResult.RESULT_POLICY_CLEARED;
import static android.app.admin.PolicyUpdateResult.RESULT_SUCCESS;
import static android.app.admin.PolicyUpdatesReceiver.EXTRA_POLICY_TARGET_USER_ID;
import static android.app.admin.PolicyUpdatesReceiver.EXTRA_POLICY_UPDATE_RESULT_KEY;
import static android.content.pm.UserProperties.INHERIT_DEVICE_POLICY_FROM_PARENT;
+import static android.provider.DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyState;
+import android.app.admin.PolicyKey;
import android.app.admin.PolicyUpdatesReceiver;
+import android.app.admin.PolicyValue;
import android.app.admin.TargetUser;
import android.content.Context;
import android.content.Intent;
@@ -39,6 +44,7 @@ import android.os.Bundle;
import android.os.Environment;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.DeviceConfig;
import android.util.AtomicFile;
import android.util.Log;
import android.util.SparseArray;
@@ -59,6 +65,7 @@ import java.io.InputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -71,6 +78,9 @@ import java.util.Set;
final class DevicePolicyEngine {
static final String TAG = "DevicePolicyEngine";
+ private static final String ENABLE_COEXISTENCE_FLAG = "enable_coexistence";
+ private static final boolean DEFAULT_ENABLE_COEXISTENCE_FLAG = true;
+
private final Context mContext;
private final UserManager mUserManager;
@@ -105,17 +115,20 @@ final class DevicePolicyEngine {
mEnforcingAdmins = new SparseArray<>();
}
- // TODO: add more documentation on broadcasts/callbacks to use to get current enforced values
/**
- * Set the policy for the provided {@code policyDefinition}
- * (see {@link PolicyDefinition}) and {@code enforcingAdmin} to the provided {@code value}.
+ * Set the policy for the provided {@code policyDefinition} (see {@link PolicyDefinition}) and
+ * {@code enforcingAdmin} to the provided {@code value}.
+ *
+ * <p>If {@code skipEnforcePolicy} is true, it sets the policies in the internal data structure
+ * but doesn't call the enforcing logic.
+ *
*/
<V> void setLocalPolicy(
@NonNull PolicyDefinition<V> policyDefinition,
@NonNull EnforcingAdmin enforcingAdmin,
- @NonNull V value,
- int userId) {
-
+ @NonNull PolicyValue<V> value,
+ int userId,
+ boolean skipEnforcePolicy) {
Objects.requireNonNull(policyDefinition);
Objects.requireNonNull(enforcingAdmin);
Objects.requireNonNull(value);
@@ -123,6 +136,12 @@ final class DevicePolicyEngine {
synchronized (mLock) {
PolicyState<V> localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId);
+ if (policyDefinition.isNonCoexistablePolicy()) {
+ setNonCoexistableLocalPolicy(policyDefinition, localPolicyState, enforcingAdmin,
+ value, userId, skipEnforcePolicy);
+ return;
+ }
+
boolean hasGlobalPolicies = hasGlobalPolicyLocked(policyDefinition);
boolean policyChanged;
if (hasGlobalPolicies) {
@@ -135,19 +154,22 @@ final class DevicePolicyEngine {
policyChanged = localPolicyState.addPolicy(enforcingAdmin, value);
}
- if (policyChanged) {
- onLocalPolicyChanged(policyDefinition, enforcingAdmin, userId);
+ // No need to notify admins as no new policy is actually enforced, we're just filling in
+ // the data structures.
+ if (!skipEnforcePolicy) {
+ if (policyChanged) {
+ onLocalPolicyChanged(policyDefinition, enforcingAdmin, userId);
+ }
+ boolean policyEnforced = Objects.equals(
+ localPolicyState.getCurrentResolvedPolicy(), value);
+ sendPolicyResultToAdmin(
+ enforcingAdmin,
+ policyDefinition,
+ // TODO: we're always sending this for now, should properly handle errors.
+ policyEnforced ? RESULT_SUCCESS : RESULT_FAILURE_CONFLICTING_ADMIN_POLICY,
+ userId);
}
- boolean policyEnforced = Objects.equals(
- localPolicyState.getCurrentResolvedPolicy(), value);
- sendPolicyResultToAdmin(
- enforcingAdmin,
- policyDefinition,
- // TODO: we're always sending this for now, should properly handle errors.
- policyEnforced ? RESULT_SUCCESS : RESULT_FAILURE_CONFLICTING_ADMIN_POLICY,
- userId);
-
updateDeviceAdminServiceOnPolicyAddLocked(enforcingAdmin);
write();
@@ -156,6 +178,49 @@ final class DevicePolicyEngine {
}
}
+ /**
+ * Sets a non-coexistable policy, meaning it doesn't get resolved against other policies set
+ * by other admins, and no callbacks are sent to admins, this is just storing and
+ * enforcing the policy.
+ *
+ * <p>Passing a {@code null} value means the policy set by this admin should be removed.
+ */
+ private <V> void setNonCoexistableLocalPolicy(
+ PolicyDefinition<V> policyDefinition,
+ PolicyState<V> localPolicyState,
+ EnforcingAdmin enforcingAdmin,
+ @Nullable PolicyValue<V> value,
+ int userId,
+ boolean skipEnforcePolicy) {
+ if (value == null) {
+ localPolicyState.removePolicy(enforcingAdmin);
+ } else {
+ localPolicyState.addPolicy(enforcingAdmin, value);
+ }
+ if (!skipEnforcePolicy) {
+ enforcePolicy(policyDefinition, value, userId);
+ }
+ if (localPolicyState.getPoliciesSetByAdmins().isEmpty()) {
+ removeLocalPolicyStateLocked(policyDefinition, userId);
+ }
+ updateDeviceAdminServiceOnPolicyAddLocked(enforcingAdmin);
+ write();
+ }
+
+ // TODO: add more documentation on broadcasts/callbacks to use to get current enforced values
+ /**
+ * Set the policy for the provided {@code policyDefinition}
+ * (see {@link PolicyDefinition}) and {@code enforcingAdmin} to the provided {@code value}.
+ */
+ <V> void setLocalPolicy(
+ @NonNull PolicyDefinition<V> policyDefinition,
+ @NonNull EnforcingAdmin enforcingAdmin,
+ @NonNull PolicyValue<V> value,
+ int userId) {
+ setLocalPolicy(
+ policyDefinition, enforcingAdmin, value, userId, /* skipEnforcePolicy= */ false);
+ }
+
// TODO: add more documentation on broadcasts/callbacks to use to get current enforced values
/**
* Removes any previously set policy for the provided {@code policyDefinition}
@@ -174,6 +239,12 @@ final class DevicePolicyEngine {
}
PolicyState<V> localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId);
+ if (policyDefinition.isNonCoexistablePolicy()) {
+ setNonCoexistableLocalPolicy(policyDefinition, localPolicyState, enforcingAdmin,
+ /* value= */ null, userId, /* skipEnforcePolicy= */ false);
+ return;
+ }
+
boolean policyChanged;
if (hasGlobalPolicyLocked(policyDefinition)) {
PolicyState<V> globalPolicyState = getGlobalPolicyStateLocked(policyDefinition);
@@ -189,12 +260,11 @@ final class DevicePolicyEngine {
}
// For a removePolicy to be enforced, it means no current policy exists
- boolean policyEnforced = localPolicyState.getCurrentResolvedPolicy() == null;
sendPolicyResultToAdmin(
enforcingAdmin,
policyDefinition,
// TODO: we're always sending this for now, should properly handle errors.
- policyEnforced ? RESULT_SUCCESS : RESULT_FAILURE_CONFLICTING_ADMIN_POLICY,
+ RESULT_POLICY_CLEARED,
userId);
if (localPolicyState.getPoliciesSetByAdmins().isEmpty()) {
@@ -215,7 +285,7 @@ final class DevicePolicyEngine {
* else remove the policy from child.
*/
private <V> void applyToInheritableProfiles(PolicyDefinition<V> policyDefinition,
- EnforcingAdmin enforcingAdmin, V value, int userId) {
+ EnforcingAdmin enforcingAdmin, PolicyValue<V> value, int userId) {
if (policyDefinition.isInheritable()) {
Binder.withCleanCallingIdentity(() -> {
List<UserInfo> userInfos = mUserManager.getProfiles(userId);
@@ -280,6 +350,18 @@ final class DevicePolicyEngine {
userId);
}
}
+
+ /**
+ * Set the policy for the provided {@code policyDefinition}
+ * (see {@link PolicyDefinition}) and {@code enforcingAdmin} to the provided {@code value}.
+ */
+ <V> void setGlobalPolicy(
+ @NonNull PolicyDefinition<V> policyDefinition,
+ @NonNull EnforcingAdmin enforcingAdmin,
+ @NonNull PolicyValue<V> value) {
+ setGlobalPolicy(policyDefinition, enforcingAdmin, value, /* skipEnforcePolicy= */ false);
+ }
+
// TODO: add more documentation on broadcasts/callbacks to use to get current enforced values
/**
* Set the policy for the provided {@code policyDefinition}
@@ -288,7 +370,8 @@ final class DevicePolicyEngine {
<V> void setGlobalPolicy(
@NonNull PolicyDefinition<V> policyDefinition,
@NonNull EnforcingAdmin enforcingAdmin,
- @NonNull V value) {
+ @NonNull PolicyValue<V> value,
+ boolean skipEnforcePolicy) {
Objects.requireNonNull(policyDefinition);
Objects.requireNonNull(enforcingAdmin);
@@ -298,22 +381,27 @@ final class DevicePolicyEngine {
PolicyState<V> globalPolicyState = getGlobalPolicyStateLocked(policyDefinition);
boolean policyChanged = globalPolicyState.addPolicy(enforcingAdmin, value);
- if (policyChanged) {
- onGlobalPolicyChanged(policyDefinition, enforcingAdmin);
- }
+ boolean policyAppliedOnAllUsers = applyGlobalPolicyOnUsersWithLocalPoliciesLocked(
+ policyDefinition, enforcingAdmin, value, skipEnforcePolicy);
+
+ // No need to notify admins as no new policy is actually enforced, we're just filling in
+ // the data structures.
+ if (!skipEnforcePolicy) {
+ if (policyChanged) {
+ onGlobalPolicyChanged(policyDefinition, enforcingAdmin);
+ }
- boolean policyEnforcedOnAllUsers = enforceGlobalPolicyOnUsersWithLocalPoliciesLocked(
- policyDefinition, enforcingAdmin, value);
- boolean policyEnforcedGlobally = Objects.equals(
- globalPolicyState.getCurrentResolvedPolicy(), value);
- boolean policyEnforced = policyEnforcedGlobally && policyEnforcedOnAllUsers;
+ boolean policyAppliedGlobally = Objects.equals(
+ globalPolicyState.getCurrentResolvedPolicy(), value);
+ boolean policyApplied = policyAppliedGlobally && policyAppliedOnAllUsers;
- sendPolicyResultToAdmin(
- enforcingAdmin,
- policyDefinition,
- // TODO: we're always sending this for now, should properly handle errors.
- policyEnforced ? RESULT_SUCCESS : RESULT_FAILURE_CONFLICTING_ADMIN_POLICY,
- UserHandle.USER_ALL);
+ sendPolicyResultToAdmin(
+ enforcingAdmin,
+ policyDefinition,
+ // TODO: we're always sending this for now, should properly handle errors.
+ policyApplied ? RESULT_SUCCESS : RESULT_FAILURE_CONFLICTING_ADMIN_POLICY,
+ UserHandle.USER_ALL);
+ }
updateDeviceAdminServiceOnPolicyAddLocked(enforcingAdmin);
@@ -341,17 +429,14 @@ final class DevicePolicyEngine {
onGlobalPolicyChanged(policyDefinition, enforcingAdmin);
}
- boolean policyEnforcedOnAllUsers = enforceGlobalPolicyOnUsersWithLocalPoliciesLocked(
- policyDefinition, enforcingAdmin, /* value= */ null);
- // For a removePolicy to be enforced, it means no current policy exists
- boolean policyEnforcedGlobally = policyState.getCurrentResolvedPolicy() == null;
- boolean policyEnforced = policyEnforcedGlobally && policyEnforcedOnAllUsers;
+ applyGlobalPolicyOnUsersWithLocalPoliciesLocked(
+ policyDefinition, enforcingAdmin, /* value= */ null, /* enforcePolicy= */ true);
sendPolicyResultToAdmin(
enforcingAdmin,
policyDefinition,
// TODO: we're always sending this for now, should properly handle errors.
- policyEnforced ? RESULT_SUCCESS : RESULT_FAILURE_CONFLICTING_ADMIN_POLICY,
+ RESULT_POLICY_CLEARED,
UserHandle.USER_ALL);
if (policyState.getPoliciesSetByAdmins().isEmpty()) {
@@ -392,15 +477,16 @@ final class DevicePolicyEngine {
*
* <p>Returns {@code true} if the policy is enforced successfully on all users.
*/
- private <V> boolean enforceGlobalPolicyOnUsersWithLocalPoliciesLocked(
+ private <V> boolean applyGlobalPolicyOnUsersWithLocalPoliciesLocked(
@NonNull PolicyDefinition<V> policyDefinition,
@NonNull EnforcingAdmin enforcingAdmin,
- @Nullable V value) {
+ @Nullable PolicyValue<V> value,
+ boolean skipEnforcePolicy) {
// Global only policies can't be applied locally, return early.
if (policyDefinition.isGlobalOnlyPolicy()) {
return true;
}
- boolean isAdminPolicyEnforced = true;
+ boolean isAdminPolicyApplied = true;
for (int i = 0; i < mLocalPolicies.size(); i++) {
int userId = mLocalPolicies.keyAt(i);
if (!hasLocalPolicyLocked(policyDefinition, userId)) {
@@ -412,9 +498,11 @@ final class DevicePolicyEngine {
boolean policyChanged = localPolicyState.resolvePolicy(
globalPolicyState.getPoliciesSetByAdmins());
- if (policyChanged) {
+ if (policyChanged && !skipEnforcePolicy) {
enforcePolicy(
- policyDefinition, localPolicyState.getCurrentResolvedPolicy(), userId);
+ policyDefinition,
+ localPolicyState.getCurrentResolvedPolicy(),
+ userId);
sendPolicyChangedToAdmins(
localPolicyState,
enforcingAdmin,
@@ -424,10 +512,10 @@ final class DevicePolicyEngine {
userId);
}
- isAdminPolicyEnforced &= Objects.equals(
+ isAdminPolicyApplied &= Objects.equals(
value, localPolicyState.getCurrentResolvedPolicy());
}
- return isAdminPolicyEnforced;
+ return isAdminPolicyApplied;
}
/**
@@ -438,14 +526,16 @@ final class DevicePolicyEngine {
Objects.requireNonNull(policyDefinition);
synchronized (mLock) {
+ PolicyValue<V> resolvedValue = null;
if (hasLocalPolicyLocked(policyDefinition, userId)) {
- return getLocalPolicyStateLocked(
+ resolvedValue = getLocalPolicyStateLocked(
policyDefinition, userId).getCurrentResolvedPolicy();
}
if (hasGlobalPolicyLocked(policyDefinition)) {
- return getGlobalPolicyStateLocked(policyDefinition).getCurrentResolvedPolicy();
+ resolvedValue = getGlobalPolicyStateLocked(
+ policyDefinition).getCurrentResolvedPolicy();
}
- return null;
+ return resolvedValue == null ? null : resolvedValue.getValue();
}
}
@@ -466,13 +556,30 @@ final class DevicePolicyEngine {
return null;
}
return getLocalPolicyStateLocked(policyDefinition, userId)
- .getPoliciesSetByAdmins().get(enforcingAdmin);
+ .getPoliciesSetByAdmins().get(enforcingAdmin).getValue();
+ }
+ }
+
+ /**
+ * Retrieves the values set for the provided {@code policyDefinition} by each admin.
+ */
+ @NonNull
+ <V> LinkedHashMap<EnforcingAdmin, PolicyValue<V>> getLocalPoliciesSetByAdmins(
+ @NonNull PolicyDefinition<V> policyDefinition,
+ int userId) {
+ Objects.requireNonNull(policyDefinition);
+
+ synchronized (mLock) {
+ if (!hasLocalPolicyLocked(policyDefinition, userId)) {
+ return new LinkedHashMap<>();
+ }
+ return getLocalPolicyStateLocked(policyDefinition, userId).getPoliciesSetByAdmins();
}
}
/**
- * Returns the policies set by the given admin that share the same {@link PolicyKey#getKey()} as
- * the provided {@code policyDefinition}.
+ * Returns the policies set by the given admin that share the same
+ * {@link PolicyKey#getIdentifier()} as the provided {@code policyDefinition}.
*
* <p>For example, getLocalPolicyKeysSetByAdmin(PERMISSION_GRANT, admin) returns all permission
* grants set by the given admin.
@@ -496,7 +603,7 @@ final class DevicePolicyEngine {
}
Set<PolicyKey> keys = new HashSet<>();
for (PolicyKey key : mLocalPolicies.get(userId).keySet()) {
- if (key.hasSameKeyAs(policyDefinition.getPolicyKey())
+ if (key.hasSameIdentifierAs(policyDefinition.getPolicyKey())
&& mLocalPolicies.get(userId).get(key).getPoliciesSetByAdmins()
.containsKey(enforcingAdmin)) {
keys.add(key);
@@ -591,11 +698,12 @@ final class DevicePolicyEngine {
}
}
- private <V> void enforcePolicy(
- PolicyDefinition<V> policyDefinition, @Nullable V policyValue, int userId) {
+ private <V> void enforcePolicy(PolicyDefinition<V> policyDefinition,
+ @Nullable PolicyValue<V> policyValue, int userId) {
// null policyValue means remove any enforced policies, ensure callbacks handle this
// properly
- policyDefinition.enforcePolicy(policyValue, mContext, userId);
+ policyDefinition.enforcePolicy(
+ policyValue == null ? null : policyValue.getValue(), mContext, userId);
}
private <V> void sendPolicyResultToAdmin(
@@ -766,7 +874,7 @@ final class DevicePolicyEngine {
if (!policyState.getPolicyDefinition().isInheritable()) {
return;
}
- for (Map.Entry<EnforcingAdmin, V> enforcingAdminEntry :
+ for (Map.Entry<EnforcingAdmin, PolicyValue<V>> enforcingAdminEntry :
policyState.getPoliciesSetByAdmins().entrySet()) {
setLocalPolicy(policyState.getPolicyDefinition(),
enforcingAdminEntry.getKey(),
@@ -810,6 +918,34 @@ final class DevicePolicyEngine {
}
/**
+ * Returns all current enforced policies set on the device, and the individual values set by
+ * each admin. Global policies are returned under {@link UserHandle#ALL}.
+ */
+ @NonNull
+ DevicePolicyState getDevicePolicyState() {
+ Map<UserHandle, Map<PolicyKey, android.app.admin.PolicyState<?>>> policies =
+ new HashMap<>();
+ for (int i = 0; i < mLocalPolicies.size(); i++) {
+ UserHandle user = UserHandle.of(mLocalPolicies.keyAt(i));
+ policies.put(user, new HashMap<>());
+ for (PolicyKey policyKey : mLocalPolicies.valueAt(i).keySet()) {
+ policies.get(user).put(
+ policyKey,
+ mLocalPolicies.valueAt(i).get(policyKey).getParcelablePolicyState());
+ }
+ }
+ if (!mGlobalPolicies.isEmpty()) {
+ policies.put(UserHandle.ALL, new HashMap<>());
+ for (PolicyKey policyKey : mGlobalPolicies.keySet()) {
+ policies.get(UserHandle.ALL).put(
+ policyKey,
+ mGlobalPolicies.get(policyKey).getParcelablePolicyState());
+ }
+ }
+ return new DevicePolicyState(policies);
+ }
+
+ /**
* Reestablishes the service that handles
* {@link DevicePolicyManager#ACTION_DEVICE_ADMIN_SERVICE} in the enforcing admin if the package
* was updated, as a package update results in the persistent connection getting reset.
@@ -835,11 +971,6 @@ final class DevicePolicyEngine {
private void updateDeviceAdminServiceOnPolicyAddLocked(@NonNull EnforcingAdmin enforcingAdmin) {
int userId = enforcingAdmin.getUserId();
- // A connection is established with DPCs as soon as they are provisioned, so no need to
- // connect when a policy is set.
- if (enforcingAdmin.hasAuthority(EnforcingAdmin.DPC_AUTHORITY)) {
- return;
- }
if (mEnforcingAdmins.contains(userId)
&& mEnforcingAdmins.get(userId).contains(enforcingAdmin)) {
return;
@@ -850,6 +981,11 @@ final class DevicePolicyEngine {
}
mEnforcingAdmins.get(enforcingAdmin.getUserId()).add(enforcingAdmin);
+ // A connection is established with DPCs as soon as they are provisioned, so no need to
+ // connect when a policy is set.
+ if (enforcingAdmin.hasAuthority(EnforcingAdmin.DPC_AUTHORITY)) {
+ return;
+ }
mDeviceAdminServiceController.startServiceForAdmin(
enforcingAdmin.getPackageName(),
userId,
@@ -862,18 +998,10 @@ final class DevicePolicyEngine {
*/
private void updateDeviceAdminServiceOnPolicyRemoveLocked(
@NonNull EnforcingAdmin enforcingAdmin) {
- // TODO(b/263364434): centralise handling in one place.
- // DPCs rely on a constant connection being established as soon as they are provisioned,
- // so we shouldn't disconnect it even if they no longer have policies set.
- if (enforcingAdmin.hasAuthority(EnforcingAdmin.DPC_AUTHORITY)) {
- return;
- }
if (doesAdminHavePolicies(enforcingAdmin)) {
return;
}
-
int userId = enforcingAdmin.getUserId();
-
if (mEnforcingAdmins.contains(userId)) {
mEnforcingAdmins.get(userId).remove(enforcingAdmin);
if (mEnforcingAdmins.get(userId).isEmpty()) {
@@ -881,6 +1009,12 @@ final class DevicePolicyEngine {
}
}
+ // TODO(b/263364434): centralise handling in one place.
+ // DPCs rely on a constant connection being established as soon as they are provisioned,
+ // so we shouldn't disconnect it even if they no longer have policies set.
+ if (enforcingAdmin.hasAuthority(EnforcingAdmin.DPC_AUTHORITY)) {
+ return;
+ }
mDeviceAdminServiceController.stopServiceForAdmin(
enforcingAdmin.getPackageName(),
userId,
@@ -927,21 +1061,70 @@ final class DevicePolicyEngine {
}
}
+ /**
+ * Clear all policies set in the policy engine.
+ *
+ * <p>Note that this doesn't clear any enforcements, it only clears the data structures.
+ */
+ void clearAllPolicies() {
+ synchronized (mLock) {
+ clear();
+ write();
+ }
+ }
private void clear() {
synchronized (mLock) {
mGlobalPolicies.clear();
mLocalPolicies.clear();
+ mEnforcingAdmins.clear();
+ }
+ }
+
+ // TODO: we need to listen for user removal and package removal and update out internal policy
+ // map and enforcing admins for this is be accurate.
+ boolean hasActivePolicies() {
+ return mEnforcingAdmins.size() > 0;
+ }
+
+ /**
+ * Returns {@code true} if the coexistence flag is enabled or:
+ * <ul>
+ * <li>If the provided package is an admin with existing policies
+ * <li>A new admin and no other admin have policies set
+ * <li>More than one admin have policies set
+ */
+ boolean canAdminAddPolicies(String packageName, int userId) {
+ if (isCoexistenceFlagEnabled()) {
+ return true;
+ }
+
+ if (mEnforcingAdmins.contains(userId)
+ && mEnforcingAdmins.get(userId).stream().anyMatch(admin ->
+ admin.getPackageName().equals(packageName))) {
+ return true;
}
+
+ int numOfEnforcingAdmins = 0;
+ for (int i = 0; i < mEnforcingAdmins.size(); i++) {
+ numOfEnforcingAdmins += mEnforcingAdmins.get(i).size();
+ }
+ return numOfEnforcingAdmins == 0 || numOfEnforcingAdmins > 1;
+ }
+
+ private boolean isCoexistenceFlagEnabled() {
+ return DeviceConfig.getBoolean(
+ NAMESPACE_DEVICE_POLICY_MANAGER,
+ ENABLE_COEXISTENCE_FLAG,
+ DEFAULT_ENABLE_COEXISTENCE_FLAG);
}
private class DevicePoliciesReaderWriter {
- private static final String DEVICE_POLICIES_XML = "device_policies.xml";
+ private static final String DEVICE_POLICIES_XML = "device_policy_state.xml";
private static final String TAG_LOCAL_POLICY_ENTRY = "local-policy-entry";
private static final String TAG_GLOBAL_POLICY_ENTRY = "global-policy-entry";
private static final String TAG_ADMINS_POLICY_ENTRY = "admins-policy-entry";
private static final String TAG_ENFORCING_ADMINS_ENTRY = "enforcing-admins-entry";
private static final String ATTR_USER_ID = "user-id";
- private static final String ATTR_POLICY_ID = "policy-id";
private final File mFile;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index fbde1ba1bbb1..90b7c122b858 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -21,6 +21,7 @@ import static android.Manifest.permission.MANAGE_CA_CERTIFICATES;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL;
+import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY;
import static android.Manifest.permission.QUERY_ADMIN_POLICY;
import static android.Manifest.permission.REQUEST_PASSWORD_COMPLEXITY;
import static android.Manifest.permission.SET_TIME;
@@ -41,6 +42,7 @@ import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEV
import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE;
import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_USER;
import static android.app.admin.DevicePolicyManager.ACTION_SYSTEM_UPDATE_POLICY_CHANGED;
+import static android.app.admin.DevicePolicyManager.AUTO_TIMEZONE_POLICY;
import static android.app.admin.DevicePolicyManager.DELEGATION_APP_RESTRICTIONS;
import static android.app.admin.DevicePolicyManager.DELEGATION_BLOCK_UNINSTALL;
import static android.app.admin.DevicePolicyManager.DELEGATION_CERT_INSTALL;
@@ -205,6 +207,9 @@ import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.StatusBarManager;
+import android.app.admin.BooleanPolicyValue;
+import android.app.admin.BundlePolicyValue;
+import android.app.admin.ComponentNamePolicyValue;
import android.app.admin.DeviceAdminInfo;
import android.app.admin.DeviceAdminReceiver;
import android.app.admin.DevicePolicyCache;
@@ -219,11 +224,15 @@ import android.app.admin.DevicePolicyManager.PersonalAppsSuspensionReason;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.DevicePolicyManagerLiteInternal;
import android.app.admin.DevicePolicySafetyChecker;
+import android.app.admin.DevicePolicyState;
import android.app.admin.DevicePolicyStringResource;
import android.app.admin.DeviceStateCache;
import android.app.admin.FactoryResetProtectionPolicy;
import android.app.admin.FullyManagedDeviceProvisioningParams;
import android.app.admin.IDevicePolicyManager;
+import android.app.admin.IntegerPolicyValue;
+import android.app.admin.IntentFilterPolicyKey;
+import android.app.admin.LockTaskPolicy;
import android.app.admin.ManagedProfileProvisioningParams;
import android.app.admin.ManagedSubscriptionsPolicy;
import android.app.admin.NetworkEvent;
@@ -232,10 +241,13 @@ import android.app.admin.ParcelableGranteeMap;
import android.app.admin.ParcelableResource;
import android.app.admin.PasswordMetrics;
import android.app.admin.PasswordPolicy;
+import android.app.admin.PolicyKey;
+import android.app.admin.PolicyValue;
import android.app.admin.PreferentialNetworkServiceConfig;
import android.app.admin.SecurityLog;
import android.app.admin.SecurityLog.SecurityEvent;
import android.app.admin.StartInstallingUpdateCallback;
+import android.app.admin.StringSetPolicyValue;
import android.app.admin.SystemUpdateInfo;
import android.app.admin.SystemUpdatePolicy;
import android.app.admin.UnsafeStateException;
@@ -280,6 +292,7 @@ import android.content.pm.Signature;
import android.content.pm.StringParceledListSlice;
import android.content.pm.UserInfo;
import android.content.pm.UserPackage;
+import android.content.pm.parsing.FrameworkParsingPackageUtils;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.database.Cursor;
@@ -432,6 +445,7 @@ import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -736,11 +750,18 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
+ "management app's authentication policy";
private static final String NOT_SYSTEM_CALLER_MSG = "Only the system can %s";
+ // ENABLE_DEVICE_POLICY_ENGINE_FLAG must be enabled before this could be enabled.
private static final String PERMISSION_BASED_ACCESS_EXPERIMENT_FLAG =
"enable_permission_based_access";
- private static final String ENABLE_COEXISTENCE_FLAG = "enable_coexistence";
private static final boolean DEFAULT_VALUE_PERMISSION_BASED_ACCESS_FLAG = false;
- private static final boolean DEFAULT_ENABLE_COEXISTENCE_FLAG = false;
+
+ // This must be enabled before PERMISSION_BASED_ACCESS_EXPERIMENT_FLAG is enabled, the reason
+ // we're not just relying on PERMISSION_BASED_ACCESS_EXPERIMENT_FLAG to enable the policy engine
+ // is that we might want to enable it before the permission changes are ready if we want to test
+ // it on DPCs.
+ // Once this is enabled, it can no longer be disabled in production
+ private static final String ENABLE_DEVICE_POLICY_ENGINE_FLAG = "enable_device_policy_engine";
+ private static final boolean DEFAULT_ENABLE_DEVICE_POLICY_ENGINE_FLAG = false;
// TODO(b/258425381) remove the flag after rollout.
private static final String KEEP_PROFILES_RUNNING_FLAG = "enable_keep_profiles_running";
@@ -1300,10 +1321,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
&& (owner.getPackageName().equals(packageName))) {
startOwnerService(userHandle, "package-broadcast");
}
- if (isCoexistenceFlagEnabled()) {
+ if (shouldMigrateToDevicePolicyEngine()) {
+ migratePoliciesToDevicePolicyEngine();
+ }
+ if (isDevicePolicyEngineFlagEnabled()) {
mDevicePolicyEngine.handlePackageChanged(packageName, userHandle);
}
-
// Persist updates if the removed package was an admin or delegate.
if (removedAdmin || removedDelegate) {
saveSettingsLocked(policy.mUserId);
@@ -1993,7 +2016,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
mUserManagerInternal.addUserLifecycleListener(new UserLifecycleListener());
mDeviceManagementResourcesProvider.load();
- if (isCoexistenceFlagEnabled()) {
+ if (isDevicePolicyEngineFlagEnabled()) {
mDevicePolicyEngine.load();
}
@@ -3124,6 +3147,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
synchronized (getLockObject()) {
migrateToProfileOnOrganizationOwnedDeviceIfCompLocked();
applyProfileRestrictionsIfDeviceOwnerLocked();
+
+ // TODO: Is this the right place to trigger the migration?
+ if (shouldMigrateToDevicePolicyEngine()) {
+ migratePoliciesToDevicePolicyEngine();
+ }
}
maybeStartSecurityLogMonitorOnActivityManagerReady();
break;
@@ -3332,7 +3360,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
updateNetworkPreferenceForUser(userId, preferentialNetworkServiceConfigs);
startOwnerService(userId, "start-user");
- if (isCoexistenceFlagEnabled()) {
+ if (isDevicePolicyEngineFlagEnabled()) {
mDevicePolicyEngine.handleStartUser(userId);
}
}
@@ -3357,7 +3385,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
void handleUnlockUser(int userId) {
startOwnerService(userId, "unlock-user");
- if (isCoexistenceFlagEnabled()) {
+ if (isDevicePolicyEngineFlagEnabled()) {
mDevicePolicyEngine.handleUnlockUser(userId);
}
}
@@ -3369,7 +3397,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
void handleStopUser(int userId) {
updateNetworkPreferenceForUser(userId, List.of(PreferentialNetworkServiceConfig.DEFAULT));
mDeviceAdminServiceController.stopServicesForUser(userId, /* actionForLog= */ "stop-user");
- if (isCoexistenceFlagEnabled()) {
+ if (isDevicePolicyEngineFlagEnabled()) {
mDevicePolicyEngine.handleStopUser(userId);
}
}
@@ -3477,7 +3505,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
* @param refreshing true = update an active admin, no error
*/
@Override
- public void setActiveAdmin(ComponentName adminReceiver, boolean refreshing, int userHandle) {
+ public void setActiveAdmin(
+ ComponentName adminReceiver, boolean refreshing, int userHandle) {
if (!mHasFeature) {
return;
}
@@ -3494,6 +3523,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
synchronized (getLockObject()) {
checkActiveAdminPrecondition(adminReceiver, info, policy);
mInjector.binderWithCleanCallingIdentity(() -> {
+ if (!canAddActiveAdminIfPolicyEngineEnabled(
+ adminReceiver.getPackageName(), userHandle)) {
+ throw new IllegalStateException("Can't add non-coexistable admin.");
+ }
+
final ActiveAdmin existingAdmin
= getActiveAdminUncheckedLocked(adminReceiver, userHandle);
if (!refreshing && existingAdmin != null) {
@@ -8237,14 +8271,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
caller));
}
- if (isCoexistenceEnabled(caller)) {
+ if (useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
mDevicePolicyEngine.setGlobalPolicy(
PolicyDefinition.AUTO_TIMEZONE,
// TODO(b/260573124): add correct enforcing admin when permission changes are
// merged.
EnforcingAdmin.createEnterpriseEnforcingAdmin(
caller.getComponentName(), caller.getUserId()),
- enabled);
+ new BooleanPolicyValue(enabled));
} else {
mInjector.binderWithCleanCallingIdentity(() ->
mInjector.settingsGlobalPutInt(Global.AUTO_TIME_ZONE, enabled ? 1 : 0));
@@ -10347,11 +10381,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
|| isDefaultDeviceOwner(caller) || isFinancedDeviceOwner(caller));
final int userHandle = caller.getUserId();
- if (isCoexistenceEnabled(caller)) {
+ if (useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
mDevicePolicyEngine.setLocalPolicy(
PolicyDefinition.PERSISTENT_PREFERRED_ACTIVITY(filter),
EnforcingAdmin.createEnterpriseEnforcingAdmin(who, userHandle),
- activity,
+ new ComponentNamePolicyValue(activity),
userHandle);
} else {
synchronized (getLockObject()) {
@@ -10385,7 +10419,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
final int userHandle = caller.getUserId();
- if (isCoexistenceEnabled(caller)) {
+ if (useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
clearPackagePersistentPreferredActivitiesFromPolicyEngine(
EnforcingAdmin.createEnterpriseEnforcingAdmin(who, userHandle),
packageName,
@@ -10421,13 +10455,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
admin,
userId);
for (PolicyKey key : keys) {
- if (!(key instanceof PersistentPreferredActivityPolicyKey)) {
+ if (!(key instanceof IntentFilterPolicyKey)) {
throw new IllegalStateException("PolicyKey for PERSISTENT_PREFERRED_ACTIVITY is not"
+ "of type PersistentPreferredActivityPolicyKey");
}
- PersistentPreferredActivityPolicyKey parsedKey =
- (PersistentPreferredActivityPolicyKey) key;
- IntentFilter filter = Objects.requireNonNull(parsedKey.getFilter());
+ IntentFilterPolicyKey parsedKey =
+ (IntentFilterPolicyKey) key;
+ IntentFilter filter = Objects.requireNonNull(parsedKey.getIntentFilter());
ComponentName preferredActivity = mDevicePolicyEngine.getLocalPolicySetByAdmin(
PolicyDefinition.PERSISTENT_PREFERRED_ACTIVITY(filter),
@@ -10485,25 +10519,79 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public void setApplicationRestrictions(ComponentName who, String callerPackage,
- String packageName, Bundle settings) {
+ String packageName, Bundle restrictions) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
&& (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_APP_RESTRICTIONS)));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_APPLICATION_RESTRICTIONS);
+ if (useDevicePolicyEngine(caller, DELEGATION_APP_RESTRICTIONS)) {
+ // This check is eventually made in UMS, checking here to fail early.
+ String validationResult =
+ FrameworkParsingPackageUtils.validateName(packageName, false, false);
+ if (validationResult != null) {
+ throw new IllegalArgumentException("Invalid package name: " + validationResult);
+ }
+
+ EnforcingAdmin admin = EnforcingAdmin.createEnterpriseEnforcingAdmin(
+ who == null
+ ? new ComponentName(caller.getPackageName(), "Delegate")
+ : who,
+ caller.getUserId());
+
+ if (restrictions == null || restrictions.isEmpty()) {
+ mDevicePolicyEngine.removeLocalPolicy(
+ PolicyDefinition.APPLICATION_RESTRICTIONS(packageName),
+ admin,
+ caller.getUserId());
+ } else {
+ mDevicePolicyEngine.setLocalPolicy(
+ PolicyDefinition.APPLICATION_RESTRICTIONS(packageName),
+ admin,
+ new BundlePolicyValue(restrictions),
+ caller.getUserId());
+ }
+ setBackwardsCompatibleAppRestrictions(
+ packageName, restrictions, caller.getUserHandle());
+ } else {
+ mInjector.binderWithCleanCallingIdentity(() -> {
+ mUserManager.setApplicationRestrictions(packageName, restrictions,
+ caller.getUserHandle());
+ });
+ }
+
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_APPLICATION_RESTRICTIONS)
+ .setAdmin(caller.getPackageName())
+ .setBoolean(/* isDelegate */ who == null)
+ .setStrings(packageName)
+ .write();
+ }
+
+ /**
+ * Set app restrictions in user manager to keep backwards compatibility for the old
+ * getApplicationRestrictions API.
+ */
+ private void setBackwardsCompatibleAppRestrictions(
+ String packageName, Bundle restrictions, UserHandle userHandle) {
+ Bundle restrictionsToApply = restrictions == null || restrictions.isEmpty()
+ ? getAppRestrictionsSetByAnyAdmin(packageName, userHandle)
+ : restrictions;
mInjector.binderWithCleanCallingIdentity(() -> {
- mUserManager.setApplicationRestrictions(packageName, settings,
- caller.getUserHandle());
- DevicePolicyEventLogger
- .createEvent(DevicePolicyEnums.SET_APPLICATION_RESTRICTIONS)
- .setAdmin(caller.getPackageName())
- .setBoolean(/* isDelegate */ who == null)
- .setStrings(packageName)
- .write();
+ mUserManager.setApplicationRestrictions(packageName, restrictionsToApply, userHandle);
});
}
+ private Bundle getAppRestrictionsSetByAnyAdmin(String packageName, UserHandle userHandle) {
+ LinkedHashMap<EnforcingAdmin, PolicyValue<Bundle>> policies =
+ mDevicePolicyEngine.getLocalPoliciesSetByAdmins(
+ PolicyDefinition.APPLICATION_RESTRICTIONS(packageName),
+ userHandle.getIdentifier());
+ return policies.isEmpty()
+ ? null : policies.entrySet().stream().findAny().get().getValue().getValue();
+ }
+
@Override
public void setTrustAgentConfiguration(ComponentName admin, ComponentName agent,
PersistableBundle args, boolean parent) {
@@ -11339,7 +11427,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
final int userId = user.id;
- if (isCoexistenceFlagEnabled()) {
+ if (isDevicePolicyEngineFlagEnabled()) {
mDevicePolicyEngine.handleUserCreated(user);
}
@@ -11712,13 +11800,28 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
&& (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_APP_RESTRICTIONS)));
- return mInjector.binderWithCleanCallingIdentity(() -> {
- Bundle bundle = mUserManager.getApplicationRestrictions(packageName,
- caller.getUserHandle());
- // if no restrictions were saved, mUserManager.getApplicationRestrictions
- // returns null, but DPM method should return an empty Bundle as per JavaDoc
- return bundle != null ? bundle : Bundle.EMPTY;
- });
+ if (useDevicePolicyEngine(caller, DELEGATION_APP_RESTRICTIONS)) {
+ EnforcingAdmin admin = EnforcingAdmin.createEnterpriseEnforcingAdmin(
+ who != null ? who : new ComponentName(callerPackage, "Delegate"),
+ caller.getUserId());
+
+ LinkedHashMap<EnforcingAdmin, PolicyValue<Bundle>> policies =
+ mDevicePolicyEngine.getLocalPoliciesSetByAdmins(
+ PolicyDefinition.APPLICATION_RESTRICTIONS(packageName),
+ caller.getUserId());
+ if (policies.isEmpty() || !policies.containsKey(admin)) {
+ return Bundle.EMPTY;
+ }
+ return policies.get(admin).getValue();
+ } else {
+ return mInjector.binderWithCleanCallingIdentity(() -> {
+ Bundle bundle = mUserManager.getApplicationRestrictions(packageName,
+ caller.getUserHandle());
+ // if no restrictions were saved, mUserManager.getApplicationRestrictions
+ // returns null, but DPM method should return an empty Bundle as per JavaDoc
+ return bundle != null ? bundle : Bundle.EMPTY;
+ });
+ }
}
/**
@@ -12369,7 +12472,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
|| isFinancedDeviceOwner(caller)))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_BLOCK_UNINSTALL)));
- if (isCoexistenceEnabled(caller)) {
+ if (useDevicePolicyEngine(caller, DELEGATION_BLOCK_UNINSTALL)) {
// TODO(b/260573124): Add correct enforcing admin when permission changes are
// merged, and don't forget to handle delegates! Enterprise admins assume
// component name isn't null.
@@ -12379,7 +12482,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
mDevicePolicyEngine.setLocalPolicy(
PolicyDefinition.PACKAGE_UNINSTALL_BLOCKED(packageName),
admin,
- uninstallBlocked,
+ new BooleanPolicyValue(uninstallBlocked),
caller.getUserId());
} else {
final int userId = caller.getUserId();
@@ -12933,7 +13036,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_LOCK_TASK_PACKAGES);
}
- if (isCoexistenceEnabled(caller)) {
+ if (useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
EnforcingAdmin admin = EnforcingAdmin.createEnterpriseEnforcingAdmin(
who, caller.getUserId());
if (packages.length == 0) {
@@ -12950,7 +13053,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (currentPolicy == null) {
policy = new LockTaskPolicy(Set.of(packages));
} else {
- policy = currentPolicy.clone();
+ policy = new LockTaskPolicy(currentPolicy);
policy.setPackages(Set.of(packages));
}
@@ -12987,7 +13090,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
enforceCanCallLockTaskLocked(caller);
}
- if (isCoexistenceEnabled(caller)) {
+ if (useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
LockTaskPolicy policy = mDevicePolicyEngine.getResolvedPolicy(
PolicyDefinition.LOCK_TASK, userHandle);
if (policy == null) {
@@ -13015,9 +13118,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
final int userId = mInjector.userHandleGetCallingUserId();
- // TODO(b/260560985): This is not the right check, as the flag could be enabled but there
- // could be an admin that hasn't targeted U.
- if (isCoexistenceFlagEnabled()) {
+ // Is it ok to just check that no active policies exist currently?
+ if (mDevicePolicyEngine.hasActivePolicies()) {
LockTaskPolicy policy = mDevicePolicyEngine.getResolvedPolicy(
PolicyDefinition.LOCK_TASK, userId);
if (policy == null) {
@@ -13051,7 +13153,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
enforceCanSetLockTaskFeaturesOnFinancedDevice(caller, flags);
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_LOCK_TASK_FEATURES);
}
- if (isCoexistenceEnabled(caller)) {
+ if (useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
EnforcingAdmin admin = EnforcingAdmin.createEnterpriseEnforcingAdmin(who, userHandle);
LockTaskPolicy currentPolicy = mDevicePolicyEngine.getLocalPolicySetByAdmin(
PolicyDefinition.LOCK_TASK,
@@ -13061,7 +13163,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
throw new IllegalArgumentException("Can't set a lock task flags without setting "
+ "lock task packages first.");
}
- LockTaskPolicy policy = currentPolicy.clone();
+ LockTaskPolicy policy = new LockTaskPolicy(currentPolicy);
policy.setFlags(flags);
mDevicePolicyEngine.setLocalPolicy(
@@ -13092,7 +13194,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
enforceCanCallLockTaskLocked(caller);
}
- if (isCoexistenceEnabled(caller)) {
+ if (useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
LockTaskPolicy policy = mDevicePolicyEngine.getResolvedPolicy(
PolicyDefinition.LOCK_TASK, userHandle);
if (policy == null) {
@@ -14237,6 +14339,37 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
APPLICATION_EXEMPTIONS_FLAG,
DEFAULT_APPLICATION_EXEMPTIONS_FLAG);
}
+
+ @Override
+ public Map<String, Bundle> getApplicationRestrictionsPerAdmin(
+ String packageName, int userId) {
+ LinkedHashMap<EnforcingAdmin, PolicyValue<Bundle>> policies =
+ mDevicePolicyEngine.getLocalPoliciesSetByAdmins(
+ PolicyDefinition.APPLICATION_RESTRICTIONS(packageName),
+ userId);
+ Map<String, Bundle> restrictions = new HashMap<>();
+ for (EnforcingAdmin admin : policies.keySet()) {
+ restrictions.put(admin.getPackageName(), policies.get(admin).getValue());
+ }
+ if (!restrictions.isEmpty()) {
+ return restrictions;
+ }
+
+ return mInjector.binderWithCleanCallingIdentity(() -> {
+ // Could be a device that hasn't migrated yet, so just return any restrictions saved
+ // in userManager.
+ Bundle bundle = mUserManager.getApplicationRestrictions(
+ packageName, UserHandle.of(userId));
+ if (bundle == null || bundle.isEmpty()) {
+ return new HashMap<>();
+ }
+ ActiveAdmin admin = getMostProbableDPCAdminForLocalPolicy(userId);
+ if (admin == null) {
+ return new HashMap<>();
+ }
+ return Map.of(admin.info.getPackageName(), bundle);
+ });
+ }
}
private Intent createShowAdminSupportIntent(int userId) {
@@ -14764,7 +14897,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
enforcePermissionGrantStateOnFinancedDevice(packageName, permission);
}
}
- if (isCoexistenceEnabled(caller)) {
+ if (useDevicePolicyEngine(caller, DELEGATION_PERMISSION_GRANT)) {
+ // TODO(b/266924257): decide how to handle the internal state if the package doesn't
+ // exist, or the permission isn't requested by the app, because we could end up with
+ // inconsistent state between the policy engine and package manager. Also a package
+ // might get removed or has it's permission updated after we've set the policy.
mDevicePolicyEngine.setLocalPolicy(
PolicyDefinition.PERMISSION_GRANT(packageName, permission),
// TODO(b/260573124): Add correct enforcing admin when permission changes are
@@ -14772,7 +14909,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// component name isn't null.
EnforcingAdmin.createEnterpriseEnforcingAdmin(
caller.getComponentName(), caller.getUserId()),
- grantState,
+ new IntegerPolicyValue(grantState),
caller.getUserId());
// TODO: update javadoc to reflect that callback no longer return success/failure
callback.sendResult(Bundle.EMPTY);
@@ -14859,41 +14996,47 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
enforcePermissionGrantStateOnFinancedDevice(packageName, permission);
}
return mInjector.binderWithCleanCallingIdentity(() -> {
- int granted;
- if (getTargetSdk(caller.getPackageName(), caller.getUserId())
- < android.os.Build.VERSION_CODES.Q) {
- // The per-Q behavior was to not check the app-ops state.
- granted = mIPackageManager.checkPermission(permission, packageName,
- caller.getUserId());
- } else {
- try {
- int uid = mInjector.getPackageManager().getPackageUidAsUser(packageName,
- caller.getUserId());
- if (PermissionChecker.checkPermissionForPreflight(mContext, permission,
- PermissionChecker.PID_UNKNOWN, uid, packageName)
- != PermissionChecker.PERMISSION_GRANTED) {
- granted = PackageManager.PERMISSION_DENIED;
- } else {
- granted = PackageManager.PERMISSION_GRANTED;
- }
- } catch (NameNotFoundException e) {
- // Package does not exit
- return DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT;
- }
- }
- int permFlags = mInjector.getPackageManager().getPermissionFlags(
- permission, packageName, caller.getUserHandle());
- if ((permFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED)
- != PackageManager.FLAG_PERMISSION_POLICY_FIXED) {
- // Not controlled by policy
- return DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT;
+ return getPermissionGrantStateForUser(
+ packageName, permission, caller, caller.getUserId());
+ });
+ }
+ }
+
+ private int getPermissionGrantStateForUser(
+ String packageName, String permission, CallerIdentity caller, int userId)
+ throws RemoteException {
+ int granted;
+ if (getTargetSdk(caller.getPackageName(), caller.getUserId())
+ < android.os.Build.VERSION_CODES.Q) {
+ // The per-Q behavior was to not check the app-ops state.
+ granted = mIPackageManager.checkPermission(permission, packageName, userId);
+ } else {
+ try {
+ int uid = mInjector.getPackageManager().getPackageUidAsUser(
+ packageName, userId);
+ if (PermissionChecker.checkPermissionForPreflight(mContext, permission,
+ PermissionChecker.PID_UNKNOWN, uid, packageName)
+ != PermissionChecker.PERMISSION_GRANTED) {
+ granted = PackageManager.PERMISSION_DENIED;
} else {
- // Policy controlled so return result based on permission grant state
- return granted == PackageManager.PERMISSION_GRANTED
- ? DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED
- : DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED;
+ granted = PackageManager.PERMISSION_GRANTED;
}
- });
+ } catch (NameNotFoundException e) {
+ // Package does not exit
+ return DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT;
+ }
+ }
+ int permFlags = mInjector.getPackageManager().getPermissionFlags(
+ permission, packageName, UserHandle.of(userId));
+ if ((permFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED)
+ != PackageManager.FLAG_PERMISSION_POLICY_FIXED) {
+ // Not controlled by policy
+ return DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT;
+ } else {
+ // Policy controlled so return result based on permission grant state
+ return granted == PackageManager.PERMISSION_GRANTED
+ ? DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED
+ : DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED;
}
}
@@ -15436,16 +15579,27 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
@Override
- public void setOrganizationName(@NonNull ComponentName who, CharSequence text) {
+ public void setOrganizationName(@Nullable ComponentName who, CharSequence text) {
if (!mHasFeature) {
return;
}
- Objects.requireNonNull(who, "ComponentName is null");
- final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ CallerIdentity caller = getCallerIdentity(who);
+ ActiveAdmin admin = null;
+
+ if (isPermissionCheckFlagEnabled()) {
+ EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
+ who,
+ MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY,
+ caller.getUserId());
+ admin = enforcingAdmin.getActiveAdmin();
+ } else {
+ Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ }
synchronized (getLockObject()) {
- ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
+ if (!isPermissionCheckFlagEnabled()) {
+ admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
+ }
if (!TextUtils.equals(admin.organizationName, text)) {
admin.organizationName = (text == null || text.length() == 0)
? null : text.toString();
@@ -15455,20 +15609,29 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
@Override
- public CharSequence getOrganizationName(@NonNull ComponentName who) {
+ public CharSequence getOrganizationName(@Nullable ComponentName who) {
if (!mHasFeature) {
return null;
}
- Objects.requireNonNull(who, "ComponentName is null");
+ CallerIdentity caller = getCallerIdentity(who);
+ ActiveAdmin admin = null;
- final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallingUser(isManagedProfile(caller.getUserId()));
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ if (isPermissionCheckFlagEnabled()) {
+ EnforcingAdmin enforcingAdmin = enforceCanQueryAndGetEnforcingAdmin(
+ who,
+ MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY,
+ caller.getUserId());
+ admin = enforcingAdmin.getActiveAdmin();
+ } else {
+ Preconditions.checkCallingUser(isManagedProfile(caller.getUserId()));
+ Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
- synchronized (getLockObject()) {
- ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
- return admin.organizationName;
+ synchronized (getLockObject()) {
+ admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
+ }
}
+
+ return admin.organizationName;
}
/**
@@ -16154,6 +16317,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// The removed admin might have disabled camera, so update user
// restrictions.
pushUserRestrictions(userHandle);
+
+ // The removed admin might've been stopping the migration if it was targeting pre Android U
+ if (shouldMigrateToDevicePolicyEngine()) {
+ migratePoliciesToDevicePolicyEngine();
+ }
}
@Override
@@ -18047,7 +18215,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
checkCanExecuteOrThrowUnsafe(
DevicePolicyManager.OPERATION_SET_USER_CONTROL_DISABLED_PACKAGES);
- if (isCoexistenceEnabled(caller)) {
+ if (useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
Binder.withCleanCallingIdentity(() -> {
if (packages.isEmpty()) {
removeUserControlDisabledPackages(caller);
@@ -18081,7 +18249,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// merged.
EnforcingAdmin.createEnterpriseEnforcingAdmin(
caller.getComponentName(), caller.getUserId()),
- packages);
+ new StringSetPolicyValue(packages));
} else {
mDevicePolicyEngine.setLocalPolicy(
PolicyDefinition.USER_CONTROLLED_DISABLED_PACKAGES,
@@ -18089,7 +18257,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// merged.
EnforcingAdmin.createEnterpriseEnforcingAdmin(
caller.getComponentName(), caller.getUserId()),
- packages,
+ new StringSetPolicyValue(packages),
caller.getUserId());
}
}
@@ -18127,7 +18295,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller) || isProfileOwner(caller)
|| isFinancedDeviceOwner(caller));
- if (isCoexistenceEnabled(caller)) {
+ if (useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
// This retrieves the policy for the calling user only, DOs for example can't know
// what's enforced globally or on another user.
Set<String> packages = mDevicePolicyEngine.getResolvedPolicy(
@@ -20092,22 +20260,26 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
MANAGE_DEVICE_POLICY_ACROSS_USERS,
MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL,
SET_TIME,
- SET_TIME_ZONE);
+ SET_TIME_ZONE,
+ MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY);
private static final List<String> FINANCED_DEVICE_OWNER_PERMISSIONS = List.of(
MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL,
MANAGE_DEVICE_POLICY_ACROSS_USERS,
- MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL);
+ MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL,
+ MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY);
private static final List<String> PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE_PERMISSIONS =
List.of(
MANAGE_DEVICE_POLICY_ACROSS_USERS,
MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL,
SET_TIME,
- SET_TIME_ZONE);
+ SET_TIME_ZONE,
+ MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY);
private static final List<String> PROFILE_OWNER_ON_USER_0_PERMISSIONS = List.of(
SET_TIME,
SET_TIME_ZONE);
private static final List<String> PROFILE_OWNER_PERMISSIONS = List.of(
- MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL);
+ MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL,
+ MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY);
private static final HashMap<Integer, List<String>> DPC_PERMISSIONS = new HashMap<>();
{
@@ -20125,9 +20297,53 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
private static final HashMap<String, String> CROSS_USER_PERMISSIONS =
new HashMap<>();
{
- // Auto time is intrinsically global so there is no cross-user permission.
+ // Time and Timezone is intrinsically global so there is no cross-user permission.
CROSS_USER_PERMISSIONS.put(SET_TIME, null);
CROSS_USER_PERMISSIONS.put(SET_TIME_ZONE, null);
+ // Organisation identity policy will involve data of other organisations on the device and
+ // therefore the FULL cross-user permission is required.
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
+ }
+
+ /**
+ * Checks if the calling process has been granted permission to apply a device policy on a
+ * specific user.
+ * The given permission will be checked along with its associated cross-user permission if it
+ * exists and the target user is different to the calling user.
+ * Returns the {@link ActiveAdmin} of the caller.
+ *
+ * @param permission The name of the permission being checked.
+ * @param targetUserId The userId of the user which the caller needs permission to act on.
+ * @throws SecurityException if the caller has not been granted the given permission,
+ * the associated cross-user permission if the caller's user is different to the target user.
+ */
+ private EnforcingAdmin enforcePermissionAndGetEnforcingAdmin(@Nullable ComponentName admin,
+ String permission, int targetUserId) {
+ enforcePermission(permission, targetUserId);
+ return getEnforcingAdminForCaller(admin, getCallerIdentity());
+ }
+
+ /**
+ * Checks whether the calling process has been granted permission to query a device policy on
+ * a specific user.
+ * The given permission will be checked along with its associated cross-user permission if it
+ * exists and the target user is different to the calling user.
+ *
+ * @param permission The name of the permission being checked.
+ * @param targetUserId The userId of the user which the caller needs permission to act on.
+ * @throws SecurityException if the caller has not been granted the given permission,
+ * the associated cross-user permission if the caller's user is different to the target user.
+ */
+ private EnforcingAdmin enforceCanQueryAndGetEnforcingAdmin(@Nullable ComponentName admin,
+ String permission, int targetUserId) {
+ enforceCanQuery(permission, targetUserId);
+ return getEnforcingAdminForCaller(admin, getCallerIdentity());
+ }
+
+ private static final HashMap<String, String> POLICY_IDENTIFIER_TO_PERMISSION = new HashMap<>();
+ {
+ POLICY_IDENTIFIER_TO_PERMISSION.put(AUTO_TIMEZONE_POLICY, SET_TIME_ZONE);
}
/**
@@ -20139,7 +20355,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
* @param permission The name of the permission being checked.
* @param targetUserId The userId of the user which the caller needs permission to act on.
* @throws SecurityException if the caller has not been granted the given permission,
- * the associtated cross-user permission if the caller's user is different to the target user.
+ * the associated cross-user permission if the caller's user is different to the target user.
*/
private void enforcePermission(String permission, int targetUserId)
throws SecurityException {
@@ -20154,13 +20370,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
/**
- * Return whether the calling process has been granted permission to query a device policy on
+ * Checks whether the calling process has been granted permission to query a device policy on
* a specific user.
+ * The given permission will be checked along with its associated cross-user permission if it
+ * exists and the target user is different to the calling user.
*
* @param permission The name of the permission being checked.
* @param targetUserId The userId of the user which the caller needs permission to act on.
* @throws SecurityException if the caller has not been granted the given permission,
- * the associatated cross-user permission if the caller's user is different to the target user
+ * the associated cross-user permission if the caller's user is different to the target user
* and if the user has not been granted {@link QUERY_ADMIN_POLICY}.
*/
private void enforceCanQuery(String permission, int targetUserId) throws SecurityException {
@@ -20225,10 +20443,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
// Check if the caller is an active admin that uses a certain policy.
if (ACTIVE_ADMIN_POLICIES.containsKey(permission)) {
- return getActiveAdminForCallerLocked(
- null, ACTIVE_ADMIN_POLICIES.get(permission), false) != null;
+ try {
+ return getActiveAdminForCallerLocked(
+ null, ACTIVE_ADMIN_POLICIES.get(permission), false) != null;
+ } catch (SecurityException e) {
+ // A security exception means there is not an active admin with permission and
+ // therefore
+ return false;
+ }
}
-
return false;
}
@@ -20258,6 +20481,22 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return false;
}
+ private EnforcingAdmin getEnforcingAdminForCaller(@Nullable ComponentName who,
+ CallerIdentity caller) {
+ int userId = caller.getUserId();
+ ActiveAdmin admin = null;
+ synchronized (getLockObject()) {
+ admin = getActiveAdminUncheckedLocked(who, userId);
+ }
+ if (isDeviceOwner(caller) || isProfileOwner(caller)) {
+ return EnforcingAdmin.createEnterpriseEnforcingAdmin(who, userId, admin);
+ }
+ if (getActiveAdminUncheckedLocked(who, userId) != null) {
+ return EnforcingAdmin.createDeviceAdminEnforcingAdmin(who, userId, admin);
+ }
+ return EnforcingAdmin.createEnforcingAdmin(caller.getPackageName(), userId);
+ }
+
private boolean isPermissionCheckFlagEnabled() {
return DeviceConfig.getBoolean(
NAMESPACE_DEVICE_POLICY_MANAGER,
@@ -20265,20 +20504,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
DEFAULT_VALUE_PERMISSION_BASED_ACCESS_FLAG);
}
- // TODO(b/260560985): properly gate coexistence changes
- private boolean isCoexistenceEnabled(CallerIdentity caller) {
- return isCoexistenceFlagEnabled()
- && mInjector.isChangeEnabled(
- ENABLE_COEXISTENCE_CHANGE, caller.getPackageName(), caller.getUserId());
- }
-
- private boolean isCoexistenceFlagEnabled() {
- return DeviceConfig.getBoolean(
- NAMESPACE_DEVICE_POLICY_MANAGER,
- ENABLE_COEXISTENCE_FLAG,
- DEFAULT_ENABLE_COEXISTENCE_FLAG);
- }
-
private static boolean isKeepProfilesRunningFlagEnabled() {
return DeviceConfig.getBoolean(
NAMESPACE_DEVICE_POLICY_MANAGER,
@@ -20522,4 +20747,363 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
}
+
+ @Override
+ public DevicePolicyState getDevicePolicyState() {
+ Preconditions.checkCallAuthorization(
+ hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
+ return mInjector.binderWithCleanCallingIdentity(mDevicePolicyEngine::getDevicePolicyState);
+ }
+
+ @Override
+ public boolean triggerDevicePolicyEngineMigration(boolean forceMigration) {
+ Preconditions.checkCallAuthorization(
+ hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
+ return mInjector.binderWithCleanCallingIdentity(() -> {
+ boolean canForceMigration = forceMigration && !hasNonTestOnlyActiveAdmins();
+ if (!canForceMigration && !shouldMigrateToDevicePolicyEngine()) {
+ return false;
+ }
+ return migratePoliciesToDevicePolicyEngine();
+ });
+ }
+
+ private boolean hasNonTestOnlyActiveAdmins() {
+ return mInjector.binderWithCleanCallingIdentity(() -> {
+ for (UserInfo userInfo : mUserManager.getUsers()) {
+ List<ComponentName> activeAdmins = getActiveAdmins(userInfo.id);
+ if (activeAdmins == null) {
+ continue;
+ }
+ for (ComponentName admin : activeAdmins) {
+ if (!isAdminTestOnlyLocked(admin, userInfo.id)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ });
+ }
+
+ // TODO(b/266808047): handle DeviceAdmin migration when there is no DPCs on the device
+ private boolean shouldMigrateToDevicePolicyEngine() {
+ return mInjector.binderWithCleanCallingIdentity(() -> {
+ if (!isDevicePolicyEngineFlagEnabled()) {
+ return false;
+ }
+ if (mOwners.isMigratedToPolicyEngine()) {
+ return false;
+ }
+ // We're only checking if existing DPCs are not targeting U, regardless of what
+ // DeviceAdmins are targeting, as they can access very limited APIs, and we'll ensure
+ // that these APIs maintain the current behaviour of strictest applies.
+ boolean hasDPCs = false;
+ for (UserInfo userInfo : mUserManager.getUsers()) {
+ List<ComponentName> activeAdmins = getActiveAdmins(userInfo.id);
+ if (activeAdmins == null) {
+ continue;
+ }
+ for (ComponentName admin : activeAdmins) {
+ if ((isProfileOwner(admin, userInfo.id) || isDeviceOwner(admin, userInfo.id))) {
+ if (!mInjector.isChangeEnabled(ENABLE_COEXISTENCE_CHANGE,
+ admin.getPackageName(), userInfo.id)) {
+ return false;
+ }
+ hasDPCs = true;
+ }
+ }
+ }
+ return hasDPCs;
+ });
+ }
+
+ /**
+ * @return {@code true} if policies were migrated successfully, {@code false} otherwise.
+ */
+ private boolean migratePoliciesToDevicePolicyEngine() {
+ return mInjector.binderWithCleanCallingIdentity(() -> {
+ try {
+ Slogf.i(LOG_TAG, "Started device policies migration to the device policy engine.");
+ migrateAutoTimezonePolicy();
+ migratePermissionGrantStatePolicies();
+ // TODO(b/258811766): add migration logic for all policies
+
+ mOwners.markMigrationToPolicyEngine();
+ return true;
+ } catch (Exception e) {
+ mDevicePolicyEngine.clearAllPolicies();
+ Slogf.e(LOG_TAG, e, "Error occurred during device policy migration, will "
+ + "reattempt on the next system server restart.");
+ return false;
+ }
+ });
+ }
+
+ private void migrateAutoTimezonePolicy() {
+ Slogf.i(LOG_TAG, "Skipping Migration of AUTO_TIMEZONE policy to device policy engine,"
+ + "as no way to identify if the value was set by the admin or the user.");
+ }
+
+ private void migratePermissionGrantStatePolicies() {
+ Slogf.i(LOG_TAG, "Migrating PERMISSION_GRANT policy to device policy engine.");
+ for (UserInfo userInfo : mUserManager.getUsers()) {
+ ActiveAdmin admin = getMostProbableDPCAdminForLocalPolicy(userInfo.id);
+ if (admin == null) {
+ Slogf.i(LOG_TAG, "No admin found that can set permission grant state on user "
+ + userInfo.id);
+ continue;
+ }
+ for (PackageInfo packageInfo : getInstalledPackagesOnUser(userInfo.id)) {
+ if (packageInfo.requestedPermissions == null) {
+ continue;
+ }
+ for (String permission : packageInfo.requestedPermissions) {
+ if (!isRuntimePermission(permission)) {
+ continue;
+ }
+ int grantState = DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT;
+ try {
+ grantState = getPermissionGrantStateForUser(
+ packageInfo.packageName, permission,
+ new CallerIdentity(
+ mInjector.binderGetCallingUid(),
+ admin.info.getComponent().getPackageName(),
+ admin.info.getComponent()),
+ userInfo.id);
+ } catch (RemoteException e) {
+ Slogf.e(LOG_TAG, e, "Error retrieving permission grant state for %s "
+ + "and %s", packageInfo.packageName, permission);
+ }
+ if (grantState == DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT) {
+ // Not Controlled by a policy
+ continue;
+ }
+
+ mDevicePolicyEngine.setLocalPolicy(
+ PolicyDefinition.PERMISSION_GRANT(packageInfo.packageName,
+ permission),
+ EnforcingAdmin.createEnterpriseEnforcingAdmin(
+ admin.info.getComponent(),
+ admin.getUserHandle().getIdentifier()),
+ new IntegerPolicyValue(grantState),
+ userInfo.id,
+ /* skipEnforcePolicy= */ true);
+ }
+ }
+ }
+ }
+
+ private List<PackageInfo> getInstalledPackagesOnUser(int userId) {
+ return mInjector.binderWithCleanCallingIdentity(() ->
+ mContext.getPackageManager().getInstalledPackagesAsUser(
+ PackageManager.PackageInfoFlags.of(
+ PackageManager.GET_PERMISSIONS), userId));
+ }
+
+ /**
+ * Returns the most probable admin to have set a global policy according to the following
+ * heuristics:
+ *
+ * <ul>
+ * <li>The device owner on any user</li>
+ * <li>The org owned profile owner on any user</li>
+ * <li>The profile owner on any user</li>
+ * </ul>
+ */
+ @Nullable
+ // TODO(b/266928216): Check what the admin capabilities are when deciding which admin to return.
+ private ActiveAdmin getMostProbableDPCAdminForGlobalPolicy() {
+ synchronized (getLockObject()) {
+ ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
+ if (deviceOwner != null) {
+ return deviceOwner;
+ }
+
+ List<UserInfo> users = mUserManager.getUsers();
+ for (UserInfo userInfo : users) {
+ if (isProfileOwnerOfOrganizationOwnedDevice(userInfo.id)) {
+ return getProfileOwnerAdminLocked(userInfo.id);
+ }
+ }
+
+ for (UserInfo userInfo : users) {
+ ActiveAdmin profileOwner = getProfileOwnerLocked(userInfo.id);
+ if (profileOwner != null) {
+ return profileOwner;
+ }
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Returns the most probable admin to have set a policy on the given {@code userId} according
+ * to the following heuristics:
+ *
+ * <ul>
+ * <li>The device owner on the given userId</li>
+ * <li>The profile owner on the given userId</li>
+ * <li>The org owned profile owner of which the given userId is its parent</li>
+ * <li>The profile owner of which the given userId is its parent</li>
+ * <li>The device owner on any user</li>
+ * <li>The profile owner on any user</li>
+ * </ul>
+ */
+ @Nullable
+ // TODO(b/266928216): Check what the admin capabilities are when deciding which admin to return.
+ private ActiveAdmin getMostProbableDPCAdminForLocalPolicy(int userId) {
+ synchronized (getLockObject()) {
+ ActiveAdmin localDeviceOwner = getDeviceOwnerLocked(userId);
+ if (localDeviceOwner != null) {
+ return localDeviceOwner;
+ }
+
+ ActiveAdmin localProfileOwner = getProfileOwnerLocked(userId);
+ if (localProfileOwner != null) {
+ return localProfileOwner;
+ }
+
+ int[] profileIds = mUserManager.getProfileIds(userId, /* enabledOnly= */ false);
+ for (int id : profileIds) {
+ if (id == userId) {
+ continue;
+ }
+ if (isProfileOwnerOfOrganizationOwnedDevice(id)) {
+ return getProfileOwnerAdminLocked(id);
+ }
+ }
+
+ for (int id : profileIds) {
+ if (id == userId) {
+ continue;
+ }
+ if (isManagedProfile(id)) {
+ return getProfileOwnerAdminLocked(id);
+ }
+ }
+
+ ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
+ if (deviceOwner != null) {
+ return deviceOwner;
+ }
+
+ for (UserInfo userInfo : mUserManager.getUsers()) {
+ ActiveAdmin profileOwner = getProfileOwnerLocked(userInfo.id);
+ if (profileOwner != null) {
+ return profileOwner;
+ }
+ }
+ return null;
+ }
+ }
+
+ // We need to add a mapping of policyId to permission in POLICY_IDENTIFIER_TO_PERMISSION
+ // for each migrated permission.
+ private List<ActiveAdmin> getNonDPCActiveAdminsForPolicyLocked(String policyIdentifier) {
+ String permission = POLICY_IDENTIFIER_TO_PERMISSION.get(policyIdentifier);
+ if (permission == null) {
+ Slogf.e(LOG_TAG, "Can't find a permission for %s in POLICY_IDENTIFIER_TO_PERMISSION",
+ policyIdentifier);
+ return new ArrayList<>();
+ }
+ if (!ACTIVE_ADMIN_POLICIES.containsKey(permission)) {
+ return new ArrayList<>();
+ }
+
+ List<ActiveAdmin> admins = new ArrayList<>();
+ for (UserInfo userInfo : mUserManager.getUsers()) {
+ List<ComponentName> activeAdmins = getActiveAdmins(userInfo.id);
+ for (ComponentName admin : activeAdmins) {
+ if (isDeviceOwner(admin, userInfo.id) || isProfileOwner(admin, userInfo.id)) {
+ continue;
+ }
+ DevicePolicyData policy = getUserDataUnchecked(userInfo.id);
+ if (isActiveAdminWithPolicyForUserLocked(
+ policy.mAdminMap.get(admin), ACTIVE_ADMIN_POLICIES.get(permission),
+ userInfo.id)) {
+ admins.add(policy.mAdminMap.get(admin));
+ }
+ }
+ }
+ return admins;
+ }
+
+ // TODO: This can actually accept an EnforcingAdmin that gets created in the permission check
+ // method.
+ private boolean useDevicePolicyEngine(CallerIdentity caller, @Nullable String delegateScope) {
+ if (!isCallerActiveAdminOrDelegate(caller, delegateScope)) {
+ if (!isDevicePolicyEngineFlagEnabled()) {
+ throw new IllegalStateException("Non DPC caller can't set device policies.");
+ }
+ if (hasDPCsNotSupportingCoexistence()) {
+ throw new IllegalStateException("Non DPC caller can't set device policies with "
+ + "existing legacy admins on the device.");
+ }
+ return true;
+ } else {
+ return isDevicePolicyEngineFlagEnabled() && !hasDPCsNotSupportingCoexistence();
+ }
+ }
+
+ private boolean isDevicePolicyEngineFlagEnabled() {
+ return DeviceConfig.getBoolean(
+ NAMESPACE_DEVICE_POLICY_MANAGER,
+ ENABLE_DEVICE_POLICY_ENGINE_FLAG,
+ DEFAULT_ENABLE_DEVICE_POLICY_ENGINE_FLAG);
+ }
+
+ private boolean hasDPCsNotSupportingCoexistence() {
+ return mInjector.binderWithCleanCallingIdentity(() -> {
+ for (UserInfo userInfo : mUserManager.getUsers()) {
+ List<ComponentName> activeAdmins = getActiveAdmins(userInfo.id);
+ if (activeAdmins == null) {
+ continue;
+ }
+ for (ComponentName admin : activeAdmins) {
+ if ((isProfileOwner(admin, userInfo.id) || isDeviceOwner(admin, userInfo.id))
+ && !mInjector.isChangeEnabled(ENABLE_COEXISTENCE_CHANGE,
+ admin.getPackageName(), userInfo.id)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ });
+ }
+
+ // TODO: this can actually accept an EnforcingAdmin that gets created in the permission
+ // check method.
+ private boolean isCallerActiveAdminOrDelegate(
+ CallerIdentity caller, @Nullable String delegateScope) {
+ return mInjector.binderWithCleanCallingIdentity(() -> {
+ List<ComponentName> activeAdmins = getActiveAdmins(caller.getUserId());
+ if (activeAdmins != null) {
+ for (ComponentName admin : activeAdmins) {
+ if (admin.getPackageName().equals(caller.getPackageName())) {
+ return true;
+ }
+ }
+ }
+ return delegateScope != null && isCallerDelegate(caller, delegateScope);
+ });
+ }
+
+ // TODO(b/266808047): This will return false for DeviceAdmins not targetting U, which is
+ // inconsistent with the migration logic that allows migration with old DeviceAdmins.
+ private boolean canAddActiveAdminIfPolicyEngineEnabled(String packageName, int userId) {
+ if (!isDevicePolicyEngineFlagEnabled()) {
+ return true;
+ }
+ if (hasDPCsNotSupportingCoexistence()) {
+ return true;
+ }
+ if (mInjector.isChangeEnabled(ENABLE_COEXISTENCE_CHANGE, packageName, userId)) {
+ // This will always return true unless we turn off coexistence, in which case it will
+ // return true if no current admins exist, or more than one admin exist
+ return mDevicePolicyEngine.canAdminAddPolicies(packageName, userId);
+ }
+ // Is it ok to just check that no active policies exist currently, or should we return false
+ // if the policy engine was ever used?
+ return !mDevicePolicyEngine.hasActivePolicies();
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java
index da895f46f1aa..10e972d3698a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java
@@ -18,7 +18,13 @@ package com.android.server.devicepolicy;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.admin.Authority;
+import android.app.admin.UnknownAuthority;
+import android.app.admin.DeviceAdminAuthority;
+import android.app.admin.DpcAuthority;
+import android.app.admin.RoleAuthority;
import android.content.ComponentName;
+import android.os.UserHandle;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
@@ -63,6 +69,7 @@ final class EnforcingAdmin {
private Set<String> mAuthorities;
private final int mUserId;
private final boolean mIsRoleAuthority;
+ private final ActiveAdmin mActiveAdmin;
static EnforcingAdmin createEnforcingAdmin(@NonNull String packageName, int userId) {
Objects.requireNonNull(packageName);
@@ -73,14 +80,31 @@ final class EnforcingAdmin {
@NonNull ComponentName componentName, int userId) {
Objects.requireNonNull(componentName);
return new EnforcingAdmin(
- componentName.getPackageName(), componentName, Set.of(DPC_AUTHORITY), userId);
+ componentName.getPackageName(), componentName, Set.of(DPC_AUTHORITY), userId,
+ /* activeAdmin=*/ null);
+ }
+
+ static EnforcingAdmin createEnterpriseEnforcingAdmin(
+ @NonNull ComponentName componentName, int userId, ActiveAdmin activeAdmin) {
+ Objects.requireNonNull(componentName);
+ return new EnforcingAdmin(
+ componentName.getPackageName(), componentName, Set.of(DPC_AUTHORITY), userId,
+ activeAdmin);
}
static EnforcingAdmin createDeviceAdminEnforcingAdmin(ComponentName componentName, int userId) {
Objects.requireNonNull(componentName);
return new EnforcingAdmin(
componentName.getPackageName(), componentName, Set.of(DEVICE_ADMIN_AUTHORITY),
- userId);
+ userId, /* activeAdmin=*/ null);
+ }
+
+ static EnforcingAdmin createDeviceAdminEnforcingAdmin(ComponentName componentName, int userId,
+ ActiveAdmin activeAdmin) {
+ Objects.requireNonNull(componentName);
+ return new EnforcingAdmin(
+ componentName.getPackageName(), componentName, Set.of(DEVICE_ADMIN_AUTHORITY),
+ userId, activeAdmin);
}
static String getRoleAuthorityOf(String roleName) {
@@ -88,7 +112,8 @@ final class EnforcingAdmin {
}
private EnforcingAdmin(
- String packageName, ComponentName componentName, Set<String> authorities, int userId) {
+ String packageName, ComponentName componentName, Set<String> authorities, int userId,
+ ActiveAdmin activeAdmin) {
Objects.requireNonNull(packageName);
Objects.requireNonNull(componentName);
Objects.requireNonNull(authorities);
@@ -99,6 +124,7 @@ final class EnforcingAdmin {
mComponentName = componentName;
mAuthorities = new HashSet<>(authorities);
mUserId = userId;
+ mActiveAdmin = activeAdmin;
}
private EnforcingAdmin(String packageName, int userId) {
@@ -111,6 +137,7 @@ final class EnforcingAdmin {
mComponentName = null;
// authorities will be loaded when needed
mAuthorities = null;
+ mActiveAdmin = null;
}
private static Set<String> getRoleAuthoritiesOrDefault(String packageName, int userId) {
@@ -156,6 +183,34 @@ final class EnforcingAdmin {
return mUserId;
}
+ @Nullable
+ public ActiveAdmin getActiveAdmin() {
+ return mActiveAdmin;
+ }
+
+ @NonNull
+ android.app.admin.EnforcingAdmin getParcelableAdmin() {
+ Authority authority;
+ if (mIsRoleAuthority) {
+ Set<String> roles = getRoles(mPackageName, mUserId);
+ if (roles.isEmpty()) {
+ authority = UnknownAuthority.UNKNOWN_AUTHORITY;
+ } else {
+ authority = new RoleAuthority(roles);
+ }
+ } else if (mAuthorities.contains(DPC_AUTHORITY)) {
+ authority = DpcAuthority.DPC_AUTHORITY;
+ } else if (mAuthorities.contains(DEVICE_ADMIN_AUTHORITY)) {
+ authority = DeviceAdminAuthority.DEVICE_ADMIN_AUTHORITY;
+ } else {
+ authority = UnknownAuthority.UNKNOWN_AUTHORITY;
+ }
+ return new android.app.admin.EnforcingAdmin(
+ mPackageName,
+ authority,
+ UserHandle.of(mUserId));
+ }
+
/**
* For two EnforcingAdmins to be equal they must:
*
@@ -224,7 +279,7 @@ final class EnforcingAdmin {
String className = parser.getAttributeValue(/* namespace= */ null, ATTR_CLASS_NAME);
ComponentName componentName = new ComponentName(packageName, className);
Set<String> authorities = Set.of(authoritiesStr.split(ATTR_AUTHORITIES_SEPARATOR));
- return new EnforcingAdmin(packageName, componentName, authorities, userId);
+ return new EnforcingAdmin(packageName, componentName, authorities, userId, null);
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/IntegerUnion.java b/services/devicepolicy/java/com/android/server/devicepolicy/FlagUnion.java
index a051a2bebae6..6c4be2164edf 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/IntegerUnion.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/FlagUnion.java
@@ -17,24 +17,32 @@
package com.android.server.devicepolicy;
import android.annotation.NonNull;
+import android.app.admin.IntegerPolicyValue;
+import android.app.admin.PolicyValue;
import java.util.LinkedHashMap;
import java.util.Objects;
-final class IntegerUnion extends ResolutionMechanism<Integer> {
+final class FlagUnion extends ResolutionMechanism<Integer> {
@Override
- Integer resolve(@NonNull LinkedHashMap<EnforcingAdmin, Integer> adminPolicies) {
+ IntegerPolicyValue resolve(
+ @NonNull LinkedHashMap<EnforcingAdmin, PolicyValue<Integer>> adminPolicies) {
Objects.requireNonNull(adminPolicies);
if (adminPolicies.isEmpty()) {
return null;
}
Integer unionOfPolicies = 0;
- for (Integer policy : adminPolicies.values()) {
- unionOfPolicies |= policy;
+ for (PolicyValue<Integer> policy : adminPolicies.values()) {
+ unionOfPolicies |= policy.getValue();
}
- return unionOfPolicies;
+ return new IntegerPolicyValue(unionOfPolicies);
+ }
+
+ @Override
+ android.app.admin.FlagUnion getParcelableResolutionMechanism() {
+ return android.app.admin.FlagUnion.FLAG_UNION;
}
@Override
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/IntegerPolicySerializer.java b/services/devicepolicy/java/com/android/server/devicepolicy/IntegerPolicySerializer.java
index d5949dda8b30..bff6d3288dca 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/IntegerPolicySerializer.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/IntegerPolicySerializer.java
@@ -18,6 +18,8 @@ package com.android.server.devicepolicy;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.admin.IntegerPolicyValue;
+import android.app.admin.PolicyKey;
import android.util.Log;
import com.android.modules.utils.TypedXmlPullParser;
@@ -31,17 +33,18 @@ import java.util.Objects;
final class IntegerPolicySerializer extends PolicySerializer<Integer> {
@Override
- void saveToXml(TypedXmlSerializer serializer, String attributeName, @NonNull Integer value)
- throws IOException {
+ void saveToXml(PolicyKey policyKey, TypedXmlSerializer serializer, String attributeName,
+ @NonNull Integer value) throws IOException {
Objects.requireNonNull(value);
serializer.attributeInt(/* namespace= */ null, attributeName, value);
}
@Nullable
@Override
- Integer readFromXml(TypedXmlPullParser parser, String attributeName) {
+ IntegerPolicyValue readFromXml(TypedXmlPullParser parser, String attributeName) {
try {
- return parser.getAttributeInt(/* namespace= */ null, attributeName);
+ return new IntegerPolicyValue(
+ parser.getAttributeInt(/* namespace= */ null, attributeName));
} catch (XmlPullParserException e) {
Log.e(DevicePolicyEngine.TAG, "Error parsing Integer policy value", e);
return null;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/LockTaskPolicy.java b/services/devicepolicy/java/com/android/server/devicepolicy/LockTaskPolicy.java
deleted file mode 100644
index d3e8de488e0b..000000000000
--- a/services/devicepolicy/java/com/android/server/devicepolicy/LockTaskPolicy.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.devicepolicy;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.admin.DevicePolicyManager;
-import android.util.Log;
-
-import com.android.modules.utils.TypedXmlPullParser;
-import com.android.modules.utils.TypedXmlSerializer;
-
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.util.HashSet;
-import java.util.Objects;
-import java.util.Set;
-
-final class LockTaskPolicy {
- static final int DEFAULT_LOCK_TASK_FLAG = DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS;
- private Set<String> mPackages = new HashSet<>();
- private int mFlags = DEFAULT_LOCK_TASK_FLAG;
-
- @NonNull
- Set<String> getPackages() {
- return mPackages;
- }
-
- int getFlags() {
- return mFlags;
- }
-
- LockTaskPolicy(Set<String> packages) {
- Objects.requireNonNull(packages);
- mPackages.addAll(packages);
- }
-
- private LockTaskPolicy(Set<String> packages, int flags) {
- Objects.requireNonNull(packages);
- mPackages = new HashSet<>(packages);
- mFlags = flags;
- }
-
- void setPackages(@NonNull Set<String> packages) {
- Objects.requireNonNull(packages);
- mPackages = new HashSet<>(packages);
- }
-
- void setFlags(int flags) {
- mFlags = flags;
- }
-
- @Override
- public LockTaskPolicy clone() {
- LockTaskPolicy policy = new LockTaskPolicy(mPackages);
- policy.setFlags(mFlags);
- return policy;
- }
-
- @Override
- public boolean equals(@Nullable Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- LockTaskPolicy other = (LockTaskPolicy) o;
- return Objects.equals(mPackages, other.mPackages)
- && mFlags == other.mFlags;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mPackages, mFlags);
- }
-
- @Override
- public String toString() {
- return "mPackages= " + String.join(", ", mPackages) + "; mFlags= " + mFlags;
- }
-
- static final class LockTaskPolicySerializer extends PolicySerializer<LockTaskPolicy> {
-
- private static final String ATTR_PACKAGES = ":packages";
- private static final String ATTR_PACKAGES_SEPARATOR = ";";
- private static final String ATTR_FLAGS = ":flags";
-
- @Override
- void saveToXml(TypedXmlSerializer serializer, String attributeNamePrefix,
- @NonNull LockTaskPolicy value) throws IOException {
- Objects.requireNonNull(value);
- if (value.mPackages == null || value.mPackages.isEmpty()) {
- throw new IllegalArgumentException("Error saving LockTaskPolicy to file, lock task "
- + "packages must be present");
- }
- serializer.attribute(
- /* namespace= */ null,
- attributeNamePrefix + ATTR_PACKAGES,
- String.join(ATTR_PACKAGES_SEPARATOR, value.mPackages));
- serializer.attributeInt(
- /* namespace= */ null,
- attributeNamePrefix + ATTR_FLAGS,
- value.mFlags);
- }
-
- @Override
- LockTaskPolicy readFromXml(TypedXmlPullParser parser, String attributeNamePrefix) {
- String packagesStr = parser.getAttributeValue(
- /* namespace= */ null,
- attributeNamePrefix + ATTR_PACKAGES);
- if (packagesStr == null) {
- Log.e(DevicePolicyEngine.TAG, "Error parsing LockTask policy value.");
- return null;
- }
- Set<String> packages = Set.of(packagesStr.split(ATTR_PACKAGES_SEPARATOR));
- try {
- int flags = parser.getAttributeInt(
- /* namespace= */ null,
- attributeNamePrefix + ATTR_FLAGS);
- return new LockTaskPolicy(packages, flags);
- } catch (XmlPullParserException e) {
- Log.e(DevicePolicyEngine.TAG, "Error parsing LockTask policy value", e);
- return null;
- }
- }
- }
-}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/LockTaskPolicySerializer.java b/services/devicepolicy/java/com/android/server/devicepolicy/LockTaskPolicySerializer.java
new file mode 100644
index 000000000000..3265b61a3543
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/LockTaskPolicySerializer.java
@@ -0,0 +1,77 @@
+/*
+ * 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.server.devicepolicy;
+
+import android.annotation.NonNull;
+import android.app.admin.LockTaskPolicy;
+import android.app.admin.PolicyKey;
+import android.util.Log;
+
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.Objects;
+import java.util.Set;
+
+final class LockTaskPolicySerializer extends PolicySerializer<LockTaskPolicy> {
+
+ private static final String ATTR_PACKAGES = ":packages";
+ private static final String ATTR_PACKAGES_SEPARATOR = ";";
+ private static final String ATTR_FLAGS = ":flags";
+
+ @Override
+ void saveToXml(PolicyKey policyKey, TypedXmlSerializer serializer, String attributeNamePrefix,
+ @NonNull LockTaskPolicy value) throws IOException {
+ Objects.requireNonNull(value);
+ if (value.getPackages() == null || value.getPackages().isEmpty()) {
+ throw new IllegalArgumentException("Error saving LockTaskPolicy to file, lock task "
+ + "packages must be present");
+ }
+ serializer.attribute(
+ /* namespace= */ null,
+ attributeNamePrefix + ATTR_PACKAGES,
+ String.join(ATTR_PACKAGES_SEPARATOR, value.getPackages()));
+ serializer.attributeInt(
+ /* namespace= */ null,
+ attributeNamePrefix + ATTR_FLAGS,
+ value.getFlags());
+ }
+
+ @Override
+ LockTaskPolicy readFromXml(TypedXmlPullParser parser, String attributeNamePrefix) {
+ String packagesStr = parser.getAttributeValue(
+ /* namespace= */ null,
+ attributeNamePrefix + ATTR_PACKAGES);
+ if (packagesStr == null) {
+ Log.e(DevicePolicyEngine.TAG, "Error parsing LockTask policy value.");
+ return null;
+ }
+ Set<String> packages = Set.of(packagesStr.split(ATTR_PACKAGES_SEPARATOR));
+ try {
+ int flags = parser.getAttributeInt(
+ /* namespace= */ null,
+ attributeNamePrefix + ATTR_FLAGS);
+ return new LockTaskPolicy(packages, flags);
+ } catch (XmlPullParserException e) {
+ Log.e(DevicePolicyEngine.TAG, "Error parsing LockTask policy value", e);
+ return null;
+ }
+ }
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/SetPolicySerializer.java b/services/devicepolicy/java/com/android/server/devicepolicy/MostRecent.java
index 736627b610b0..423a497252b7 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/SetPolicySerializer.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/MostRecent.java
@@ -16,28 +16,31 @@
package com.android.server.devicepolicy;
+
import android.annotation.NonNull;
-import android.annotation.Nullable;
+import android.app.admin.PolicyValue;
-import com.android.modules.utils.TypedXmlPullParser;
-import com.android.modules.utils.TypedXmlSerializer;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
-import java.io.IOException;
-import java.util.Objects;
-import java.util.Set;
+final class MostRecent<V> extends ResolutionMechanism<V> {
-// TODO(scottjonathan): Replace with actual implementation
-final class SetPolicySerializer<V> extends PolicySerializer<Set<V>> {
+ @Override
+ PolicyValue<V> resolve(@NonNull LinkedHashMap<EnforcingAdmin, PolicyValue<V>> adminPolicies) {
+ List<Map.Entry<EnforcingAdmin, PolicyValue<V>>> policiesList = new ArrayList<>(
+ adminPolicies.entrySet());
+ return policiesList.isEmpty() ? null : policiesList.get(policiesList.size() - 1).getValue();
+ }
@Override
- void saveToXml(TypedXmlSerializer serializer, String attributeName, @NonNull Set<V> value)
- throws IOException {
- Objects.requireNonNull(value);
+ android.app.admin.MostRecent<V> getParcelableResolutionMechanism() {
+ return new android.app.admin.MostRecent<V>();
}
- @Nullable
@Override
- Set<V> readFromXml(TypedXmlPullParser parser, String attributeName) {
- return null;
+ public String toString() {
+ return "MostRecent {}";
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/MostRestrictive.java b/services/devicepolicy/java/com/android/server/devicepolicy/MostRestrictive.java
index edb3d2ef4856..7e8eaa76431b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/MostRestrictive.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/MostRestrictive.java
@@ -17,6 +17,7 @@
package com.android.server.devicepolicy;
import android.annotation.NonNull;
+import android.app.admin.PolicyValue;
import java.util.LinkedHashMap;
import java.util.List;
@@ -24,28 +25,34 @@ import java.util.Map;
final class MostRestrictive<V> extends ResolutionMechanism<V> {
- private List<V> mMostToLeastRestrictive;
+ private List<PolicyValue<V>> mMostToLeastRestrictive;
- MostRestrictive(@NonNull List<V> mostToLeastRestrictive) {
+ MostRestrictive(@NonNull List<PolicyValue<V>> mostToLeastRestrictive) {
mMostToLeastRestrictive = mostToLeastRestrictive;
}
@Override
- V resolve(@NonNull LinkedHashMap<EnforcingAdmin, V> adminPolicies) {
+ PolicyValue<V> resolve(@NonNull LinkedHashMap<EnforcingAdmin, PolicyValue<V>> adminPolicies) {
if (adminPolicies.isEmpty()) {
return null;
}
- for (V value : mMostToLeastRestrictive) {
+ for (PolicyValue<V> value : mMostToLeastRestrictive) {
if (adminPolicies.containsValue(value)) {
return value;
}
}
// Return first set policy if none can be found in known values
- Map.Entry<EnforcingAdmin, V> policy = adminPolicies.entrySet().stream().findFirst().get();
+ Map.Entry<EnforcingAdmin, PolicyValue<V>> policy =
+ adminPolicies.entrySet().stream().findFirst().get();
return policy.getValue();
}
@Override
+ android.app.admin.MostRestrictive<V> getParcelableResolutionMechanism() {
+ return new android.app.admin.MostRestrictive<V>(mMostToLeastRestrictive);
+ }
+
+ @Override
public String toString() {
return "MostRestrictive { mMostToLeastRestrictive= " + mMostToLeastRestrictive + " }";
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 581a19913530..3ca158dc9c96 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -579,6 +579,19 @@ class Owners {
}
}
+ void markMigrationToPolicyEngine() {
+ synchronized (mData) {
+ mData.mMigratedToPolicyEngine = true;
+ mData.writeDeviceOwner();
+ }
+ }
+
+ boolean isMigratedToPolicyEngine() {
+ synchronized (mData) {
+ return mData.mMigratedToPolicyEngine;
+ }
+ }
+
@GuardedBy("mData")
void pushToAppOpsLocked() {
if (!mSystemReady) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java b/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java
index 3040af23155e..63b250d4acfc 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java
@@ -66,6 +66,7 @@ class OwnersData {
private static final String TAG_DEVICE_OWNER_TYPE = "device-owner-type";
private static final String TAG_DEVICE_OWNER_PROTECTED_PACKAGES =
"device-owner-protected-packages";
+ private static final String TAG_POLICY_ENGINE_MIGRATION = "policy-engine-migration";
private static final String ATTR_NAME = "name";
private static final String ATTR_PACKAGE = "package";
@@ -84,6 +85,8 @@ class OwnersData {
"isPoOrganizationOwnedDevice";
private static final String ATTR_DEVICE_OWNER_TYPE_VALUE = "value";
+ private static final String ATTR_MIGRATED_TO_POLICY_ENGINE = "migratedToPolicyEngine";
+
// Internal state for the device owner package.
OwnerInfo mDeviceOwner;
int mDeviceOwnerUserId = UserHandle.USER_NULL;
@@ -109,6 +112,8 @@ class OwnersData {
SystemUpdateInfo mSystemUpdateInfo;
private final PolicyPathProvider mPathProvider;
+ boolean mMigratedToPolicyEngine = false;
+
OwnersData(PolicyPathProvider pathProvider) {
mPathProvider = pathProvider;
}
@@ -389,6 +394,11 @@ class OwnersData {
}
out.endTag(null, TAG_FREEZE_PERIOD_RECORD);
}
+
+ out.startTag(null, TAG_POLICY_ENGINE_MIGRATION);
+ out.attributeBoolean(null, ATTR_MIGRATED_TO_POLICY_ENGINE, mMigratedToPolicyEngine);
+ out.endTag(null, TAG_POLICY_ENGINE_MIGRATION);
+
}
@Override
@@ -444,6 +454,9 @@ class OwnersData {
}
mDeviceOwnerProtectedPackages.put(packageName, protectedPackages);
break;
+ case TAG_POLICY_ENGINE_MIGRATION:
+ mMigratedToPolicyEngine = parser.getAttributeBoolean(
+ null, ATTR_MIGRATED_TO_POLICY_ENGINE, false);
default:
Slog.e(TAG, "Unexpected tag: " + tag);
return false;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PackageSpecificPolicyKey.java b/services/devicepolicy/java/com/android/server/devicepolicy/PackageSpecificPolicyKey.java
deleted file mode 100644
index 1665830f4762..000000000000
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PackageSpecificPolicyKey.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.devicepolicy;
-
-import static android.app.admin.PolicyUpdatesReceiver.EXTRA_PACKAGE_NAME;
-import static android.app.admin.PolicyUpdatesReceiver.EXTRA_POLICY_BUNDLE_KEY;
-
-import android.annotation.Nullable;
-import android.os.Bundle;
-
-import com.android.modules.utils.TypedXmlPullParser;
-import com.android.modules.utils.TypedXmlSerializer;
-
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.util.Objects;
-
-/**
- * Class used to identify a policy that relates to a certain package in the policy engine's data
- * structure.
- */
-final class PackageSpecificPolicyKey extends PolicyKey {
- private static final String ATTR_POLICY_KEY = "policy-key";
- private static final String ATTR_PACKAGE_NAME = "package-name";
- private static final String ATTR_PERMISSION_NAME = "permission-name";
-
- private final String mPackageName;
-
- PackageSpecificPolicyKey(String key, String packageName) {
- super(key);
- mPackageName = Objects.requireNonNull((packageName));
- }
-
- PackageSpecificPolicyKey(String key) {
- super(key);
- mPackageName = null;
- }
-
- @Nullable
- String getPackageName() {
- return mPackageName;
- }
-
- @Override
- void saveToXml(TypedXmlSerializer serializer) throws IOException {
- serializer.attribute(/* namespace= */ null, ATTR_POLICY_KEY, mKey);
- serializer.attribute(/* namespace= */ null, ATTR_PACKAGE_NAME, mPackageName);
- }
-
- @Override
- PackageSpecificPolicyKey readFromXml(TypedXmlPullParser parser)
- throws XmlPullParserException, IOException {
- String policyKey = parser.getAttributeValue(/* namespace= */ null, ATTR_POLICY_KEY);
- String packageName = parser.getAttributeValue(/* namespace= */ null, ATTR_PACKAGE_NAME);
- String permissionName = parser.getAttributeValue(
- /* namespace= */ null, ATTR_PERMISSION_NAME);
- return new PackageSpecificPolicyKey(policyKey, packageName);
- }
-
- @Override
- void writeToBundle(Bundle bundle) {
- super.writeToBundle(bundle);
- Bundle extraPolicyParams = new Bundle();
- extraPolicyParams.putString(EXTRA_PACKAGE_NAME, mPackageName);
- bundle.putBundle(EXTRA_POLICY_BUNDLE_KEY, extraPolicyParams);
- }
-
- @Override
- public boolean equals(@Nullable Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- PackageSpecificPolicyKey other = (PackageSpecificPolicyKey) o;
- return Objects.equals(mKey, other.mKey)
- && Objects.equals(mPackageName, other.mPackageName);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mKey, mPackageName);
- }
-
- @Override
- public String toString() {
- return "mPolicyKey= " + mKey + "; mPackageName= " + mPackageName;
- }
-}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PermissionGrantStatePolicyKey.java b/services/devicepolicy/java/com/android/server/devicepolicy/PermissionGrantStatePolicyKey.java
deleted file mode 100644
index b7d805e5be33..000000000000
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PermissionGrantStatePolicyKey.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.devicepolicy;
-
-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 android.annotation.Nullable;
-import android.os.Bundle;
-
-import com.android.modules.utils.TypedXmlPullParser;
-import com.android.modules.utils.TypedXmlSerializer;
-
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.util.Objects;
-
-/**
- * Class used to identify a PermissionGrantState policy in the policy engine's data structure.
- */
-final class PermissionGrantStatePolicyKey extends PolicyKey {
- private static final String ATTR_POLICY_KEY = "policy-key";
- private static final String ATTR_PACKAGE_NAME = "package-name";
- private static final String ATTR_PERMISSION_NAME = "permission-name";
-
- private final String mPackageName;
- private final String mPermissionName;
-
- PermissionGrantStatePolicyKey(String key, String packageName, String permissionName) {
- super(key);
- mPackageName = Objects.requireNonNull((packageName));
- mPermissionName = Objects.requireNonNull((permissionName));
- }
-
- PermissionGrantStatePolicyKey(String key) {
- super(key);
- mPackageName = null;
- mPermissionName = null;
- }
-
- @Nullable
- String getPackageName() {
- return mPackageName;
- }
-
- @Nullable
- String getPermissionName() {
- return mPermissionName;
- }
-
- @Override
- void saveToXml(TypedXmlSerializer serializer) throws IOException {
- serializer.attribute(/* namespace= */ null, ATTR_POLICY_KEY, mKey);
- serializer.attribute(/* namespace= */ null, ATTR_PACKAGE_NAME, mPackageName);
- serializer.attribute(/* namespace= */ null, ATTR_PERMISSION_NAME, mPermissionName);
- }
-
- @Override
- PermissionGrantStatePolicyKey readFromXml(TypedXmlPullParser parser)
- throws XmlPullParserException, IOException {
- String policyKey = parser.getAttributeValue(/* namespace= */ null, ATTR_POLICY_KEY);
- String packageName = parser.getAttributeValue(/* namespace= */ null, ATTR_PACKAGE_NAME);
- String permissionName = parser.getAttributeValue(
- /* namespace= */ null, ATTR_PERMISSION_NAME);
- return new PermissionGrantStatePolicyKey(policyKey, packageName, permissionName);
- }
-
- @Override
- void writeToBundle(Bundle bundle) {
- super.writeToBundle(bundle);
- Bundle extraPolicyParams = new Bundle();
- extraPolicyParams.putString(EXTRA_PACKAGE_NAME, mPackageName);
- extraPolicyParams.putString(EXTRA_PERMISSION_NAME, mPermissionName);
- bundle.putBundle(EXTRA_POLICY_BUNDLE_KEY, extraPolicyParams);
- }
-
- @Override
- public boolean equals(@Nullable Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- PermissionGrantStatePolicyKey other = (PermissionGrantStatePolicyKey) o;
- return Objects.equals(mKey, other.mKey)
- && Objects.equals(mPackageName, other.mPackageName)
- && Objects.equals(mPermissionName, other.mPermissionName);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mKey, mPackageName, mPermissionName);
- }
-
- @Override
- public String toString() {
- return "mPolicyKey= " + mKey + "; mPackageName= " + mPackageName + "; mPermissionName= "
- + mPermissionName;
- }
-}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PersistentPreferredActivityPolicyKey.java b/services/devicepolicy/java/com/android/server/devicepolicy/PersistentPreferredActivityPolicyKey.java
deleted file mode 100644
index f8c075950fbb..000000000000
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PersistentPreferredActivityPolicyKey.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.devicepolicy;
-
-import static android.app.admin.PolicyUpdatesReceiver.EXTRA_INTENT_FILTER;
-import static android.app.admin.PolicyUpdatesReceiver.EXTRA_POLICY_BUNDLE_KEY;
-
-import android.annotation.Nullable;
-import android.content.IntentFilter;
-import android.os.Bundle;
-
-import com.android.modules.utils.TypedXmlPullParser;
-import com.android.modules.utils.TypedXmlSerializer;
-import com.android.server.IntentResolver;
-
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.util.Objects;
-
-/**
- * Class used to identify a PersistentPreferredActivity policy in the policy engine's data
- * structure.
- */
-final class PersistentPreferredActivityPolicyKey extends PolicyKey {
- private static final String ATTR_POLICY_KEY = "policy-key";
- private IntentFilter mFilter;
-
- PersistentPreferredActivityPolicyKey(String policyKey, IntentFilter filter) {
- super(policyKey);
- mFilter = Objects.requireNonNull((filter));
- }
-
- PersistentPreferredActivityPolicyKey(String policyKey) {
- super(policyKey);
- mFilter = null;
- }
-
- @Nullable
- IntentFilter getFilter() {
- return mFilter;
- }
-
- @Override
- void saveToXml(TypedXmlSerializer serializer) throws IOException {
- serializer.attribute(/* namespace= */ null, ATTR_POLICY_KEY, mKey);
- mFilter.writeToXml(serializer);
- }
-
- @Override
- PersistentPreferredActivityPolicyKey readFromXml(TypedXmlPullParser parser)
- throws XmlPullParserException, IOException {
- String policyKey = parser.getAttributeValue(/* namespace= */ null, ATTR_POLICY_KEY);
- IntentFilter filter = new IntentFilter();
- filter.readFromXml(parser);
- return new PersistentPreferredActivityPolicyKey(policyKey, filter);
- }
-
- @Override
- void writeToBundle(Bundle bundle) {
- super.writeToBundle(bundle);
- Bundle extraPolicyParams = new Bundle();
- extraPolicyParams.putParcelable(EXTRA_INTENT_FILTER, mFilter);
- bundle.putBundle(EXTRA_POLICY_BUNDLE_KEY, extraPolicyParams);
- }
-
- @Override
- public boolean equals(@Nullable Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- PersistentPreferredActivityPolicyKey other = (PersistentPreferredActivityPolicyKey) o;
- return Objects.equals(mKey, other.mKey)
- && IntentResolver.filterEquals(mFilter, other.mFilter);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mKey, mFilter);
- }
-
- @Override
- public String toString() {
- return "mKey= " + mKey + "; mFilter= " + mFilter;
- }
-}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
index a7c4d5a6fb4e..ab6f7327b0c2 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
@@ -18,10 +18,20 @@ package com.android.server.devicepolicy;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.admin.BooleanPolicyValue;
import android.app.admin.DevicePolicyManager;
+import android.app.admin.IntegerPolicyValue;
+import android.app.admin.IntentFilterPolicyKey;
+import android.app.admin.LockTaskPolicy;
+import android.app.admin.NoArgsPolicyKey;
+import android.app.admin.PackagePermissionPolicyKey;
+import android.app.admin.PackagePolicyKey;
+import android.app.admin.PolicyKey;
+import android.app.admin.PolicyValue;
import android.content.ComponentName;
import android.content.Context;
import android.content.IntentFilter;
+import android.os.Bundle;
import com.android.internal.util.function.QuadFunction;
import com.android.modules.utils.TypedXmlPullParser;
@@ -47,35 +57,47 @@ final class PolicyDefinition<V> {
// Only use this flag if a policy is inheritable by child profile from parent.
private static final int POLICY_FLAG_INHERITABLE = 1 << 2;
+ // Use this flag if admin policies should be treated independently of each other and should not
+ // have any resolution logic applied, this should only be used for very limited policies were
+ // this would make sense and the enforcing logic should handle it appropriately, e.g.
+ // application restrictions set by different admins for a single package should not be merged,
+ // but saved and queried independent of each other.
+ // Currently, support is added for local only policies, if you need to add a non coexistable
+ // global policy please add support.
+ private static final int POLICY_FLAG_NON_COEXISTABLE_POLICY = 1 << 3;
+
private static final MostRestrictive<Boolean> FALSE_MORE_RESTRICTIVE = new MostRestrictive<>(
- List.of(false, true));
+ List.of(new BooleanPolicyValue(false), new BooleanPolicyValue(true)));
private static final MostRestrictive<Boolean> TRUE_MORE_RESTRICTIVE = new MostRestrictive<>(
- List.of(true, false));
+ List.of(new BooleanPolicyValue(true), new BooleanPolicyValue(false)));
static PolicyDefinition<Boolean> AUTO_TIMEZONE = new PolicyDefinition<>(
- new DefaultPolicyKey(DevicePolicyManager.AUTO_TIMEZONE_POLICY),
- // auto timezone is enabled by default, hence disabling it is more restrictive.
- FALSE_MORE_RESTRICTIVE,
+ new NoArgsPolicyKey(DevicePolicyManager.AUTO_TIMEZONE_POLICY),
+ // auto timezone is disabled by default, hence enabling it is more restrictive.
+ TRUE_MORE_RESTRICTIVE,
POLICY_FLAG_GLOBAL_ONLY_POLICY,
(Boolean value, Context context, Integer userId, PolicyKey policyKey) ->
PolicyEnforcerCallbacks.setAutoTimezoneEnabled(value, context),
new BooleanPolicySerializer());
// This is saved in the static map sPolicyDefinitions so that we're able to reconstruct the
- // actual permission grant policy with the correct arguments (packageName and permission name)
+ // actual policy with the correct arguments (packageName and permission name)
// when reading the policies from xml.
static final PolicyDefinition<Integer> GENERIC_PERMISSION_GRANT =
new PolicyDefinition<>(
- new PermissionGrantStatePolicyKey(
- DevicePolicyManager.PERMISSION_GRANT_POLICY_KEY),
+ new PackagePermissionPolicyKey(DevicePolicyManager.PERMISSION_GRANT_POLICY),
// TODO: is this really the best mechanism, what makes denied more
// restrictive than
// granted?
new MostRestrictive<>(
- List.of(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED,
- DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED,
- DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT)),
+ List.of(
+ new IntegerPolicyValue(
+ DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED),
+ new IntegerPolicyValue(
+ DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED),
+ new IntegerPolicyValue(
+ DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT))),
POLICY_FLAG_LOCAL_ONLY_POLICY,
PolicyEnforcerCallbacks::setPermissionGrantState,
new IntegerPolicySerializer());
@@ -90,14 +112,14 @@ final class PolicyDefinition<V> {
return GENERIC_PERMISSION_GRANT;
}
return GENERIC_PERMISSION_GRANT.createPolicyDefinition(
- new PermissionGrantStatePolicyKey(
- DevicePolicyManager.PERMISSION_GRANT_POLICY_KEY,
+ new PackagePermissionPolicyKey(
+ DevicePolicyManager.PERMISSION_GRANT_POLICY,
packageName,
permissionName));
}
static PolicyDefinition<LockTaskPolicy> LOCK_TASK = new PolicyDefinition<>(
- new DefaultPolicyKey(DevicePolicyManager.LOCK_TASK_POLICY),
+ new NoArgsPolicyKey(DevicePolicyManager.LOCK_TASK_POLICY),
new TopPriority<>(List.of(
// TODO(b/258166155): add correct device lock role name
EnforcingAdmin.getRoleAuthorityOf("DeviceLock"),
@@ -105,21 +127,22 @@ final class PolicyDefinition<V> {
POLICY_FLAG_LOCAL_ONLY_POLICY,
(LockTaskPolicy value, Context context, Integer userId, PolicyKey policyKey) ->
PolicyEnforcerCallbacks.setLockTask(value, context, userId),
- new LockTaskPolicy.LockTaskPolicySerializer());
+ new LockTaskPolicySerializer());
- static PolicyDefinition<Set<String>> USER_CONTROLLED_DISABLED_PACKAGES = new PolicyDefinition<>(
- new DefaultPolicyKey(DevicePolicyManager.USER_CONTROL_DISABLED_PACKAGES_POLICY),
- new SetUnion<>(),
- (Set<String> value, Context context, Integer userId, PolicyKey policyKey) ->
- PolicyEnforcerCallbacks.setUserControlDisabledPackages(value, userId),
- new SetPolicySerializer<>());
+ static PolicyDefinition<Set<String>> USER_CONTROLLED_DISABLED_PACKAGES =
+ new PolicyDefinition<>(
+ new NoArgsPolicyKey(DevicePolicyManager.USER_CONTROL_DISABLED_PACKAGES_POLICY),
+ new StringSetUnion(),
+ (Set<String> value, Context context, Integer userId, PolicyKey policyKey) ->
+ PolicyEnforcerCallbacks.setUserControlDisabledPackages(value, userId),
+ new StringSetPolicySerializer());
// This is saved in the static map sPolicyDefinitions so that we're able to reconstruct the
- // actual permission grant policy with the correct arguments (packageName and permission name)
- // when reading the policies from xml.
+ // actual policy with the correct arguments (i.e. packageName) when reading the policies from
+ // xml.
static PolicyDefinition<ComponentName> GENERIC_PERSISTENT_PREFERRED_ACTIVITY =
new PolicyDefinition<>(
- new PersistentPreferredActivityPolicyKey(
+ new IntentFilterPolicyKey(
DevicePolicyManager.PERSISTENT_PREFERRED_ACTIVITY_POLICY),
new TopPriority<>(List.of(
// TODO(b/258166155): add correct device lock role name
@@ -139,16 +162,16 @@ final class PolicyDefinition<V> {
return GENERIC_PERSISTENT_PREFERRED_ACTIVITY;
}
return GENERIC_PERSISTENT_PREFERRED_ACTIVITY.createPolicyDefinition(
- new PersistentPreferredActivityPolicyKey(
+ new IntentFilterPolicyKey(
DevicePolicyManager.PERSISTENT_PREFERRED_ACTIVITY_POLICY, intentFilter));
}
// This is saved in the static map sPolicyDefinitions so that we're able to reconstruct the
- // actual uninstall blocked policy with the correct arguments (i.e. packageName)
- // when reading the policies from xml.
+ // actual policy with the correct arguments (i.e. packageName) when reading the policies from
+ // xml.
static PolicyDefinition<Boolean> GENERIC_PACKAGE_UNINSTALL_BLOCKED =
new PolicyDefinition<>(
- new PackageSpecificPolicyKey(
+ new PackagePolicyKey(
DevicePolicyManager.PACKAGE_UNINSTALL_BLOCKED_POLICY),
TRUE_MORE_RESTRICTIVE,
POLICY_FLAG_LOCAL_ONLY_POLICY,
@@ -165,19 +188,49 @@ final class PolicyDefinition<V> {
return GENERIC_PACKAGE_UNINSTALL_BLOCKED;
}
return GENERIC_PACKAGE_UNINSTALL_BLOCKED.createPolicyDefinition(
- new PackageSpecificPolicyKey(
+ new PackagePolicyKey(
DevicePolicyManager.PACKAGE_UNINSTALL_BLOCKED_POLICY, packageName));
}
+ // This is saved in the static map sPolicyDefinitions so that we're able to reconstruct the
+ // actual policy with the correct arguments (i.e. packageName) when reading the policies from
+ // xml.
+ static PolicyDefinition<Bundle> GENERIC_APPLICATION_RESTRICTIONS =
+ new PolicyDefinition<>(
+ new PackagePolicyKey(
+ DevicePolicyManager.APPLICATION_RESTRICTIONS_POLICY),
+ new MostRecent<>(),
+ POLICY_FLAG_LOCAL_ONLY_POLICY | POLICY_FLAG_NON_COEXISTABLE_POLICY,
+ // Application restrictions are now stored and retrieved from DPMS, so no
+ // enforcing is required, however DPMS calls into UM to set restrictions for
+ // backwards compatibility.
+ (Bundle value, Context context, Integer userId, PolicyKey policyKey) -> true,
+ new BundlePolicySerializer());
+
+ /**
+ * Passing in {@code null} for {@code packageName} will return
+ * {@link #GENERIC_APPLICATION_RESTRICTIONS}.
+ */
+ static PolicyDefinition<Bundle> APPLICATION_RESTRICTIONS(
+ String packageName) {
+ if (packageName == null) {
+ return GENERIC_APPLICATION_RESTRICTIONS;
+ }
+ return GENERIC_APPLICATION_RESTRICTIONS.createPolicyDefinition(
+ new PackagePolicyKey(
+ DevicePolicyManager.APPLICATION_RESTRICTIONS_POLICY, packageName));
+ }
+
private static final Map<String, PolicyDefinition<?>> sPolicyDefinitions = Map.of(
DevicePolicyManager.AUTO_TIMEZONE_POLICY, AUTO_TIMEZONE,
- DevicePolicyManager.PERMISSION_GRANT_POLICY_KEY, GENERIC_PERMISSION_GRANT,
+ DevicePolicyManager.PERMISSION_GRANT_POLICY, GENERIC_PERMISSION_GRANT,
DevicePolicyManager.LOCK_TASK_POLICY, LOCK_TASK,
DevicePolicyManager.USER_CONTROL_DISABLED_PACKAGES_POLICY,
USER_CONTROLLED_DISABLED_PACKAGES,
DevicePolicyManager.PERSISTENT_PREFERRED_ACTIVITY_POLICY,
GENERIC_PERSISTENT_PREFERRED_ACTIVITY,
- DevicePolicyManager.PACKAGE_UNINSTALL_BLOCKED_POLICY, GENERIC_PACKAGE_UNINSTALL_BLOCKED
+ DevicePolicyManager.PACKAGE_UNINSTALL_BLOCKED_POLICY, GENERIC_PACKAGE_UNINSTALL_BLOCKED,
+ DevicePolicyManager.APPLICATION_RESTRICTIONS_POLICY, GENERIC_APPLICATION_RESTRICTIONS
);
@@ -199,6 +252,10 @@ final class PolicyDefinition<V> {
return mPolicyKey;
}
+ @NonNull
+ ResolutionMechanism<V> getResolutionMechanism() {
+ return mResolutionMechanism;
+ }
/**
* Returns {@code true} if the policy is a global policy by nature and can't be applied locally.
*/
@@ -220,8 +277,16 @@ final class PolicyDefinition<V> {
return (mPolicyFlags & POLICY_FLAG_INHERITABLE) != 0;
}
+ /**
+ * Returns {@code true} if the policy engine should not try to resolve policies set by different
+ * admins and should just store it and pass it on to the enforcing logic.
+ */
+ boolean isNonCoexistablePolicy() {
+ return (mPolicyFlags & POLICY_FLAG_NON_COEXISTABLE_POLICY) != 0;
+ }
+
@Nullable
- V resolvePolicy(LinkedHashMap<EnforcingAdmin, V> adminsPolicy) {
+ PolicyValue<V> resolvePolicy(LinkedHashMap<EnforcingAdmin, PolicyValue<V>> adminsPolicy) {
return mResolutionMechanism.resolve(adminsPolicy);
}
@@ -257,6 +322,10 @@ final class PolicyDefinition<V> {
mPolicyEnforcerCallback = policyEnforcerCallback;
mPolicySerializer = policySerializer;
+ if (isNonCoexistablePolicy() && !isLocalOnlyPolicy()) {
+ throw new UnsupportedOperationException("Non-coexistable global policies not supported,"
+ + "please add support.");
+ }
// TODO: maybe use this instead of manually adding to the map
// sPolicyDefinitions.put(policyDefinitionKey, this);
}
@@ -271,26 +340,27 @@ final class PolicyDefinition<V> {
// TODO: can we avoid casting?
PolicyKey policyKey = readPolicyKeyFromXml(parser);
PolicyDefinition<V> genericPolicyDefinition =
- (PolicyDefinition<V>) sPolicyDefinitions.get(policyKey.mKey);
+ (PolicyDefinition<V>) sPolicyDefinitions.get(policyKey.getIdentifier());
return genericPolicyDefinition.createPolicyDefinition(policyKey);
}
static <V> PolicyKey readPolicyKeyFromXml(TypedXmlPullParser parser)
throws XmlPullParserException, IOException {
// TODO: can we avoid casting?
- PolicyKey policyKey = DefaultPolicyKey.readGenericPolicyKeyFromXml(parser);
- PolicyDefinition<V> genericPolicyDefinition =
- (PolicyDefinition<V>) sPolicyDefinitions.get(policyKey.mKey);
+ PolicyKey policyKey = PolicyKey.readGenericPolicyKeyFromXml(parser);
+ PolicyDefinition<PolicyValue<V>> genericPolicyDefinition =
+ (PolicyDefinition<PolicyValue<V>>) sPolicyDefinitions.get(
+ policyKey.getIdentifier());
return genericPolicyDefinition.mPolicyKey.readFromXml(parser);
}
void savePolicyValueToXml(TypedXmlSerializer serializer, String attributeName, V value)
throws IOException {
- mPolicySerializer.saveToXml(serializer, attributeName, value);
+ mPolicySerializer.saveToXml(mPolicyKey, serializer, attributeName, value);
}
@Nullable
- V readPolicyValueFromXml(TypedXmlPullParser parser, String attributeName) {
+ PolicyValue<V> readPolicyValueFromXml(TypedXmlPullParser parser, String attributeName) {
return mPolicySerializer.readFromXml(parser, attributeName);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
index e2aa23d7fc01..4ae7ca6377a4 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
@@ -20,6 +20,11 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppGlobals;
import android.app.admin.DevicePolicyManager;
+import android.app.admin.IntentFilterPolicyKey;
+import android.app.admin.LockTaskPolicy;
+import android.app.admin.PackagePermissionPolicyKey;
+import android.app.admin.PackagePolicyKey;
+import android.app.admin.PolicyKey;
import android.content.ComponentName;
import android.content.Context;
import android.content.IntentFilter;
@@ -64,11 +69,11 @@ final class PolicyEnforcerCallbacks {
@Nullable Integer grantState, @NonNull Context context, int userId,
@NonNull PolicyKey policyKey) {
return Boolean.TRUE.equals(Binder.withCleanCallingIdentity(() -> {
- if (!(policyKey instanceof PermissionGrantStatePolicyKey)) {
+ if (!(policyKey instanceof PackagePermissionPolicyKey)) {
throw new IllegalArgumentException("policyKey is not of type "
+ "PermissionGrantStatePolicyKey");
}
- PermissionGrantStatePolicyKey parsedKey = (PermissionGrantStatePolicyKey) policyKey;
+ PackagePermissionPolicyKey parsedKey = (PackagePermissionPolicyKey) policyKey;
Objects.requireNonNull(parsedKey.getPermissionName());
Objects.requireNonNull(parsedKey.getPackageName());
Objects.requireNonNull(context);
@@ -156,13 +161,13 @@ final class PolicyEnforcerCallbacks {
@NonNull PolicyKey policyKey) {
Binder.withCleanCallingIdentity(() -> {
try {
- if (!(policyKey instanceof PersistentPreferredActivityPolicyKey)) {
+ if (!(policyKey instanceof IntentFilterPolicyKey)) {
throw new IllegalArgumentException("policyKey is not of type "
- + "PersistentPreferredActivityPolicyKey");
+ + "IntentFilterPolicyKey");
}
- PersistentPreferredActivityPolicyKey parsedKey =
- (PersistentPreferredActivityPolicyKey) policyKey;
- IntentFilter filter = Objects.requireNonNull(parsedKey.getFilter());
+ IntentFilterPolicyKey parsedKey =
+ (IntentFilterPolicyKey) policyKey;
+ IntentFilter filter = Objects.requireNonNull(parsedKey.getIntentFilter());
IPackageManager packageManager = AppGlobals.getPackageManager();
if (preferredActivity != null) {
@@ -184,11 +189,11 @@ final class PolicyEnforcerCallbacks {
@Nullable Boolean uninstallBlocked, @NonNull Context context, int userId,
@NonNull PolicyKey policyKey) {
return Boolean.TRUE.equals(Binder.withCleanCallingIdentity(() -> {
- if (!(policyKey instanceof PackageSpecificPolicyKey)) {
+ if (!(policyKey instanceof PackagePolicyKey)) {
throw new IllegalArgumentException("policyKey is not of type "
- + "PackageSpecificPolicyKey");
+ + "PackagePolicyKey");
}
- PackageSpecificPolicyKey parsedKey = (PackageSpecificPolicyKey) policyKey;
+ PackagePolicyKey parsedKey = (PackagePolicyKey) policyKey;
String packageName = Objects.requireNonNull(parsedKey.getPackageName());
DevicePolicyManagerService.setUninstallBlockedUnchecked(
packageName,
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyKey.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyKey.java
deleted file mode 100644
index 571f0ee8530c..000000000000
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyKey.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.devicepolicy;
-
-import static android.app.admin.PolicyUpdatesReceiver.EXTRA_POLICY_KEY;
-
-import android.annotation.Nullable;
-import android.os.Bundle;
-
-import com.android.modules.utils.TypedXmlPullParser;
-import com.android.modules.utils.TypedXmlSerializer;
-
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.util.Objects;
-
-/**
- * Abstract class used to identify a policy in the policy engine's data structure.
- */
-abstract class PolicyKey {
- private static final String ATTR_GENERIC_POLICY_KEY = "generic-policy-key";
-
- protected final String mKey;
-
- PolicyKey(String policyKey) {
- mKey = Objects.requireNonNull(policyKey);
- }
-
- String getKey() {
- return mKey;
- }
-
- boolean hasSameKeyAs(PolicyKey other) {
- if (other == null) {
- return false;
- }
- return mKey.equals(other.mKey);
- }
-
- void saveToXml(TypedXmlSerializer serializer) throws IOException {
- serializer.attribute(/* namespace= */ null, ATTR_GENERIC_POLICY_KEY, mKey);
- }
-
- PolicyKey readFromXml(TypedXmlPullParser parser)
- throws XmlPullParserException, IOException {
- // No need to read anything
- return this;
- }
-
- void writeToBundle(Bundle bundle) {
- bundle.putString(EXTRA_POLICY_KEY, mKey);
- }
-
- @Override
- public boolean equals(@Nullable Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- PolicyKey other = (PolicyKey) o;
- return Objects.equals(mKey, other.mKey);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mKey);
- }
-}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicySerializer.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicySerializer.java
index 528d3b0c8055..0ef431f6ff90 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicySerializer.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicySerializer.java
@@ -17,6 +17,8 @@
package com.android.server.devicepolicy;
import android.annotation.NonNull;
+import android.app.admin.PolicyKey;
+import android.app.admin.PolicyValue;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
@@ -24,7 +26,8 @@ import com.android.modules.utils.TypedXmlSerializer;
import java.io.IOException;
abstract class PolicySerializer<V> {
- abstract void saveToXml(TypedXmlSerializer serializer, String attributeName, @NonNull V value)
+ abstract void saveToXml(PolicyKey policyKey, TypedXmlSerializer serializer,
+ String attributeName, @NonNull V value)
throws IOException;
- abstract V readFromXml(TypedXmlPullParser parser, String attributeName);
+ abstract PolicyValue<V> readFromXml(TypedXmlPullParser parser, String attributeName);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyState.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyState.java
index c293e09daae9..3a792d82d2ba 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyState.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyState.java
@@ -18,6 +18,7 @@ package com.android.server.devicepolicy;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.admin.PolicyValue;
import android.util.Log;
import com.android.internal.util.XmlUtils;
@@ -40,8 +41,9 @@ final class PolicyState<V> {
private static final String ATTR_RESOLVED_POLICY = "resolved-policy";
private final PolicyDefinition<V> mPolicyDefinition;
- private final LinkedHashMap<EnforcingAdmin, V> mPoliciesSetByAdmins = new LinkedHashMap<>();
- private V mCurrentResolvedPolicy;
+ private final LinkedHashMap<EnforcingAdmin, PolicyValue<V>> mPoliciesSetByAdmins =
+ new LinkedHashMap<>();
+ private PolicyValue<V> mCurrentResolvedPolicy;
PolicyState(@NonNull PolicyDefinition<V> policyDefinition) {
mPolicyDefinition = Objects.requireNonNull(policyDefinition);
@@ -49,8 +51,8 @@ final class PolicyState<V> {
private PolicyState(
@NonNull PolicyDefinition<V> policyDefinition,
- @NonNull LinkedHashMap<EnforcingAdmin, V> policiesSetByAdmins,
- V currentEnforcedPolicy) {
+ @NonNull LinkedHashMap<EnforcingAdmin, PolicyValue<V>> policiesSetByAdmins,
+ PolicyValue<V> currentEnforcedPolicy) {
Objects.requireNonNull(policyDefinition);
Objects.requireNonNull(policiesSetByAdmins);
@@ -62,8 +64,14 @@ final class PolicyState<V> {
/**
* Returns {@code true} if the resolved policy has changed, {@code false} otherwise.
*/
- boolean addPolicy(@NonNull EnforcingAdmin admin, @NonNull V policy) {
- mPoliciesSetByAdmins.put(Objects.requireNonNull(admin), Objects.requireNonNull(policy));
+ boolean addPolicy(@NonNull EnforcingAdmin admin, @NonNull PolicyValue<V> policy) {
+ Objects.requireNonNull(admin);
+ Objects.requireNonNull(policy);
+
+ //LinkedHashMap doesn't update the insertion order of existing keys, removing the existing
+ // key will cause it to update.
+ mPoliciesSetByAdmins.remove(admin);
+ mPoliciesSetByAdmins.put(admin, policy);
return resolvePolicy();
}
@@ -78,8 +86,8 @@ final class PolicyState<V> {
* Returns {@code true} if the resolved policy has changed, {@code false} otherwise.
*/
boolean addPolicy(
- @NonNull EnforcingAdmin admin, @NonNull V policy,
- LinkedHashMap<EnforcingAdmin, V> globalPoliciesSetByAdmins) {
+ @NonNull EnforcingAdmin admin, @NonNull PolicyValue<V> policy,
+ LinkedHashMap<EnforcingAdmin, PolicyValue<V>> globalPoliciesSetByAdmins) {
mPoliciesSetByAdmins.put(Objects.requireNonNull(admin), Objects.requireNonNull(policy));
return resolvePolicy(globalPoliciesSetByAdmins);
@@ -109,7 +117,7 @@ final class PolicyState<V> {
*/
boolean removePolicy(
@NonNull EnforcingAdmin admin,
- LinkedHashMap<EnforcingAdmin, V> globalPoliciesSetByAdmins) {
+ LinkedHashMap<EnforcingAdmin, PolicyValue<V>> globalPoliciesSetByAdmins) {
Objects.requireNonNull(admin);
if (mPoliciesSetByAdmins.remove(admin) == null) {
@@ -128,13 +136,17 @@ final class PolicyState<V> {
*
* Returns {@code true} if the resolved policy has changed, {@code false} otherwise.
*/
- boolean resolvePolicy(LinkedHashMap<EnforcingAdmin, V> globalPoliciesSetByAdmins) {
+ boolean resolvePolicy(LinkedHashMap<EnforcingAdmin, PolicyValue<V>> globalPoliciesSetByAdmins) {
+ //Non coexistable policies don't need resolving
+ if (mPolicyDefinition.isNonCoexistablePolicy()) {
+ return false;
+ }
// Add global policies first then override with local policies for the same admin.
- LinkedHashMap<EnforcingAdmin, V> mergedPolicies =
+ LinkedHashMap<EnforcingAdmin, PolicyValue<V>> mergedPolicies =
new LinkedHashMap<>(globalPoliciesSetByAdmins);
mergedPolicies.putAll(mPoliciesSetByAdmins);
- V resolvedPolicy = mPolicyDefinition.resolvePolicy(mergedPolicies);
+ PolicyValue<V> resolvedPolicy = mPolicyDefinition.resolvePolicy(mergedPolicies);
boolean policyChanged = !Objects.equals(resolvedPolicy, mCurrentResolvedPolicy);
mCurrentResolvedPolicy = resolvedPolicy;
@@ -142,12 +154,16 @@ final class PolicyState<V> {
}
@NonNull
- LinkedHashMap<EnforcingAdmin, V> getPoliciesSetByAdmins() {
+ LinkedHashMap<EnforcingAdmin, PolicyValue<V>> getPoliciesSetByAdmins() {
return new LinkedHashMap<>(mPoliciesSetByAdmins);
}
private boolean resolvePolicy() {
- V resolvedPolicy = mPolicyDefinition.resolvePolicy(mPoliciesSetByAdmins);
+ //Non coexistable policies don't need resolving
+ if (mPolicyDefinition.isNonCoexistablePolicy()) {
+ return false;
+ }
+ PolicyValue<V> resolvedPolicy = mPolicyDefinition.resolvePolicy(mPoliciesSetByAdmins);
boolean policyChanged = !Objects.equals(resolvedPolicy, mCurrentResolvedPolicy);
mCurrentResolvedPolicy = resolvedPolicy;
@@ -155,10 +171,20 @@ final class PolicyState<V> {
}
@Nullable
- V getCurrentResolvedPolicy() {
+ PolicyValue<V> getCurrentResolvedPolicy() {
return mCurrentResolvedPolicy;
}
+ android.app.admin.PolicyState<V> getParcelablePolicyState() {
+ LinkedHashMap<android.app.admin.EnforcingAdmin, PolicyValue<V>> adminPolicies =
+ new LinkedHashMap<>();
+ for (EnforcingAdmin admin : mPoliciesSetByAdmins.keySet()) {
+ adminPolicies.put(admin.getParcelableAdmin(), mPoliciesSetByAdmins.get(admin));
+ }
+ return new android.app.admin.PolicyState<>(adminPolicies, mCurrentResolvedPolicy,
+ mPolicyDefinition.getResolutionMechanism().getParcelableResolutionMechanism());
+ }
+
@Override
public String toString() {
return "PolicyState { mPolicyDefinition= " + mPolicyDefinition + ", mPoliciesSetByAdmins= "
@@ -171,14 +197,14 @@ final class PolicyState<V> {
if (mCurrentResolvedPolicy != null) {
mPolicyDefinition.savePolicyValueToXml(
- serializer, ATTR_RESOLVED_POLICY, mCurrentResolvedPolicy);
+ serializer, ATTR_RESOLVED_POLICY, mCurrentResolvedPolicy.getValue());
}
for (EnforcingAdmin admin : mPoliciesSetByAdmins.keySet()) {
serializer.startTag(/* namespace= */ null, TAG_ADMIN_POLICY_ENTRY);
mPolicyDefinition.savePolicyValueToXml(
- serializer, ATTR_POLICY_VALUE, mPoliciesSetByAdmins.get(admin));
+ serializer, ATTR_POLICY_VALUE, mPoliciesSetByAdmins.get(admin).getValue());
serializer.startTag(/* namespace= */ null, TAG_ENFORCING_ADMIN_ENTRY);
admin.saveToXml(serializer);
@@ -193,15 +219,15 @@ final class PolicyState<V> {
PolicyDefinition<V> policyDefinition = PolicyDefinition.readFromXml(parser);
- V currentResolvedPolicy = policyDefinition.readPolicyValueFromXml(
+ PolicyValue<V> currentResolvedPolicy = policyDefinition.readPolicyValueFromXml(
parser, ATTR_RESOLVED_POLICY);
- LinkedHashMap<EnforcingAdmin, V> policiesSetByAdmins = new LinkedHashMap<>();
+ LinkedHashMap<EnforcingAdmin, PolicyValue<V>> policiesSetByAdmins = new LinkedHashMap<>();
int outerDepth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
String tag = parser.getName();
if (TAG_ADMIN_POLICY_ENTRY.equals(tag)) {
- V value = policyDefinition.readPolicyValueFromXml(
+ PolicyValue<V> value = policyDefinition.readPolicyValueFromXml(
parser, ATTR_POLICY_VALUE);
EnforcingAdmin admin;
int adminPolicyDepth = parser.getDepth();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ResolutionMechanism.java b/services/devicepolicy/java/com/android/server/devicepolicy/ResolutionMechanism.java
index 7b720bcd12e6..c321aa1ef89e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ResolutionMechanism.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ResolutionMechanism.java
@@ -17,10 +17,12 @@
package com.android.server.devicepolicy;
import android.annotation.Nullable;
+import android.app.admin.PolicyValue;
import java.util.LinkedHashMap;
abstract class ResolutionMechanism<V> {
@Nullable
- abstract V resolve(LinkedHashMap<EnforcingAdmin, V> adminPolicies);
+ abstract PolicyValue<V> resolve(LinkedHashMap<EnforcingAdmin, PolicyValue<V>> adminPolicies);
+ abstract android.app.admin.ResolutionMechanism<V> getParcelableResolutionMechanism();
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/StringSetPolicySerializer.java b/services/devicepolicy/java/com/android/server/devicepolicy/StringSetPolicySerializer.java
new file mode 100644
index 000000000000..dc6592d73116
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/StringSetPolicySerializer.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.devicepolicy;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.admin.PolicyKey;
+import android.app.admin.PolicyValue;
+import android.app.admin.StringSetPolicyValue;
+import android.util.Log;
+
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+
+import java.io.IOException;
+import java.util.Objects;
+import java.util.Set;
+
+// TODO(scottjonathan): Replace with generic set implementation
+final class StringSetPolicySerializer extends PolicySerializer<Set<String>> {
+ private static final String ATTR_VALUES = ":strings";
+ private static final String ATTR_VALUES_SEPARATOR = ";";
+
+ @Override
+ void saveToXml(PolicyKey policyKey, TypedXmlSerializer serializer, String attributeNamePrefix,
+ @NonNull Set<String> value) throws IOException {
+ Objects.requireNonNull(value);
+ serializer.attribute(
+ /* namespace= */ null,
+ attributeNamePrefix + ATTR_VALUES,
+ String.join(ATTR_VALUES_SEPARATOR, value));
+ }
+
+ @Nullable
+ @Override
+ PolicyValue<Set<String>> readFromXml(TypedXmlPullParser parser, String attributeNamePrefix) {
+ String valuesStr = parser.getAttributeValue(
+ /* namespace= */ null,
+ attributeNamePrefix + ATTR_VALUES);
+ if (valuesStr == null) {
+ Log.e(DevicePolicyEngine.TAG, "Error parsing StringSet policy value.");
+ return null;
+ }
+ Set<String> values = Set.of(valuesStr.split(ATTR_VALUES_SEPARATOR));
+ return new StringSetPolicyValue(values);
+ }
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/SetUnion.java b/services/devicepolicy/java/com/android/server/devicepolicy/StringSetUnion.java
index cf2698357d2c..5298960892a3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/SetUnion.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/StringSetUnion.java
@@ -17,28 +17,37 @@
package com.android.server.devicepolicy;
import android.annotation.NonNull;
+import android.app.admin.PolicyValue;
+import android.app.admin.StringSetPolicyValue;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Objects;
import java.util.Set;
-final class SetUnion<V> extends ResolutionMechanism<Set<V>> {
+final class StringSetUnion extends ResolutionMechanism<Set<String>> {
@Override
- Set<V> resolve(@NonNull LinkedHashMap<EnforcingAdmin, Set<V>> adminPolicies) {
+ PolicyValue<Set<String>> resolve(
+ @NonNull LinkedHashMap<EnforcingAdmin, PolicyValue<Set<String>>> adminPolicies) {
Objects.requireNonNull(adminPolicies);
if (adminPolicies.isEmpty()) {
return null;
}
- Set<V> unionOfPolicies = new HashSet<>();
- for (Set<V> policy : adminPolicies.values()) {
- unionOfPolicies.addAll(policy);
+ Set<String> unionOfPolicies = new HashSet<>();
+ for (PolicyValue<Set<String>> policy : adminPolicies.values()) {
+ unionOfPolicies.addAll(policy.getValue());
}
- return unionOfPolicies;
+ return new StringSetPolicyValue(unionOfPolicies);
}
@Override
+ android.app.admin.StringSetUnion getParcelableResolutionMechanism() {
+ return new android.app.admin.StringSetUnion();
+ }
+
+
+ @Override
public String toString() {
return "SetUnion {}";
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/TopPriority.java b/services/devicepolicy/java/com/android/server/devicepolicy/TopPriority.java
index 571cf64978d8..839840b33a03 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/TopPriority.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/TopPriority.java
@@ -17,6 +17,7 @@
package com.android.server.devicepolicy;
import android.annotation.NonNull;
+import android.app.admin.PolicyValue;
import java.util.LinkedHashMap;
import java.util.List;
@@ -34,7 +35,7 @@ final class TopPriority<V> extends ResolutionMechanism<V> {
}
@Override
- V resolve(@NonNull LinkedHashMap<EnforcingAdmin, V> adminPolicies) {
+ PolicyValue<V> resolve(@NonNull LinkedHashMap<EnforcingAdmin, PolicyValue<V>> adminPolicies) {
if (adminPolicies.isEmpty()) {
return null;
}
@@ -46,11 +47,17 @@ final class TopPriority<V> extends ResolutionMechanism<V> {
}
}
// Return first set policy if no known authority is found
- Map.Entry<EnforcingAdmin, V> policy = adminPolicies.entrySet().stream().findFirst().get();
+ Map.Entry<EnforcingAdmin, PolicyValue<V>> policy =
+ adminPolicies.entrySet().stream().findFirst().get();
return policy.getValue();
}
@Override
+ android.app.admin.TopPriority<V> getParcelableResolutionMechanism() {
+ return new android.app.admin.TopPriority<>(mHighestToLowestPriorityAuthorities);
+ }
+
+ @Override
public String toString() {
return "TopPriority { mHighestToLowestPriorityAuthorities= "
+ mHighestToLowestPriorityAuthorities + " }";
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 7ed95991642f..81a547290d5e 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -419,6 +419,17 @@ int64_t IncrementalService::elapsedUsSinceMonoTs(uint64_t monoTsUs) {
return nowUs - monoTsUs;
}
+static const char* loadingStateToString(incfs::LoadingState state) {
+ switch (state) {
+ case (incfs::LoadingState::Full):
+ return "Full";
+ case (incfs::LoadingState::MissingBlocks):
+ return "MissingBlocks";
+ default:
+ return "error obtaining loading state";
+ }
+}
+
void IncrementalService::onDump(int fd) {
dprintf(fd, "Incremental is %s\n", incfs::enabled() ? "ENABLED" : "DISABLED");
dprintf(fd, "IncFs features: 0x%x\n", int(mIncFs->features()));
@@ -453,9 +464,13 @@ void IncrementalService::onDump(int fd) {
}
dprintf(fd, " storages (%d): {\n", int(mnt.storages.size()));
for (auto&& [storageId, storage] : mnt.storages) {
- dprintf(fd, " [%d] -> [%s] (%d %% loaded) \n", storageId, storage.name.c_str(),
+ auto&& ifs = getIfsLocked(storageId);
+ dprintf(fd, " [%d] -> [%s] (%d %% loaded)(%s) \n", storageId,
+ storage.name.c_str(),
(int)(getLoadingProgressFromPath(mnt, storage.name.c_str()).getProgress() *
- 100));
+ 100),
+ ifs ? loadingStateToString(mIncFs->isEverythingFullyLoaded(ifs->control))
+ : "error obtaining ifs");
}
dprintf(fd, " }\n");
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 271a3d3779b2..b117cae5c97b 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1793,6 +1793,10 @@ public final class SystemServer implements Dumpable {
}
t.traceEnd();
+ t.traceBegin("ArtManagerLocal");
+ DexOptHelper.initializeArtManagerLocal(context, mPackageManagerService);
+ t.traceEnd();
+
t.traceBegin("UpdatePackagesIfNeeded");
try {
Watchdog.getInstance().pauseWatchingCurrentThread("dexopt");
@@ -2766,10 +2770,6 @@ public final class SystemServer implements Dumpable {
mSystemServiceManager.startService(PermissionPolicyService.class);
t.traceEnd();
- t.traceBegin("ArtManagerLocal");
- DexOptHelper.initializeArtManagerLocal(context, mPackageManagerService);
- t.traceEnd();
-
t.traceBegin("MakePackageManagerServiceReady");
mPackageManagerService.systemReady();
t.traceEnd();
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
index 0e9c171b7f42..5ebc901fae66 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -55,6 +55,8 @@ public final class DisplayDeviceConfigTest {
private static final int DEFAULT_REFRESH_RATE = 120;
private static final int DEFAULT_HIGH_BLOCKING_ZONE_REFRESH_RATE = 55;
private static final int DEFAULT_LOW_BLOCKING_ZONE_REFRESH_RATE = 95;
+ private static final int DEFAULT_REFRESH_RATE_IN_HBM_HDR = 90;
+ private static final int DEFAULT_REFRESH_RATE_IN_HBM_SUNLIGHT = 100;
private static final int[] LOW_BRIGHTNESS_THRESHOLD_OF_PEAK_REFRESH_RATE = new int[]{10, 30};
private static final int[] LOW_AMBIENT_THRESHOLD_OF_PEAK_REFRESH_RATE = new int[]{1, 21};
private static final int[] HIGH_BRIGHTNESS_THRESHOLD_OF_PEAK_REFRESH_RATE = new int[]{160};
@@ -295,9 +297,11 @@ public final class DisplayDeviceConfigTest {
DEFAULT_HIGH_BLOCKING_ZONE_REFRESH_RATE);
assertEquals(mDisplayDeviceConfig.getDefaultPeakRefreshRate(), DEFAULT_PEAK_REFRESH_RATE);
assertEquals(mDisplayDeviceConfig.getDefaultRefreshRate(), DEFAULT_REFRESH_RATE);
-
assertEquals(0, mDisplayDeviceConfig.getRefreshRangeProfiles().size());
-
+ assertEquals(mDisplayDeviceConfig.getDefaultRefreshRateInHbmSunlight(),
+ DEFAULT_REFRESH_RATE_IN_HBM_SUNLIGHT);
+ assertEquals(mDisplayDeviceConfig.getDefaultRefreshRateInHbmHdr(),
+ DEFAULT_REFRESH_RATE_IN_HBM_HDR);
assertArrayEquals(mDisplayDeviceConfig.getLowDisplayBrightnessThresholds(),
LOW_BRIGHTNESS_THRESHOLD_OF_PEAK_REFRESH_RATE);
assertArrayEquals(mDisplayDeviceConfig.getLowAmbientBrightnessThresholds(),
@@ -694,6 +698,12 @@ public final class DisplayDeviceConfigTest {
when(mResources.getIntArray(
R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate))
.thenReturn(HIGH_AMBIENT_THRESHOLD_OF_PEAK_REFRESH_RATE);
+ when(mResources.getInteger(
+ R.integer.config_defaultRefreshRateInHbmHdr))
+ .thenReturn(DEFAULT_REFRESH_RATE_IN_HBM_HDR);
+ when(mResources.getInteger(
+ R.integer.config_defaultRefreshRateInHbmSunlight))
+ .thenReturn(DEFAULT_REFRESH_RATE_IN_HBM_SUNLIGHT);
mDisplayDeviceConfig = DisplayDeviceConfig.create(mContext, true);
}
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index 0eff0da0ed3e..7f6385b2080f 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -2264,6 +2264,10 @@ public class DisplayModeDirectorTest {
.thenReturn(65);
when(resources.getInteger(R.integer.config_defaultRefreshRateInZone))
.thenReturn(85);
+ when(resources.getInteger(R.integer.config_defaultRefreshRateInHbmHdr))
+ .thenReturn(95);
+ when(resources.getInteger(R.integer.config_defaultRefreshRateInHbmSunlight))
+ .thenReturn(100);
when(resources.getIntArray(R.array.config_brightnessThresholdsOfPeakRefreshRate))
.thenReturn(new int[]{5});
when(resources.getIntArray(R.array.config_ambientThresholdsOfPeakRefreshRate))
@@ -2274,8 +2278,21 @@ public class DisplayModeDirectorTest {
when(
resources.getIntArray(R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate))
.thenReturn(new int[]{7000});
+ when(resources.getInteger(
+ com.android.internal.R.integer.config_displayWhiteBalanceBrightnessFilterHorizon))
+ .thenReturn(3);
+ ArgumentCaptor<TypedValue> valueArgumentCaptor = ArgumentCaptor.forClass(TypedValue.class);
+ doAnswer((Answer<Void>) invocation -> {
+ valueArgumentCaptor.getValue().type = 4;
+ valueArgumentCaptor.getValue().data = 13;
+ return null;
+ }).when(resources).getValue(eq(com.android.internal.R.dimen
+ .config_displayWhiteBalanceBrightnessFilterIntercept),
+ valueArgumentCaptor.capture(), eq(true));
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[]{60.0f, 90.0f}, 0);
+ SensorManager sensorManager = createMockSensorManager(createLightSensor());
+ director.start(sensorManager);
// We don't expect any interaction with DeviceConfig when the director is initialized
// because we explicitly avoid doing this as this can lead to a latency spike in the
// startup of DisplayManagerService
@@ -2285,6 +2302,8 @@ public class DisplayModeDirectorTest {
0.0);
assertEquals(director.getBrightnessObserver().getRefreshRateInHighZone(), 65);
assertEquals(director.getBrightnessObserver().getRefreshRateInLowZone(), 85);
+ assertEquals(director.getHbmObserver().getRefreshRateInHbmHdr(), 95);
+ assertEquals(director.getHbmObserver().getRefreshRateInHbmSunlight(), 100);
assertArrayEquals(director.getBrightnessObserver().getHighDisplayBrightnessThreshold(),
new int[]{250});
assertArrayEquals(director.getBrightnessObserver().getHighAmbientBrightnessThreshold(),
@@ -2294,6 +2313,7 @@ public class DisplayModeDirectorTest {
assertArrayEquals(director.getBrightnessObserver().getLowAmbientBrightnessThreshold(),
new int[]{10});
+
// Notify that the default display is updated, such that DisplayDeviceConfig has new values
DisplayDeviceConfig displayDeviceConfig = mock(DisplayDeviceConfig.class);
when(displayDeviceConfig.getDefaultLowBlockingZoneRefreshRate()).thenReturn(50);
@@ -2304,6 +2324,8 @@ public class DisplayModeDirectorTest {
when(displayDeviceConfig.getLowAmbientBrightnessThresholds()).thenReturn(new int[]{30});
when(displayDeviceConfig.getHighDisplayBrightnessThresholds()).thenReturn(new int[]{210});
when(displayDeviceConfig.getHighAmbientBrightnessThresholds()).thenReturn(new int[]{2100});
+ when(displayDeviceConfig.getDefaultRefreshRateInHbmHdr()).thenReturn(65);
+ when(displayDeviceConfig.getDefaultRefreshRateInHbmSunlight()).thenReturn(75);
director.defaultDisplayDeviceUpdated(displayDeviceConfig);
assertEquals(director.getSettingsObserver().getDefaultRefreshRate(), 60, 0.0);
@@ -2319,6 +2341,8 @@ public class DisplayModeDirectorTest {
new int[]{25});
assertArrayEquals(director.getBrightnessObserver().getLowAmbientBrightnessThreshold(),
new int[]{30});
+ assertEquals(director.getHbmObserver().getRefreshRateInHbmHdr(), 65);
+ assertEquals(director.getHbmObserver().getRefreshRateInHbmSunlight(), 75);
// Notify that the default display is updated, such that DeviceConfig has new values
FakeDeviceConfig config = mInjector.getDeviceConfig();
@@ -2329,7 +2353,8 @@ public class DisplayModeDirectorTest {
config.setLowDisplayBrightnessThresholds(new int[]{10});
config.setHighDisplayBrightnessThresholds(new int[]{255});
config.setHighAmbientBrightnessThresholds(new int[]{8000});
-
+ config.setRefreshRateInHbmHdr(70);
+ config.setRefreshRateInHbmSunlight(80);
director.defaultDisplayDeviceUpdated(displayDeviceConfig);
assertEquals(director.getSettingsObserver().getDefaultRefreshRate(), 60, 0.0);
@@ -2345,6 +2370,8 @@ public class DisplayModeDirectorTest {
new int[]{10});
assertArrayEquals(director.getBrightnessObserver().getLowAmbientBrightnessThreshold(),
new int[]{20});
+ assertEquals(director.getHbmObserver().getRefreshRateInHbmHdr(), 70);
+ assertEquals(director.getHbmObserver().getRefreshRateInHbmSunlight(), 80);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
index 92fddc76343d..d999aa315940 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
@@ -22,6 +22,7 @@ import static android.content.pm.UserInfo.FLAG_EPHEMERAL;
import static android.content.pm.UserInfo.FLAG_FULL;
import static android.content.pm.UserInfo.FLAG_GUEST;
import static android.content.pm.UserInfo.FLAG_INITIALIZED;
+import static android.content.pm.UserInfo.FLAG_MAIN;
import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE;
import static android.content.pm.UserInfo.FLAG_PROFILE;
import static android.content.pm.UserInfo.FLAG_RESTRICTED;
@@ -206,6 +207,13 @@ public class UserManagerServiceUserInfoTest {
assertFalse("Switching to a profiles should be disabled", userInfo.supportsSwitchTo());
}
+ /** Test UserInfo.canHaveProfile for main user */
+ @Test
+ public void testCanHaveProfile() throws Exception {
+ UserInfo userInfo = createUser(100, FLAG_MAIN, null);
+ assertTrue("Main users can have profile", userInfo.canHaveProfile());
+ }
+
/** Tests upgradeIfNecessaryLP (but without locking) for upgrading from version 8 to 9+. */
@Test
public void testUpgradeIfNecessaryLP_9() {
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java b/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java
index 2ac8b3700043..6edef75b645d 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java
@@ -263,7 +263,8 @@ public class InputDeviceDelegateTest {
public void vibrateIfAvailable_withNoInputDevice_returnsFalse() {
assertFalse(mInputDeviceDelegate.isAvailable());
assertFalse(mInputDeviceDelegate.vibrateIfAvailable(
- UID, PACKAGE_NAME, SYNCED_EFFECT, REASON, VIBRATION_ATTRIBUTES));
+ new Vibration.CallerInfo(VIBRATION_ATTRIBUTES, UID, -1, PACKAGE_NAME, REASON),
+ SYNCED_EFFECT));
}
@Test
@@ -277,7 +278,8 @@ public class InputDeviceDelegateTest {
mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ true);
assertTrue(mInputDeviceDelegate.vibrateIfAvailable(
- UID, PACKAGE_NAME, SYNCED_EFFECT, REASON, VIBRATION_ATTRIBUTES));
+ new Vibration.CallerInfo(VIBRATION_ATTRIBUTES, UID, -1, PACKAGE_NAME, REASON),
+ SYNCED_EFFECT));
verify(mIInputManagerMock).vibrateCombined(eq(1), same(SYNCED_EFFECT), any());
verify(mIInputManagerMock).vibrateCombined(eq(2), same(SYNCED_EFFECT), any());
}
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
index 508e7b0f5918..d50aca94e06b 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -564,17 +564,17 @@ public class VibrationSettingsTest {
for (int usage : ALL_USAGES) {
// Non-system vibration
- assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff(
- UID, "some.app", usage, vibrateStartTime));
+ assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff(createCallerInfo(
+ UID, "some.app", usage), vibrateStartTime));
// Vibration with UID zero
assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff(
- /* uid= */ 0, "", usage, vibrateStartTime));
+ createCallerInfo(/* uid= */ 0, "", usage), vibrateStartTime));
// System vibration
assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff(
- Process.SYSTEM_UID, "", usage, vibrateStartTime));
+ createCallerInfo(Process.SYSTEM_UID, "", usage), vibrateStartTime));
// SysUI vibration
assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff(
- UID, SYSUI_PACKAGE_NAME, usage, vibrateStartTime));
+ createCallerInfo(UID, SYSUI_PACKAGE_NAME, usage), vibrateStartTime));
}
}
@@ -592,16 +592,16 @@ public class VibrationSettingsTest {
for (int usage : ALL_USAGES) {
// Non-system vibration
assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff(
- UID, "some.app", usage, vibrateStartTime));
+ createCallerInfo(UID, "some.app", usage), vibrateStartTime));
// Vibration with UID zero
assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff(
- /* uid= */ 0, "", usage, vibrateStartTime));
+ createCallerInfo(/* uid= */ 0, "", usage), vibrateStartTime));
// System vibration
assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff(
- Process.SYSTEM_UID, "", usage, vibrateStartTime));
+ createCallerInfo(Process.SYSTEM_UID, "", usage), vibrateStartTime));
// SysUI vibration
assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff(
- UID, SYSUI_PACKAGE_NAME, usage, vibrateStartTime));
+ createCallerInfo(UID, SYSUI_PACKAGE_NAME, usage), vibrateStartTime));
}
}
}
@@ -613,7 +613,7 @@ public class VibrationSettingsTest {
for (int usage : ALL_USAGES) {
assertTrue(mVibrationSettings.shouldCancelVibrationOnScreenOff(
- UID, "some.app", usage, vibrateStartTime));
+ createCallerInfo(UID, "some.app", usage), vibrateStartTime));
}
}
@@ -626,10 +626,10 @@ public class VibrationSettingsTest {
if (usage == USAGE_TOUCH || usage == USAGE_HARDWARE_FEEDBACK
|| usage == USAGE_PHYSICAL_EMULATION) {
assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff(
- /* uid= */ 0, "", usage, vibrateStartTime));
+ createCallerInfo(/* uid= */ 0, "", usage), vibrateStartTime));
} else {
assertTrue(mVibrationSettings.shouldCancelVibrationOnScreenOff(
- /* uid= */ 0, "", usage, vibrateStartTime));
+ createCallerInfo(/* uid= */ 0, "", usage), vibrateStartTime));
}
}
}
@@ -643,10 +643,10 @@ public class VibrationSettingsTest {
if (usage == USAGE_TOUCH || usage == USAGE_HARDWARE_FEEDBACK
|| usage == USAGE_PHYSICAL_EMULATION) {
assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff(
- Process.SYSTEM_UID, "", usage, vibrateStartTime));
+ createCallerInfo(Process.SYSTEM_UID, "", usage), vibrateStartTime));
} else {
assertTrue(mVibrationSettings.shouldCancelVibrationOnScreenOff(
- Process.SYSTEM_UID, "", usage, vibrateStartTime));
+ createCallerInfo(Process.SYSTEM_UID, "", usage), vibrateStartTime));
}
}
}
@@ -660,10 +660,10 @@ public class VibrationSettingsTest {
if (usage == USAGE_TOUCH || usage == USAGE_HARDWARE_FEEDBACK
|| usage == USAGE_PHYSICAL_EMULATION) {
assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff(
- UID, SYSUI_PACKAGE_NAME, usage, vibrateStartTime));
+ createCallerInfo(UID, SYSUI_PACKAGE_NAME, usage), vibrateStartTime));
} else {
assertTrue(mVibrationSettings.shouldCancelVibrationOnScreenOff(
- UID, SYSUI_PACKAGE_NAME, usage, vibrateStartTime));
+ createCallerInfo(UID, SYSUI_PACKAGE_NAME, usage), vibrateStartTime));
}
}
}
@@ -755,10 +755,10 @@ public class VibrationSettingsTest {
private void assertVibrationIgnoredForUsageAndDisplay(@VibrationAttributes.Usage int usage,
int displayId, Vibration.Status expectedStatus) {
- assertEquals(errorMessageForUsage(usage),
- expectedStatus,
- mVibrationSettings.shouldIgnoreVibration(UID, displayId,
- VibrationAttributes.createForUsage(usage)));
+ Vibration.CallerInfo callerInfo = new Vibration.CallerInfo(
+ VibrationAttributes.createForUsage(usage), UID, displayId, null, null);
+ assertEquals(errorMessageForUsage(usage), expectedStatus,
+ mVibrationSettings.shouldIgnoreVibration(callerInfo));
}
private void assertVibrationNotIgnoredForUsage(@VibrationAttributes.Usage int usage) {
@@ -767,24 +767,22 @@ public class VibrationSettingsTest {
private void assertVibrationNotIgnoredForUsageAndFlags(@VibrationAttributes.Usage int usage,
@VibrationAttributes.Flag int flags) {
- assertVibrationNotIgnoredForUsageAndFlagsAndDidsplay(usage, Display.DEFAULT_DISPLAY, flags);
+ assertVibrationNotIgnoredForUsageAndFlagsAndDisplay(usage, Display.DEFAULT_DISPLAY, flags);
}
private void assertVibrationNotIgnoredForUsageAndDisplay(@VibrationAttributes.Usage int usage,
int displayId) {
- assertVibrationNotIgnoredForUsageAndFlagsAndDidsplay(usage, displayId, /* flags= */ 0);
+ assertVibrationNotIgnoredForUsageAndFlagsAndDisplay(usage, displayId, /* flags= */ 0);
}
- private void assertVibrationNotIgnoredForUsageAndFlagsAndDidsplay(
+ private void assertVibrationNotIgnoredForUsageAndFlagsAndDisplay(
@VibrationAttributes.Usage int usage, int displayId,
@VibrationAttributes.Flag int flags) {
+ Vibration.CallerInfo callerInfo = new Vibration.CallerInfo(
+ new VibrationAttributes.Builder().setUsage(usage).setFlags(flags).build(), UID,
+ displayId, null, null);
assertNull(errorMessageForUsage(usage),
- mVibrationSettings.shouldIgnoreVibration(UID,
- displayId,
- new VibrationAttributes.Builder()
- .setUsage(usage)
- .setFlags(flags)
- .build()));
+ mVibrationSettings.shouldIgnoreVibration(callerInfo));
}
@@ -826,4 +824,10 @@ public class VibrationSettingsTest {
when(mPowerManagerInternalMock.getLastGoToSleep()).thenReturn(
new PowerManager.SleepData(sleepTime, reason));
}
+
+ private Vibration.CallerInfo createCallerInfo(int uid, String opPkg,
+ @VibrationAttributes.Usage int usage) {
+ VibrationAttributes attrs = VibrationAttributes.createForUsage(usage);
+ return new Vibration.CallerInfo(attrs, uid, VIRTUAL_DISPLAY_ID, opPkg, null);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
index 9facb4bc87eb..12810bbd95e2 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -154,10 +154,10 @@ public class VibrationThreadTest {
@Test
public void vibrate_noVibrator_ignoresVibration() {
mVibratorProviders.clear();
- long vibrationId = 1;
CombinedVibration effect = CombinedVibration.createParallel(
VibrationEffect.get(VibrationEffect.EFFECT_CLICK));
- startThreadAndDispatcher(vibrationId, effect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(effect);
+ long vibrationId = conductor.getVibration().id;
waitForCompletion();
verify(mControllerCallbacks, never()).onComplete(anyInt(), eq(vibrationId));
@@ -166,12 +166,12 @@ public class VibrationThreadTest {
@Test
public void vibrate_missingVibrators_ignoresVibration() {
- long vibrationId = 1;
CombinedVibration effect = CombinedVibration.startSequential()
.addNext(2, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
.addNext(3, VibrationEffect.get(VibrationEffect.EFFECT_TICK))
.combine();
- startThreadAndDispatcher(vibrationId, effect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(effect);
+ long vibrationId = conductor.getVibration().id;
waitForCompletion();
verify(mControllerCallbacks, never()).onComplete(anyInt(), eq(vibrationId));
@@ -182,9 +182,9 @@ public class VibrationThreadTest {
public void vibrate_singleVibratorOneShot_runsVibrationAndSetsAmplitude() throws Exception {
mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
- long vibrationId = 1;
VibrationEffect effect = VibrationEffect.createOneShot(10, 100);
- startThreadAndDispatcher(vibrationId, effect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(effect);
+ long vibrationId = conductor.getVibration().id;
waitForCompletion();
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L));
@@ -201,9 +201,9 @@ public class VibrationThreadTest {
@Test
public void vibrate_oneShotWithoutAmplitudeControl_runsVibrationWithDefaultAmplitude()
throws Exception {
- long vibrationId = 1;
VibrationEffect effect = VibrationEffect.createOneShot(10, 100);
- startThreadAndDispatcher(vibrationId, effect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(effect);
+ long vibrationId = conductor.getVibration().id;
waitForCompletion();
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L));
@@ -222,10 +222,10 @@ public class VibrationThreadTest {
throws Exception {
mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
- long vibrationId = 1;
VibrationEffect effect = VibrationEffect.createWaveform(
new long[]{5, 5, 5}, new int[]{1, 2, 3}, -1);
- startThreadAndDispatcher(vibrationId, effect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(effect);
+ long vibrationId = conductor.getVibration().id;
waitForCompletion();
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(15L));
@@ -246,10 +246,10 @@ public class VibrationThreadTest {
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
- long vibrationId = 1;
int[] amplitudes = new int[]{1, 2, 3};
VibrationEffect effect = VibrationEffect.createWaveform(new long[]{5, 5, 5}, amplitudes, 0);
- VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, effect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(effect);
+ long vibrationId = conductor.getVibration().id;
assertTrue(
waitUntil(() -> fakeVibrator.getAmplitudes().size() > 2 * amplitudes.length,
@@ -259,8 +259,9 @@ public class VibrationThreadTest {
assertTrue(mControllers.get(VIBRATOR_ID).isVibrating());
Vibration.EndInfo cancelVibrationInfo = new Vibration.EndInfo(
- Vibration.Status.CANCELLED_SUPERSEDED, /* endedByUid= */ 1,
- /* endedByUsage= */ VibrationAttributes.USAGE_ALARM);
+ Vibration.Status.CANCELLED_SUPERSEDED, new Vibration.CallerInfo(
+ VibrationAttributes.createForUsage(VibrationAttributes.USAGE_ALARM), /* uid= */
+ 1, /* displayId= */ -1, /* opPkg= */ null, /* reason= */ null));
conductor.notifyCancelled(
cancelVibrationInfo,
/* immediate= */ false);
@@ -287,11 +288,11 @@ public class VibrationThreadTest {
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
- long vibrationId = 1;
int[] amplitudes = new int[]{1, 2, 3};
VibrationEffect effect = VibrationEffect.createWaveform(
new long[]{1, 10, 100}, amplitudes, 0);
- VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, effect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(effect);
+ long vibrationId = conductor.getVibration().id;
assertTrue(waitUntil(() -> !fakeVibrator.getAmplitudes().isEmpty(), TEST_TIMEOUT_MILLIS));
conductor.notifyCancelled(
@@ -315,7 +316,6 @@ public class VibrationThreadTest {
fakeVibrator.setMaxAmplitudes(1, 1, 1);
fakeVibrator.setPwleSizeMax(10);
- long vibrationId = 1;
VibrationEffect effect = VibrationEffect.startWaveform(targetAmplitude(1))
// Very long segment so thread will be cancelled after first PWLE is triggered.
.addTransition(Duration.ofMillis(100), targetFrequency(100))
@@ -323,7 +323,8 @@ public class VibrationThreadTest {
VibrationEffect repeatingEffect = VibrationEffect.startComposition()
.repeatEffectIndefinitely(effect)
.compose();
- VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, repeatingEffect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(repeatingEffect);
+ long vibrationId = conductor.getVibration().id;
assertTrue(waitUntil(() -> !fakeVibrator.getEffectSegments(vibrationId).isEmpty(),
TEST_TIMEOUT_MILLIS));
@@ -346,7 +347,6 @@ public class VibrationThreadTest {
fakeVibrator.setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK);
fakeVibrator.setCompositionSizeMax(10);
- long vibrationId = 1;
VibrationEffect effect = VibrationEffect.startComposition()
// Very long delay so thread will be cancelled after first PWLE is triggered.
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 100)
@@ -354,7 +354,8 @@ public class VibrationThreadTest {
VibrationEffect repeatingEffect = VibrationEffect.startComposition()
.repeatEffectIndefinitely(effect)
.compose();
- VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, repeatingEffect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(repeatingEffect);
+ long vibrationId = conductor.getVibration().id;
assertTrue(waitUntil(() -> !fakeVibrator.getEffectSegments(vibrationId).isEmpty(),
TEST_TIMEOUT_MILLIS));
@@ -375,11 +376,11 @@ public class VibrationThreadTest {
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
- long vibrationId = 1;
int[] amplitudes = new int[]{1, 2, 3};
VibrationEffect effect = VibrationEffect.createWaveform(
new long[]{5000, 500, 50}, amplitudes, 0);
- VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, effect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(effect);
+ long vibrationId = conductor.getVibration().id;
assertTrue(waitUntil(() -> !fakeVibrator.getAmplitudes().isEmpty(), TEST_TIMEOUT_MILLIS));
conductor.notifyCancelled(
@@ -400,11 +401,11 @@ public class VibrationThreadTest {
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
- long vibrationId = 1;
int[] amplitudes = new int[]{1, 2};
VibrationEffect effect = VibrationEffect.createWaveform(
new long[]{4900, 50}, amplitudes, 0);
- VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, effect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(effect);
+ long vibrationId = conductor.getVibration().id;
assertTrue(waitUntil(() -> fakeVibrator.getEffectSegments(vibrationId).size() > 1,
5000 + TEST_TIMEOUT_MILLIS));
@@ -433,13 +434,13 @@ public class VibrationThreadTest {
mVibratorProviders.get(VIBRATOR_ID).setSupportedPrimitives(
VibrationEffect.Composition.PRIMITIVE_CLICK);
- long vibrationId = 1;
VibrationEffect effect = VibrationEffect.startComposition()
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 100)
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 100)
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 100)
.compose();
- VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, effect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(effect);
+ long vibrationId = conductor.getVibration().id;
assertTrue(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(),
TEST_TIMEOUT_MILLIS));
@@ -466,9 +467,9 @@ public class VibrationThreadTest {
throws Exception {
mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
- long vibrationId = 1;
VibrationEffect effect = VibrationEffect.createWaveform(new long[]{100}, new int[]{100}, 0);
- VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, effect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(effect);
+ long vibrationId = conductor.getVibration().id;
assertTrue(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(),
TEST_TIMEOUT_MILLIS));
@@ -494,9 +495,9 @@ public class VibrationThreadTest {
public void vibrate_singleVibratorPrebaked_runsVibration() throws Exception {
mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_THUD);
- long vibrationId = 1;
VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_THUD);
- startThreadAndDispatcher(vibrationId, effect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(effect);
+ long vibrationId = conductor.getVibration().id;
waitForCompletion();
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L));
@@ -514,12 +515,12 @@ public class VibrationThreadTest {
throws Exception {
mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
- long vibrationId = 1;
VibrationEffect fallback = VibrationEffect.createOneShot(10, 100);
- HalVibration vibration = createVibration(vibrationId, CombinedVibration.createParallel(
+ HalVibration vibration = createVibration(CombinedVibration.createParallel(
VibrationEffect.get(VibrationEffect.EFFECT_CLICK)));
vibration.addFallback(VibrationEffect.EFFECT_CLICK, fallback);
- startThreadAndDispatcher(vibration);
+ VibrationStepConductor conductor = startThreadAndDispatcher(vibration);
+ long vibrationId = conductor.getVibration().id;
waitForCompletion();
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L));
@@ -536,9 +537,9 @@ public class VibrationThreadTest {
@Test
public void vibrate_singleVibratorPrebakedAndUnsupportedEffect_ignoresVibration()
throws Exception {
- long vibrationId = 1;
VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
- startThreadAndDispatcher(vibrationId, effect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(effect);
+ long vibrationId = conductor.getVibration().id;
waitForCompletion();
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(0L));
@@ -555,12 +556,12 @@ public class VibrationThreadTest {
fakeVibrator.setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK,
VibrationEffect.Composition.PRIMITIVE_TICK);
- long vibrationId = 1;
VibrationEffect effect = VibrationEffect.startComposition()
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f)
.compose();
- startThreadAndDispatcher(vibrationId, effect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(effect);
+ long vibrationId = conductor.getVibration().id;
waitForCompletion();
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(40L));
@@ -576,11 +577,11 @@ public class VibrationThreadTest {
@Test
public void vibrate_singleVibratorComposedAndNoCapability_ignoresVibration() {
- long vibrationId = 1;
VibrationEffect effect = VibrationEffect.startComposition()
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
.compose();
- startThreadAndDispatcher(vibrationId, effect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(effect);
+ long vibrationId = conductor.getVibration().id;
waitForCompletion();
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(0L));
@@ -600,13 +601,13 @@ public class VibrationThreadTest {
VibrationEffect.Composition.PRIMITIVE_SPIN);
fakeVibrator.setCompositionSizeMax(2);
- long vibrationId = 1;
VibrationEffect effect = VibrationEffect.startComposition()
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f)
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_SPIN, 0.8f)
.compose();
- startThreadAndDispatcher(vibrationId, effect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(effect);
+ long vibrationId = conductor.getVibration().id;
waitForCompletion();
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
@@ -630,7 +631,6 @@ public class VibrationThreadTest {
fakeVibrator.setMaxAmplitudes(
0.5f /* 100Hz*/, 1 /* 150Hz */, 0.6f /* 200Hz */);
- long vibrationId = 1;
VibrationEffect effect = VibrationEffect.startComposition()
.addEffect(VibrationEffect.createOneShot(10, 100))
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
@@ -643,7 +643,8 @@ public class VibrationThreadTest {
.build())
.addEffect(VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
.compose();
- startThreadAndDispatcher(vibrationId, effect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(effect);
+ long vibrationId = conductor.getVibration().id;
waitForCompletion();
// Use first duration the vibrator is turned on since we cannot estimate the clicks.
@@ -675,7 +676,6 @@ public class VibrationThreadTest {
VibrationEffect.Composition.PRIMITIVE_TICK);
fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
- long vibrationId = 1;
VibrationEffect fallback = VibrationEffect.createOneShot(10, 100);
VibrationEffect effect = VibrationEffect.startComposition()
.addEffect(VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
@@ -683,9 +683,10 @@ public class VibrationThreadTest {
.addEffect(VibrationEffect.get(VibrationEffect.EFFECT_TICK))
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f)
.compose();
- HalVibration vib = createVibration(vibrationId, CombinedVibration.createParallel(effect));
+ HalVibration vib = createVibration(CombinedVibration.createParallel(effect));
vib.addFallback(VibrationEffect.EFFECT_TICK, fallback);
- startThreadAndDispatcher(vib);
+ VibrationStepConductor conductor = startThreadAndDispatcher(vib);
+ long vibrationId = conductor.getVibration().id;
waitForCompletion();
// Use first duration the vibrator is turned on since we cannot estimate the clicks.
@@ -718,7 +719,6 @@ public class VibrationThreadTest {
fakeVibrator.setMaxAmplitudes(
0.5f /* 100Hz*/, 1 /* 150Hz */, 0.6f /* 200Hz */);
- long vibrationId = 1;
VibrationEffect effect = VibrationEffect.startWaveform(targetAmplitude(1))
.addSustain(Duration.ofMillis(10))
.addTransition(Duration.ofMillis(20), targetAmplitude(0))
@@ -726,7 +726,8 @@ public class VibrationThreadTest {
.addSustain(Duration.ofMillis(30))
.addTransition(Duration.ofMillis(40), targetAmplitude(0.6f), targetFrequency(200))
.build();
- startThreadAndDispatcher(vibrationId, effect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(effect);
+ long vibrationId = conductor.getVibration().id;
waitForCompletion();
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(100L));
@@ -756,7 +757,6 @@ public class VibrationThreadTest {
fakeVibrator.setMaxAmplitudes(1, 1, 1);
fakeVibrator.setPwleSizeMax(3);
- long vibrationId = 1;
VibrationEffect effect = VibrationEffect.startWaveform(targetAmplitude(1))
.addSustain(Duration.ofMillis(10))
.addTransition(Duration.ofMillis(20), targetAmplitude(0))
@@ -768,7 +768,8 @@ public class VibrationThreadTest {
.addTransition(Duration.ofMillis(40), targetAmplitude(0.7f), targetFrequency(200))
.addTransition(Duration.ofMillis(40), targetAmplitude(0.6f), targetFrequency(200))
.build();
- startThreadAndDispatcher(vibrationId, effect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(effect);
+ long vibrationId = conductor.getVibration().id;
waitForCompletion();
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
@@ -784,9 +785,9 @@ public class VibrationThreadTest {
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
- long vibrationId = 1;
VibrationEffect effect = VibrationEffect.createWaveform(new long[]{5}, new int[]{100}, 0);
- VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, effect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(effect);
+ long vibrationId = conductor.getVibration().id;
assertTrue(waitUntil(() -> fakeVibrator.getAmplitudes().size() > 2, TEST_TIMEOUT_MILLIS));
// Vibration still running after 2 cycles.
@@ -804,9 +805,9 @@ public class VibrationThreadTest {
public void vibrate_singleVibrator_skipsSyncedCallbacks() {
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
- long vibrationId = 1;
- startThreadAndDispatcher(vibrationId,
+ VibrationStepConductor conductor = startThreadAndDispatcher(
VibrationEffect.createOneShot(10, 100));
+ long vibrationId = conductor.getVibration().id;
waitForCompletion();
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
@@ -820,12 +821,12 @@ public class VibrationThreadTest {
throws Exception {
mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_TICK);
- long vibrationId = 1;
CombinedVibration effect = CombinedVibration.startParallel()
.addVibrator(VIBRATOR_ID, VibrationEffect.get(VibrationEffect.EFFECT_TICK))
.addVibrator(2, VibrationEffect.get(VibrationEffect.EFFECT_TICK))
.combine();
- startThreadAndDispatcher(vibrationId, effect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(effect);
+ long vibrationId = conductor.getVibration().id;
waitForCompletion();
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L));
@@ -846,10 +847,10 @@ public class VibrationThreadTest {
mVibratorProviders.get(2).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
mVibratorProviders.get(3).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
- long vibrationId = 1;
CombinedVibration effect = CombinedVibration.createParallel(
VibrationEffect.get(VibrationEffect.EFFECT_CLICK));
- startThreadAndDispatcher(vibrationId, effect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(effect);
+ long vibrationId = conductor.getVibration().id;
waitForCompletion();
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L));
@@ -881,7 +882,6 @@ public class VibrationThreadTest {
mVibratorProviders.get(4).setSupportedPrimitives(
VibrationEffect.Composition.PRIMITIVE_CLICK);
- long vibrationId = 1;
VibrationEffect composed = VibrationEffect.startComposition()
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
.compose();
@@ -892,7 +892,8 @@ public class VibrationThreadTest {
new long[]{10, 10}, new int[]{1, 2}, -1))
.addVibrator(4, composed)
.combine();
- startThreadAndDispatcher(vibrationId, effect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(effect);
+ long vibrationId = conductor.getVibration().id;
waitForCompletion();
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L));
@@ -929,7 +930,6 @@ public class VibrationThreadTest {
VibrationEffect.Composition.PRIMITIVE_CLICK);
mVibratorProviders.get(3).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
- long vibrationId = 1;
VibrationEffect composed = VibrationEffect.startComposition()
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
.compose();
@@ -938,7 +938,8 @@ public class VibrationThreadTest {
.addNext(1, VibrationEffect.createOneShot(10, 100), /* delay= */ 50)
.addNext(2, composed, /* delay= */ 50)
.combine();
- startThreadAndDispatcher(vibrationId, effect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(effect);
+ long vibrationId = conductor.getVibration().id;
waitForCompletion();
InOrder controllerVerifier = inOrder(mControllerCallbacks);
@@ -972,7 +973,6 @@ public class VibrationThreadTest {
@Test
public void vibrate_multipleSyncedCallbackTriggered_finishSteps() throws Exception {
int[] vibratorIds = new int[]{1, 2};
- long vibrationId = 1;
mockVibrators(vibratorIds);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
mVibratorProviders.get(1).setSupportedPrimitives(
@@ -981,13 +981,15 @@ public class VibrationThreadTest {
mVibratorProviders.get(2).setSupportedPrimitives(
VibrationEffect.Composition.PRIMITIVE_CLICK);
when(mManagerHooks.prepareSyncedVibration(anyLong(), eq(vibratorIds))).thenReturn(true);
- when(mManagerHooks.triggerSyncedVibration(eq(vibrationId))).thenReturn(true);
VibrationEffect composed = VibrationEffect.startComposition()
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 100)
.compose();
CombinedVibration effect = CombinedVibration.createParallel(composed);
- VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, effect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(effect);
+ long vibrationId = conductor.getVibration().id;
+
+ when(mManagerHooks.triggerSyncedVibration(eq(vibrationId))).thenReturn(true);
assertTrue(waitUntil(
() -> !mVibratorProviders.get(1).getEffectSegments(vibrationId).isEmpty()
@@ -1021,7 +1023,6 @@ public class VibrationThreadTest {
when(mManagerHooks.prepareSyncedVibration(anyLong(), any())).thenReturn(true);
when(mManagerHooks.triggerSyncedVibration(anyLong())).thenReturn(true);
- long vibrationId = 1;
VibrationEffect composed = VibrationEffect.startComposition()
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
.compose();
@@ -1031,7 +1032,8 @@ public class VibrationThreadTest {
.addVibrator(3, VibrationEffect.createWaveform(new long[]{10}, new int[]{100}, -1))
.addVibrator(4, composed)
.combine();
- startThreadAndDispatcher(vibrationId, effect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(effect);
+ long vibrationId = conductor.getVibration().id;
waitForCompletion();
long expectedCap = IVibratorManager.CAP_SYNC
@@ -1055,12 +1057,12 @@ public class VibrationThreadTest {
mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
when(mManagerHooks.prepareSyncedVibration(anyLong(), any())).thenReturn(false);
- long vibrationId = 1;
CombinedVibration effect = CombinedVibration.startParallel()
.addVibrator(1, VibrationEffect.createOneShot(10, 100))
.addVibrator(2, VibrationEffect.createWaveform(new long[]{5}, new int[]{200}, -1))
.combine();
- startThreadAndDispatcher(vibrationId, effect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(effect);
+ long vibrationId = conductor.getVibration().id;
waitForCompletion();
long expectedCap = IVibratorManager.CAP_SYNC | IVibratorManager.CAP_PREPARE_ON;
@@ -1084,12 +1086,12 @@ public class VibrationThreadTest {
when(mManagerHooks.prepareSyncedVibration(anyLong(), any())).thenReturn(true);
when(mManagerHooks.triggerSyncedVibration(anyLong())).thenReturn(false);
- long vibrationId = 1;
CombinedVibration effect = CombinedVibration.startParallel()
.addVibrator(1, VibrationEffect.createOneShot(10, 100))
.addVibrator(2, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
.combine();
- startThreadAndDispatcher(vibrationId, effect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(effect);
+ long vibrationId = conductor.getVibration().id;
waitForCompletion();
long expectedCap = IVibratorManager.CAP_SYNC
@@ -1110,7 +1112,6 @@ public class VibrationThreadTest {
mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
mVibratorProviders.get(3).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
- long vibrationId = 1;
CombinedVibration effect = CombinedVibration.startParallel()
.addVibrator(1, VibrationEffect.createWaveform(
new long[]{5, 10, 10}, new int[]{1, 2, 3}, -1))
@@ -1119,7 +1120,8 @@ public class VibrationThreadTest {
.addVibrator(3, VibrationEffect.createWaveform(
new long[]{60}, new int[]{6}, -1))
.combine();
- startThreadAndDispatcher(vibrationId, effect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(effect);
+ long vibrationId = conductor.getVibration().id;
// All vibrators are turned on in parallel.
assertTrue(waitUntil(
@@ -1169,8 +1171,7 @@ public class VibrationThreadTest {
Arrays.fill(amplitudes, VibrationEffect.DEFAULT_AMPLITUDE);
VibrationEffect effect = VibrationEffect.createWaveform(timings, amplitudes, -1);
- long vibrationId = 1;
- startThreadAndDispatcher(vibrationId, effect);
+ startThreadAndDispatcher(effect);
long startTime = SystemClock.elapsedRealtime();
waitForCompletion(totalDuration + TEST_TIMEOUT_MILLIS);
@@ -1192,9 +1193,9 @@ public class VibrationThreadTest {
long latency = 5_000; // 5s
fakeVibrator.setOnLatency(latency);
- long vibrationId = 1;
VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
- VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, effect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(effect);
+ long vibrationId = conductor.getVibration().id;
assertTrue(waitUntil(() -> !fakeVibrator.getEffectSegments(vibrationId).isEmpty(),
TEST_TIMEOUT_MILLIS));
@@ -1226,7 +1227,6 @@ public class VibrationThreadTest {
mVibratorProviders.get(2).setSupportedPrimitives(
VibrationEffect.Composition.PRIMITIVE_CLICK);
- long vibrationId = 1;
CombinedVibration effect = CombinedVibration.startParallel()
.addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
.addVibrator(2, VibrationEffect.startComposition()
@@ -1235,7 +1235,8 @@ public class VibrationThreadTest {
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 100)
.compose())
.combine();
- VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, effect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(effect);
+ long vibrationId = conductor.getVibration().id;
assertTrue(waitUntil(() -> mControllers.get(2).isVibrating(),
TEST_TIMEOUT_MILLIS));
@@ -1264,13 +1265,13 @@ public class VibrationThreadTest {
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
- long vibrationId = 1;
CombinedVibration effect = CombinedVibration.startParallel()
.addVibrator(1, VibrationEffect.createWaveform(
new long[]{100, 100}, new int[]{1, 2}, 0))
.addVibrator(2, VibrationEffect.createOneShot(100, 100))
.combine();
- VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, effect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(effect);
+ long vibrationId = conductor.getVibration().id;
assertTrue(waitUntil(() -> mControllers.get(1).isVibrating()
&& mControllers.get(2).isVibrating(),
@@ -1296,9 +1297,9 @@ public class VibrationThreadTest {
@Test
public void vibrate_binderDied_cancelsVibration() throws Exception {
- long vibrationId = 1;
VibrationEffect effect = VibrationEffect.createWaveform(new long[]{5}, new int[]{100}, 0);
- VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, effect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(effect);
+ long vibrationId = conductor.getVibration().id;
assertTrue(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(),
TEST_TIMEOUT_MILLIS));
@@ -1320,10 +1321,10 @@ public class VibrationThreadTest {
mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings);
mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
- long vibrationId = 1;
VibrationEffect effect = VibrationEffect.createWaveform(
new long[]{5, 5, 5}, new int[]{60, 120, 240}, -1);
- startThreadAndDispatcher(vibrationId, effect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(effect);
+ long vibrationId = conductor.getVibration().id;
waitForCompletion();
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
@@ -1346,9 +1347,9 @@ public class VibrationThreadTest {
mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings);
mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
- long vibrationId = 1;
VibrationEffect effect = VibrationEffect.createOneShot(10, 200);
- VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, effect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(effect);
+ long vibrationId = conductor.getVibration().id;
// Vibration completed but vibrator not yet released.
verify(mManagerHooks, timeout(TEST_TIMEOUT_MILLIS)).onVibrationCompleted(eq(vibrationId),
@@ -1381,9 +1382,9 @@ public class VibrationThreadTest {
mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings);
mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
- long vibrationId = 1;
VibrationEffect effect = VibrationEffect.createOneShot(10_000, 240);
- VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, effect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(effect);
+ long vibrationId = conductor.getVibration().id;
assertTrue(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(),
TEST_TIMEOUT_MILLIS));
conductor.notifyCancelled(
@@ -1410,9 +1411,9 @@ public class VibrationThreadTest {
mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
mVibratorProviders.get(VIBRATOR_ID).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
- long vibrationId = 1;
VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
- startThreadAndDispatcher(vibrationId, effect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(effect);
+ long vibrationId = conductor.getVibration().id;
waitForCompletion();
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
@@ -1432,11 +1433,11 @@ public class VibrationThreadTest {
mVibratorProviders.get(VIBRATOR_ID).setSupportedPrimitives(
VibrationEffect.Composition.PRIMITIVE_CLICK);
- long vibrationId = 1;
VibrationEffect effect = VibrationEffect.startComposition()
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
.compose();
- startThreadAndDispatcher(vibrationId, effect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(effect);
+ long vibrationId = conductor.getVibration().id;
waitForCompletion();
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
@@ -1461,11 +1462,11 @@ public class VibrationThreadTest {
fakeVibrator.setMaxAmplitudes(1, 1, 1);
fakeVibrator.setPwleSizeMax(2);
- long vibrationId = 1;
VibrationEffect effect = VibrationEffect.startWaveform()
.addTransition(Duration.ofMillis(1), targetAmplitude(1))
.build();
- startThreadAndDispatcher(vibrationId, effect);
+ VibrationStepConductor conductor = startThreadAndDispatcher(effect);
+ long vibrationId = conductor.getVibration().id;
waitForCompletion();
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
@@ -1485,12 +1486,6 @@ public class VibrationThreadTest {
mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL,
IVibrator.CAP_COMPOSE_EFFECTS);
- long vibrationId1 = 1;
- long vibrationId2 = 2;
- long vibrationId3 = 3;
- long vibrationId4 = 4;
- long vibrationId5 = 5;
-
// A simple effect, followed by a repeating effect that gets cancelled, followed by another
// simple effect.
VibrationEffect effect1 = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
@@ -1503,12 +1498,14 @@ public class VibrationThreadTest {
VibrationEffect effect4 = VibrationEffect.createOneShot(8000, 100);
VibrationEffect effect5 = VibrationEffect.createOneShot(20, 222);
- startThreadAndDispatcher(vibrationId1, effect1);
+ VibrationStepConductor conductor1 = startThreadAndDispatcher(effect1);
+ long vibrationId1 = conductor1.getVibration().id;
waitForCompletion();
verify(mControllerCallbacks).onComplete(VIBRATOR_ID, vibrationId1);
verifyCallbacksTriggered(vibrationId1, Vibration.Status.FINISHED);
- VibrationStepConductor conductor2 = startThreadAndDispatcher(vibrationId2, effect2);
+ VibrationStepConductor conductor2 = startThreadAndDispatcher(effect2);
+ long vibrationId2 = conductor2.getVibration().id;
// Effect2 won't complete on its own. Cancel it after a couple of repeats.
Thread.sleep(150); // More than two TICKs.
conductor2.notifyCancelled(
@@ -1516,12 +1513,14 @@ public class VibrationThreadTest {
/* immediate= */ false);
waitForCompletion();
- startThreadAndDispatcher(vibrationId3, effect3);
+ VibrationStepConductor conductor3 = startThreadAndDispatcher(effect3);
+ long vibrationId3 = conductor3.getVibration().id;
waitForCompletion();
// Effect4 is a long oneshot, but it gets cancelled as fast as possible.
long start4 = System.currentTimeMillis();
- VibrationStepConductor conductor4 = startThreadAndDispatcher(vibrationId4, effect4);
+ VibrationStepConductor conductor4 = startThreadAndDispatcher(effect4);
+ long vibrationId4 = conductor4.getVibration().id;
conductor4.notifyCancelled(
new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SCREEN_OFF),
/* immediate= */ true);
@@ -1529,7 +1528,8 @@ public class VibrationThreadTest {
long duration4 = System.currentTimeMillis() - start4;
// Effect5 is to show that things keep going after the immediate cancel.
- startThreadAndDispatcher(vibrationId5, effect5);
+ VibrationStepConductor conductor5 = startThreadAndDispatcher(effect5);
+ long vibrationId5 = conductor5.getVibration().id;
waitForCompletion();
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
@@ -1582,14 +1582,12 @@ public class VibrationThreadTest {
}
}
- private VibrationStepConductor startThreadAndDispatcher(
- long vibrationId, VibrationEffect effect) {
- return startThreadAndDispatcher(vibrationId, CombinedVibration.createParallel(effect));
+ private VibrationStepConductor startThreadAndDispatcher(VibrationEffect effect) {
+ return startThreadAndDispatcher(CombinedVibration.createParallel(effect));
}
- private VibrationStepConductor startThreadAndDispatcher(long vibrationId,
- CombinedVibration effect) {
- return startThreadAndDispatcher(createVibration(vibrationId, effect));
+ private VibrationStepConductor startThreadAndDispatcher(CombinedVibration effect) {
+ return startThreadAndDispatcher(createVibration(effect));
}
private VibrationStepConductor startThreadAndDispatcher(HalVibration vib) {
@@ -1624,9 +1622,9 @@ public class VibrationThreadTest {
mTestLooper.dispatchAll(); // Flush callbacks
}
- private HalVibration createVibration(long id, CombinedVibration effect) {
- return new HalVibration(mVibrationToken, (int) id, effect, ATTRS, UID, DISPLAY_ID,
- PACKAGE_NAME, "reason");
+ private HalVibration createVibration(CombinedVibration effect) {
+ return new HalVibration(mVibrationToken, effect,
+ new Vibration.CallerInfo(ATTRS, UID, DISPLAY_ID, PACKAGE_NAME, "reason"));
}
private SparseArray<VibratorController> createVibratorControllers() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 1ce8c61fa07a..bfd99fd38a4b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -2020,13 +2020,13 @@ public class DisplayContentTests extends WindowTestsBase {
assertEquals(origRot, dc.getConfiguration().windowConfiguration.getRotation());
// Once transition starts, rotation is applied and transition shows DC rotating.
- testPlayer.start();
+ testPlayer.startTransition();
assertNotEquals(origRot, dc.getConfiguration().windowConfiguration.getRotation());
assertNotNull(testPlayer.mLastReady);
+ assertTrue(testPlayer.mController.isPlaying());
WindowContainerToken dcToken = dc.mRemoteToken.toWindowContainerToken();
assertNotEquals(testPlayer.mLastReady.getChange(dcToken).getEndRotation(),
testPlayer.mLastReady.getChange(dcToken).getStartRotation());
- assertTrue(testPlayer.mLastTransit.applyDisplayChangeIfNeeded());
testPlayer.finish();
}