Merge "CDM: increase char limit for nearby_device_streaming"
diff --git a/core/api/current.txt b/core/api/current.txt
index 5f3b1f3..dd3d6eb 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -125,6 +125,18 @@
field public static final String MANAGE_DEVICE_POLICY_ACROSS_USERS = "android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS";
field public static final String MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL = "android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL";
field public static final String MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL = "android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL";
+ field public static final String MANAGE_DEVICE_POLICY_APPS_CONTROL = "android.permission.MANAGE_DEVICE_POLICY_APPS_CONTROL";
+ field public static final String MANAGE_DEVICE_POLICY_APP_RESTRICTIONS = "android.permission.MANAGE_DEVICE_POLICY_APP_RESTRICTIONS";
+ field public static final String MANAGE_DEVICE_POLICY_BACKUP_SERVICE = "android.permission.MANAGE_DEVICE_POLICY_BACKUP_SERVICE";
+ field public static final String MANAGE_DEVICE_POLICY_CALLS = "android.permission.MANAGE_DEVICE_POLICY_CALLS";
+ field public static final String MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES = "android.permission.MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES";
+ field public static final String MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES = "android.permission.MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES";
+ field public static final String MANAGE_DEVICE_POLICY_LOCK_TASK = "android.permission.MANAGE_DEVICE_POLICY_LOCK_TASK";
+ field public static final String MANAGE_DEVICE_POLICY_MODIFY_USERS = "android.permission.MANAGE_DEVICE_POLICY_MODIFY_USERS";
+ field public static final String MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY = "android.permission.MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY";
+ field public static final String MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS = "android.permission.MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS";
+ field public static final String MANAGE_DEVICE_POLICY_SAFE_BOOT = "android.permission.MANAGE_DEVICE_POLICY_SAFE_BOOT";
+ field public static final String MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE = "android.permission.MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE";
field public static final String MANAGE_DEVICE_POLICY_TIME = "android.permission.MANAGE_DEVICE_POLICY_TIME";
field public static final String MANAGE_DOCUMENTS = "android.permission.MANAGE_DOCUMENTS";
field public static final String MANAGE_EXTERNAL_STORAGE = "android.permission.MANAGE_EXTERNAL_STORAGE";
@@ -7535,6 +7547,7 @@
method @Nullable public android.app.admin.PackagePolicy getManagedProfileCallerIdAccessPolicy();
method @Nullable public android.app.admin.PackagePolicy getManagedProfileContactsAccessPolicy();
method public long getManagedProfileMaximumTimeOff(@NonNull android.content.ComponentName);
+ method @NonNull public android.app.admin.ManagedSubscriptionsPolicy getManagedSubscriptionsPolicy();
method public int getMaximumFailedPasswordsForWipe(@Nullable android.content.ComponentName);
method public long getMaximumTimeToLock(@Nullable android.content.ComponentName);
method @NonNull public java.util.List<java.lang.String> getMeteredDataDisabledPackages(@NonNull android.content.ComponentName);
@@ -7685,6 +7698,7 @@
method public void setManagedProfileCallerIdAccessPolicy(@Nullable android.app.admin.PackagePolicy);
method public void setManagedProfileContactsAccessPolicy(@Nullable android.app.admin.PackagePolicy);
method public void setManagedProfileMaximumTimeOff(@NonNull android.content.ComponentName, long);
+ method public void setManagedSubscriptionsPolicy(@Nullable android.app.admin.ManagedSubscriptionsPolicy);
method public void setMasterVolumeMuted(@NonNull android.content.ComponentName, boolean);
method public void setMaximumFailedPasswordsForWipe(@NonNull android.content.ComponentName, int);
method public void setMaximumTimeToLock(@NonNull android.content.ComponentName, long);
@@ -7987,6 +8001,16 @@
method public java.time.MonthDay getStart();
}
+ public final class ManagedSubscriptionsPolicy implements android.os.Parcelable {
+ ctor public ManagedSubscriptionsPolicy(int);
+ method public int describeContents();
+ method public int getPolicyType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.ManagedSubscriptionsPolicy> CREATOR;
+ field public static final int TYPE_ALL_MANAGED_SUBSCRIPTIONS = 1; // 0x1
+ field public static final int TYPE_ALL_PERSONAL_SUBSCRIPTIONS = 0; // 0x0
+ }
+
public abstract class NetworkEvent implements android.os.Parcelable {
method public int describeContents();
method public long getId();
@@ -23773,6 +23797,8 @@
method @NonNull public android.media.MediaRoute2Info.Builder setDescription(@Nullable CharSequence);
method @NonNull public android.media.MediaRoute2Info.Builder setExtras(@Nullable android.os.Bundle);
method @NonNull public android.media.MediaRoute2Info.Builder setIconUri(@Nullable android.net.Uri);
+ method @NonNull public android.media.MediaRoute2Info.Builder setVisibilityPublic();
+ method @NonNull public android.media.MediaRoute2Info.Builder setVisibilityRestricted(@NonNull java.util.Set<java.lang.String>);
method @NonNull public android.media.MediaRoute2Info.Builder setVolume(int);
method @NonNull public android.media.MediaRoute2Info.Builder setVolumeHandling(int);
method @NonNull public android.media.MediaRoute2Info.Builder setVolumeMax(int);
@@ -24314,15 +24340,19 @@
public final class RouteListingPreference implements android.os.Parcelable {
method public int describeContents();
+ method @Nullable public android.content.ComponentName getInAppOnlyItemRoutingReceiver();
method @NonNull public java.util.List<android.media.RouteListingPreference.Item> getItems();
method public boolean getUseSystemOrdering();
method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field public static final String ACTION_TRANSFER_MEDIA = "android.media.action.TRANSFER_MEDIA";
field @NonNull public static final android.os.Parcelable.Creator<android.media.RouteListingPreference> CREATOR;
+ field public static final String EXTRA_ROUTE_ID = "android.media.extra.ROUTE_ID";
}
public static final class RouteListingPreference.Builder {
ctor public RouteListingPreference.Builder();
method @NonNull public android.media.RouteListingPreference build();
+ method @NonNull public android.media.RouteListingPreference.Builder setInAppOnlyItemRoutingReceiver(@Nullable android.content.ComponentName);
method @NonNull public android.media.RouteListingPreference.Builder setItems(@NonNull java.util.List<android.media.RouteListingPreference.Item>);
method @NonNull public android.media.RouteListingPreference.Builder setUseSystemOrdering(boolean);
}
@@ -24337,6 +24367,7 @@
field @NonNull public static final android.os.Parcelable.Creator<android.media.RouteListingPreference.Item> CREATOR;
field public static final int DISABLE_REASON_AD = 3; // 0x3
field public static final int DISABLE_REASON_DOWNLOADED_CONTENT = 2; // 0x2
+ field public static final int DISABLE_REASON_IN_APP_ONLY = 4; // 0x4
field public static final int DISABLE_REASON_NONE = 0; // 0x0
field public static final int DISABLE_REASON_SUBSCRIPTION_REQUIRED = 1; // 0x1
field public static final int FLAG_ONGOING_SESSION = 1; // 0x1
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index de03ba4..484ef4f 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -10997,6 +10997,51 @@
}
/**
+ * Called by a profile owner of an organization-owned device to specify {@link
+ * ManagedSubscriptionsPolicy}
+ *
+ * <p>Managed subscriptions policy controls how SIMs would be associated with the
+ * managed profile. For example a policy of type
+ * {@link ManagedSubscriptionsPolicy#TYPE_ALL_MANAGED_SUBSCRIPTIONS} assigns all
+ * SIM-based subscriptions to the managed profile. In this case OEM default
+ * dialer and messages app are automatically installed in the managed profile
+ * and all incoming and outgoing calls and text messages are handled by them.
+ * <p>This API can only be called during device setup.
+ *
+ * @param policy {@link ManagedSubscriptionsPolicy} policy, passing null for this resets the
+ * policy to be the default.
+ * @throws SecurityException if the caller is not a profile owner on an organization-owned
+ * managed profile.
+ * @throws IllegalStateException if called after the device setup has been completed.
+ * @see ManagedSubscriptionsPolicy
+ */
+ public void setManagedSubscriptionsPolicy(@Nullable ManagedSubscriptionsPolicy policy) {
+ throwIfParentInstance("setManagedSubscriptionsPolicy");
+ try {
+ mService.setManagedSubscriptionsPolicy(policy);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the current {@link ManagedSubscriptionsPolicy}.
+ * If the policy has not been set, it will return a default policy of Type {@link
+ * ManagedSubscriptionsPolicy#TYPE_ALL_PERSONAL_SUBSCRIPTIONS}.
+ *
+ * @see #setManagedSubscriptionsPolicy(ManagedSubscriptionsPolicy)
+ */
+ @NonNull
+ public ManagedSubscriptionsPolicy getManagedSubscriptionsPolicy() {
+ throwIfParentInstance("getManagedSubscriptionsPolicy");
+ try {
+ return mService.getManagedSubscriptionsPolicy();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Similar to {@link #logoutUser(ComponentName)}, except:
*
* <ul>
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 26d7667..20695ca 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -34,6 +34,7 @@
import android.app.admin.FactoryResetProtectionPolicy;
import android.app.admin.ManagedProfileProvisioningParams;
import android.app.admin.FullyManagedDeviceProvisioningParams;
+import android.app.admin.ManagedSubscriptionsPolicy;
import android.app.admin.WifiSsidPolicy;
import android.content.ComponentName;
import android.content.Intent;
@@ -583,4 +584,7 @@
void setMtePolicy(int flag);
int getMtePolicy();
+
+ void setManagedSubscriptionsPolicy(in ManagedSubscriptionsPolicy policy);
+ ManagedSubscriptionsPolicy getManagedSubscriptionsPolicy();
}
diff --git a/core/java/android/app/timedetector/TimePoint.aidl b/core/java/android/app/admin/ManagedSubscriptionsPolicy.aidl
similarity index 74%
rename from core/java/android/app/timedetector/TimePoint.aidl
rename to core/java/android/app/admin/ManagedSubscriptionsPolicy.aidl
index 80d4bc1..f634f11 100644
--- a/core/java/android/app/timedetector/TimePoint.aidl
+++ b/core/java/android/app/admin/ManagedSubscriptionsPolicy.aidl
@@ -1,11 +1,11 @@
/*
- * Copyright (C) 2022, The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * 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,
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.app.timedetector;
+package android.app.admin;
-parcelable TimePoint;
+parcelable ManagedSubscriptionsPolicy;
\ No newline at end of file
diff --git a/core/java/android/app/admin/ManagedSubscriptionsPolicy.java b/core/java/android/app/admin/ManagedSubscriptionsPolicy.java
new file mode 100644
index 0000000..1098c38
--- /dev/null
+++ b/core/java/android/app/admin/ManagedSubscriptionsPolicy.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.admin;
+
+import android.annotation.IntDef;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * A policy class that describes how managed SIM subscriptions should behave on the device.
+ */
+public final class ManagedSubscriptionsPolicy implements Parcelable {
+ private static final String TAG = "ManagedSubscriptionsPolicy";
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"TYPE_"}, value = {TYPE_ALL_PERSONAL_SUBSCRIPTIONS,
+ TYPE_ALL_MANAGED_SUBSCRIPTIONS})
+ @interface ManagedSubscriptionsPolicyType {
+ }
+
+ /**
+ * Represents default policy to not have any managed subscriptions on the device.
+ */
+ public static final int TYPE_ALL_PERSONAL_SUBSCRIPTIONS = 0;
+
+ /**
+ * Represents policy to have only managed subscriptions on the device, any existing and
+ * future subscriptions on the device are exclusively associated with the managed profile.
+ *
+ * <p>When a subscription is associated with the managed profile, incoming/outgoing calls and
+ * text message using that subscription would only work via apps on managed profile.
+ * Also, Call logs and messages would be accessible only from the managed profile.
+ */
+ public static final int TYPE_ALL_MANAGED_SUBSCRIPTIONS = 1;
+
+ @ManagedSubscriptionsPolicyType
+ private final int mPolicyType;
+
+ private static final String KEY_POLICY_TYPE = "policy_type";
+
+ public ManagedSubscriptionsPolicy(@ManagedSubscriptionsPolicyType int policyType) {
+ if (policyType != TYPE_ALL_PERSONAL_SUBSCRIPTIONS
+ && policyType != TYPE_ALL_MANAGED_SUBSCRIPTIONS) {
+ throw new IllegalArgumentException("Invalid policy type");
+ }
+ mPolicyType = policyType;
+ }
+
+ @NonNull
+ public static final Parcelable.Creator<ManagedSubscriptionsPolicy> CREATOR =
+ new Parcelable.Creator<ManagedSubscriptionsPolicy>() {
+ public ManagedSubscriptionsPolicy createFromParcel(Parcel in) {
+ ManagedSubscriptionsPolicy policy = new ManagedSubscriptionsPolicy(
+ in.readInt());
+ return policy;
+ }
+
+ @Override
+ public ManagedSubscriptionsPolicy[] newArray(int size) {
+ return new ManagedSubscriptionsPolicy[size];
+ }
+ };
+
+ /**
+ * Returns the type of managed subscriptions policy, or {@link #TYPE_ALL_PERSONAL_SUBSCRIPTIONS}
+ * if no policy has been set.
+ *
+ * @return The policy type.
+ */
+ @ManagedSubscriptionsPolicyType
+ public int getPolicyType() {
+ return mPolicyType;
+ }
+
+ @Override
+ public String toString() {
+ return TextUtils.formatSimple("ManagedSubscriptionsPolicy (type: %d)", mPolicyType);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mPolicyType);
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (this == thatObject) {
+ return true;
+ }
+ if (!(thatObject instanceof ManagedSubscriptionsPolicy)) {
+ return false;
+ }
+ ManagedSubscriptionsPolicy that = (ManagedSubscriptionsPolicy) thatObject;
+ return mPolicyType == that.mPolicyType;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mPolicyType);
+ }
+
+ /** @hide */
+ @Nullable
+ public static ManagedSubscriptionsPolicy readFromXml(@NonNull TypedXmlPullParser parser) {
+ try {
+ ManagedSubscriptionsPolicy policy = new ManagedSubscriptionsPolicy(
+ parser.getAttributeInt(null, KEY_POLICY_TYPE, -1));
+
+ return policy;
+ } catch (IllegalArgumentException e) {
+ // Fail through
+ Log.w(TAG, "Load xml failed", e);
+ }
+ return null;
+ }
+
+ /**
+ * @hide
+ */
+ public void saveToXml(TypedXmlSerializer out) throws IOException {
+ out.attributeInt(null, KEY_POLICY_TYPE, mPolicyType);
+ }
+}
diff --git a/core/java/android/app/timedetector/ITimeDetectorService.aidl b/core/java/android/app/timedetector/ITimeDetectorService.aidl
index a0c898e..96eddd2 100644
--- a/core/java/android/app/timedetector/ITimeDetectorService.aidl
+++ b/core/java/android/app/timedetector/ITimeDetectorService.aidl
@@ -24,7 +24,6 @@
import android.app.time.UnixEpochTime;
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.TelephonyTimeSuggestion;
-import android.app.timedetector.TimePoint;
/**
* Binder APIs to communicate with the time detector service.
@@ -55,5 +54,5 @@
boolean suggestManualTime(in ManualTimeSuggestion timeSuggestion);
void suggestTelephonyTime(in TelephonyTimeSuggestion timeSuggestion);
- TimePoint latestNetworkTime();
+ UnixEpochTime latestNetworkTime();
}
diff --git a/core/java/android/app/timedetector/TimePoint.java b/core/java/android/app/timedetector/TimePoint.java
deleted file mode 100644
index aa079a9..0000000
--- a/core/java/android/app/timedetector/TimePoint.java
+++ /dev/null
@@ -1,103 +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 android.app.timedetector;
-
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.Objects;
-
-/**
- * Data class for passing a Unix epoch time anchored to the elapsed realtime clock.
- *
- * @hide
- */
-public final class TimePoint implements Parcelable {
-
- private final long mUnixEpochTimeMillis;
- private final long mElapsedRealtimeMillis;
-
- public TimePoint(long unixEpochTimeMillis, long elapsedRealtimeMillis) {
- mUnixEpochTimeMillis = unixEpochTimeMillis;
- mElapsedRealtimeMillis = elapsedRealtimeMillis;
- }
-
- /**
- * The current Unix epoch time, according to the external source.
- */
- public long getUnixEpochTimeMillis() {
- return mUnixEpochTimeMillis;
- }
-
- /**
- * The elapsed millis since boot when {@link #getUnixEpochTimeMillis} was computed.
- */
- public long getElapsedRealtimeMillis() {
- return mElapsedRealtimeMillis;
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeLong(mUnixEpochTimeMillis);
- out.writeLong(mElapsedRealtimeMillis);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (!(o instanceof TimePoint)) {
- return false;
- }
- TimePoint timePoint = (TimePoint) o;
- return mUnixEpochTimeMillis == timePoint.mUnixEpochTimeMillis
- && mElapsedRealtimeMillis == timePoint.mElapsedRealtimeMillis;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mUnixEpochTimeMillis, mElapsedRealtimeMillis);
- }
-
- @Override
- public String toString() {
- return "TimePoint{"
- + "mUnixEpochTimeMillis=" + mUnixEpochTimeMillis
- + ", mElapsedRealtimeMillis=" + mElapsedRealtimeMillis
- + '}';
- }
-
- public static final @NonNull Creator<TimePoint> CREATOR =
- new Creator<TimePoint>() {
- public TimePoint createFromParcel(Parcel in) {
- long unixEpochTime = in.readLong();
- long elapsedRealtimeMillis = in.readLong();
- return new TimePoint(unixEpochTime, elapsedRealtimeMillis);
- }
-
- public TimePoint[] newArray(int size) {
- return new TimePoint[size];
- }
- };
-}
diff --git a/core/java/android/os/SystemClock.java b/core/java/android/os/SystemClock.java
index 2b75a23..831ca86 100644
--- a/core/java/android/os/SystemClock.java
+++ b/core/java/android/os/SystemClock.java
@@ -18,8 +18,8 @@
import android.annotation.NonNull;
import android.app.IAlarmManager;
+import android.app.time.UnixEpochTime;
import android.app.timedetector.ITimeDetectorService;
-import android.app.timedetector.TimePoint;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.location.ILocationManager;
@@ -300,7 +300,7 @@
ITimeDetectorService timeDetectorService = ITimeDetectorService.Stub
.asInterface(ServiceManager.getService(Context.TIME_DETECTOR_SERVICE));
if (timeDetectorService != null) {
- TimePoint time;
+ UnixEpochTime time;
try {
time = timeDetectorService.latestNetworkTime();
} catch (ParcelableException e) {
diff --git a/core/java/android/window/BackEvent.java b/core/java/android/window/BackEvent.java
index 40c0fee..f53737a 100644
--- a/core/java/android/window/BackEvent.java
+++ b/core/java/android/window/BackEvent.java
@@ -74,6 +74,9 @@
* and animation should seek to its end state. Exact end value may vary depending on
* screen size.
* </ol>
+ * <li> After the gesture finishes in cancel state, this method keeps getting invoked until the
+ * progress value animates back to 0.
+ * </ol>
* In-between locations are linearly interpolated based on horizontal distance from the starting
* edge and smooth clamped to 1 when the distance exceeds a system-wide threshold.
*/
diff --git a/core/java/android/window/BackProgressAnimator.java b/core/java/android/window/BackProgressAnimator.java
index 14a57e0..618670a 100644
--- a/core/java/android/window/BackProgressAnimator.java
+++ b/core/java/android/window/BackProgressAnimator.java
@@ -16,8 +16,10 @@
package android.window;
+import android.annotation.NonNull;
import android.util.FloatProperty;
+import com.android.internal.dynamicanimation.animation.DynamicAnimation;
import com.android.internal.dynamicanimation.animation.SpringAnimation;
import com.android.internal.dynamicanimation.animation.SpringForce;
@@ -126,6 +128,27 @@
mProgress = 0;
}
+ /**
+ * Animate the back progress animation from current progress to start position.
+ * This should be called when back is cancelled.
+ *
+ * @param finishCallback the callback to be invoked when the progress is reach to 0.
+ */
+ public void onBackCancelled(@NonNull Runnable finishCallback) {
+ final DynamicAnimation.OnAnimationEndListener listener =
+ new DynamicAnimation.OnAnimationEndListener() {
+ @Override
+ public void onAnimationEnd(DynamicAnimation animation, boolean canceled, float value,
+ float velocity) {
+ mSpring.removeEndListener(this);
+ finishCallback.run();
+ reset();
+ }
+ };
+ mSpring.addEndListener(listener);
+ mSpring.animateToFinalPosition(0);
+ }
+
private void updateProgressValue(float progress) {
if (mLastBackEvent == null || mCallback == null || !mStarted) {
return;
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index 64992b9..7a5510c 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -266,11 +266,12 @@
@Override
public void onBackCancelled() {
Handler.getMain().post(() -> {
- mProgressAnimator.reset();
- final OnBackAnimationCallback callback = getBackAnimationCallback();
- if (callback != null) {
- callback.onBackCancelled();
- }
+ mProgressAnimator.onBackCancelled(() -> {
+ final OnBackAnimationCallback callback = getBackAnimationCallback();
+ if (callback != null) {
+ callback.onBackCancelled();
+ }
+ });
});
}
diff --git a/core/java/com/android/internal/util/FunctionalUtils.java b/core/java/com/android/internal/util/FunctionalUtils.java
index 91cc4b9..d64a474 100644
--- a/core/java/com/android/internal/util/FunctionalUtils.java
+++ b/core/java/com/android/internal/util/FunctionalUtils.java
@@ -248,6 +248,19 @@
}
/**
+ * A {@link Supplier} that allows the caller to specify a custom checked {@link Exception} that
+ * can be thrown by the implementer. This is usually used when proxying/wrapping calls between
+ * different classes.
+ *
+ * @param <Output> Method return type
+ * @param <ExceptionType> Checked exception type
+ */
+ @FunctionalInterface
+ public interface ThrowingCheckedSupplier<Output, ExceptionType extends Exception> {
+ Output get() throws ExceptionType;
+ }
+
+ /**
* A {@link Consumer} that allows the caller to specify a custom checked {@link Exception} that
* can be thrown by the implementer. This is usually used when proxying/wrapping calls between
* different classes.
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index bfa5301..76fbffd 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3119,12 +3119,99 @@
<!-- @SystemApi @hide Allows an application to exempt apps from platform restrictions.-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_APP_EXEMPTIONS"
- android:protectionLevel="signature|role" />
+ android:protectionLevel="internal|role" />
- <!-- Allows an application to manage date and time device policy. -->
+ <!-- Allows an application to manage device policy relating to time.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+ APIs protected by this permission on users different to the calling user.-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_TIME"
android:protectionLevel="internal|role" />
+ <!-- Allows an application to set the grant state of runtime permissions on packages.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to manage the identity of the managing organization.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set support messages for when a user action is affected by an
+ active policy.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to manage backup service policy.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_BACKUP_SERVICE"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to manage lock task policy.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_LOCK_TASK"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to manage policy regarding modifying applications.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_APPS_CONTROL"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to manage installing from unknown sources policy.
+ <p>MANAGE_SECURITY_CRITICAL_DEVICE_POLICY_ACROSS_USERS is required to call APIs protected
+ by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to manage application restrictions.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_APP_RESTRICTIONS"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to manage calling policy.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_CALLS"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to manage debugging features policy.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to manage policy preventing users from modifying users.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+ APIs protected by this permission on users different to the calling user
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_MODIFY_USERS"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to manage safe boot policy.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SAFE_BOOT"
+ android:protectionLevel="internal|role" />
+
<!-- Allows an application to set device policies outside the current user
that are critical for securing data within the current user.
<p>Holding this permission allows the use of other held MANAGE_DEVICE_POLICY_*
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java
index e36e16c..5d38494 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java
@@ -171,6 +171,7 @@
mInitialTouchPos.set(0, 0);
mEnteringWindowShow = false;
mEnteringMargin = 0;
+ mEnteringAnimator = null;
if (mFinishCallback != null) {
try {
@@ -276,7 +277,7 @@
}
// End the fade in animation.
- if (mEnteringAnimator.isRunning()) {
+ if (mEnteringAnimator != null && mEnteringAnimator.isRunning()) {
mEnteringAnimator.cancel();
}
@@ -329,12 +330,10 @@
@Override
public void onBackCancelled() {
// End the fade in animation.
- if (mEnteringAnimator.isRunning()) {
+ if (mEnteringAnimator != null && mEnteringAnimator.isRunning()) {
mEnteringAnimator.cancel();
}
- // TODO (b259608500): Let BackProgressAnimator could play cancel animation.
- mProgressAnimator.reset();
- finishAnimation();
+ mProgressAnimator.onBackCancelled(CrossActivityAnimation.this::finishAnimation);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java
index 676e259..99a434a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java
@@ -329,8 +329,7 @@
@Override
public void onBackCancelled() {
- mProgressAnimator.reset();
- finishAnimation();
+ mProgressAnimator.onBackCancelled(CrossTaskBackAnimation.this::finishAnimation);
}
@Override
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
new file mode 100644
index 0000000..3608474
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.wm.shell.back;
+
+import static android.window.BackEvent.EDGE_LEFT;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.window.BackEvent;
+import android.window.BackMotionEvent;
+import android.window.BackProgressAnimator;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@SmallTest
+@TestableLooper.RunWithLooper
+@RunWith(AndroidTestingRunner.class)
+public class BackProgressAnimatorTest {
+ private BackProgressAnimator mProgressAnimator;
+ private BackEvent mReceivedBackEvent;
+ private float mTargetProgress = 0.5f;
+ private CountDownLatch mTargetProgressCalled = new CountDownLatch(1);
+ private Handler mMainThreadHandler;
+
+ @Before
+ public void setUp() throws Exception {
+ mMainThreadHandler = new Handler(Looper.getMainLooper());
+ final BackMotionEvent backEvent = new BackMotionEvent(
+ 0, 0,
+ 0, EDGE_LEFT, null);
+ mMainThreadHandler.post(
+ () -> {
+ mProgressAnimator = new BackProgressAnimator();
+ mProgressAnimator.onBackStarted(backEvent, this::onGestureProgress);
+ });
+ }
+
+ @Test
+ public void testBackProgressed() throws InterruptedException {
+ final BackMotionEvent backEvent = new BackMotionEvent(
+ 100, 0,
+ mTargetProgress, EDGE_LEFT, null);
+ mMainThreadHandler.post(
+ () -> mProgressAnimator.onBackProgressed(backEvent));
+
+ mTargetProgressCalled.await(1, TimeUnit.SECONDS);
+
+ assertNotNull(mReceivedBackEvent);
+ assertEquals(mReceivedBackEvent.getProgress(), mTargetProgress, 0 /* delta */);
+ }
+
+ @Test
+ public void testBackCancelled() throws InterruptedException {
+ // Give the animator some progress.
+ final BackMotionEvent backEvent = new BackMotionEvent(
+ 100, 0,
+ mTargetProgress, EDGE_LEFT, null);
+ mMainThreadHandler.post(
+ () -> mProgressAnimator.onBackProgressed(backEvent));
+ mTargetProgressCalled.await(1, TimeUnit.SECONDS);
+ assertNotNull(mReceivedBackEvent);
+
+ // Trigger animation cancel, the target progress should be 0.
+ mTargetProgress = 0;
+ mTargetProgressCalled = new CountDownLatch(1);
+ CountDownLatch cancelCallbackCalled = new CountDownLatch(1);
+ mMainThreadHandler.post(
+ () -> mProgressAnimator.onBackCancelled(() -> cancelCallbackCalled.countDown()));
+ cancelCallbackCalled.await(1, TimeUnit.SECONDS);
+ mTargetProgressCalled.await(1, TimeUnit.SECONDS);
+ assertNotNull(mReceivedBackEvent);
+ assertEquals(mReceivedBackEvent.getProgress(), mTargetProgress, 0 /* delta */);
+ }
+
+ private void onGestureProgress(BackEvent backEvent) {
+ if (mTargetProgress == backEvent.getProgress()) {
+ mReceivedBackEvent = backEvent;
+ mTargetProgressCalled.countDown();
+ }
+ }
+}
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index e8648cc..1022d93 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -353,6 +353,8 @@
final Set<String> mDeduplicationIds;
final Bundle mExtras;
final String mProviderId;
+ final boolean mIsVisibilityRestricted;
+ final Set<String> mAllowedPackages;
MediaRoute2Info(@NonNull Builder builder) {
mId = builder.mId;
@@ -372,6 +374,8 @@
mDeduplicationIds = builder.mDeduplicationIds;
mExtras = builder.mExtras;
mProviderId = builder.mProviderId;
+ mIsVisibilityRestricted = builder.mIsVisibilityRestricted;
+ mAllowedPackages = builder.mAllowedPackages;
}
MediaRoute2Info(@NonNull Parcel in) {
@@ -393,6 +397,8 @@
mDeduplicationIds = Set.of(in.readStringArray());
mExtras = in.readBundle();
mProviderId = in.readString();
+ mIsVisibilityRestricted = in.readBoolean();
+ mAllowedPackages = Set.of(in.createString8Array());
}
/**
@@ -628,6 +634,15 @@
}
/**
+ * Returns whether this route is visible to the package with the given name.
+ * @hide
+ */
+ public boolean isVisibleTo(String packageName) {
+ return !mIsVisibilityRestricted || getPackageName().equals(packageName)
+ || mAllowedPackages.contains(packageName);
+ }
+
+ /**
* Dumps the current state of the object to the given {@code pw} as a human-readable string.
*
* <p> Used in the context of dumpsys. </p>
@@ -655,6 +670,8 @@
pw.println(indent + "mDeduplicationIds=" + mDeduplicationIds);
pw.println(indent + "mExtras=" + mExtras);
pw.println(indent + "mProviderId=" + mProviderId);
+ pw.println(indent + "mIsVisibilityRestricted=" + mIsVisibilityRestricted);
+ pw.println(indent + "mAllowedPackages=" + mAllowedPackages);
}
private void dumpVolume(@NonNull PrintWriter pw, @NonNull String prefix) {
@@ -705,7 +722,9 @@
&& (mVolume == other.mVolume)
&& Objects.equals(mAddress, other.mAddress)
&& Objects.equals(mDeduplicationIds, other.mDeduplicationIds)
- && Objects.equals(mProviderId, other.mProviderId);
+ && Objects.equals(mProviderId, other.mProviderId)
+ && (mIsVisibilityRestricted == other.mIsVisibilityRestricted)
+ && Objects.equals(mAllowedPackages, other.mAllowedPackages);
}
@Override
@@ -713,7 +732,8 @@
// Note: mExtras is not included.
return Objects.hash(mId, mName, mFeatures, mType, mIsSystem, mIconUri, mDescription,
mConnectionState, mClientPackageName, mPackageName, mVolumeHandling, mVolumeMax,
- mVolume, mAddress, mDeduplicationIds, mProviderId);
+ mVolume, mAddress, mDeduplicationIds, mProviderId, mIsVisibilityRestricted,
+ mAllowedPackages);
}
@Override
@@ -733,6 +753,8 @@
.append(", volume=").append(getVolume())
.append(", deduplicationIds=").append(String.join(",", getDeduplicationIds()))
.append(", providerId=").append(getProviderId())
+ .append(", isVisibilityRestricted=").append(mIsVisibilityRestricted)
+ .append(", allowedPackages=").append(String.join(",", mAllowedPackages))
.append(" }");
return result.toString();
}
@@ -761,6 +783,8 @@
dest.writeStringArray(mDeduplicationIds.toArray(new String[mDeduplicationIds.size()]));
dest.writeBundle(mExtras);
dest.writeString(mProviderId);
+ dest.writeBoolean(mIsVisibilityRestricted);
+ dest.writeString8Array(mAllowedPackages.toArray(new String[0]));
}
/**
@@ -787,6 +811,8 @@
Set<String> mDeduplicationIds;
Bundle mExtras;
String mProviderId;
+ boolean mIsVisibilityRestricted;
+ Set<String> mAllowedPackages;
/**
* Constructor for builder to create {@link MediaRoute2Info}.
@@ -809,6 +835,7 @@
mName = name;
mFeatures = new ArrayList<>();
mDeduplicationIds = Set.of();
+ mAllowedPackages = Set.of();
}
/**
@@ -854,6 +881,8 @@
mExtras = new Bundle(routeInfo.mExtras);
}
mProviderId = routeInfo.mProviderId;
+ mIsVisibilityRestricted = routeInfo.mIsVisibilityRestricted;
+ mAllowedPackages = routeInfo.mAllowedPackages;
}
/**
@@ -1057,6 +1086,29 @@
}
/**
+ * Sets the visibility of this route to public. This is the default
+ * visibility for routes that are public to all other apps.
+ */
+ @NonNull
+ public Builder setVisibilityPublic() {
+ mIsVisibilityRestricted = false;
+ mAllowedPackages = Set.of();
+ return this;
+ }
+
+ /**
+ * Sets the visibility of this route to restricted. This means that the
+ * route is only visible to a set of package name.
+ * @param allowedPackages set of package names which are allowed to see this route.
+ */
+ @NonNull
+ public Builder setVisibilityRestricted(@NonNull Set<String> allowedPackages) {
+ mIsVisibilityRestricted = true;
+ mAllowedPackages = Set.copyOf(allowedPackages);
+ return this;
+ }
+
+ /**
* Builds the {@link MediaRoute2Info media route info}.
*
* @throws IllegalArgumentException if no features are added.
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index aea6bcb..3abfc629 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -247,7 +247,6 @@
return getTransferableRoutes(sessions.get(sessions.size() - 1));
}
-
/**
* Gets available routes for the given routing session.
* The returned routes can be passed to
@@ -313,9 +312,15 @@
mDiscoveryPreferenceMap.getOrDefault(packageName, RouteDiscoveryPreference.EMPTY);
for (MediaRoute2Info route : getSortedRoutes(discoveryPreference)) {
- if (sessionInfo.getTransferableRoutes().contains(route.getId())
- || (includeSelectedRoutes
- && sessionInfo.getSelectedRoutes().contains(route.getId()))) {
+ if (!route.isVisibleTo(packageName)) {
+ continue;
+ }
+ boolean transferableRoutesContainRoute =
+ sessionInfo.getTransferableRoutes().contains(route.getId());
+ boolean selectedRoutesContainRoute =
+ sessionInfo.getSelectedRoutes().contains(route.getId());
+ if (transferableRoutesContainRoute
+ || (includeSelectedRoutes && selectedRoutesContainRoute)) {
routes.add(route);
continue;
}
diff --git a/media/java/android/media/RouteListingPreference.java b/media/java/android/media/RouteListingPreference.java
index b1d74d4..b03653c0 100644
--- a/media/java/android/media/RouteListingPreference.java
+++ b/media/java/android/media/RouteListingPreference.java
@@ -19,6 +19,9 @@
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Intent;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -40,6 +43,18 @@
*/
public final class RouteListingPreference implements Parcelable {
+ /**
+ * {@link Intent} action for apps to take the user to a screen for transferring media playback
+ * to the route with the id provided by the extra with key {@link #EXTRA_ROUTE_ID}.
+ */
+ public static final String ACTION_TRANSFER_MEDIA = "android.media.action.TRANSFER_MEDIA";
+
+ /**
+ * {@link Intent} string extra key that contains the {@link Item#getRouteId() id} of the route
+ * to transfer to, as part of an {@link #ACTION_TRANSFER_MEDIA} intent.
+ */
+ public static final String EXTRA_ROUTE_ID = "android.media.extra.ROUTE_ID";
+
@NonNull
public static final Creator<RouteListingPreference> CREATOR =
new Creator<>() {
@@ -56,10 +71,12 @@
@NonNull private final List<Item> mItems;
private final boolean mUseSystemOrdering;
+ @Nullable private final ComponentName mInAppOnlyItemRoutingReceiver;
private RouteListingPreference(Builder builder) {
mItems = builder.mItems;
mUseSystemOrdering = builder.mUseSystemOrdering;
+ mInAppOnlyItemRoutingReceiver = builder.mInAppOnlyItemRoutingReceiver;
}
private RouteListingPreference(Parcel in) {
@@ -67,6 +84,7 @@
in.readParcelableList(new ArrayList<>(), Item.class.getClassLoader(), Item.class);
mItems = List.copyOf(items);
mUseSystemOrdering = in.readBoolean();
+ mInAppOnlyItemRoutingReceiver = ComponentName.readFromParcel(in);
}
/**
@@ -90,6 +108,21 @@
return mUseSystemOrdering;
}
+ /**
+ * Returns a {@link ComponentName} for handling routes disabled via {@link
+ * Item#DISABLE_REASON_IN_APP_ONLY}, or null if the user needs to manually navigate to the app
+ * in order to route to select the corresponding routes.
+ *
+ * <p>If the user selects an {@link Item} disabled via {@link Item#DISABLE_REASON_IN_APP_ONLY},
+ * and this method returns a non-null {@link ComponentName}, the system takes the user back to
+ * the app by launching an intent to the returned {@link ComponentName}, using action {@link
+ * #ACTION_TRANSFER_MEDIA}, with the extra {@link #EXTRA_ROUTE_ID}.
+ */
+ @Nullable
+ public ComponentName getInAppOnlyItemRoutingReceiver() {
+ return mInAppOnlyItemRoutingReceiver;
+ }
+
// RouteListingPreference Parcelable implementation.
@Override
@@ -101,6 +134,7 @@
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeParcelableList(mItems, flags);
dest.writeBoolean(mUseSystemOrdering);
+ ComponentName.writeToParcel(mInAppOnlyItemRoutingReceiver, dest);
}
// Equals and hashCode.
@@ -114,12 +148,15 @@
return false;
}
RouteListingPreference that = (RouteListingPreference) other;
- return mItems.equals(that.mItems) && mUseSystemOrdering == that.mUseSystemOrdering;
+ return mItems.equals(that.mItems)
+ && mUseSystemOrdering == that.mUseSystemOrdering
+ && Objects.equals(
+ mInAppOnlyItemRoutingReceiver, that.mInAppOnlyItemRoutingReceiver);
}
@Override
public int hashCode() {
- return Objects.hash(mItems, mUseSystemOrdering);
+ return Objects.hash(mItems, mUseSystemOrdering, mInAppOnlyItemRoutingReceiver);
}
/** Builder for {@link RouteListingPreference}. */
@@ -127,6 +164,7 @@
private List<Item> mItems;
private boolean mUseSystemOrdering;
+ private ComponentName mInAppOnlyItemRoutingReceiver;
/** Creates a new instance with default values (documented in the setters). */
public Builder() {
@@ -159,6 +197,18 @@
}
/**
+ * See {@link #getInAppOnlyItemRoutingReceiver()}.
+ *
+ * <p>The default value is {@code null}.
+ */
+ @NonNull
+ public Builder setInAppOnlyItemRoutingReceiver(
+ @Nullable ComponentName inAppOnlyItemRoutingReceiver) {
+ mInAppOnlyItemRoutingReceiver = inAppOnlyItemRoutingReceiver;
+ return this;
+ }
+
+ /**
* Creates and returns a new {@link RouteListingPreference} instance with the given
* parameters.
*/
@@ -203,7 +253,8 @@
DISABLE_REASON_NONE,
DISABLE_REASON_SUBSCRIPTION_REQUIRED,
DISABLE_REASON_DOWNLOADED_CONTENT,
- DISABLE_REASON_AD
+ DISABLE_REASON_AD,
+ DISABLE_REASON_IN_APP_ONLY
})
public @interface DisableReason {}
@@ -221,6 +272,14 @@
public static final int DISABLE_REASON_DOWNLOADED_CONTENT = 2;
/** The corresponding route is not available because an ad is in progress. */
public static final int DISABLE_REASON_AD = 3;
+ /**
+ * The corresponding route is only available for routing from within the app.
+ *
+ * <p>The user may still select the corresponding route if the app provides an {@link
+ * #getInAppOnlyItemRoutingReceiver() in-app routing receiver}, in which case the system
+ * will take the user to the app.
+ */
+ public static final int DISABLE_REASON_IN_APP_ONLY = 4;
@NonNull
public static final Creator<Item> CREATOR =
@@ -257,7 +316,11 @@
Preconditions.checkArgument(mSessionParticipantCount >= 0);
}
- /** Returns the id of the route that corresponds to this route listing preference item. */
+ /**
+ * Returns the id of the route that corresponds to this route listing preference item.
+ *
+ * @see MediaRoute2Info#getId()
+ */
@NonNull
public String getRouteId() {
return mRouteId;
@@ -282,6 +345,7 @@
* @see #DISABLE_REASON_SUBSCRIPTION_REQUIRED
* @see #DISABLE_REASON_DOWNLOADED_CONTENT
* @see #DISABLE_REASON_AD
+ * @see #DISABLE_REASON_IN_APP_ONLY
*/
@DisableReason
public int getDisableReason() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
index 3903404..cd6609e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
@@ -39,7 +39,7 @@
BluetoothMediaDevice(Context context, CachedBluetoothDevice device,
MediaRouter2Manager routerManager, MediaRoute2Info info, String packageName) {
- super(context, routerManager, info, packageName);
+ super(context, routerManager, info, packageName, null);
mCachedDevice = device;
mAudioManager = context.getSystemService(AudioManager.class);
initDeviceRecord();
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
index 1b5ce8fe..6fb5555 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
@@ -25,6 +25,7 @@
import android.graphics.drawable.Drawable;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
+import android.media.RouteListingPreference;
import androidx.annotation.VisibleForTesting;
@@ -40,11 +41,16 @@
private static final String TAG = "InfoMediaDevice";
InfoMediaDevice(Context context, MediaRouter2Manager routerManager, MediaRoute2Info info,
- String packageName) {
- super(context, routerManager, info, packageName);
+ String packageName, RouteListingPreference.Item item) {
+ super(context, routerManager, info, packageName, item);
initDeviceRecord();
}
+ InfoMediaDevice(Context context, MediaRouter2Manager routerManager, MediaRoute2Info info,
+ String packageName) {
+ this(context, routerManager, info, packageName, null);
+ }
+
@Override
public String getName() {
return mRouteInfo.getName().toString();
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index c7bfe12..2bdbb16 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -58,7 +58,9 @@
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
@@ -82,6 +84,8 @@
private MediaDevice mCurrentConnectedDevice;
private LocalBluetoothManager mBluetoothManager;
+ private final Map<String, RouteListingPreference.Item> mPreferenceItemMap =
+ new ConcurrentHashMap<>();
public InfoMediaManager(Context context, String packageName, Notification notification,
LocalBluetoothManager localBluetoothManager) {
@@ -248,7 +252,7 @@
if (info != null) {
for (MediaRoute2Info route : mRouterManager.getSelectableRoutes(info)) {
deviceList.add(new InfoMediaDevice(mContext, mRouterManager,
- route, mPackageName));
+ route, mPackageName, mPreferenceItemMap.get(route.getId())));
}
return deviceList;
}
@@ -275,7 +279,7 @@
if (info != null) {
for (MediaRoute2Info route : mRouterManager.getDeselectableRoutes(info)) {
deviceList.add(new InfoMediaDevice(mContext, mRouterManager,
- route, mPackageName));
+ route, mPackageName, mPreferenceItemMap.get(route.getId())));
Log.d(TAG, route.getName() + " is deselectable for " + mPackageName);
}
return deviceList;
@@ -302,7 +306,7 @@
if (info != null) {
for (MediaRoute2Info route : mRouterManager.getSelectedRoutes(info)) {
deviceList.add(new InfoMediaDevice(mContext, mRouterManager,
- route, mPackageName));
+ route, mPackageName, mPreferenceItemMap.get(route.getId())));
}
return deviceList;
}
@@ -510,7 +514,7 @@
case TYPE_GROUP:
//TODO(b/148765806): use correct device type once api is ready.
mediaDevice = new InfoMediaDevice(mContext, mRouterManager, route,
- mPackageName);
+ mPackageName, mPreferenceItemMap.get(route.getId()));
if (!TextUtils.isEmpty(mPackageName)
&& getRoutingSessionInfo().getSelectedRoutes().contains(route.getId())) {
mediaDevice.setState(STATE_SELECTED);
@@ -601,6 +605,16 @@
public void onSessionUpdated(RoutingSessionInfo sessionInfo) {
refreshDevices();
}
+
+ @Override
+ public void onRouteListingPreferenceUpdated(
+ String packageName,
+ RouteListingPreference routeListingPreference) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+ Api34Impl.onRouteListingPreferenceUpdated(packageName, routeListingPreference,
+ mPreferenceItemMap);
+ }
+ }
}
@RequiresApi(34)
@@ -666,5 +680,18 @@
return routeListingPreference != null
&& !routeListingPreference.getUseSystemOrdering();
}
+
+ @DoNotInline
+ static void onRouteListingPreferenceUpdated(
+ String packageName,
+ RouteListingPreference routeListingPreference,
+ Map<String, RouteListingPreference.Item> preferenceItemMap) {
+ preferenceItemMap.clear();
+ if (routeListingPreference != null) {
+ routeListingPreference.getItems().forEach((item) -> {
+ preferenceItemMap.put(item.getRouteId(), item);
+ });
+ }
+ }
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index 3ba51d2..b73e7a3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -30,6 +30,7 @@
import static android.media.MediaRoute2Info.TYPE_USB_HEADSET;
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;
+import static android.media.RouteListingPreference.Item.FLAG_SUGGESTED_ROUTE;
import static com.android.settingslib.media.LocalMediaManager.MediaDeviceState.STATE_SELECTED;
@@ -39,10 +40,14 @@
import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
import android.media.NearbyDevice;
+import android.media.RouteListingPreference;
+import android.os.Build;
import android.text.TextUtils;
import android.util.Log;
+import androidx.annotation.DoNotInline;
import androidx.annotation.IntDef;
+import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting;
import java.lang.annotation.Retention;
@@ -87,14 +92,16 @@
protected final Context mContext;
protected final MediaRoute2Info mRouteInfo;
protected final MediaRouter2Manager mRouterManager;
+ protected final RouteListingPreference.Item mItem;
protected final String mPackageName;
MediaDevice(Context context, MediaRouter2Manager routerManager, MediaRoute2Info info,
- String packageName) {
+ String packageName, RouteListingPreference.Item item) {
mContext = context;
mRouteInfo = info;
mRouterManager = routerManager;
mPackageName = packageName;
+ mItem = item;
setType(info);
}
@@ -180,10 +187,21 @@
/**
* Get unique ID that represent MediaDevice
+ *
* @return unique id of MediaDevice
*/
public abstract String getId();
+ /**
+ * Checks if device is suggested device from application
+ *
+ * @return true if device is suggested device
+ */
+ public boolean isSuggestedDevice() {
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE
+ && Api34Impl.isSuggestedDevice(mItem);
+ }
+
void setConnectedRecord() {
mConnectedRecord++;
ConnectionRecordManager.getInstance().setConnectionRecord(mContext, getId(),
@@ -466,4 +484,12 @@
final MediaDevice otherDevice = (MediaDevice) obj;
return otherDevice.getId().equals(getId());
}
+
+ @RequiresApi(34)
+ private static class Api34Impl {
+ @DoNotInline
+ static boolean isSuggestedDevice(RouteListingPreference.Item item) {
+ return item != null && item.getFlags() == FLAG_SUGGESTED_ROUTE;
+ }
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
index 921c245..de16d4a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
@@ -51,7 +51,7 @@
PhoneMediaDevice(Context context, MediaRouter2Manager routerManager, MediaRoute2Info info,
String packageName) {
- super(context, routerManager, info, packageName);
+ super(context, routerManager, info, packageName, null);
mDeviceIconUtil = new DeviceIconUtil();
initDeviceRecord();
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index 9260013..2c8aa26 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -257,14 +257,17 @@
Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
final List<RouteListingPreference.Item> preferenceItemList = new ArrayList<>();
RouteListingPreference.Item item1 = new RouteListingPreference.Item.Builder(
- TEST_ID_4).build();
+ TEST_ID_4).setFlags(RouteListingPreference.Item.FLAG_SUGGESTED_ROUTE).build();
RouteListingPreference.Item item2 = new RouteListingPreference.Item.Builder(
TEST_ID_3).build();
preferenceItemList.add(item1);
preferenceItemList.add(item2);
+
+ RouteListingPreference routeListingPreference =
+ new RouteListingPreference.Builder().setItems(
+ preferenceItemList).setUseSystemOrdering(false).build();
when(mRouterManager.getRouteListingPreference(TEST_PACKAGE_NAME))
- .thenReturn(new RouteListingPreference.Builder().setItems(
- preferenceItemList).setUseSystemOrdering(false).build());
+ .thenReturn(routeListingPreference);
final List<MediaRoute2Info> selectedRoutes = new ArrayList<>();
final MediaRoute2Info info = mock(MediaRoute2Info.class);
@@ -284,11 +287,13 @@
setTransferableRoutesList();
mInfoMediaManager.mRouterManager = mRouterManager;
-
+ mInfoMediaManager.mMediaRouterCallback.onRouteListingPreferenceUpdated(TEST_PACKAGE_NAME,
+ routeListingPreference);
mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated();
assertThat(mInfoMediaManager.mMediaDevices.get(0).getId()).isEqualTo(TEST_ID);
assertThat(mInfoMediaManager.mMediaDevices.get(1).getId()).isEqualTo(TEST_ID_4);
+ assertThat(mInfoMediaManager.mMediaDevices.get(1).isSuggestedDevice()).isTrue();
assertThat(mInfoMediaManager.mMediaDevices.get(2).getId()).isEqualTo(TEST_ID_3);
assertThat(mInfoMediaManager.mMediaDevices).hasSize(3);
}
@@ -620,6 +625,7 @@
mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
mShadowRouter2Manager.setDeselectableRoutes(mediaRoute2Infos);
when(mediaRoute2Info.getName()).thenReturn(TEST_NAME);
+ when(mediaRoute2Info.getId()).thenReturn(TEST_ID);
final List<MediaDevice> mediaDevices = mInfoMediaManager.getDeselectableMediaDevice();
@@ -873,21 +879,25 @@
final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
when(route2Info.getType()).thenReturn(TYPE_REMOTE_SPEAKER);
+ when(route2Info.getId()).thenReturn(TEST_ID);
mInfoMediaManager.addMediaDevice(route2Info);
assertThat(mInfoMediaManager.mMediaDevices.get(0) instanceof InfoMediaDevice).isTrue();
when(route2Info.getType()).thenReturn(TYPE_USB_DEVICE);
+ when(route2Info.getId()).thenReturn(TEST_ID);
mInfoMediaManager.mMediaDevices.clear();
mInfoMediaManager.addMediaDevice(route2Info);
assertThat(mInfoMediaManager.mMediaDevices.get(0) instanceof PhoneMediaDevice).isTrue();
when(route2Info.getType()).thenReturn(TYPE_WIRED_HEADSET);
+ when(route2Info.getId()).thenReturn(TEST_ID);
mInfoMediaManager.mMediaDevices.clear();
mInfoMediaManager.addMediaDevice(route2Info);
assertThat(mInfoMediaManager.mMediaDevices.get(0) instanceof PhoneMediaDevice).isTrue();
when(route2Info.getType()).thenReturn(TYPE_BLUETOOTH_A2DP);
when(route2Info.getAddress()).thenReturn("00:00:00:00:00:00");
+ when(route2Info.getId()).thenReturn(TEST_ID);
when(mLocalBluetoothManager.getCachedDeviceManager())
.thenReturn(cachedBluetoothDeviceManager);
when(cachedBluetoothDeviceManager.findDevice(any(BluetoothDevice.class)))
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
index 1fdbc99..d5558b2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
@@ -184,6 +184,7 @@
private val configListener =
object : ConfigurationController.ConfigurationListener {
+ var lastOrientation = -1
override fun onDensityOrFontScaleChanged() {
// System font changes should only happen when UMO is offscreen or a flicker may
@@ -200,7 +201,13 @@
override fun onConfigChanged(newConfig: Configuration?) {
if (newConfig == null) return
isRtl = newConfig.layoutDirection == View.LAYOUT_DIRECTION_RTL
- updatePlayers(recreateMedia = true)
+ val newOrientation = newConfig.orientation
+ if (lastOrientation != newOrientation) {
+ // The players actually depend on the orientation possibly, so we have to
+ // recreate them (at least on large screen devices)
+ lastOrientation = newOrientation
+ updatePlayers(recreateMedia = true)
+ }
}
override fun onUiModeChanged() {
@@ -717,6 +724,9 @@
private fun updatePlayers(recreateMedia: Boolean) {
pageIndicator.tintList =
ColorStateList.valueOf(context.getColor(R.color.media_paging_indicator))
+ val previousVisibleKey =
+ MediaPlayerData.visiblePlayerKeys()
+ .elementAtOrNull(mediaCarouselScrollHandler.visibleMediaIndex)
MediaPlayerData.mediaData().forEach { (key, data, isSsMediaRec) ->
if (isSsMediaRec) {
@@ -741,6 +751,9 @@
isSsReactivated = isSsReactivated
)
}
+ if (recreateMedia) {
+ reorderAllPlayers(previousVisibleKey)
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
index 6ca34e0..039dd4d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.media.controls.ui
import android.app.PendingIntent
+import android.content.res.Configuration
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
@@ -86,6 +87,9 @@
@Mock lateinit var mediaViewController: MediaViewController
@Mock lateinit var smartspaceMediaData: SmartspaceMediaData
@Captor lateinit var listener: ArgumentCaptor<MediaDataManager.Listener>
+ @Captor
+ lateinit var configListener: ArgumentCaptor<ConfigurationController.ConfigurationListener>
+ @Captor lateinit var newConfig: ArgumentCaptor<Configuration>
@Captor lateinit var visualStabilityCallback: ArgumentCaptor<OnReorderingAllowedListener>
private val clock = FakeSystemClock()
@@ -111,6 +115,7 @@
logger,
debugLogger
)
+ verify(configurationController).addCallback(capture(configListener))
verify(mediaDataManager).addListener(capture(listener))
verify(visualStabilityProvider)
.addPersistentReorderingAllowedListener(capture(visualStabilityCallback))
@@ -662,4 +667,39 @@
mediaCarouselController.updatePageIndicatorAlpha()
assertEquals(mediaCarouselController.pageIndicator.alpha, 1.0F, delta)
}
+
+ @Ignore("b/253229241")
+ @Test
+ fun testOnConfigChanged_playersAreAddedBack() {
+ listener.value.onMediaDataLoaded(
+ "playing local",
+ null,
+ DATA.copy(
+ active = true,
+ isPlaying = true,
+ playbackLocation = MediaData.PLAYBACK_LOCAL,
+ resumption = false
+ )
+ )
+ listener.value.onMediaDataLoaded(
+ "paused local",
+ null,
+ DATA.copy(
+ active = true,
+ isPlaying = false,
+ playbackLocation = MediaData.PLAYBACK_LOCAL,
+ resumption = false
+ )
+ )
+
+ val playersSize = MediaPlayerData.players().size
+
+ configListener.value.onConfigChanged(capture(newConfig))
+
+ assertEquals(playersSize, MediaPlayerData.players().size)
+ assertEquals(
+ MediaPlayerData.getMediaPlayerIndex("playing local"),
+ mediaCarouselController.mediaCarouselScrollHandler.visibleMediaIndex
+ )
+ }
}
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index cc485ba..cde4ea9 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -31,6 +31,7 @@
import android.app.ActivityManager;
import android.app.ActivityThread;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -285,6 +286,15 @@
public void setRouteListingPreference(
@NonNull IMediaRouter2 router,
@Nullable RouteListingPreference routeListingPreference) {
+ ComponentName inAppOnlyItemRoutingReceiver =
+ routeListingPreference != null
+ ? routeListingPreference.getInAppOnlyItemRoutingReceiver()
+ : null;
+ if (inAppOnlyItemRoutingReceiver != null) {
+ MediaServerUtils.enforcePackageName(
+ inAppOnlyItemRoutingReceiver.getPackageName(), Binder.getCallingUid());
+ }
+
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
@@ -787,6 +797,12 @@
obtainMessage(UserHandler::notifyDiscoveryPreferenceChangedToManagers,
routerRecord.mUserRecord.mHandler,
routerRecord.mPackageName, null));
+ routerRecord.mUserRecord.mHandler.sendMessage(
+ obtainMessage(
+ UserHandler::notifyRouteListingPreferenceChangeToManagers,
+ routerRecord.mUserRecord.mHandler,
+ routerRecord.mPackageName,
+ /* routeListingPreference= */ null));
userRecord.mHandler.sendMessage(
obtainMessage(UserHandler::updateDiscoveryPreferenceOnHandler,
userRecord.mHandler));
@@ -1626,7 +1642,7 @@
* <p>This list contains all routes exposed by route providers. This includes routes from
* both system route providers and user route providers.
*
- * <p>See {@link #getRouters(boolean hasModifyAudioRoutingPermission)}.
+ * <p>See {@link #getRouterRecords(boolean hasModifyAudioRoutingPermission)}.
*/
private final Map<String, MediaRoute2Info> mLastNotifiedRoutesToPrivilegedRouters =
new ArrayMap<>();
@@ -1896,9 +1912,10 @@
if (!hasAddedOrModifiedRoutes && !hasRemovedRoutes) {
return;
}
-
- List<IMediaRouter2> routersWithModifyAudioRoutingPermission = getRouters(true);
- List<IMediaRouter2> routersWithoutModifyAudioRoutingPermission = getRouters(false);
+ List<RouterRecord> routerRecordsWithModifyAudioRoutingPermission =
+ getRouterRecords(true);
+ List<RouterRecord> routerRecordsWithoutModifyAudioRoutingPermission =
+ getRouterRecords(false);
List<IMediaRouter2Manager> managers = getManagers();
// Managers receive all provider updates with all routes.
@@ -1907,22 +1924,22 @@
// Routers with modify audio permission (usually system routers) receive all provider
// updates with all routes.
- notifyRoutesUpdatedToRouters(
- routersWithModifyAudioRoutingPermission,
+ notifyRoutesUpdatedToRouterRecords(
+ routerRecordsWithModifyAudioRoutingPermission,
new ArrayList<>(mLastNotifiedRoutesToPrivilegedRouters.values()));
if (!isSystemProvider) {
// Regular routers receive updates from all non-system providers with all non-system
// routes.
- notifyRoutesUpdatedToRouters(
- routersWithoutModifyAudioRoutingPermission,
+ notifyRoutesUpdatedToRouterRecords(
+ routerRecordsWithoutModifyAudioRoutingPermission,
new ArrayList<>(mLastNotifiedRoutesToNonPrivilegedRouters.values()));
} else if (hasAddedOrModifiedRoutes) {
// On system provider updates, regular routers receive the updated default route.
// This is the only system route they should receive.
mLastNotifiedRoutesToNonPrivilegedRouters.put(defaultRoute.getId(), defaultRoute);
- notifyRoutesUpdatedToRouters(
- routersWithoutModifyAudioRoutingPermission,
+ notifyRoutesUpdatedToRouterRecords(
+ routerRecordsWithoutModifyAudioRoutingPermission,
new ArrayList<>(mLastNotifiedRoutesToNonPrivilegedRouters.values()));
}
}
@@ -2191,8 +2208,8 @@
if (mServiceRef.get() == null) {
return;
}
- notifySessionInfoChangedToRouters(getRouters(true), sessionInfo);
- notifySessionInfoChangedToRouters(getRouters(false),
+ notifySessionInfoChangedToRouters(getRouterRecords(true), sessionInfo);
+ notifySessionInfoChangedToRouters(getRouterRecords(false),
mSystemProvider.getDefaultSessionInfo());
return;
}
@@ -2203,7 +2220,7 @@
+ sessionInfo);
return;
}
- notifySessionInfoChangedToRouters(Arrays.asList(routerRecord.mRouter), sessionInfo);
+ notifySessionInfoChangedToRouters(Arrays.asList(routerRecord), sessionInfo);
}
private void onSessionReleasedOnHandler(@NonNull MediaRoute2Provider provider,
@@ -2316,6 +2333,7 @@
== routerRecord.mHasModifyAudioRoutingPermission) {
routers.add(routerRecord.mRouter);
}
+ routers.add(routerRecord.mRouter);
}
}
return routers;
@@ -2345,6 +2363,23 @@
}
}
+ private List<RouterRecord> getRouterRecords(boolean hasModifyAudioRoutingPermission) {
+ MediaRouter2ServiceImpl service = mServiceRef.get();
+ List<RouterRecord> routerRecords = new ArrayList<>();
+ if (service == null) {
+ return routerRecords;
+ }
+ synchronized (service.mLock) {
+ for (RouterRecord routerRecord : mUserRecord.mRouterRecords) {
+ if (hasModifyAudioRoutingPermission
+ == routerRecord.mHasModifyAudioRoutingPermission) {
+ routerRecords.add(routerRecord);
+ }
+ }
+ return routerRecords;
+ }
+ }
+
private List<ManagerRecord> getManagerRecords() {
MediaRouter2ServiceImpl service = mServiceRef.get();
if (service == null) {
@@ -2395,22 +2430,45 @@
}
}
- private void notifyRoutesUpdatedToRouters(
- @NonNull List<IMediaRouter2> routers, @NonNull List<MediaRoute2Info> routes) {
- for (IMediaRouter2 router : routers) {
+ private static void notifyRoutesUpdatedToRouterRecords(
+ @NonNull List<RouterRecord> routerRecords,
+ @NonNull List<MediaRoute2Info> routes) {
+ for (RouterRecord routerRecord: routerRecords) {
+ List<MediaRoute2Info> filteredRoutes = getFilteredRoutesForPackageName(routes,
+ routerRecord.mPackageName);
try {
- router.notifyRoutesUpdated(routes);
+ routerRecord.mRouter.notifyRoutesUpdated(filteredRoutes);
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to notify routes updated. Router probably died.", ex);
}
}
}
+ /**
+ * Filters list of routes to return only public routes or routes provided by
+ * the same package name or routes containing this package name in its allow list.
+ * @param routes initial list of routes to be filtered.
+ * @param packageName router's package name to filter routes for it.
+ * @return only the routes that this package name is allowed to see.
+ */
+ private static List<MediaRoute2Info> getFilteredRoutesForPackageName(
+ @NonNull List<MediaRoute2Info> routes,
+ @NonNull String packageName) {
+ List<MediaRoute2Info> filteredRoutes = new ArrayList<>();
+ for (MediaRoute2Info route : routes) {
+ if (route.isVisibleTo(packageName)) {
+ filteredRoutes.add(route);
+ }
+ }
+ return filteredRoutes;
+ }
+
private void notifySessionInfoChangedToRouters(
- @NonNull List<IMediaRouter2> routers, @NonNull RoutingSessionInfo sessionInfo) {
- for (IMediaRouter2 router : routers) {
+ @NonNull List<RouterRecord> routerRecords,
+ @NonNull RoutingSessionInfo sessionInfo) {
+ for (RouterRecord routerRecord : routerRecords) {
try {
- router.notifySessionInfoChanged(sessionInfo);
+ routerRecord.mRouter.notifySessionInfoChanged(sessionInfo);
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to notify session info changed. Router probably died.", ex);
}
diff --git a/services/core/java/com/android/server/media/MediaServerUtils.java b/services/core/java/com/android/server/media/MediaServerUtils.java
index 5fa2b1c..a4a99af 100644
--- a/services/core/java/com/android/server/media/MediaServerUtils.java
+++ b/services/core/java/com/android/server/media/MediaServerUtils.java
@@ -18,7 +18,13 @@
import android.content.Context;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.os.Binder;
+import android.os.Process;
+import android.os.UserHandle;
+import android.text.TextUtils;
+
+import com.android.server.LocalServices;
import java.io.PrintWriter;
@@ -26,6 +32,39 @@
* Util class for media server.
*/
class MediaServerUtils {
+
+ /**
+ * Throws if the given {@code packageName} does not correspond to the given {@code uid}.
+ *
+ * <p>This method trusts calls from {@link Process#ROOT_UID} and {@link Process#SHELL_UID}.
+ *
+ * @param packageName A package name to verify (usually sent over binder by an app).
+ * @param uid The calling uid, obtained via {@link Binder#getCallingUid()}.
+ * @throws IllegalArgumentException If the given {@code packageName} does not correspond to the
+ * given {@code uid}, and {@code uid} is not the root uid, or the shell uid.
+ */
+ public static void enforcePackageName(String packageName, int uid) {
+ if (uid == Process.ROOT_UID || uid == Process.SHELL_UID) {
+ return;
+ }
+ if (TextUtils.isEmpty(packageName)) {
+ throw new IllegalArgumentException("packageName may not be empty");
+ }
+ final PackageManagerInternal packageManagerInternal =
+ LocalServices.getService(PackageManagerInternal.class);
+ final int actualUid =
+ packageManagerInternal.getPackageUid(
+ packageName, 0 /* flags */, UserHandle.getUserId(uid));
+ if (!UserHandle.isSameApp(uid, actualUid)) {
+ throw new IllegalArgumentException(
+ "packageName does not belong to the calling uid; "
+ + "pkg="
+ + packageName
+ + ", uid="
+ + uid);
+ }
+ }
+
/**
* Verify that caller holds {@link android.Manifest.permission#DUMP}.
*/
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index e51ed1b..3a20cd9 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -538,30 +538,11 @@
mHandler.postSessionsChanged(session);
}
- private void enforcePackageName(String packageName, int uid) {
- if (TextUtils.isEmpty(packageName)) {
- throw new IllegalArgumentException("packageName may not be empty");
- }
- if (uid == Process.ROOT_UID || uid == Process.SHELL_UID) {
- // If the caller is shell, then trust the packageName given and allow it
- // to proceed.
- return;
- }
- final PackageManagerInternal packageManagerInternal =
- LocalServices.getService(PackageManagerInternal.class);
- final int actualUid = packageManagerInternal.getPackageUid(
- packageName, 0 /* flags */, UserHandle.getUserId(uid));
- if (!UserHandle.isSameApp(uid, actualUid)) {
- throw new IllegalArgumentException("packageName does not belong to the calling uid; "
- + "pkg=" + packageName + ", uid=" + uid);
- }
- }
-
void tempAllowlistTargetPkgIfPossible(int targetUid, String targetPackage,
int callingPid, int callingUid, String callingPackage, String reason) {
final long token = Binder.clearCallingIdentity();
try {
- enforcePackageName(callingPackage, callingUid);
+ MediaServerUtils.enforcePackageName(callingPackage, callingUid);
if (targetUid != callingUid) {
boolean canAllowWhileInUse = mActivityManagerLocal
.canAllowWhileInUsePermissionInFgs(callingPid, callingUid, callingPackage);
@@ -1206,7 +1187,7 @@
final int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
- enforcePackageName(packageName, uid);
+ MediaServerUtils.enforcePackageName(packageName, uid);
int resolvedUserId = handleIncomingUser(pid, uid, userId, packageName);
if (cb == null) {
throw new IllegalArgumentException("Controller callback cannot be null");
@@ -1258,7 +1239,7 @@
final int userId = userHandle.getIdentifier();
final long token = Binder.clearCallingIdentity();
try {
- enforcePackageName(packageName, uid);
+ MediaServerUtils.enforcePackageName(packageName, uid);
enforceMediaPermissions(packageName, pid, uid, userId);
MediaSessionRecordImpl record;
@@ -1289,7 +1270,7 @@
final int userId = userHandle.getIdentifier();
final long token = Binder.clearCallingIdentity();
try {
- enforcePackageName(packageName, uid);
+ MediaServerUtils.enforcePackageName(packageName, uid);
enforceMediaPermissions(packageName, pid, uid, userId);
MediaSessionRecordImpl record;
@@ -1615,7 +1596,7 @@
final int userId = userHandle.getIdentifier();
final long token = Binder.clearCallingIdentity();
try {
- enforcePackageName(packageName, uid);
+ MediaServerUtils.enforcePackageName(packageName, uid);
enforceMediaPermissions(packageName, pid, uid, userId);
synchronized (mLock) {
@@ -2129,7 +2110,7 @@
// If they gave us a component name verify they own the
// package
packageName = componentName.getPackageName();
- enforcePackageName(packageName, uid);
+ MediaServerUtils.enforcePackageName(packageName, uid);
}
// Check that they can make calls on behalf of the user and get the final user id
int resolvedUserId = handleIncomingUser(pid, uid, userId, packageName);
diff --git a/services/core/java/com/android/server/pm/AppDataHelper.java b/services/core/java/com/android/server/pm/AppDataHelper.java
index ed31187..abaff4a 100644
--- a/services/core/java/com/android/server/pm/AppDataHelper.java
+++ b/services/core/java/com/android/server/pm/AppDataHelper.java
@@ -18,6 +18,7 @@
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+import static com.android.server.pm.DexOptHelper.useArtService;
import static com.android.server.pm.PackageManagerService.TAG;
import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
@@ -45,6 +46,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
import com.android.server.SystemServerInitThreadPool;
+import com.android.server.pm.Installer.LegacyDexoptDisabledException;
import com.android.server.pm.dex.ArtManagerService;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.pkg.AndroidPackage;
@@ -242,33 +244,39 @@
}
}
- // Prepare the application profiles only for upgrades and
- // first boot (so that we don't repeat the same operation at
- // each boot).
- //
- // We only have to cover the upgrade and first boot here
- // because for app installs we prepare the profiles before
- // invoking dexopt (in installPackageLI).
- //
- // We also have to cover non system users because we do not
- // call the usual install package methods for them.
- //
- // NOTE: in order to speed up first boot time we only create
- // the current profile and do not update the content of the
- // reference profile. A system image should already be
- // configured with the right profile keys and the profiles
- // for the speed-profile prebuilds should already be copied.
- // That's done in #performDexOptUpgrade.
- //
- // TODO(calin, mathieuc): We should use .dm files for
- // prebuilds profiles instead of manually copying them in
- // #performDexOptUpgrade. When we do that we should have a
- // more granular check here and only update the existing
- // profiles.
- if (mPm.isDeviceUpgrading() || mPm.isFirstBoot()
- || (userId != UserHandle.USER_SYSTEM)) {
- mArtManagerService.prepareAppProfiles(pkg, userId,
- /* updateReferenceProfileContent= */ false);
+ if (!useArtService()) { // ART Service handles this on demand instead.
+ // Prepare the application profiles only for upgrades and
+ // first boot (so that we don't repeat the same operation at
+ // each boot).
+ //
+ // We only have to cover the upgrade and first boot here
+ // because for app installs we prepare the profiles before
+ // invoking dexopt (in installPackageLI).
+ //
+ // We also have to cover non system users because we do not
+ // call the usual install package methods for them.
+ //
+ // NOTE: in order to speed up first boot time we only create
+ // the current profile and do not update the content of the
+ // reference profile. A system image should already be
+ // configured with the right profile keys and the profiles
+ // for the speed-profile prebuilds should already be copied.
+ // That's done in #performDexOptUpgrade.
+ //
+ // TODO(calin, mathieuc): We should use .dm files for
+ // prebuilds profiles instead of manually copying them in
+ // #performDexOptUpgrade. When we do that we should have a
+ // more granular check here and only update the existing
+ // profiles.
+ if (mPm.isDeviceUpgrading() || mPm.isFirstBoot()
+ || (userId != UserHandle.USER_SYSTEM)) {
+ try {
+ mArtManagerService.prepareAppProfiles(pkg, userId,
+ /* updateReferenceProfileContent= */ false);
+ } catch (LegacyDexoptDisabledException e2) {
+ throw new RuntimeException(e2);
+ }
+ }
}
if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 && ceDataInode != -1) {
@@ -578,7 +586,12 @@
Slog.wtf(TAG, "Package was null!", new Throwable());
return;
}
- mArtManagerService.clearAppProfiles(pkg);
+ // TODO(b/251903639): Call into ART Service.
+ try {
+ mArtManagerService.clearAppProfiles(pkg);
+ } catch (LegacyDexoptDisabledException e) {
+ throw new RuntimeException(e);
+ }
}
public void destroyAppDataLIF(AndroidPackage pkg, int userId, int flags) {
@@ -615,8 +628,11 @@
}
private void destroyAppProfilesLeafLIF(AndroidPackage pkg) {
+ // TODO(b/251903639): Call into ART Service.
try {
mInstaller.destroyAppProfiles(pkg.getPackageName());
+ } catch (LegacyDexoptDisabledException e) {
+ throw new RuntimeException(e);
} catch (Installer.InstallerException e) {
Slog.w(TAG, String.valueOf(e));
}
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index fe122f8..9785d47 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -55,9 +55,11 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.internal.util.FunctionalUtils.ThrowingCheckedSupplier;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices;
import com.android.server.PinnerService;
+import com.android.server.pm.Installer.LegacyDexoptDisabledException;
import com.android.server.pm.PackageDexOptimizer.DexOptResult;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.DexoptOptions;
@@ -71,7 +73,6 @@
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
-import java.util.function.Supplier;
/**
* Controls background dex optimization run as idle job or command line.
@@ -202,7 +203,8 @@
}
/** Start scheduling job after boot completion */
- public void systemReady() {
+ public void systemReady() throws LegacyDexoptDisabledException {
+ Installer.checkLegacyDexoptDisabled();
if (mInjector.isBackgroundDexOptDisabled()) {
return;
}
@@ -272,7 +274,8 @@
* @return true if dex optimization is complete. false if the task is cancelled or if there was
* an error.
*/
- public boolean runBackgroundDexoptJob(@Nullable List<String> packageNames) {
+ public boolean runBackgroundDexoptJob(@Nullable List<String> packageNames)
+ throws LegacyDexoptDisabledException {
enforceRootOrShell();
long identity = Binder.clearCallingIdentity();
try {
@@ -301,7 +304,8 @@
*
* <p>This is only for shell command and only root or shell user can use this.
*/
- public void cancelBackgroundDexoptJob() {
+ public void cancelBackgroundDexoptJob() throws LegacyDexoptDisabledException {
+ Installer.checkLegacyDexoptDisabled();
enforceRootOrShell();
Binder.withCleanCallingIdentity(() -> cancelDexOptAndWaitForCompletion());
}
@@ -315,7 +319,8 @@
*
* @param disable True if JobScheduler invocations should be disabled, false otherwise.
*/
- public void setDisableJobSchedulerJobs(boolean disable) {
+ public void setDisableJobSchedulerJobs(boolean disable) throws LegacyDexoptDisabledException {
+ Installer.checkLegacyDexoptDisabled();
enforceRootOrShell();
synchronized (mLock) {
mDisableJobSchedulerJobs = disable;
@@ -323,14 +328,18 @@
}
/** Adds listener for package update */
- public void addPackagesUpdatedListener(PackagesUpdatedListener listener) {
+ public void addPackagesUpdatedListener(PackagesUpdatedListener listener)
+ throws LegacyDexoptDisabledException {
+ Installer.checkLegacyDexoptDisabled();
synchronized (mLock) {
mPackagesUpdatedListeners.add(listener);
}
}
/** Removes package update listener */
- public void removePackagesUpdatedListener(PackagesUpdatedListener listener) {
+ public void removePackagesUpdatedListener(PackagesUpdatedListener listener)
+ throws LegacyDexoptDisabledException {
+ Installer.checkLegacyDexoptDisabled();
synchronized (mLock) {
mPackagesUpdatedListeners.remove(listener);
}
@@ -340,7 +349,8 @@
* Notifies package change and removes the package from the failed package list so that
* the package can run dexopt again.
*/
- public void notifyPackageChanged(String packageName) {
+ public void notifyPackageChanged(String packageName) throws LegacyDexoptDisabledException {
+ Installer.checkLegacyDexoptDisabled();
// The idle maintenance job skips packages which previously failed to
// compile. The given package has changed and may successfully compile
// now. Remove it from the list of known failing packages.
@@ -387,37 +397,43 @@
// Post boot job not finished yet. Run post boot job first.
return false;
}
- resetStatesForNewDexOptRunLocked(mInjector.createAndStartThread(
- "BackgroundDexOptService_" + (isPostBootUpdateJob ? "PostBoot" : "Idle"),
- () -> {
- TimingsTraceAndSlog tr =
- new TimingsTraceAndSlog(TAG, Trace.TRACE_TAG_PACKAGE_MANAGER);
- tr.traceBegin("jobExecution");
- boolean completed = false;
- try {
- completed = runIdleOptimization(
- pm, pkgs, params.getJobId() == JOB_POST_BOOT_UPDATE);
- } finally { // Those cleanup should be done always.
- tr.traceEnd();
- Slog.i(TAG,
- "dexopt finishing. jobid:" + params.getJobId()
- + " completed:" + completed);
+ try {
+ resetStatesForNewDexOptRunLocked(mInjector.createAndStartThread(
+ "BackgroundDexOptService_" + (isPostBootUpdateJob ? "PostBoot" : "Idle"),
+ () -> {
+ TimingsTraceAndSlog tr =
+ new TimingsTraceAndSlog(TAG, Trace.TRACE_TAG_PACKAGE_MANAGER);
+ tr.traceBegin("jobExecution");
+ boolean completed = false;
+ try {
+ completed = runIdleOptimization(
+ pm, pkgs, params.getJobId() == JOB_POST_BOOT_UPDATE);
+ } catch (LegacyDexoptDisabledException e) {
+ Slog.wtf(TAG, e);
+ } finally { // Those cleanup should be done always.
+ tr.traceEnd();
+ Slog.i(TAG,
+ "dexopt finishing. jobid:" + params.getJobId()
+ + " completed:" + completed);
- writeStatsLog(params);
+ writeStatsLog(params);
- if (params.getJobId() == JOB_POST_BOOT_UPDATE) {
- if (completed) {
- markPostBootUpdateCompleted(params);
+ if (params.getJobId() == JOB_POST_BOOT_UPDATE) {
+ if (completed) {
+ markPostBootUpdateCompleted(params);
+ }
+ // Reschedule when cancelled
+ job.jobFinished(params, !completed);
+ } else {
+ // Periodic job
+ job.jobFinished(params, false /* reschedule */);
}
- // Reschedule when cancelled
- job.jobFinished(params, !completed);
- } else {
- // Periodic job
- job.jobFinished(params, false /* reschedule */);
+ markDexOptCompleted();
}
- markDexOptCompleted();
- }
- }));
+ }));
+ } catch (LegacyDexoptDisabledException e) {
+ Slog.wtf(TAG, e);
+ }
}
return true;
}
@@ -425,11 +441,16 @@
/** For BackgroundDexOptJobService to dispatch onStopJob event */
/* package */ boolean onStopJob(BackgroundDexOptJobService job, JobParameters params) {
Slog.i(TAG, "onStopJob:" + params.getJobId());
- // This cannot block as it is in main thread, thus dispatch to a newly created thread and
- // cancel it from there.
- // As this event does not happen often, creating a new thread is justified rather than
- // having one thread kept permanently.
- mInjector.createAndStartThread("DexOptCancel", this::cancelDexOptAndWaitForCompletion);
+ // This cannot block as it is in main thread, thus dispatch to a newly created thread
+ // and cancel it from there. As this event does not happen often, creating a new thread
+ // is justified rather than having one thread kept permanently.
+ mInjector.createAndStartThread("DexOptCancel", () -> {
+ try {
+ cancelDexOptAndWaitForCompletion();
+ } catch (LegacyDexoptDisabledException e) {
+ Slog.wtf(TAG, e);
+ }
+ });
// Always reschedule for cancellation.
return true;
}
@@ -438,7 +459,7 @@
* Cancels pending dexopt and wait for completion of the cancellation. This can block the caller
* until cancellation is done.
*/
- private void cancelDexOptAndWaitForCompletion() {
+ private void cancelDexOptAndWaitForCompletion() throws LegacyDexoptDisabledException {
synchronized (mLock) {
if (mDexOptThread == null) {
return;
@@ -496,7 +517,8 @@
}
@GuardedBy("mLock")
- private void resetStatesForNewDexOptRunLocked(Thread thread) {
+ private void resetStatesForNewDexOptRunLocked(Thread thread)
+ throws LegacyDexoptDisabledException {
mDexOptThread = thread;
mLastCancelledPackages.clear();
controlDexOptBlockingLocked(false);
@@ -510,7 +532,7 @@
}
@GuardedBy("mLock")
- private void controlDexOptBlockingLocked(boolean block) {
+ private void controlDexOptBlockingLocked(boolean block) throws LegacyDexoptDisabledException {
PackageManagerService pm = mInjector.getPackageManagerService();
mDexOptHelper.controlDexOptBlocking(block);
}
@@ -564,8 +586,8 @@
* Returns whether we've successfully run the job. Note that it will return true even if some
* packages may have failed compiling.
*/
- private boolean runIdleOptimization(
- PackageManagerService pm, List<String> pkgs, boolean isPostBootUpdate) {
+ private boolean runIdleOptimization(PackageManagerService pm, List<String> pkgs,
+ boolean isPostBootUpdate) throws LegacyDexoptDisabledException {
synchronized (mLock) {
mLastExecutionStatus = STATUS_UNSPECIFIED;
mLastExecutionStartUptimeMs = SystemClock.uptimeMillis();
@@ -631,7 +653,8 @@
@Status
private int idleOptimizePackages(PackageManagerService pm, List<String> pkgs,
- long lowStorageThreshold, boolean isPostBootUpdate) {
+ long lowStorageThreshold, boolean isPostBootUpdate)
+ throws LegacyDexoptDisabledException {
ArraySet<String> updatedPackages = new ArraySet<>();
try {
@@ -707,7 +730,8 @@
@Status
private int optimizePackages(List<String> pkgs, long lowStorageThreshold,
- ArraySet<String> updatedPackages, boolean isPostBootUpdate) {
+ ArraySet<String> updatedPackages, boolean isPostBootUpdate)
+ throws LegacyDexoptDisabledException {
boolean supportSecondaryDex = mInjector.supportSecondaryDex();
// Keep the error if there is any error from any package.
@@ -760,7 +784,8 @@
*/
@DexOptResult
private int downgradePackage(@NonNull Computer snapshot, PackageManagerService pm, String pkg,
- boolean isForPrimaryDex, boolean isPostBootUpdate) {
+ boolean isForPrimaryDex, boolean isPostBootUpdate)
+ throws LegacyDexoptDisabledException {
if (DEBUG) {
Slog.d(TAG, "Downgrading " + pkg);
}
@@ -808,7 +833,7 @@
}
@Status
- private int reconcileSecondaryDexFiles() {
+ private int reconcileSecondaryDexFiles() throws LegacyDexoptDisabledException {
// TODO(calin): should we denylist packages for which we fail to reconcile?
for (String p : mInjector.getDexManager().getAllPackagesWithSecondaryDexFiles()) {
if (isCancelling()) {
@@ -829,7 +854,8 @@
* @return PackageDexOptimizer#DEX_OPT_*
*/
@DexOptResult
- private int optimizePackage(String pkg, boolean isForPrimaryDex, boolean isPostBootUpdate) {
+ private int optimizePackage(String pkg, boolean isForPrimaryDex, boolean isPostBootUpdate)
+ throws LegacyDexoptDisabledException {
int reason = isPostBootUpdate ? PackageManagerService.REASON_POST_BOOT
: PackageManagerService.REASON_BACKGROUND_DEXOPT;
String filter = getCompilerFilterForReason(reason);
@@ -857,7 +883,8 @@
}
@DexOptResult
- private int performDexOptPrimary(String pkg, int reason, String filter, int dexoptFlags) {
+ private int performDexOptPrimary(String pkg, int reason, String filter, int dexoptFlags)
+ throws LegacyDexoptDisabledException {
DexoptOptions dexoptOptions =
new DexoptOptions(pkg, reason, filter, /*splitName=*/null, dexoptFlags);
return trackPerformDexOpt(pkg, /*isForPrimaryDex=*/true,
@@ -865,7 +892,8 @@
}
@DexOptResult
- private int performDexOptSecondary(String pkg, int reason, String filter, int dexoptFlags) {
+ private int performDexOptSecondary(String pkg, int reason, String filter, int dexoptFlags)
+ throws LegacyDexoptDisabledException {
DexoptOptions dexoptOptions = new DexoptOptions(pkg, reason, filter, /*splitName=*/null,
dexoptFlags | DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX);
return trackPerformDexOpt(pkg, /*isForPrimaryDex=*/false,
@@ -885,8 +913,9 @@
* {@link PackageDexOptimizer#DEX_OPT_FAILED}
*/
@DexOptResult
- private int trackPerformDexOpt(
- String pkg, boolean isForPrimaryDex, Supplier<Integer> performDexOptWrapper) {
+ private int trackPerformDexOpt(String pkg, boolean isForPrimaryDex,
+ ThrowingCheckedSupplier<Integer, LegacyDexoptDisabledException> performDexOptWrapper)
+ throws LegacyDexoptDisabledException {
ArraySet<String> failedPackageNames;
synchronized (mLock) {
failedPackageNames =
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index f433d6ad..d321b09 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -124,6 +124,7 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.modules.utils.TypedXmlSerializer;
+import com.android.server.pm.Installer.LegacyDexoptDisabledException;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.PackageDexUsage;
import com.android.server.pm.parsing.PackageInfoUtils;
@@ -3009,8 +3010,13 @@
ipw.println("[" + pkgName + "]");
ipw.increaseIndent();
- mPackageDexOptimizer.dumpDexoptState(ipw, pkg, pkgSetting,
- mDexManager.getPackageUseInfoOrDefault(pkgName));
+ // TODO(b/251903639): Call into ART Service.
+ try {
+ mPackageDexOptimizer.dumpDexoptState(ipw, pkg, pkgSetting,
+ mDexManager.getPackageUseInfoOrDefault(pkgName));
+ } catch (LegacyDexoptDisabledException e) {
+ throw new RuntimeException(e);
+ }
ipw.decreaseIndent();
}
ipw.println("BgDexopt state:");
diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java
index 69aaa0d..39aa9c1 100644
--- a/services/core/java/com/android/server/pm/DexOptHelper.java
+++ b/services/core/java/com/android/server/pm/DexOptHelper.java
@@ -66,6 +66,8 @@
import com.android.server.art.model.ArtFlags;
import com.android.server.art.model.OptimizeParams;
import com.android.server.art.model.OptimizeResult;
+import com.android.server.pm.Installer.InstallerException;
+import com.android.server.pm.Installer.LegacyDexoptDisabledException;
import com.android.server.pm.PackageDexOptimizer.DexOptResult;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.DexoptOptions;
@@ -125,8 +127,9 @@
* and {@code numberOfPackagesFailed}.
*/
public int[] performDexOptUpgrade(List<PackageStateInternal> packageStates, boolean showDialog,
- final int compilationReason, boolean bootComplete) {
-
+ final int compilationReason, boolean bootComplete)
+ throws LegacyDexoptDisabledException {
+ Installer.checkLegacyDexoptDisabled();
int numberOfPackagesVisited = 0;
int numberOfPackagesOptimized = 0;
int numberOfPackagesSkipped = 0;
@@ -158,7 +161,7 @@
// even if things are already compiled.
// useProfileForDexopt = true;
}
- } catch (Exception e) {
+ } catch (InstallerException | RuntimeException e) {
Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath() + " ",
e);
}
@@ -193,7 +196,7 @@
} else {
useProfileForDexopt = true;
}
- } catch (Exception e) {
+ } catch (InstallerException | RuntimeException e) {
Log.e(TAG, "Failed to copy profile "
+ profileFile.getAbsolutePath() + " ", e);
}
@@ -284,7 +287,8 @@
* Checks if system UI package (typically "com.android.systemui") needs to be re-compiled, and
* compiles it if needed.
*/
- private void checkAndDexOptSystemUi() {
+ private void checkAndDexOptSystemUi() throws LegacyDexoptDisabledException {
+ Installer.checkLegacyDexoptDisabled();
Computer snapshot = mPm.snapshotComputer();
String sysUiPackageName =
mPm.mContext.getString(com.android.internal.R.string.config_systemUi);
@@ -320,7 +324,7 @@
Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath());
}
}
- } catch (Exception e) {
+ } catch (InstallerException | RuntimeException e) {
Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath(), e);
}
}
@@ -341,7 +345,7 @@
}
@RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
- public void performPackageDexOptUpgradeIfNeeded() {
+ public void performPackageDexOptUpgradeIfNeeded() throws LegacyDexoptDisabledException {
PackageManagerServiceUtils.enforceSystemOrRoot(
"Only the system can request package update");
@@ -413,7 +417,12 @@
}
if (options.isDexoptOnlySecondaryDex()) {
- return mPm.getDexManager().dexoptSecondaryDex(options);
+ // TODO(b/251903639): Call into ART Service.
+ try {
+ return mPm.getDexManager().dexoptSecondaryDex(options);
+ } catch (LegacyDexoptDisabledException e) {
+ throw new RuntimeException(e);
+ }
} else {
int dexoptStatus = performDexOptWithStatus(options);
return dexoptStatus != PackageDexOptimizer.DEX_OPT_FAILED;
@@ -470,6 +479,8 @@
final long callingId = Binder.clearCallingIdentity();
try {
return performDexOptInternalWithDependenciesLI(p, pkgSetting, options);
+ } catch (LegacyDexoptDisabledException e) {
+ throw new RuntimeException(e);
} finally {
Binder.restoreCallingIdentity(callingId);
}
@@ -542,7 +553,8 @@
@DexOptResult
private int performDexOptInternalWithDependenciesLI(
- AndroidPackage p, @NonNull PackageStateInternal pkgSetting, DexoptOptions options) {
+ AndroidPackage p, @NonNull PackageStateInternal pkgSetting, DexoptOptions options)
+ throws LegacyDexoptDisabledException {
// System server gets a special path.
if (PLATFORM_PACKAGE_NAME.equals(p.getPackageName())) {
return mPm.getDexManager().dexoptSystemServer(options);
@@ -621,7 +633,11 @@
if (artSrvRes.isPresent()) {
res = artSrvRes.get();
} else {
- res = performDexOptInternalWithDependenciesLI(pkg, packageState, options);
+ try {
+ res = performDexOptInternalWithDependenciesLI(pkg, packageState, options);
+ } catch (LegacyDexoptDisabledException e) {
+ throw new RuntimeException(e);
+ }
}
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
@@ -666,8 +682,8 @@
return installerPkg.getUid() == Binder.getCallingUid();
}
- public boolean performDexOptSecondary(String packageName, String compilerFilter,
- boolean force) {
+ public boolean performDexOptSecondary(
+ String packageName, String compilerFilter, boolean force) {
int flags = DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX
| DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES
| DexoptOptions.DEXOPT_BOOT_COMPLETE
@@ -869,7 +885,7 @@
}
}
- /*package*/ void controlDexOptBlocking(boolean block) {
+ /*package*/ void controlDexOptBlocking(boolean block) throws LegacyDexoptDisabledException {
mPm.mPackageDexOptimizer.controlDexOptBlocking(block);
}
@@ -929,10 +945,17 @@
}
/**
+ * Returns true if ART Service should be used for package optimization.
+ */
+ public static boolean useArtService() {
+ return SystemProperties.getBoolean("dalvik.vm.useartservice", false);
+ }
+
+ /**
* Returns {@link DexUseManagerLocal} if ART Service should be used for package optimization.
*/
public static @Nullable DexUseManagerLocal getDexUseManagerLocal() {
- if (!"true".equals(SystemProperties.get("dalvik.vm.useartservice", ""))) {
+ if (!useArtService()) {
return null;
}
try {
@@ -946,7 +969,7 @@
* Returns {@link ArtManagerLocal} if ART Service should be used for package optimization.
*/
private static @Nullable ArtManagerLocal getArtManagerLocal() {
- if (!"true".equals(SystemProperties.get("dalvik.vm.useartservice", ""))) {
+ if (!useArtService()) {
return null;
}
try {
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 16bf0fe..6f7484e 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -46,6 +46,7 @@
import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL;
+import static com.android.server.pm.DexOptHelper.useArtService;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
import static com.android.server.pm.InstructionSets.getPreferredInstructionSet;
@@ -157,6 +158,7 @@
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.EventLogTags;
+import com.android.server.pm.Installer.LegacyDexoptDisabledException;
import com.android.server.pm.dex.ArtManagerService;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.DexoptOptions;
@@ -261,7 +263,8 @@
final PackageSetting oldPkgSetting = request.getScanRequestOldPackageSetting();
final PackageSetting originalPkgSetting = request.getScanRequestOriginalPackageSetting();
final String realPkgName = request.getRealPackageName();
- final List<String> changedAbiCodePath = request.getChangedAbiCodePath();
+ final List<String> changedAbiCodePath =
+ useArtService() ? null : request.getChangedAbiCodePath();
final PackageSetting pkgSetting;
if (request.getScanRequestPackageSetting() != null) {
SharedUserSetting requestSharedUserSetting = mPm.mSettings.getSharedUserSettingLPr(
@@ -362,6 +365,8 @@
}
pkgSetting.setSigningDetails(reconciledPkg.mSigningDetails);
+ // The conditional on useArtService() for changedAbiCodePath above means this is skipped
+ // when ART Service is in use, since it has its own dex file GC.
if (changedAbiCodePath != null && changedAbiCodePath.size() > 0) {
for (int i = changedAbiCodePath.size() - 1; i >= 0; --i) {
final String codePathString = changedAbiCodePath.get(i);
@@ -370,6 +375,8 @@
mPm.mInstaller.rmdex(codePathString,
getDexCodeInstructionSet(getPreferredInstructionSet()));
}
+ } catch (LegacyDexoptDisabledException e) {
+ throw new RuntimeException(e);
} catch (Installer.InstallerException ignored) {
}
}
@@ -2282,12 +2289,18 @@
pkg.getBaseApkPath(), pkg.getSplitCodePaths());
}
- // Prepare the application profiles for the new code paths.
- // This needs to be done before invoking dexopt so that any install-time profile
- // can be used for optimizations.
- mArtManagerService.prepareAppProfiles(
- pkg, mPm.resolveUserIds(installRequest.getUserId()),
- /* updateReferenceProfileContent= */ true);
+ if (!useArtService()) { // ART Service handles this on demand instead.
+ // Prepare the application profiles for the new code paths.
+ // This needs to be done before invoking dexopt so that any install-time profile
+ // can be used for optimizations.
+ try {
+ mArtManagerService.prepareAppProfiles(pkg,
+ mPm.resolveUserIds(installRequest.getUserId()),
+ /* updateReferenceProfileContent= */ true);
+ } catch (LegacyDexoptDisabledException e) {
+ throw new RuntimeException(e);
+ }
+ }
// Compute the compilation reason from the installation scenario.
final int compilationReason =
@@ -2365,19 +2378,30 @@
realPkgSetting.getPkgState().setUpdatedSystemApp(isUpdatedSystemApp);
- mPackageDexOptimizer.performDexOpt(pkg, realPkgSetting,
- null /* instructionSets */,
- mPm.getOrCreateCompilerPackageStats(pkg),
- mDexManager.getPackageUseInfoOrDefault(packageName),
- dexoptOptions);
+ // TODO(b/251903639): Call into ART Service.
+ try {
+ mPackageDexOptimizer.performDexOpt(pkg, realPkgSetting,
+ null /* instructionSets */, mPm.getOrCreateCompilerPackageStats(pkg),
+ mDexManager.getPackageUseInfoOrDefault(packageName), dexoptOptions);
+ } catch (LegacyDexoptDisabledException e) {
+ throw new RuntimeException(e);
+ }
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
- // Notify BackgroundDexOptService that the package has been changed.
- // If this is an update of a package which used to fail to compile,
- // BackgroundDexOptService will remove it from its denylist.
- // TODO: Layering violation
- BackgroundDexOptService.getService().notifyPackageChanged(packageName);
+ if (!useArtService()) {
+ // Notify BackgroundDexOptService that the package has been changed.
+ // If this is an update of a package which used to fail to compile,
+ // BackgroundDexOptService will remove it from its denylist.
+ // ART Service currently doesn't support this and will retry packages in every
+ // background dexopt.
+ // TODO: Layering violation
+ try {
+ BackgroundDexOptService.getService().notifyPackageChanged(packageName);
+ } catch (LegacyDexoptDisabledException e) {
+ throw new RuntimeException(e);
+ }
+ }
}
PackageManagerServiceUtils.waitForNativeBinariesExtractionForIncremental(
incrementalStorages);
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index a078b5d..9329f06 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -16,6 +16,8 @@
package com.android.server.pm;
+import static com.android.server.pm.DexOptHelper.useArtService;
+
import android.annotation.AppIdInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -609,11 +611,13 @@
* @throws InstallerException if {@code dexopt} fails.
*/
public boolean dexopt(String apkPath, int uid, String pkgName, String instructionSet,
- int dexoptNeeded, @Nullable String outputPath, int dexFlags,
- String compilerFilter, @Nullable String volumeUuid, @Nullable String classLoaderContext,
+ int dexoptNeeded, @Nullable String outputPath, int dexFlags, String compilerFilter,
+ @Nullable String volumeUuid, @Nullable String classLoaderContext,
@Nullable String seInfo, boolean downgrade, int targetSdkVersion,
@Nullable String profileName, @Nullable String dexMetadataPath,
- @Nullable String compilationReason) throws InstallerException {
+ @Nullable String compilationReason)
+ throws InstallerException, LegacyDexoptDisabledException {
+ checkLegacyDexoptDisabled();
assertValidInstructionSet(instructionSet);
BlockGuard.getVmPolicy().onPathAccess(apkPath);
BlockGuard.getVmPolicy().onPathAccess(outputPath);
@@ -637,7 +641,8 @@
*
* @param block set to true to enable blocking / false to disable blocking.
*/
- public void controlDexOptBlocking(boolean block) {
+ public void controlDexOptBlocking(boolean block) throws LegacyDexoptDisabledException {
+ checkLegacyDexoptDisabled();
try {
mInstalld.controlDexOptBlocking(block);
} catch (Exception e) {
@@ -655,7 +660,8 @@
* {@link #PROFILE_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES}
*/
public int mergeProfiles(int uid, String packageName, String profileName)
- throws InstallerException {
+ throws InstallerException, LegacyDexoptDisabledException {
+ checkLegacyDexoptDisabled();
if (!checkBeforeRemote()) return PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA;
try {
return mInstalld.mergeProfiles(uid, packageName, profileName);
@@ -668,8 +674,9 @@
* Dumps profiles associated with a package in a human readable format.
*/
public boolean dumpProfiles(int uid, String packageName, String profileName, String codePath,
- boolean dumpClassesAndMethods)
- throws InstallerException {
+ boolean dumpClassesAndMethods)
+ throws InstallerException, LegacyDexoptDisabledException {
+ checkLegacyDexoptDisabled();
if (!checkBeforeRemote()) return false;
BlockGuard.getVmPolicy().onPathAccess(codePath);
try {
@@ -681,7 +688,8 @@
}
public boolean copySystemProfile(String systemProfile, int uid, String packageName,
- String profileName) throws InstallerException {
+ String profileName) throws InstallerException, LegacyDexoptDisabledException {
+ checkLegacyDexoptDisabled();
if (!checkBeforeRemote()) return false;
try {
return mInstalld.copySystemProfile(systemProfile, uid, packageName, profileName);
@@ -690,7 +698,9 @@
}
}
- public void rmdex(String codePath, String instructionSet) throws InstallerException {
+ public void rmdex(String codePath, String instructionSet)
+ throws InstallerException, LegacyDexoptDisabledException {
+ checkLegacyDexoptDisabled();
assertValidInstructionSet(instructionSet);
if (!checkBeforeRemote()) return;
BlockGuard.getVmPolicy().onPathAccess(codePath);
@@ -714,7 +724,9 @@
}
}
- public void clearAppProfiles(String packageName, String profileName) throws InstallerException {
+ public void clearAppProfiles(String packageName, String profileName)
+ throws InstallerException, LegacyDexoptDisabledException {
+ checkLegacyDexoptDisabled();
if (!checkBeforeRemote()) return;
try {
mInstalld.clearAppProfiles(packageName, profileName);
@@ -723,7 +735,9 @@
}
}
- public void destroyAppProfiles(String packageName) throws InstallerException {
+ public void destroyAppProfiles(String packageName)
+ throws InstallerException, LegacyDexoptDisabledException {
+ checkLegacyDexoptDisabled();
if (!checkBeforeRemote()) return;
try {
mInstalld.destroyAppProfiles(packageName);
@@ -737,7 +751,8 @@
* @throws InstallerException if the deletion fails.
*/
public void deleteReferenceProfile(String packageName, String profileName)
- throws InstallerException {
+ throws InstallerException, LegacyDexoptDisabledException {
+ checkLegacyDexoptDisabled();
if (!checkBeforeRemote()) return;
try {
mInstalld.deleteReferenceProfile(packageName, profileName);
@@ -799,7 +814,8 @@
* Creates an oat dir for given package and instruction set.
*/
public void createOatDir(String packageName, String oatDir, String dexInstructionSet)
- throws InstallerException {
+ throws InstallerException, LegacyDexoptDisabledException {
+ checkLegacyDexoptDisabled();
if (!checkBeforeRemote()) return;
try {
mInstalld.createOatDir(packageName, oatDir, dexInstructionSet);
@@ -843,7 +859,8 @@
* of freed bytes.
*/
public long deleteOdex(String packageName, String apkPath, String instructionSet,
- String outputPath) throws InstallerException {
+ String outputPath) throws InstallerException, LegacyDexoptDisabledException {
+ checkLegacyDexoptDisabled();
if (!checkBeforeRemote()) return -1;
BlockGuard.getVmPolicy().onPathAccess(apkPath);
BlockGuard.getVmPolicy().onPathAccess(outputPath);
@@ -855,7 +872,9 @@
}
public boolean reconcileSecondaryDexFile(String apkPath, String packageName, int uid,
- String[] isas, @Nullable String volumeUuid, int flags) throws InstallerException {
+ String[] isas, @Nullable String volumeUuid, int flags)
+ throws InstallerException, LegacyDexoptDisabledException {
+ checkLegacyDexoptDisabled();
for (int i = 0; i < isas.length; i++) {
assertValidInstructionSet(isas[i]);
}
@@ -881,7 +900,8 @@
}
public boolean createProfileSnapshot(int appId, String packageName, String profileName,
- String classpath) throws InstallerException {
+ String classpath) throws InstallerException, LegacyDexoptDisabledException {
+ checkLegacyDexoptDisabled();
if (!checkBeforeRemote()) return false;
try {
return mInstalld.createProfileSnapshot(appId, packageName, profileName, classpath);
@@ -891,7 +911,8 @@
}
public void destroyProfileSnapshot(String packageName, String profileName)
- throws InstallerException {
+ throws InstallerException, LegacyDexoptDisabledException {
+ checkLegacyDexoptDisabled();
if (!checkBeforeRemote()) return;
try {
mInstalld.destroyProfileSnapshot(packageName, profileName);
@@ -952,7 +973,9 @@
* </ul>
*/
public boolean prepareAppProfile(String pkg, @UserIdInt int userId, @AppIdInt int appId,
- String profileName, String codePath, String dexMetadataPath) throws InstallerException {
+ String profileName, String codePath, String dexMetadataPath)
+ throws InstallerException, LegacyDexoptDisabledException {
+ checkLegacyDexoptDisabled();
if (!checkBeforeRemote()) return false;
BlockGuard.getVmPolicy().onPathAccess(codePath);
BlockGuard.getVmPolicy().onPathAccess(dexMetadataPath);
@@ -1117,7 +1140,8 @@
* @throws InstallerException if failed to get the visibility of the optimized artifacts.
*/
public int getOdexVisibility(String packageName, String apkPath, String instructionSet,
- String outputPath) throws InstallerException {
+ String outputPath) throws InstallerException, LegacyDexoptDisabledException {
+ checkLegacyDexoptDisabled();
if (!checkBeforeRemote()) return -1;
BlockGuard.getVmPolicy().onPathAccess(apkPath);
BlockGuard.getVmPolicy().onPathAccess(outputPath);
@@ -1137,4 +1161,26 @@
throw new InstallerException(e.toString());
}
}
+
+ /**
+ * A checked exception that is thrown in legacy dexopt code paths when ART Service should be
+ * used instead.
+ */
+ public static class LegacyDexoptDisabledException extends Exception {
+ // TODO(b/260124949): Remove the legacy dexopt code paths, i.e. this exception and all code
+ // that may throw it.
+ public LegacyDexoptDisabledException() {
+ super("Invalid call to legacy dexopt installd method while ART Service is in use.");
+ }
+ }
+
+ /**
+ * Throws LegacyDexoptDisabledException if ART Service should be used instead of the
+ * {@link android.os.IInstalld} method that follows this method call.
+ */
+ public static void checkLegacyDexoptDisabled() throws LegacyDexoptDisabledException {
+ if (useArtService()) {
+ throw new LegacyDexoptDisabledException();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 56f2493..490b2a9 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -37,6 +37,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.server.LocalServices;
import com.android.server.pm.Installer.InstallerException;
+import com.android.server.pm.Installer.LegacyDexoptDisabledException;
import com.android.server.pm.dex.DexoptOptions;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.pkg.AndroidPackage;
@@ -167,7 +168,13 @@
Log.i(TAG, "Low on space, deleting oat files in an attempt to free up space: "
+ DexOptHelper.packagesToString(others));
for (PackageStateInternal pkg : others) {
- mPackageManagerService.deleteOatArtifactsOfPackage(snapshot, pkg.getPackageName());
+ // TODO(b/251903639): Call into ART Service.
+ try {
+ mPackageManagerService.deleteOatArtifactsOfPackage(
+ snapshot, pkg.getPackageName());
+ } catch (LegacyDexoptDisabledException e) {
+ throw new RuntimeException(e);
+ }
}
}
long spaceAvailableNow = getAvailableSpace();
@@ -188,7 +195,7 @@
+ pkgSetting.getTransientState()
.getLatestForegroundPackageUseTimeInMills());
}
- } catch (Exception ignored) {
+ } catch (RuntimeException ignored) {
}
}
}
@@ -352,13 +359,17 @@
PackageDexOptimizer optimizer = new OTADexoptPackageDexOptimizer(
collectingInstaller, mPackageManagerService.mInstallLock, mContext);
- optimizer.performDexOpt(pkg, pkgSetting,
- null /* ISAs */,
- null /* CompilerStats.PackageStats */,
- mPackageManagerService.getDexManager().getPackageUseInfoOrDefault(
- pkg.getPackageName()),
- new DexoptOptions(pkg.getPackageName(), compilationReason,
- DexoptOptions.DEXOPT_BOOT_COMPLETE));
+ // TODO(b/251903639): Allow this use of legacy dexopt code even when ART Service is enabled.
+ try {
+ optimizer.performDexOpt(pkg, pkgSetting, null /* ISAs */,
+ null /* CompilerStats.PackageStats */,
+ mPackageManagerService.getDexManager().getPackageUseInfoOrDefault(
+ pkg.getPackageName()),
+ new DexoptOptions(pkg.getPackageName(), compilationReason,
+ DexoptOptions.DEXOPT_BOOT_COMPLETE));
+ } catch (LegacyDexoptDisabledException e) {
+ throw new RuntimeException(e);
+ }
return commands;
}
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index aaf8755..0a71892 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -71,6 +71,7 @@
import com.android.server.LocalServices;
import com.android.server.apphibernation.AppHibernationManagerInternal;
import com.android.server.pm.Installer.InstallerException;
+import com.android.server.pm.Installer.LegacyDexoptDisabledException;
import com.android.server.pm.dex.ArtManagerService;
import com.android.server.pm.dex.ArtStatsLogUtils;
import com.android.server.pm.dex.ArtStatsLogUtils.ArtStatsLogger;
@@ -219,7 +220,8 @@
@DexOptResult
int performDexOpt(AndroidPackage pkg, @NonNull PackageStateInternal pkgSetting,
String[] instructionSets, CompilerStats.PackageStats packageStats,
- PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
+ PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options)
+ throws LegacyDexoptDisabledException {
if (PLATFORM_PACKAGE_NAME.equals(pkg.getPackageName())) {
throw new IllegalArgumentException("System server dexopting should be done via "
+ " DexManager and PackageDexOptimizer#dexoptSystemServerPath");
@@ -245,7 +247,7 @@
/**
* Cancels currently running dex optimization.
*/
- void controlDexOptBlocking(boolean block) {
+ void controlDexOptBlocking(boolean block) throws LegacyDexoptDisabledException {
// This method should not hold mInstallLock as cancelling should be possible while
// the lock is held by other thread running performDexOpt.
getInstallerWithoutLock().controlDexOptBlocking(block);
@@ -259,7 +261,8 @@
@DexOptResult
private int performDexOptLI(AndroidPackage pkg, @NonNull PackageStateInternal pkgSetting,
String[] targetInstructionSets, CompilerStats.PackageStats packageStats,
- PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
+ PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options)
+ throws LegacyDexoptDisabledException {
// ClassLoader only refers non-native (jar) shared libraries and must ignore
// native (so) shared libraries. See also LoadedApk#createSharedLibraryLoader().
final List<SharedLibraryInfo> sharedLibraries = pkgSetting.getTransientState()
@@ -439,7 +442,7 @@
*/
@GuardedBy("mInstallLock")
private boolean prepareCloudProfile(AndroidPackage pkg, String profileName, String path,
- @Nullable String dexMetadataPath) {
+ @Nullable String dexMetadataPath) throws LegacyDexoptDisabledException {
if (dexMetadataPath != null) {
try {
// Make sure we don't keep any existing contents.
@@ -472,7 +475,7 @@
String path, String isa, String compilerFilter, int profileAnalysisResult,
String classLoaderContext, int dexoptFlags, int uid,
CompilerStats.PackageStats packageStats, boolean downgrade, String profileName,
- String dexMetadataPath, int compilationReason) {
+ String dexMetadataPath, int compilationReason) throws LegacyDexoptDisabledException {
String oatDir = getPackageOatDirIfSupported(pkgSetting, pkg);
int dexoptNeeded = getDexoptNeeded(pkg.getPackageName(), path, isa, compilerFilter,
@@ -525,8 +528,8 @@
*/
@GuardedBy("mInstallLock")
@DexOptResult
- public int dexoptSystemServerPath(
- String dexPath, PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options) {
+ public int dexoptSystemServerPath(String dexPath, PackageDexUsage.DexUseInfo dexUseInfo,
+ DexoptOptions options) throws LegacyDexoptDisabledException {
int dexoptFlags = DEXOPT_PUBLIC
| (options.isBootComplete() ? DEXOPT_BOOTCOMPLETE : 0)
| (options.isDexoptIdleBackgroundJob() ? DEXOPT_IDLE_BACKGROUND_JOB : 0);
@@ -601,7 +604,8 @@
*/
@DexOptResult
public int dexOptSecondaryDexPath(ApplicationInfo info, String path,
- PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options) {
+ PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options)
+ throws LegacyDexoptDisabledException {
if (info.uid == -1) {
throw new IllegalArgumentException("Dexopt for path " + path + " has invalid uid.");
}
@@ -643,7 +647,7 @@
+ " time out. Operation took " + duration + " ms. Thread: "
+ Thread.currentThread().getName());
}
- } catch (Exception e) {
+ } catch (RuntimeException e) {
Slog.wtf(TAG, "Error while releasing " + mDexoptWakeLock.getTag() + " lock", e);
}
}
@@ -651,7 +655,8 @@
@GuardedBy("mInstallLock")
@DexOptResult
private int dexOptSecondaryDexPathLI(ApplicationInfo info, String path,
- PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options) {
+ PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options)
+ throws LegacyDexoptDisabledException {
String compilerFilter = getRealCompilerFilter(info, options.getCompilerFilter(),
dexUseInfo.isUsedByOtherApps());
// Get the dexopt flags after getRealCompilerFilter to make sure we get the correct flags.
@@ -729,7 +734,8 @@
* Dumps the dexopt state of the given package {@code pkg} to the given {@code PrintWriter}.
*/
void dumpDexoptState(IndentingPrintWriter pw, AndroidPackage pkg,
- PackageStateInternal pkgSetting, PackageDexUsage.PackageUseInfo useInfo) {
+ PackageStateInternal pkgSetting, PackageDexUsage.PackageUseInfo useInfo)
+ throws LegacyDexoptDisabledException {
final String[] instructionSets = getAppDexInstructionSets(pkgSetting.getPrimaryCpuAbi(),
pkgSetting.getSecondaryCpuAbi());
final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
@@ -929,7 +935,8 @@
@GuardedBy("mInstallLock")
private int getDexoptNeeded(String packageName, String path, String isa, String compilerFilter,
String classLoaderContext, int profileAnalysisResult, boolean downgrade,
- int dexoptFlags, String oatDir) {
+ int dexoptFlags, String oatDir) throws LegacyDexoptDisabledException {
+ Installer.checkLegacyDexoptDisabled();
final boolean shouldBePublic = (dexoptFlags & DEXOPT_PUBLIC) != 0;
final boolean isProfileGuidedFilter = (dexoptFlags & DEXOPT_PROFILE_GUIDED) != 0;
boolean newProfile = profileAnalysisResult == PROFILE_ANALYSIS_OPTIMIZE;
@@ -962,7 +969,7 @@
} catch (IOException ioe) {
Slog.w(TAG, "IOException reading apk: " + path, ioe);
return DEX_OPT_FAILED;
- } catch (Exception e) {
+ } catch (RuntimeException e) {
Slog.wtf(TAG, "Unexpected exception when calling dexoptNeeded on " + path, e);
return DEX_OPT_FAILED;
}
@@ -976,11 +983,12 @@
/** Returns true if the current artifacts of the app are private to the app itself. */
@GuardedBy("mInstallLock")
- private boolean isOdexPrivate(String packageName, String path, String isa, String oatDir) {
+ private boolean isOdexPrivate(String packageName, String path, String isa, String oatDir)
+ throws LegacyDexoptDisabledException {
try {
return mInstaller.getOdexVisibility(packageName, path, isa, oatDir)
== Installer.ODEX_IS_PRIVATE;
- } catch (Exception e) {
+ } catch (InstallerException e) {
Slog.w(TAG, "Failed to get odex visibility for " + path, e);
return false;
}
@@ -996,7 +1004,7 @@
* may return a different result.
*/
private int analyseProfiles(AndroidPackage pkg, int uid, String profileName,
- String compilerFilter) {
+ String compilerFilter) throws LegacyDexoptDisabledException {
// Check if we are allowed to merge and if the compiler filter is profile guided.
if (!isProfileGuidedCompilerFilter(compilerFilter)) {
return PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index afcd9d1..0556c3b 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -48,6 +48,7 @@
import static com.android.internal.util.XmlUtils.writeByteArrayAttribute;
import static com.android.internal.util.XmlUtils.writeStringAttribute;
import static com.android.internal.util.XmlUtils.writeUriAttribute;
+import static com.android.server.pm.DexOptHelper.useArtService;
import static com.android.server.pm.PackageInstallerService.prepareStageDir;
import static com.android.server.pm.PackageManagerService.APP_METADATA_FILE_NAME;
@@ -164,6 +165,7 @@
import com.android.modules.utils.TypedXmlSerializer;
import com.android.server.LocalServices;
import com.android.server.pm.Installer.InstallerException;
+import com.android.server.pm.Installer.LegacyDexoptDisabledException;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
@@ -2382,9 +2384,15 @@
}
if (isLinkPossible(fromFiles, toDir)) {
- if (!mResolvedInstructionSets.isEmpty()) {
- final File oatDir = new File(toDir, "oat");
- createOatDirs(tempPackageName, mResolvedInstructionSets, oatDir);
+ if (!useArtService()) { // ART Service creates oat dirs on demand instead.
+ if (!mResolvedInstructionSets.isEmpty()) {
+ final File oatDir = new File(toDir, "oat");
+ try {
+ createOatDirs(tempPackageName, mResolvedInstructionSets, oatDir);
+ } catch (LegacyDexoptDisabledException e) {
+ throw new RuntimeException(e);
+ }
+ }
}
// pre-create lib dirs for linking if necessary
if (!mResolvedNativeLibPaths.isEmpty()) {
@@ -3629,7 +3637,7 @@
}
private void createOatDirs(String packageName, List<String> instructionSets, File fromDir)
- throws PackageManagerException {
+ throws PackageManagerException, LegacyDexoptDisabledException {
for (String instructionSet : instructionSets) {
try {
mInstaller.createOatDir(packageName, fromDir.getAbsolutePath(), instructionSet);
diff --git a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
index fb47c8a..04f5e56 100644
--- a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
+++ b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
@@ -47,6 +47,7 @@
import android.util.ArraySet;
import android.util.SparseArray;
+import com.android.server.pm.Installer.LegacyDexoptDisabledException;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.DynamicCodeLogger;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
@@ -708,7 +709,12 @@
@Override
@Deprecated
public final long deleteOatArtifactsOfPackage(String packageName) {
- return mService.deleteOatArtifactsOfPackage(snapshot(), packageName);
+ // TODO(b/251903639): Call into ART Service.
+ try {
+ return mService.deleteOatArtifactsOfPackage(snapshot(), packageName);
+ } catch (LegacyDexoptDisabledException e) {
+ throw new RuntimeException(e);
+ }
}
@Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b825ec4..399e32a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -39,6 +39,7 @@
import static com.android.internal.annotations.VisibleForTesting.Visibility;
import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_INIT_TIME;
+import static com.android.server.pm.DexOptHelper.useArtService;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
import static com.android.server.pm.InstructionSets.getPreferredInstructionSet;
import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
@@ -198,6 +199,7 @@
import com.android.server.compat.CompatChange;
import com.android.server.compat.PlatformCompat;
import com.android.server.pm.Installer.InstallerException;
+import com.android.server.pm.Installer.LegacyDexoptDisabledException;
import com.android.server.pm.Settings.VersionInfo;
import com.android.server.pm.dex.ArtManagerService;
import com.android.server.pm.dex.ArtUtils;
@@ -2136,16 +2138,19 @@
// the rest of the commands above) because there's precious little we
// can do about it. A settings error is reported, though.
final List<String> changedAbiCodePath =
- ScanPackageUtils.applyAdjustedAbiToSharedUser(
- setting, null /*scannedPackage*/,
+ ScanPackageUtils.applyAdjustedAbiToSharedUser(setting,
+ null /*scannedPackage*/,
mInjector.getAbiHelper().getAdjustedAbiForSharedUser(
setting.getPackageStates(), null /*scannedPackage*/));
- if (changedAbiCodePath != null && changedAbiCodePath.size() > 0) {
+ if (!useArtService() && // Skip for ART Service since it has its own dex file GC.
+ changedAbiCodePath != null && changedAbiCodePath.size() > 0) {
for (int i = changedAbiCodePath.size() - 1; i >= 0; --i) {
final String codePathString = changedAbiCodePath.get(i);
try {
mInstaller.rmdex(codePathString,
getDexCodeInstructionSet(getPreferredInstructionSet()));
+ } catch (LegacyDexoptDisabledException e) {
+ throw new RuntimeException(e);
} catch (InstallerException ignored) {
}
}
@@ -2952,7 +2957,12 @@
}
public void updatePackagesIfNeeded() {
- mDexOptHelper.performPackageDexOptUpgradeIfNeeded();
+ // TODO(b/251903639): Call into ART Service.
+ try {
+ mDexOptHelper.performPackageDexOptUpgradeIfNeeded();
+ } catch (LegacyDexoptDisabledException e) {
+ throw new RuntimeException(e);
+ }
}
private void notifyPackageUseInternal(String packageName, int reason) {
@@ -4266,7 +4276,12 @@
}
});
- mBackgroundDexOptService.systemReady();
+ // TODO(b/251903639): Call into ART Service.
+ try {
+ mBackgroundDexOptService.systemReady();
+ } catch (LegacyDexoptDisabledException e) {
+ throw new RuntimeException(e);
+ }
// Prune unused static shared libraries which have been cached a period of time
schedulePruneUnusedStaticSharedLibraries(false /* delay */);
@@ -4615,7 +4630,8 @@
final Computer snapshot = snapshotComputer();
final AndroidPackage pkg = snapshot.getPackage(packageName);
- try (PackageFreezer ignored = freezePackage(packageName, "clearApplicationProfileData")) {
+ try (PackageFreezer ignored =
+ freezePackage(packageName, "clearApplicationProfileData")) {
synchronized (mInstallLock) {
mAppDataHelper.clearAppProfilesLIF(pkg);
}
@@ -4797,9 +4813,14 @@
throw new IllegalArgumentException("Unknown package: " + packageName);
}
+ // TODO(b/251903639): Call into ART Service.
synchronized (mInstallLock) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dump profiles");
- mArtManagerService.dumpProfiles(pkg, dumpClassesAndMethods);
+ try {
+ mArtManagerService.dumpProfiles(pkg, dumpClassesAndMethods);
+ } catch (LegacyDexoptDisabledException e) {
+ throw new RuntimeException(e);
+ }
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
@@ -5497,20 +5518,40 @@
*/
@Override
public void reconcileSecondaryDexFiles(String packageName) {
+ if (useArtService()) {
+ // ART Service currently relies on a GC to find stale oat files, including secondary
+ // dex files. Hence it doesn't use this call for anything.
+ return;
+ }
+
final Computer snapshot = snapshotComputer();
if (snapshot.getInstantAppPackageName(Binder.getCallingUid()) != null) {
return;
} else if (snapshot.isInstantAppInternal(
- packageName, UserHandle.getCallingUserId(), Process.SYSTEM_UID)) {
+ packageName, UserHandle.getCallingUserId(), Process.SYSTEM_UID)) {
return;
}
- mDexManager.reconcileSecondaryDexFiles(packageName);
+ try {
+ mDexManager.reconcileSecondaryDexFiles(packageName);
+ } catch (LegacyDexoptDisabledException e) {
+ throw new RuntimeException(e);
+ }
}
@Override
public void registerDexModule(String packageName, String dexModulePath,
boolean isSharedModule,
IDexModuleRegisterCallback callback) {
+ if (useArtService()) {
+ // ART Service currently doesn't support this explicit dexopting and instead relies
+ // on background dexopt for secondary dex files. This API is problematic since it
+ // doesn't provide the correct classloader context.
+ Slog.i(TAG,
+ "Ignored unsupported registerDexModule call for " + dexModulePath + " in "
+ + packageName);
+ return;
+ }
+
int userId = UserHandle.getCallingUserId();
ApplicationInfo ai = snapshot().getApplicationInfo(packageName, /*flags*/ 0, userId);
DexManager.RegisterDexModuleResult result;
@@ -5520,7 +5561,12 @@
" calling user. package=" + packageName + ", user=" + userId);
result = new DexManager.RegisterDexModuleResult(false, "Package not installed");
} else {
- result = mDexManager.registerDexModule(ai, dexModulePath, isSharedModule, userId);
+ try {
+ result = mDexManager.registerDexModule(
+ ai, dexModulePath, isSharedModule, userId);
+ } catch (LegacyDexoptDisabledException e) {
+ throw new RuntimeException(e);
+ }
}
if (callback != null) {
@@ -6994,7 +7040,8 @@
return AndroidPackageUtils.canHaveOatDir(packageState, packageState.getPkg());
}
- long deleteOatArtifactsOfPackage(@NonNull Computer snapshot, String packageName) {
+ long deleteOatArtifactsOfPackage(@NonNull Computer snapshot, String packageName)
+ throws LegacyDexoptDisabledException {
PackageManagerServiceUtils.enforceSystemOrRootOrShell(
"Only the system or shell can delete oat artifacts");
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 2138c20..6af1d0f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -113,6 +113,7 @@
import com.android.server.LocalServices;
import com.android.server.SystemConfig;
import com.android.server.art.ArtManagerLocal;
+import com.android.server.pm.Installer.LegacyDexoptDisabledException;
import com.android.server.pm.PackageManagerShellCommandDataLoader.Metadata;
import com.android.server.pm.permission.LegacyPermissionManagerInternal;
import com.android.server.pm.permission.PermissionAllowlist;
@@ -1953,52 +1954,62 @@
}
private int runBgDexOpt() throws RemoteException {
- String opt = getNextOption();
+ // TODO(b/251903639): Call into ART Service.
+ try {
+ String opt = getNextOption();
- if (opt == null) {
- List<String> packageNames = new ArrayList<>();
- String arg;
- while ((arg = getNextArg()) != null) {
- packageNames.add(arg);
- }
- if (!BackgroundDexOptService.getService().runBackgroundDexoptJob(
- packageNames.isEmpty() ? null : packageNames)) {
- getOutPrintWriter().println("Failure");
- return -1;
- }
- } else {
- String extraArg = getNextArg();
- if (extraArg != null) {
- getErrPrintWriter().println("Invalid argument: " + extraArg);
- return -1;
- }
-
- switch (opt) {
- case "--cancel":
- return cancelBgDexOptJob();
-
- case "--disable":
- BackgroundDexOptService.getService().setDisableJobSchedulerJobs(true);
- break;
-
- case "--enable":
- BackgroundDexOptService.getService().setDisableJobSchedulerJobs(false);
- break;
-
- default:
- getErrPrintWriter().println("Unknown option: " + opt);
+ if (opt == null) {
+ List<String> packageNames = new ArrayList<>();
+ String arg;
+ while ((arg = getNextArg()) != null) {
+ packageNames.add(arg);
+ }
+ if (!BackgroundDexOptService.getService().runBackgroundDexoptJob(
+ packageNames.isEmpty() ? null : packageNames)) {
+ getOutPrintWriter().println("Failure");
return -1;
- }
- }
+ }
+ } else {
+ String extraArg = getNextArg();
+ if (extraArg != null) {
+ getErrPrintWriter().println("Invalid argument: " + extraArg);
+ return -1;
+ }
- getOutPrintWriter().println("Success");
- return 0;
+ switch (opt) {
+ case "--cancel":
+ return cancelBgDexOptJob();
+
+ case "--disable":
+ BackgroundDexOptService.getService().setDisableJobSchedulerJobs(true);
+ break;
+
+ case "--enable":
+ BackgroundDexOptService.getService().setDisableJobSchedulerJobs(false);
+ break;
+
+ default:
+ getErrPrintWriter().println("Unknown option: " + opt);
+ return -1;
+ }
+ }
+
+ getOutPrintWriter().println("Success");
+ return 0;
+ } catch (LegacyDexoptDisabledException e) {
+ throw new RuntimeException(e);
+ }
}
private int cancelBgDexOptJob() throws RemoteException {
- BackgroundDexOptService.getService().cancelBackgroundDexoptJob();
- getOutPrintWriter().println("Success");
- return 0;
+ // TODO(b/251903639): Call into ART Service.
+ try {
+ BackgroundDexOptService.getService().cancelBackgroundDexoptJob();
+ getOutPrintWriter().println("Success");
+ return 0;
+ } catch (LegacyDexoptDisabledException e) {
+ throw new RuntimeException(e);
+ }
}
private int runDeleteDexOpt() throws RemoteException {
@@ -2008,8 +2019,8 @@
pw.println("Error: no package name");
return 1;
}
- long freedBytes = LocalServices.getService(
- PackageManagerInternal.class).deleteOatArtifactsOfPackage(packageName);
+ long freedBytes = LocalServices.getService(PackageManagerInternal.class)
+ .deleteOatArtifactsOfPackage(packageName);
if (freedBytes < 0) {
pw.println("Error: delete failed");
return 1;
@@ -2273,7 +2284,7 @@
if (abandonSession) {
try {
doAbandonSession(sessionId, false /*logSuccess*/);
- } catch (Exception ignore) {
+ } catch (RuntimeException ignore) {
}
}
}
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index eb99536..e5aaddb 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -45,6 +45,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
+import com.android.server.pm.Installer.LegacyDexoptDisabledException;
import com.android.server.pm.parsing.PackageCacher;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.parsing.pkg.PackageImpl;
@@ -424,8 +425,11 @@
String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
for (String codePath : allCodePaths) {
for (String dexCodeInstructionSet : dexCodeInstructionSets) {
+ // TODO(b/251903639): Call into ART Service.
try {
mPm.mInstaller.rmdex(codePath, dexCodeInstructionSet);
+ } catch (LegacyDexoptDisabledException e) {
+ throw new RuntimeException(e);
} catch (Installer.InstallerException ignored) {
}
}
diff --git a/services/core/java/com/android/server/pm/ScanResult.java b/services/core/java/com/android/server/pm/ScanResult.java
index 750e893..2af01d6 100644
--- a/services/core/java/com/android/server/pm/ScanResult.java
+++ b/services/core/java/com/android/server/pm/ScanResult.java
@@ -47,6 +47,9 @@
*/
@Nullable
public final PackageSetting mPkgSetting;
+
+ // TODO(b/260124949): Check if this can be dropped when the legacy PackageManager dexopt code is
+ // cleaned up.
/** ABI code paths that have changed in the package scan */
@Nullable public final List<String> mChangedAbiCodePath;
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index e19ebce..b5b6347 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -52,6 +52,7 @@
import com.android.server.LocalServices;
import com.android.server.pm.Installer;
import com.android.server.pm.Installer.InstallerException;
+import com.android.server.pm.Installer.LegacyDexoptDisabledException;
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.PackageManagerServiceCompilerMapping;
import com.android.server.pm.parsing.PackageInfoUtils;
@@ -177,7 +178,6 @@
== ApplicationInfo.FLAG_DEBUGGABLE;
}
-
@Override
public void snapshotRuntimeProfile(@ProfileType int profileType, @Nullable String packageName,
@Nullable String codePath, @NonNull ISnapshotRuntimeProfileCallback callback,
@@ -210,15 +210,20 @@
Slog.d(TAG, "Requested snapshot for " + packageName + ":" + codePath);
}
- if (bootImageProfile) {
- snapshotBootImageProfile(callback);
- } else {
- snapshotAppProfile(packageName, codePath, callback);
+ // TODO(b/251903639): Call into ART Service.
+ try {
+ if (bootImageProfile) {
+ snapshotBootImageProfile(callback);
+ } else {
+ snapshotAppProfile(packageName, codePath, callback);
+ }
+ } catch (LegacyDexoptDisabledException e) {
+ throw new RuntimeException(e);
}
}
private void snapshotAppProfile(String packageName, String codePath,
- ISnapshotRuntimeProfileCallback callback) {
+ ISnapshotRuntimeProfileCallback callback) throws LegacyDexoptDisabledException {
PackageInfo info = null;
try {
// Note that we use the default user 0 to retrieve the package info.
@@ -269,7 +274,8 @@
}
private void createProfileSnapshot(String packageName, String profileName, String classpath,
- int appId, ISnapshotRuntimeProfileCallback callback) {
+ int appId, ISnapshotRuntimeProfileCallback callback)
+ throws LegacyDexoptDisabledException {
// Ask the installer to snapshot the profile.
try {
if (!mInstaller.createProfileSnapshot(appId, packageName, profileName, classpath)) {
@@ -299,7 +305,8 @@
}
}
- private void destroyProfileSnapshot(String packageName, String profileName) {
+ private void destroyProfileSnapshot(String packageName, String profileName)
+ throws LegacyDexoptDisabledException {
if (DEBUG) {
Slog.d(TAG, "Destroying profile snapshot for" + packageName + ":" + profileName);
}
@@ -333,7 +340,8 @@
}
}
- private void snapshotBootImageProfile(ISnapshotRuntimeProfileCallback callback) {
+ private void snapshotBootImageProfile(ISnapshotRuntimeProfileCallback callback)
+ throws LegacyDexoptDisabledException {
// Combine the profiles for boot classpath and system server classpath.
// This avoids having yet another type of profiles and simplifies the processing.
String classpath = String.join(":", Os.getenv("BOOTCLASSPATH"),
@@ -364,7 +372,7 @@
mHandler.post(() -> {
try {
callback.onError(errCode);
- } catch (Exception e) {
+ } catch (RemoteException | RuntimeException e) {
Slog.w(TAG, "Failed to callback after profile snapshot for " + packageName, e);
}
});
@@ -387,7 +395,7 @@
+ packageName);
callback.onError(ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
}
- } catch (Exception e) {
+ } catch (RemoteException | RuntimeException e) {
Slog.w(TAG,
"Failed to call onSuccess after profile snapshot for " + packageName, e);
} finally {
@@ -402,9 +410,8 @@
* - create the current primary profile to save time at app startup time.
* - copy the profiles from the associated dex metadata file to the reference profile.
*/
- public void prepareAppProfiles(
- AndroidPackage pkg, @UserIdInt int user,
- boolean updateReferenceProfileContent) {
+ public void prepareAppProfiles(AndroidPackage pkg, @UserIdInt int user,
+ boolean updateReferenceProfileContent) throws LegacyDexoptDisabledException {
final int appId = UserHandle.getAppId(pkg.getUid());
if (user < 0) {
Slog.wtf(TAG, "Invalid user id: " + user);
@@ -444,9 +451,8 @@
/**
* Prepares the app profiles for a set of users. {@see ArtManagerService#prepareAppProfiles}.
*/
- public void prepareAppProfiles(
- AndroidPackage pkg, int[] user,
- boolean updateReferenceProfileContent) {
+ public void prepareAppProfiles(AndroidPackage pkg, int[] user,
+ boolean updateReferenceProfileContent) throws LegacyDexoptDisabledException {
for (int i = 0; i < user.length; i++) {
prepareAppProfiles(pkg, user[i], updateReferenceProfileContent);
}
@@ -455,7 +461,7 @@
/**
* Clear the profiles for the given package.
*/
- public void clearAppProfiles(AndroidPackage pkg) {
+ public void clearAppProfiles(AndroidPackage pkg) throws LegacyDexoptDisabledException {
try {
ArrayMap<String, String> packageProfileNames = getPackageProfileNames(pkg);
for (int i = packageProfileNames.size() - 1; i >= 0; i--) {
@@ -470,7 +476,8 @@
/**
* Dumps the profiles for the given package.
*/
- public void dumpProfiles(AndroidPackage pkg, boolean dumpClassesAndMethods) {
+ public void dumpProfiles(AndroidPackage pkg, boolean dumpClassesAndMethods)
+ throws LegacyDexoptDisabledException {
final int sharedGid = UserHandle.getSharedAppGid(pkg.getUid());
try {
ArrayMap<String, String> packageProfileNames = getPackageProfileNames(pkg);
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 8c2da45..fbf7409 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -46,6 +46,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.pm.Installer;
import com.android.server.pm.Installer.InstallerException;
+import com.android.server.pm.Installer.LegacyDexoptDisabledException;
import com.android.server.pm.PackageDexOptimizer;
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.PackageManagerServiceUtils;
@@ -190,7 +191,7 @@
try {
notifyDexLoadInternal(loadingAppInfo, classLoaderContextMap, loaderIsa,
loaderUserId, loaderIsIsolatedProcess);
- } catch (Exception e) {
+ } catch (RuntimeException e) {
Slog.w(TAG, "Exception while notifying dex load for package " +
loadingAppInfo.packageName, e);
}
@@ -325,7 +326,7 @@
public void load(Map<Integer, List<PackageInfo>> existingPackages) {
try {
loadInternal(existingPackages);
- } catch (Exception e) {
+ } catch (RuntimeException e) {
mPackageDexUsage.clear();
mDynamicCodeLogger.clear();
Slog.w(TAG, "Exception while loading. Starting with a fresh state.", e);
@@ -457,7 +458,7 @@
List<String> packagesToKeepDataAbout = new ArrayList<>();
mPackageDexUsage.syncData(
packageToUsersMap, packageToCodePaths, packagesToKeepDataAbout);
- } catch (Exception e) {
+ } catch (RuntimeException e) {
mPackageDexUsage.clear();
Slog.w(TAG, "Exception while loading package dex usage. "
+ "Starting with a fresh state.", e);
@@ -465,7 +466,7 @@
try {
mDynamicCodeLogger.readAndSync(packageToUsersMap);
- } catch (Exception e) {
+ } catch (RuntimeException e) {
mDynamicCodeLogger.clear();
Slog.w(TAG, "Exception while loading package dynamic code usage. "
+ "Starting with a fresh state.", e);
@@ -514,7 +515,7 @@
* @return true if all secondary dex files were processed successfully (compiled or skipped
* because they don't need to be compiled)..
*/
- public boolean dexoptSecondaryDex(DexoptOptions options) {
+ public boolean dexoptSecondaryDex(DexoptOptions options) throws LegacyDexoptDisabledException {
if (isPlatformPackage(options.getPackageName())) {
// We could easily redirect to #dexoptSystemServer in this case. But there should be
// no-one calling this method directly for system server.
@@ -574,7 +575,8 @@
* <p>PackageDexOptimizer.DEX_OPT_FAILED if any dexopt operation failed.
* <p>PackageDexOptimizer.DEX_OPT_PERFORMED if all dexopt operations succeeded.
*/
- public int dexoptSystemServer(DexoptOptions options) {
+ public int dexoptSystemServer(DexoptOptions options) throws LegacyDexoptDisabledException {
+ // TODO(b/254043366): Clean this up.
if (!isPlatformPackage(options.getPackageName())) {
Slog.wtf(TAG, "Non system server package used when trying to dexopt system server:"
+ options.getPackageName());
@@ -664,7 +666,8 @@
* {@code packagName} and the actual dex files. For all dex files that were
* deleted, update the internal records and delete any generated oat files.
*/
- public void reconcileSecondaryDexFiles(String packageName) {
+ public void reconcileSecondaryDexFiles(String packageName)
+ throws LegacyDexoptDisabledException {
PackageUseInfo useInfo = getPackageUseInfoOrDefault(packageName);
if (useInfo.getDexUseInfoMap().isEmpty()) {
if (DEBUG) {
@@ -756,7 +759,7 @@
// TODO(calin): questionable API in the presence of class loaders context. Needs amends as the
// compilation happening here will use a pessimistic context.
public RegisterDexModuleResult registerDexModule(ApplicationInfo info, String dexPath,
- boolean isSharedModule, int userId) {
+ boolean isSharedModule, int userId) throws LegacyDexoptDisabledException {
// Find the owning package record.
DexSearchResult searchResult = getDexPackage(info, dexPath, userId);
@@ -1012,7 +1015,8 @@
* @param packageInfo the package information.
* @return the number of freed bytes or -1 if there was an error in the process.
*/
- public long deleteOptimizedFiles(ArtPackageInfo packageInfo) {
+ public long deleteOptimizedFiles(ArtPackageInfo packageInfo)
+ throws LegacyDexoptDisabledException {
long freedBytes = 0;
boolean hadErrors = false;
final String packageName = packageInfo.getPackageName();
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
index 9d098c6..1be9074 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -29,7 +29,6 @@
import android.app.timedetector.ITimeDetectorService;
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.TelephonyTimeSuggestion;
-import android.app.timedetector.TimePoint;
import android.content.Context;
import android.os.Binder;
import android.os.Handler;
@@ -361,6 +360,34 @@
mHandler.post(() -> mTimeDetectorStrategy.suggestNetworkTime(timeSignal));
}
+ @Override
+ public UnixEpochTime latestNetworkTime() {
+ NetworkTimeSuggestion suggestion = getLatestNetworkSuggestion();
+ if (suggestion != null) {
+ return suggestion.getUnixEpochTime();
+ } else {
+ throw new ParcelableException(new DateTimeException("Missing network time fix"));
+ }
+ }
+
+ /**
+ * Returns the latest network suggestion accepted. For use by {@link TimeDetectorShellCommand}.
+ */
+ @Nullable
+ NetworkTimeSuggestion getLatestNetworkSuggestion() {
+ // TODO(b/222295093): Return the latest network time from mTimeDetectorStrategy once we can
+ // be sure that all uses of NtpTrustedTime results in a suggestion being made to the time
+ // detector. mNtpTrustedTime can be removed once this happens.
+ NtpTrustedTime.TimeResult ntpResult = mNtpTrustedTime.getCachedTimeResult();
+ if (ntpResult != null) {
+ UnixEpochTime unixEpochTime = new UnixEpochTime(
+ ntpResult.getElapsedRealtimeMillis(), ntpResult.getTimeMillis());
+ return new NetworkTimeSuggestion(unixEpochTime, ntpResult.getUncertaintyMillis());
+ } else {
+ return null;
+ }
+ }
+
void suggestGnssTime(@NonNull GnssTimeSuggestion timeSignal) {
enforceSuggestGnssTimePermission();
Objects.requireNonNull(timeSignal);
@@ -377,19 +404,6 @@
}
@Override
- public TimePoint latestNetworkTime() {
- // TODO(b/222295093): Return the latest network time from mTimeDetectorStrategy once we can
- // be sure that all uses of NtpTrustedTime results in a suggestion being made to the time
- // detector. mNtpTrustedTime can be removed once this happens.
- NtpTrustedTime.TimeResult ntpResult = mNtpTrustedTime.getCachedTimeResult();
- if (ntpResult != null) {
- return new TimePoint(ntpResult.getTimeMillis(), ntpResult.getElapsedRealtimeMillis());
- } else {
- throw new ParcelableException(new DateTimeException("Missing network time fix"));
- }
- }
-
- @Override
protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw,
@Nullable String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
@@ -421,7 +435,7 @@
private void enforceSuggestNetworkTimePermission() {
mContext.enforceCallingPermission(
android.Manifest.permission.SET_TIME,
- "set time");
+ "suggest network time");
}
private void enforceSuggestGnssTimePermission() {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 70e7dae..fd6d606 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -6344,7 +6344,7 @@
int wakefulness) {
synchronized (mGlobalLock) {
if (mHomeProcess != null && (dumpPackage == null
- || mHomeProcess.mPkgList.contains(dumpPackage))) {
+ || mHomeProcess.containsPackage(dumpPackage))) {
if (needSep) {
pw.println();
needSep = false;
@@ -6352,7 +6352,7 @@
pw.println(" mHomeProcess: " + mHomeProcess);
}
if (mPreviousProcess != null && (dumpPackage == null
- || mPreviousProcess.mPkgList.contains(dumpPackage))) {
+ || mPreviousProcess.containsPackage(dumpPackage))) {
if (needSep) {
pw.println();
needSep = false;
@@ -6360,14 +6360,14 @@
pw.println(" mPreviousProcess: " + mPreviousProcess);
}
if (dumpAll && (mPreviousProcess == null || dumpPackage == null
- || mPreviousProcess.mPkgList.contains(dumpPackage))) {
+ || mPreviousProcess.containsPackage(dumpPackage))) {
StringBuilder sb = new StringBuilder(128);
sb.append(" mPreviousProcessVisibleTime: ");
TimeUtils.formatDuration(mPreviousProcessVisibleTime, sb);
pw.println(sb);
}
if (mHeavyWeightProcess != null && (dumpPackage == null
- || mHeavyWeightProcess.mPkgList.contains(dumpPackage))) {
+ || mHeavyWeightProcess.containsPackage(dumpPackage))) {
if (needSep) {
pw.println();
needSep = false;
@@ -6491,18 +6491,18 @@
}
if (mHomeProcess != null && (dumpPackage == null
- || mHomeProcess.mPkgList.contains(dumpPackage))) {
+ || mHomeProcess.containsPackage(dumpPackage))) {
mHomeProcess.dumpDebug(proto, HOME_PROC);
}
if (mPreviousProcess != null && (dumpPackage == null
- || mPreviousProcess.mPkgList.contains(dumpPackage))) {
+ || mPreviousProcess.containsPackage(dumpPackage))) {
mPreviousProcess.dumpDebug(proto, PREVIOUS_PROC);
proto.write(PREVIOUS_PROC_VISIBLE_TIME_MS, mPreviousProcessVisibleTime);
}
if (mHeavyWeightProcess != null && (dumpPackage == null
- || mHeavyWeightProcess.mPkgList.contains(dumpPackage))) {
+ || mHeavyWeightProcess.containsPackage(dumpPackage))) {
mHeavyWeightProcess.dumpDebug(proto, HEAVY_WEIGHT_PROC);
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 103b9b2..b1bc52a 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -1714,7 +1714,7 @@
// Don't kill the home process along with tasks from the same package.
continue;
}
- if (!proc.mPkgList.contains(pkg)) {
+ if (!proc.containsPackage(pkg)) {
// Don't kill process that is not associated with this task.
continue;
}
diff --git a/services/core/java/com/android/server/wm/CompatModePackages.java b/services/core/java/com/android/server/wm/CompatModePackages.java
index a035948..d5828ef 100644
--- a/services/core/java/com/android/server/wm/CompatModePackages.java
+++ b/services/core/java/com/android/server/wm/CompatModePackages.java
@@ -566,7 +566,7 @@
SparseArray<WindowProcessController> pidMap = mService.mProcessMap.getPidMap();
for (int i = pidMap.size() - 1; i >= 0; i--) {
final WindowProcessController app = pidMap.valueAt(i);
- if (!app.mPkgList.contains(packageName)) {
+ if (!app.containsPackage(packageName)) {
continue;
}
try {
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 9b361fe..3e279b2 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -76,6 +76,7 @@
import android.util.proto.ProtoOutputStream;
import android.view.IRemoteAnimationRunner;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.HeavyWeightSwitcherActivity;
import com.android.internal.protolog.common.ProtoLog;
@@ -118,7 +119,8 @@
// communicate back to the activity manager side.
public final Object mOwner;
// List of packages running in the process
- final ArraySet<String> mPkgList = new ArraySet<>();
+ @GuardedBy("itself")
+ private final ArrayList<String> mPkgList = new ArrayList<>(1);
private final WindowProcessListener mListener;
private final ActivityTaskManagerService mAtm;
private final BackgroundLaunchProcessController mBgLaunchController;
@@ -645,18 +647,26 @@
@HotPath(caller = HotPath.PROCESS_CHANGE)
public void addPackage(String packageName) {
- synchronized (mAtm.mGlobalLockWithoutBoost) {
- mPkgList.add(packageName);
+ synchronized (mPkgList) {
+ if (!mPkgList.contains(packageName)) {
+ mPkgList.add(packageName);
+ }
}
}
@HotPath(caller = HotPath.PROCESS_CHANGE)
public void clearPackageList() {
- synchronized (mAtm.mGlobalLockWithoutBoost) {
+ synchronized (mPkgList) {
mPkgList.clear();
}
}
+ boolean containsPackage(String packageName) {
+ synchronized (mPkgList) {
+ return mPkgList.contains(packageName);
+ }
+ }
+
void addActivityIfNeeded(ActivityRecord r) {
// even if we already track this activity, note down that it has been launched
setLastActivityLaunchTime(r.lastLaunchTime);
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 5ecf737..a1f4096 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -329,8 +329,8 @@
mSurfaceController = new WindowSurfaceController(attrs.getTitle().toString(), format,
flags, this, attrs.type);
- mSurfaceController.setColorSpaceAgnostic((attrs.privateFlags
- & WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC) != 0);
+ mSurfaceController.setColorSpaceAgnostic(w.getPendingTransaction(),
+ (attrs.privateFlags & LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC) != 0);
w.setHasSurface(true);
// The surface instance is changed. Make sure the input info can be applied to the
@@ -527,7 +527,7 @@
if (mSurfaceController == null) {
return;
}
- mSurfaceController.setColorSpaceAgnostic(agnostic);
+ mSurfaceController.setColorSpaceAgnostic(mWin.getPendingTransaction(), agnostic);
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 4a5c473..607ce25 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -236,24 +236,13 @@
}
}
- void setColorSpaceAgnostic(boolean agnostic) {
+ void setColorSpaceAgnostic(SurfaceControl.Transaction t, boolean agnostic) {
ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE isColorSpaceAgnostic=%b: %s", agnostic, title);
if (mSurfaceControl == null) {
return;
}
- if (SHOW_LIGHT_TRANSACTIONS) {
- Slog.i(TAG, ">>> OPEN TRANSACTION setColorSpaceAgnosticLocked");
- }
- mService.openSurfaceTransaction();
- try {
- getGlobalTransaction().setColorSpaceAgnostic(mSurfaceControl, agnostic);
- } finally {
- mService.closeSurfaceTransaction("setColorSpaceAgnostic");
- if (SHOW_LIGHT_TRANSACTIONS) {
- Slog.i(TAG, "<<< CLOSE TRANSACTION setColorSpaceAgnosticLocked");
- }
- }
+ t.setColorSpaceAgnostic(mSurfaceControl, agnostic);
}
boolean showRobustly(SurfaceControl.Transaction t) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
index 70a7a02..4c42791 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -34,6 +34,7 @@
import android.app.admin.DeviceAdminInfo;
import android.app.admin.DevicePolicyManager;
import android.app.admin.FactoryResetProtectionPolicy;
+import android.app.admin.ManagedSubscriptionsPolicy;
import android.app.admin.PackagePolicy;
import android.app.admin.PasswordPolicy;
import android.app.admin.PreferentialNetworkServiceConfig;
@@ -168,6 +169,7 @@
private static final String TAG_PROTECTED_PACKAGES = "protected_packages";
private static final String TAG_SUSPENDED_PACKAGES = "suspended-packages";
private static final String TAG_MTE_POLICY = "mte-policy";
+ private static final String TAG_MANAGED_SUBSCRIPTIONS_POLICY = "managed_subscriptions_policy";
private static final String ATTR_VALUE = "value";
private static final String ATTR_LAST_NETWORK_LOGGING_NOTIFICATION = "last-notification";
private static final String ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS = "num-notifications";
@@ -272,6 +274,9 @@
// Wi-Fi SSID restriction policy.
WifiSsidPolicy mWifiSsidPolicy;
+ // Managed subscriptions policy.
+ ManagedSubscriptionsPolicy mManagedSubscriptionsPolicy;
+
// TODO: review implementation decisions with frameworks team
boolean specifiesGlobalProxy = false;
String globalProxySpec = null;
@@ -642,6 +647,11 @@
mManagedProfileCallerIdAccess);
writePackagePolicy(out, TAG_CROSS_PROFILE_CONTACTS_SEARCH_POLICY,
mManagedProfileContactsAccess);
+ if (mManagedSubscriptionsPolicy != null) {
+ out.startTag(null, TAG_MANAGED_SUBSCRIPTIONS_POLICY);
+ mManagedSubscriptionsPolicy.saveToXml(out);
+ out.endTag(null, TAG_MANAGED_SUBSCRIPTIONS_POLICY);
+ }
}
private void writePackagePolicy(TypedXmlSerializer out, String tag,
@@ -946,6 +956,8 @@
mManagedProfileCallerIdAccess = readPackagePolicy(parser);
} else if (TAG_CROSS_PROFILE_CONTACTS_SEARCH_POLICY.equals(tag)) {
mManagedProfileContactsAccess = readPackagePolicy(parser);
+ } else if (TAG_MANAGED_SUBSCRIPTIONS_POLICY.equals(tag)) {
+ mManagedSubscriptionsPolicy = ManagedSubscriptionsPolicy.readFromXml(parser);
} else {
Slogf.w(LOG_TAG, "Unknown admin tag: %s", tag);
XmlUtils.skipCurrentTag(parser);
@@ -1414,5 +1426,13 @@
pw.print("accountTypesWithManagementDisabled=");
pw.println(accountTypesWithManagementDisabled);
+
+ if (mManagedSubscriptionsPolicy != null) {
+ pw.println("mManagedSubscriptionsPolicy:");
+ pw.increaseIndent();
+ pw.print("mPolicyType=");
+ mManagedSubscriptionsPolicy.getPolicyType();
+ pw.decreaseIndent();
+ }
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 2b92b22..cdb2e08 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -218,6 +218,7 @@
import android.app.admin.FactoryResetProtectionPolicy;
import android.app.admin.FullyManagedDeviceProvisioningParams;
import android.app.admin.ManagedProfileProvisioningParams;
+import android.app.admin.ManagedSubscriptionsPolicy;
import android.app.admin.NetworkEvent;
import android.app.admin.PackagePolicy;
import android.app.admin.ParcelableGranteeMap;
@@ -334,6 +335,8 @@
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.ParcelableKeyGenParameterSpec;
import android.stats.devicepolicy.DevicePolicyEnums;
+import android.telecom.TelecomManager;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting;
import android.text.TextUtils;
@@ -768,6 +771,9 @@
private final DeviceStateCacheImpl mStateCache = new DeviceStateCacheImpl();
private final Object mESIDInitilizationLock = new Object();
private EnterpriseSpecificIdCalculator mEsidCalculator;
+ private final Object mSubscriptionsChangedListenerLock = new Object();
+ @GuardedBy("mSubscriptionsChangedListenerLock")
+ private SubscriptionManager.OnSubscriptionsChangedListener mSubscriptionsChangedListener;
/**
* Contains the list of OEM Default Role Holders for Contact-related roles
@@ -973,8 +979,6 @@
@GuardedBy("getLockObject()")
final SparseArray<DevicePolicyData> mUserData;
- @GuardedBy("getLockObject()")
-
final Handler mHandler;
final Handler mBackgroundHandler;
@@ -3092,6 +3096,7 @@
onLockSettingsReady();
loadAdminDataAsync();
mOwners.systemReady();
+ applyManagedSubscriptionsPolicyIfRequired();
break;
case SystemService.PHASE_ACTIVITY_MANAGER_READY:
synchronized (getLockObject()) {
@@ -3110,6 +3115,22 @@
}
}
+ private void applyManagedSubscriptionsPolicyIfRequired() {
+ int copeProfileUserId = getOrganizationOwnedProfileUserId();
+ // This policy is relevant only for COPE devices.
+ if (copeProfileUserId != UserHandle.USER_NULL) {
+ unregisterOnSubscriptionsChangedListener();
+ int policyType = getManagedSubscriptionsPolicy().getPolicyType();
+ if (policyType == ManagedSubscriptionsPolicy.TYPE_ALL_PERSONAL_SUBSCRIPTIONS) {
+ // By default, assign all current and future subs to system user on COPE devices.
+ registerListenerToAssignSubscriptionsToUser(UserHandle.USER_SYSTEM);
+ } else if (policyType == ManagedSubscriptionsPolicy.TYPE_ALL_MANAGED_SUBSCRIPTIONS) {
+ // Add listener to assign all current and future subs to managed profile.
+ registerListenerToAssignSubscriptionsToUser(copeProfileUserId);
+ }
+ }
+ }
+
private void updatePersonalAppsSuspensionOnUserStart(int userHandle) {
final int profileUserHandle = getManagedUserId(userHandle);
if (profileUserHandle >= 0) {
@@ -6993,9 +7014,23 @@
}
mLockSettingsInternal.refreshStrongAuthTimeout(parentId);
+ clearManagedSubscriptionsPolicy();
+
Slogf.i(LOG_TAG, "Cleaning up device-wide policies done.");
}
+ private void clearManagedSubscriptionsPolicy() {
+ unregisterOnSubscriptionsChangedListener();
+
+ SubscriptionManager subscriptionManager = mContext.getSystemService(
+ SubscriptionManager.class);
+ //Iterate over all the subscriptions and remove association with any user.
+ int[] subscriptionIds = subscriptionManager.getActiveSubscriptionIdList(false);
+ for (int subId : subscriptionIds) {
+ subscriptionManager.setSubscriptionUserHandle(subId, null);
+ }
+ }
+
/**
* @param factoryReset null: legacy behaviour, false: attempt to remove user, true: attempt to
* factory reset
@@ -10088,6 +10123,10 @@
mStateCache.dump(pw);
pw.println();
}
+
+ synchronized (mSubscriptionsChangedListenerLock) {
+ pw.println("Subscription changed listener : " + mSubscriptionsChangedListener);
+ }
mHandler.post(() -> handleDump(pw));
dumpResources(pw);
}
@@ -19952,4 +19991,139 @@
HEADLESS_FLAG,
DEFAULT_HEADLESS_FLAG);
}
+
+ @Override
+ public ManagedSubscriptionsPolicy getManagedSubscriptionsPolicy() {
+ synchronized (getLockObject()) {
+ ActiveAdmin admin = getProfileOwnerOfOrganizationOwnedDeviceLocked();
+ if (admin != null && admin.mManagedSubscriptionsPolicy != null) {
+ return admin.mManagedSubscriptionsPolicy;
+ }
+ }
+ return new ManagedSubscriptionsPolicy(
+ ManagedSubscriptionsPolicy.TYPE_ALL_PERSONAL_SUBSCRIPTIONS);
+ }
+
+ @Override
+ public void setManagedSubscriptionsPolicy(ManagedSubscriptionsPolicy policy) {
+ CallerIdentity caller = getCallerIdentity();
+ Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller),
+ "This policy can only be set by a profile owner on an organization-owned device.");
+
+ synchronized (getLockObject()) {
+ final ActiveAdmin admin = getProfileOwnerLocked(caller.getUserId());
+ if (hasUserSetupCompleted(UserHandle.USER_SYSTEM) && !isAdminTestOnlyLocked(
+ admin.info.getComponent(), caller.getUserId())) {
+ throw new IllegalStateException("Not allowed to apply this policy after setup");
+ }
+ boolean changed = false;
+ if (!Objects.equals(policy, admin.mManagedSubscriptionsPolicy)) {
+ admin.mManagedSubscriptionsPolicy = policy;
+ changed = true;
+ }
+ if (changed) {
+ saveSettingsLocked(caller.getUserId());
+ } else {
+ return;
+ }
+ }
+
+ applyManagedSubscriptionsPolicyIfRequired();
+
+ int policyType = getManagedSubscriptionsPolicy().getPolicyType();
+ if (policyType == ManagedSubscriptionsPolicy.TYPE_ALL_MANAGED_SUBSCRIPTIONS) {
+ final long id = mInjector.binderClearCallingIdentity();
+ try {
+ int parentUserId = getProfileParentId(caller.getUserId());
+ installOemDefaultDialerAndMessagesApp(parentUserId, caller.getUserId());
+ } finally {
+ mInjector.binderRestoreCallingIdentity(id);
+ }
+ }
+ }
+
+ private void installOemDefaultDialerAndMessagesApp(int sourceUserId, int targetUserId) {
+ try {
+ UserHandle sourceUserHandle = UserHandle.of(sourceUserId);
+ TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
+ String dialerAppPackage = telecomManager.getDefaultDialerPackage(
+ sourceUserHandle);
+ String messagesAppPackage = SmsApplication.getDefaultSmsApplicationAsUser(mContext,
+ true, sourceUserHandle).getPackageName();
+ if (dialerAppPackage != null) {
+ mIPackageManager.installExistingPackageAsUser(dialerAppPackage, targetUserId,
+ PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS,
+ PackageManager.INSTALL_REASON_POLICY, null);
+ } else {
+ Slogf.w(LOG_TAG, "Couldn't install dialer app, dialer app package is null");
+ }
+
+ if (messagesAppPackage != null) {
+ mIPackageManager.installExistingPackageAsUser(messagesAppPackage, targetUserId,
+ PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS,
+ PackageManager.INSTALL_REASON_POLICY, null);
+ } else {
+ Slogf.w(LOG_TAG, "Couldn't install messages app, messages app package is null");
+ }
+ } catch (RemoteException re) {
+ // shouldn't happen
+ Slogf.wtf(LOG_TAG, "Failed to install dialer/messages app", re);
+ }
+ }
+
+ private void registerListenerToAssignSubscriptionsToUser(int userId) {
+ synchronized (mSubscriptionsChangedListenerLock) {
+ if (mSubscriptionsChangedListener != null) {
+ return;
+ }
+ SubscriptionManager subscriptionManager = mContext.getSystemService(
+ SubscriptionManager.class);
+ // Listener to assign all current and future subs to managed profile.
+ mSubscriptionsChangedListener = new SubscriptionManager.OnSubscriptionsChangedListener(
+ mHandler.getLooper()) {
+ @Override
+ public void onSubscriptionsChanged() {
+ final long id = mInjector.binderClearCallingIdentity();
+ try {
+ int[] subscriptionIds = subscriptionManager.getActiveSubscriptionIdList(
+ false);
+ for (int subId : subscriptionIds) {
+ UserHandle associatedUserHandle =
+ subscriptionManager.getSubscriptionUserHandle(subId);
+ if (associatedUserHandle == null
+ || associatedUserHandle.getIdentifier() != userId) {
+ subscriptionManager.setSubscriptionUserHandle(subId,
+ UserHandle.of(userId));
+ }
+ }
+ } finally {
+ mInjector.binderRestoreCallingIdentity(id);
+ }
+ }
+ };
+
+ final long id = mInjector.binderClearCallingIdentity();
+ try {
+ // When listener is added onSubscriptionsChanged gets called immediately for once
+ // (even if subscriptions are not changed) and later on when subscriptions changes.
+ subscriptionManager.addOnSubscriptionsChangedListener(
+ mSubscriptionsChangedListener.getHandlerExecutor(),
+ mSubscriptionsChangedListener);
+ } finally {
+ mInjector.binderRestoreCallingIdentity(id);
+ }
+ }
+ }
+
+ private void unregisterOnSubscriptionsChangedListener() {
+ synchronized (mSubscriptionsChangedListenerLock) {
+ if (mSubscriptionsChangedListener != null) {
+ SubscriptionManager subscriptionManager = mContext.getSystemService(
+ SubscriptionManager.class);
+ subscriptionManager.removeOnSubscriptionsChangedListener(
+ mSubscriptionsChangedListener);
+ mSubscriptionsChangedListener = null;
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundDexOptServiceUnitTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundDexOptServiceUnitTest.java
index 01674bb..8b36da5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundDexOptServiceUnitTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundDexOptServiceUnitTest.java
@@ -169,19 +169,19 @@
}
@Test
- public void testBootCompleted() {
+ public void testBootCompleted() throws Exception {
initUntilBootCompleted();
}
@Test
- public void testNoExecutionForIdleJobBeforePostBootUpdate() {
+ public void testNoExecutionForIdleJobBeforePostBootUpdate() throws Exception {
initUntilBootCompleted();
assertThat(mService.onStartJob(mJobServiceForIdle, mJobParametersForIdle)).isFalse();
}
@Test
- public void testNoExecutionForLowStorage() {
+ public void testNoExecutionForLowStorage() throws Exception {
initUntilBootCompleted();
when(mPackageManager.isStorageLow()).thenReturn(true);
@@ -191,7 +191,7 @@
}
@Test
- public void testNoExecutionForNoOptimizablePackages() {
+ public void testNoExecutionForNoOptimizablePackages() throws Exception {
initUntilBootCompleted();
when(mDexOptHelper.getOptimizablePackages(any())).thenReturn(Collections.emptyList());
@@ -201,7 +201,7 @@
}
@Test
- public void testPostBootUpdateFullRun() {
+ public void testPostBootUpdateFullRun() throws Exception {
initUntilBootCompleted();
runFullJob(mJobServiceForPostBoot, mJobParametersForPostBoot,
@@ -210,7 +210,7 @@
}
@Test
- public void testPostBootUpdateFullRunWithPackageFailure() {
+ public void testPostBootUpdateFullRunWithPackageFailure() throws Exception {
mDexOptResultForPackageAAA = PackageDexOptimizer.DEX_OPT_FAILED;
initUntilBootCompleted();
@@ -224,7 +224,7 @@
}
@Test
- public void testIdleJobFullRun() {
+ public void testIdleJobFullRun() throws Exception {
initUntilBootCompleted();
runFullJob(mJobServiceForPostBoot, mJobParametersForPostBoot,
/* expectedReschedule= */ false, /* expectedStatus= */ STATUS_OK,
@@ -235,7 +235,7 @@
}
@Test
- public void testIdleJobFullRunWithFailureOnceAndSuccessAfterUpdate() {
+ public void testIdleJobFullRunWithFailureOnceAndSuccessAfterUpdate() throws Exception {
mDexOptResultForPackageAAA = PackageDexOptimizer.DEX_OPT_FAILED;
initUntilBootCompleted();
@@ -271,7 +271,7 @@
}
@Test
- public void testIdleJobFullRunWithFatalError() {
+ public void testIdleJobFullRunWithFatalError() throws Exception {
initUntilBootCompleted();
runFullJob(mJobServiceForPostBoot, mJobParametersForPostBoot,
/* expectedReschedule= */ false, /* expectedStatus= */ STATUS_OK,
@@ -285,7 +285,7 @@
}
@Test
- public void testSystemReadyWhenDisabled() {
+ public void testSystemReadyWhenDisabled() throws Exception {
when(mInjector.isBackgroundDexOptDisabled()).thenReturn(true);
mService.systemReady();
@@ -294,7 +294,7 @@
}
@Test
- public void testStopByCancelFlag() {
+ public void testStopByCancelFlag() throws Exception {
when(mInjector.createAndStartThread(any(), any())).thenReturn(Thread.currentThread());
initUntilBootCompleted();
@@ -447,7 +447,7 @@
}
@Test
- public void testStopByThermal() {
+ public void testStopByThermal() throws Exception {
when(mInjector.createAndStartThread(any(), any())).thenReturn(Thread.currentThread());
initUntilBootCompleted();
@@ -478,7 +478,7 @@
}
@Test
- public void testDisableJobSchedulerJobs() {
+ public void testDisableJobSchedulerJobs() throws Exception {
when(mInjector.getCallingUid()).thenReturn(Process.SHELL_UID);
mService.setDisableJobSchedulerJobs(true);
assertThat(mService.onStartJob(mJobServiceForIdle, mJobParametersForIdle)).isFalse();
@@ -492,7 +492,7 @@
assertThrows(SecurityException.class, () -> mService.setDisableJobSchedulerJobs(true));
}
- private void initUntilBootCompleted() {
+ private void initUntilBootCompleted() throws Exception {
ArgumentCaptor<BroadcastReceiver> argReceiver = ArgumentCaptor.forClass(
BroadcastReceiver.class);
ArgumentCaptor<IntentFilter> argIntentFilter = ArgumentCaptor.forClass(IntentFilter.class);
@@ -515,7 +515,7 @@
assertThat(jobIds).containsExactlyElementsIn(expectedJobIds);
}
- private void verifyLastControlDexOptBlockingCall(boolean expected) {
+ private void verifyLastControlDexOptBlockingCall(boolean expected) throws Exception {
ArgumentCaptor<Boolean> argDexOptBlock = ArgumentCaptor.forClass(Boolean.class);
verify(mDexOptHelper, atLeastOnce()).controlDexOptBlocking(argDexOptBlock.capture());
assertThat(argDexOptBlock.getValue()).isEqualTo(expected);
@@ -523,7 +523,7 @@
private void runFullJob(BackgroundDexOptJobService jobService, JobParameters params,
boolean expectedReschedule, int expectedStatus, int totalJobFinishedWithParams,
- @Nullable String expectedSkippedPackage) {
+ @Nullable String expectedSkippedPackage) throws Exception {
when(mInjector.createAndStartThread(any(), any())).thenReturn(Thread.currentThread());
addFullRunSequence(expectedSkippedPackage);
assertThat(mService.onStartJob(jobService, params)).isTrue();
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index a55b196..61c3f13 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -5014,7 +5014,8 @@
.thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
when(getServices().userManager.getPrimaryUser())
.thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
-
+ when(getServices().subscriptionManager.getActiveSubscriptionIdList(false)).thenReturn(
+ new int[1]);
// Set some device-wide policies:
// Security logging
when(getServices().settings.securityLogGetLoggingEnabledProperty()).thenReturn(true);
@@ -5062,6 +5063,7 @@
// Unsuspend personal apps
verify(getServices().packageManagerInternal)
.unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, UserHandle.USER_SYSTEM);
+ verify(getServices().subscriptionManager).setSubscriptionUserHandle(0, null);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index ac1667d..c739969 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -239,6 +239,8 @@
return mMockSystemServices.locationManager;
case Context.ROLE_SERVICE:
return mMockSystemServices.roleManager;
+ case Context.TELEPHONY_SUBSCRIPTION_SERVICE:
+ return mMockSystemServices.subscriptionManager;
}
throw new UnsupportedOperationException();
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index cec9d50..2a6a979 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -63,6 +63,7 @@
import android.permission.IPermissionManager;
import android.provider.Settings;
import android.security.KeyChain;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.test.mock.MockContentProvider;
import android.test.mock.MockContentResolver;
@@ -135,6 +136,7 @@
public final DevicePolicyManager devicePolicyManager;
public final LocationManager locationManager;
public final RoleManager roleManager;
+ public final SubscriptionManager subscriptionManager;
/** Note this is a partial mock, not a real mock. */
public final PackageManager packageManager;
public final BuildMock buildMock = new BuildMock();
@@ -188,6 +190,7 @@
devicePolicyManager = mock(DevicePolicyManager.class);
locationManager = mock(LocationManager.class);
roleManager = realContext.getSystemService(RoleManager.class);
+ subscriptionManager = mock(SubscriptionManager.class);
// Package manager is huge, so we use a partial mock instead.
packageManager = spy(realContext.getPackageManager());
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
index 4b417ba..a857238 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
@@ -20,6 +20,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -44,7 +45,6 @@
import android.app.time.UnixEpochTime;
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.TelephonyTimeSuggestion;
-import android.app.timedetector.TimePoint;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.HandlerThread;
@@ -385,8 +385,8 @@
1234L, 54321L, 999, InetSocketAddress.createUnresolved("test.timeserver", 123));
when(mMockNtpTrustedTime.getCachedTimeResult())
.thenReturn(latestNetworkTime);
- TimePoint expected = new TimePoint(latestNetworkTime.getTimeMillis(),
- latestNetworkTime.getElapsedRealtimeMillis());
+ UnixEpochTime expected = new UnixEpochTime(
+ latestNetworkTime.getElapsedRealtimeMillis(), latestNetworkTime.getTimeMillis());
assertEquals(expected, mTimeDetectorService.latestNetworkTime());
}
@@ -397,6 +397,25 @@
}
@Test
+ public void testGetLatestNetworkSuggestion() {
+ NtpTrustedTime.TimeResult latestNetworkTime = new NtpTrustedTime.TimeResult(
+ 1234L, 54321L, 999, InetSocketAddress.createUnresolved("test.timeserver", 123));
+ when(mMockNtpTrustedTime.getCachedTimeResult())
+ .thenReturn(latestNetworkTime);
+ UnixEpochTime expectedUnixEpochTime = new UnixEpochTime(
+ latestNetworkTime.getElapsedRealtimeMillis(), latestNetworkTime.getTimeMillis());
+ NetworkTimeSuggestion expected = new NetworkTimeSuggestion(
+ expectedUnixEpochTime, latestNetworkTime.getUncertaintyMillis());
+ assertEquals(expected, mTimeDetectorService.getLatestNetworkSuggestion());
+ }
+
+ @Test
+ public void testGetLatestNetworkSuggestion_noTimeAvailable() {
+ when(mMockNtpTrustedTime.getCachedTimeResult()).thenReturn(null);
+ assertNull(mTimeDetectorService.getLatestNetworkSuggestion());
+ }
+
+ @Test
public void testGetTimeState() {
doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
TimeState fakeState = new TimeState(new UnixEpochTime(12345L, 98765L), true);