Merge "Import translations. DO NOT MERGE ANYWHERE"
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/os/UserManager.java b/core/java/android/os/UserManager.java
index 92ee393..fc02524 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -599,8 +599,11 @@
/**
* Specifies if a user is disallowed from transferring files over USB.
*
- * <p>This restriction can only be set by a device owner, a profile owner on the primary
- * user or a profile owner of an organization-owned managed profile on the parent profile.
+ * <p>This restriction can only be set by a <a href="https://developers.google.com/android/work/terminology#device_owner_do">
+ * device owner</a> or a <a href="https://developers.google.com/android/work/terminology#profile_owner_po">
+ * profile owner</a> on the primary user's profile or a profile owner of an organization-owned
+ * <a href="https://developers.google.com/android/work/terminology#managed_profile">
+ * managed profile</a> on the parent profile.
* When it is set by a device owner, it applies globally. When it is set by a profile owner
* on the primary user or by a profile owner of an organization-owned managed profile on
* the parent profile, it disables the primary user from transferring files over USB. No other
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 943d64a..fc47e69 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -353,4 +353,6 @@
* Returns whether this window needs to cancel draw and retry later.
*/
boolean cancelDraw(IWindow window);
+
+ boolean transferEmbeddedTouchFocusToHost(IWindow embeddedWindow);
}
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index 50ce7b6..134625e 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -194,7 +194,7 @@
* is more akin to a PopupWindow in that the size is user specified
* independent of configuration width and height.
*
- * In order to receive the configuration change via
+ * In order to receive the configuration change via
* {@link View#onConfigurationChanged}, the context used with the
* SurfaceControlViewHost and it's embedded view hierarchy must
* be a WindowContext obtained from {@link Context#createWindowContext}.
@@ -460,5 +460,26 @@
(WindowManagerImpl) mViewRoot.mContext.getSystemService(Context.WINDOW_SERVICE);
attrs.token = wm.getDefaultToken();
}
-}
+ /**
+ * Transfer the currently in progress touch gesture to the parent
+ * (if any) of this SurfaceControlViewHost. This requires that the
+ * SurfaceControlViewHost was created with an associated hostInputToken.
+ *
+ * @return Whether the touch stream was transferred.
+ * @hide
+ */
+ public boolean transferTouchGestureToHost() {
+ if (mViewRoot == null) {
+ return false;
+ }
+
+ final IWindowSession realWm = WindowManagerGlobal.getWindowSession();
+ try {
+ return realWm.transferEmbeddedTouchFocusToHost(mViewRoot.mWindow);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ return false;
+ }
+}
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index edf33f1..ecb5557 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -561,4 +561,11 @@
public boolean cancelDraw(IWindow window) {
return false;
}
+
+ @Override
+ public boolean transferEmbeddedTouchFocusToHost(IWindow window) {
+ Log.e(TAG, "Received request to transferEmbeddedTouch focus on WindowlessWindowManager" +
+ " we shouldn't get here!");
+ return false;
+ }
}
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/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index e0b0110..4f97d21 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -91,6 +91,10 @@
public static final ComponentName ACCESSIBILITY_BUTTON_COMPONENT_NAME =
new ComponentName("com.android.server.accessibility", "AccessibilityButton");
+ // The component name for the sub setting of Hearing aids in Accessibility settings
+ public static final ComponentName ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME =
+ new ComponentName("com.android.server.accessibility", "HearingAids");
+
public static final ComponentName COLOR_INVERSION_TILE_COMPONENT_NAME =
new ComponentName("com.android.server.accessibility", "ColorInversionTile");
public static final ComponentName DALTONIZER_TILE_COMPONENT_NAME =
@@ -104,7 +108,7 @@
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY)
.build();
- private static Map<ComponentName, ToggleableFrameworkFeatureInfo> sFrameworkShortcutFeaturesMap;
+ private static Map<ComponentName, FrameworkFeatureInfo> sFrameworkShortcutFeaturesMap;
private final Context mContext;
private final Handler mHandler;
@@ -133,10 +137,10 @@
* @return An immutable map from placeholder component names to feature
* info for toggling a framework feature
*/
- public static Map<ComponentName, ToggleableFrameworkFeatureInfo>
+ public static Map<ComponentName, FrameworkFeatureInfo>
getFrameworkShortcutFeaturesMap() {
if (sFrameworkShortcutFeaturesMap == null) {
- Map<ComponentName, ToggleableFrameworkFeatureInfo> featuresMap = new ArrayMap<>(4);
+ Map<ComponentName, FrameworkFeatureInfo> featuresMap = new ArrayMap<>(4);
featuresMap.put(COLOR_INVERSION_COMPONENT_NAME,
new ToggleableFrameworkFeatureInfo(
Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED,
@@ -157,6 +161,8 @@
Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED,
"1" /* Value to enable */, "0" /* Value to disable */,
R.string.reduce_bright_colors_feature_name));
+ featuresMap.put(ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME,
+ new LaunchableFrameworkFeatureInfo(R.string.hearing_aids_feature_name));
sFrameworkShortcutFeaturesMap = Collections.unmodifiableMap(featuresMap);
}
return sFrameworkShortcutFeaturesMap;
@@ -391,7 +397,7 @@
if (targetComponentName == null) {
return null;
}
- final ToggleableFrameworkFeatureInfo frameworkFeatureInfo =
+ final FrameworkFeatureInfo frameworkFeatureInfo =
getFrameworkShortcutFeaturesMap().get(targetComponentName);
if (frameworkFeatureInfo != null) {
return frameworkFeatureInfo.getLabel(mContext);
@@ -670,15 +676,13 @@
/**
* Immutable class to hold info about framework features that can be controlled by shortcut
*/
- public static class ToggleableFrameworkFeatureInfo {
+ public abstract static class FrameworkFeatureInfo {
private final String mSettingKey;
private final String mSettingOnValue;
private final String mSettingOffValue;
private final int mLabelStringResourceId;
- // These go to the settings wrapper
- private int mIconDrawableId;
- ToggleableFrameworkFeatureInfo(String settingKey, String settingOnValue,
+ FrameworkFeatureInfo(String settingKey, String settingOnValue,
String settingOffValue, int labelStringResourceId) {
mSettingKey = settingKey;
mSettingOnValue = settingOnValue;
@@ -696,7 +700,7 @@
/**
* @return The value to write to settings to turn the feature on
*/
- public String getSettingOnValue() {
+ public String getSettingOnValue() {
return mSettingOnValue;
}
@@ -711,6 +715,29 @@
return context.getString(mLabelStringResourceId);
}
}
+ /**
+ * Immutable class to hold framework features that have on/off state settings key and can be
+ * controlled by shortcut.
+ */
+ public static class ToggleableFrameworkFeatureInfo extends FrameworkFeatureInfo {
+
+ ToggleableFrameworkFeatureInfo(String settingKey, String settingOnValue,
+ String settingOffValue, int labelStringResourceId) {
+ super(settingKey, settingOnValue, settingOffValue, labelStringResourceId);
+ }
+ }
+
+ /**
+ * Immutable class to hold framework features that don't have settings key and can be controlled
+ * by shortcut.
+ */
+ public static class LaunchableFrameworkFeatureInfo extends FrameworkFeatureInfo {
+
+ LaunchableFrameworkFeatureInfo(int labelStringResourceId) {
+ super(/* settingKey= */ null, /* settingOnValue= */ null, /* settingOffValue= */ null,
+ labelStringResourceId);
+ }
+ }
// Class to allow mocking of static framework calls
public static class FrameworkObjectProvider {
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
index fc2c8cc..b5455f2 100644
--- a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
@@ -18,6 +18,7 @@
import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON;
+import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
@@ -250,11 +251,21 @@
context.getDrawable(R.drawable.ic_accessibility_reduce_bright_colors),
Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED);
+ final InvisibleToggleAllowListingFeatureTarget hearingAids =
+ new InvisibleToggleAllowListingFeatureTarget(context,
+ shortcutType,
+ isShortcutContained(context, shortcutType,
+ ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString()),
+ ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString(),
+ context.getString(R.string.hearing_aids_feature_name),
+ context.getDrawable(R.drawable.ic_accessibility_hearing_aid),
+ /* key= */ null);
targets.add(magnification);
targets.add(daltonizer);
targets.add(colorInversion);
targets.add(oneHandedMode);
targets.add(reduceBrightColors);
+ targets.add(hearingAids);
return targets;
}
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/core/res/res/drawable/ic_accessibility_hearing_aid.xml b/core/res/res/drawable/ic_accessibility_hearing_aid.xml
new file mode 100644
index 0000000..e5ffeb0
--- /dev/null
+++ b/core/res/res/drawable/ic_accessibility_hearing_aid.xml
@@ -0,0 +1,24 @@
+<!--
+ 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.
+-->
+
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@color/accessibility_feature_background" />
+ <foreground>
+ <inset
+ android:drawable="@drawable/ic_accessibility_hearing_aid_foreground"
+ android:inset="@dimen/accessibility_icon_foreground_padding_ratio" />
+ </foreground>
+</adaptive-icon>
diff --git a/core/res/res/drawable/ic_accessibility_hearing_aid_foreground.xml b/core/res/res/drawable/ic_accessibility_hearing_aid_foreground.xml
new file mode 100644
index 0000000..7ced795
--- /dev/null
+++ b/core/res/res/drawable/ic_accessibility_hearing_aid_foreground.xml
@@ -0,0 +1,25 @@
+<!--
+ 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="18dp"
+ android:height="18dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#ffffff"
+ android:pathData="M17,20c-0.29,0 -0.56,-0.06 -0.76,-0.15 -0.71,-0.37 -1.21,-0.88 -1.71,-2.38 -0.51,-1.56 -1.47,-2.29 -2.39,-3 -0.79,-0.61 -1.61,-1.24 -2.32,-2.53C9.29,10.98 9,9.93 9,9c0,-2.8 2.2,-5 5,-5s5,2.2 5,5h2c0,-3.93 -3.07,-7 -7,-7S7,5.07 7,9c0,1.26 0.38,2.65 1.07,3.9 0.91,1.65 1.98,2.48 2.85,3.15 0.81,0.62 1.39,1.07 1.71,2.05 0.6,1.82 1.37,2.84 2.73,3.55 0.51,0.23 1.07,0.35 1.64,0.35 2.21,0 4,-1.79 4,-4h-2c0,1.1 -0.9,2 -2,2zM7.64,2.64L6.22,1.22C4.23,3.21 3,5.96 3,9s1.23,5.79 3.22,7.78l1.41,-1.41C6.01,13.74 5,11.49 5,9s1.01,-4.74 2.64,-6.36zM11.5,9c0,1.38 1.12,2.5 2.5,2.5s2.5,-1.12 2.5,-2.5 -1.12,-2.5 -2.5,-2.5 -2.5,1.12 -2.5,2.5z"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 08c5d9c..7fe373f 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -323,7 +323,7 @@
<string name="permgrouplab_phone" msgid="570318944091926620">"Telefon"</string>
<string name="permgroupdesc_phone" msgid="270048070781478204">"uskutečňování a spravování telefonních hovorů"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Tělesné senzory"</string>
- <string name="permgroupdesc_sensors" msgid="2610631290633747752">"přístup k datům ze snímačů vašich životních funkcí"</string>
+ <string name="permgroupdesc_sensors" msgid="2610631290633747752">"přístup k datům ze senzorů vašich životních funkcí"</string>
<string name="permgrouplab_notifications" msgid="5472972361980668884">"Oznámení"</string>
<string name="permgroupdesc_notifications" msgid="4608679556801506580">"zobrazovat oznámení"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Načítat obsah oken"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index b659de8..6733faa 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1682,13 +1682,13 @@
<string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"Verknüpfung für Bedienungshilfen aktivieren?"</string>
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Wenn du beide Lautstärketasten einige Sekunden lang gedrückt hältst, aktivierst du die Bedienungshilfen. Dadurch kann sich die Funktionsweise deines Geräts ändern.\n\nAktuelle Funktionen:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nDu kannst ausgewählte Funktionen unter \"Einstellungen\" > \"Bedienungshilfen\" ändern."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="2128323171922023762">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="1909518473488345266">"Verknüpfung für <xliff:g id="SERVICE">%1$s</xliff:g> aktivieren?"</string>
+ <string name="accessibility_shortcut_single_service_warning_title" msgid="1909518473488345266">"Kurzbefehl für <xliff:g id="SERVICE">%1$s</xliff:g> aktivieren?"</string>
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Wenn du beide Lautstärketasten einige Sekunden lang gedrückt hältst, aktivierst du die Bedienungshilfe \"<xliff:g id="SERVICE">%1$s</xliff:g>\". Dadurch kann sich die Funktionsweise deines Geräts ändern.\n\nUnter \"Einstellungen > \"Bedienungshilfen\" kannst du dieser Verknüpfung eine andere Funktion zuweisen."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Aktivieren"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Nicht aktivieren"</string>
<string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"AN"</string>
<string name="accessibility_shortcut_menu_item_status_off" msgid="5531598275559472393">"AUS"</string>
- <string name="accessibility_enable_service_title" msgid="3931558336268541484">"<xliff:g id="SERVICE">%1$s</xliff:g> die vollständige Kontrolle über dein Gerät geben?"</string>
+ <string name="accessibility_enable_service_title" msgid="3931558336268541484">"„<xliff:g id="SERVICE">%1$s</xliff:g>“ die vollständige Kontrolle über dein Gerät geben?"</string>
<string name="accessibility_service_warning_description" msgid="291674995220940133">"Die vollständige Kontrolle sollte nur für Apps aktiviert werden, die dir Zugang zu App-Funktionen erleichtern. Das ist in der Regel nur ein kleiner Teil der Apps."</string>
<string name="accessibility_service_screen_control_title" msgid="190017412626919776">"Bildschirm aufrufen und steuern"</string>
<string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Die Funktion kann alle Inhalte auf dem Bildschirm lesen und diese Inhalte über andere Apps anzeigen."</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 0791c38..43f4172 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -295,11 +295,11 @@
<string name="user_owner_label" msgid="8628726904184471211">"Vaihda henkilökohtaiseen profiiliin"</string>
<string name="managed_profile_label" msgid="7316778766973512382">"Vaihda työprofiiliin"</string>
<string name="permgrouplab_contacts" msgid="4254143639307316920">"Yhteystiedot"</string>
- <string name="permgroupdesc_contacts" msgid="9163927941244182567">"käyttää yhteystietoja"</string>
+ <string name="permgroupdesc_contacts" msgid="9163927941244182567">"pääsy yhteystietoihin"</string>
<string name="permgrouplab_location" msgid="1858277002233964394">"Sijainti"</string>
- <string name="permgroupdesc_location" msgid="1995955142118450685">"käyttää laitteen sijaintia"</string>
+ <string name="permgroupdesc_location" msgid="1995955142118450685">"pääsy laitteen sijaintiin"</string>
<string name="permgrouplab_calendar" msgid="6426860926123033230">"Kalenteri"</string>
- <string name="permgroupdesc_calendar" msgid="6762751063361489379">"käyttää kalenteria"</string>
+ <string name="permgroupdesc_calendar" msgid="6762751063361489379">"pääsy kalenteriin"</string>
<string name="permgrouplab_sms" msgid="795737735126084874">"Tekstiviestit"</string>
<string name="permgroupdesc_sms" msgid="5726462398070064542">"lähettää ja tarkastella tekstiviestejä"</string>
<string name="permgrouplab_storage" msgid="17339216290379241">"Tiedostot"</string>
@@ -309,7 +309,7 @@
<string name="permgrouplab_readMediaVisual" msgid="4724874717811908660">"Valokuvat ja videot"</string>
<string name="permgroupdesc_readMediaVisual" msgid="4080463241903508688">"pääsy laitteen kuviin ja videoihin"</string>
<string name="permgrouplab_microphone" msgid="2480597427667420076">"Mikrofoni"</string>
- <string name="permgroupdesc_microphone" msgid="1047786732792487722">"tallentaa ääntä"</string>
+ <string name="permgroupdesc_microphone" msgid="1047786732792487722">"tallentaa audiota"</string>
<string name="permgrouplab_activityRecognition" msgid="3324466667921775766">"Liikkuminen"</string>
<string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"nähdä liikkumistietosi"</string>
<string name="permgrouplab_camera" msgid="9090413408963547706">"Kamera"</string>
@@ -321,7 +321,7 @@
<string name="permgrouplab_phone" msgid="570318944091926620">"Puhelin"</string>
<string name="permgroupdesc_phone" msgid="270048070781478204">"soittaa ja hallinnoida puheluita"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Kehon anturit"</string>
- <string name="permgroupdesc_sensors" msgid="2610631290633747752">"käyttää anturitietoja elintoiminnoistasi"</string>
+ <string name="permgroupdesc_sensors" msgid="2610631290633747752">"pääsy anturidataan elintoiminnoistasi"</string>
<string name="permgrouplab_notifications" msgid="5472972361980668884">"Ilmoitukset"</string>
<string name="permgroupdesc_notifications" msgid="4608679556801506580">"näyttää ilmoituksia"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Noutaa ikkunan sisältöä"</string>
@@ -476,7 +476,7 @@
<string name="permdesc_accessBackgroundLocation" msgid="8264885066095638105">"Tällä sovelluksella on pääsy sijaintitietoihin milloin tahansa, myös silloin kun sovellusta ei käytetä."</string>
<string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"muuta ääniasetuksia"</string>
<string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Antaa sovelluksen muokata yleisiä ääniasetuksia, kuten äänenvoimakkuutta ja käytettävää kaiutinta."</string>
- <string name="permlab_recordAudio" msgid="1208457423054219147">"tallentaa ääntä"</string>
+ <string name="permlab_recordAudio" msgid="1208457423054219147">"tallentaa audiota"</string>
<string name="permdesc_recordAudio" msgid="5857246765327514062">"Tämä sovellus voi tallentaa mikrofonilla audiota, kun sovellusta käytetään."</string>
<string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"tallentaa audiota taustalla"</string>
<string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Tämä sovellus voi tallentaa mikrofonilla audiota koska tahansa."</string>
@@ -1170,7 +1170,7 @@
<string name="not_selected" msgid="410652016565864475">"ei valittu"</string>
<string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{1/{max} tähteä}other{#/{max} tähteä}}"</string>
<string name="in_progress" msgid="2149208189184319441">"käynnissä"</string>
- <string name="whichApplication" msgid="5432266899591255759">"Tee toiminto käyttäen sovellusta"</string>
+ <string name="whichApplication" msgid="5432266899591255759">"Tee toiminto käyttäen:"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Suorita sovelluksella %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Suorita toiminto"</string>
<string name="whichViewApplication" msgid="5733194231473132945">"Avaa sovelluksessa"</string>
@@ -1975,7 +1975,7 @@
<string name="pin_specific_target" msgid="7824671240625957415">"Kiinnitä <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="unpin_target" msgid="3963318576590204447">"Irrota"</string>
<string name="unpin_specific_target" msgid="3859828252160908146">"Irrota <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="app_info" msgid="6113278084877079851">"Sovelluksen tiedot"</string>
+ <string name="app_info" msgid="6113278084877079851">"Sovellustiedot"</string>
<string name="negative_duration" msgid="1938335096972945232">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="demo_starting_message" msgid="6577581216125805905">"Aloitetaan esittelyä…"</string>
<string name="demo_restarting_message" msgid="1160053183701746766">"Palautetaan asetuksia…"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index e93a05e..4a21b76 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1170,7 +1170,7 @@
<string name="not_selected" msgid="410652016565864475">"inte valt"</string>
<string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{En stjärna av {max}}other{# stjärnor av {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"pågår"</string>
- <string name="whichApplication" msgid="5432266899591255759">"Slutför åtgärd genom att använda"</string>
+ <string name="whichApplication" msgid="5432266899591255759">"Slutför åtgärd med"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Slutför åtgärden med %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Slutför åtgärd"</string>
<string name="whichViewApplication" msgid="5733194231473132945">"Öppna med"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 8a221a4..b31f920 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -311,7 +311,7 @@
<string name="permgrouplab_microphone" msgid="2480597427667420076">"మైక్రోఫోన్"</string>
<string name="permgroupdesc_microphone" msgid="1047786732792487722">"ఆడియోను రికార్డ్ చేయడానికి"</string>
<string name="permgrouplab_activityRecognition" msgid="3324466667921775766">"ఫిజికల్ యాక్టివిటీ"</string>
- <string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"భౌతిక యాక్టివిటీని యాక్సెస్ చేయండి"</string>
+ <string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"ఫిజికల్ యాక్టివిటీని యాక్సెస్ చేయండి"</string>
<string name="permgrouplab_camera" msgid="9090413408963547706">"కెమెరా"</string>
<string name="permgroupdesc_camera" msgid="7585150538459320326">"చిత్రాలను తీయడానికి మరియు వీడియోను రికార్డ్ చేయడానికి"</string>
<string name="permgrouplab_nearby_devices" msgid="5529147543651181991">"సమీపంలోని పరికరాలు"</string>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 388bc95..b754440 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4715,6 +4715,9 @@
<!-- Title of Reduce Brightness feature, shown in the warning dialog about the accessibility shortcut. [CHAR LIMIT=none] -->
<string name="reduce_bright_colors_feature_name">Extra dim</string>
+ <!-- Title of hearing aids feature, shown in the warning dialog about the accessibility shortcut. [CHAR LIMIT=none] -->
+ <string name="hearing_aids_feature_name">Hearing devices</string>
+
<!-- Text in toast to alert the user that the accessibility shortcut turned on an accessibility service. [CHAR LIMIT=none] -->
<string name="accessibility_shortcut_enabling_service">Held volume keys. <xliff:g id="service_name" example="TalkBack">%1$s</xliff:g> turned on.</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index cd39e59..9b04c6b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3516,12 +3516,13 @@
<java-symbol type="drawable" name="ic_accessibility_color_inversion" />
<java-symbol type="drawable" name="ic_accessibility_color_correction" />
+ <java-symbol type="drawable" name="ic_accessibility_hearing_aid" />
<java-symbol type="drawable" name="ic_accessibility_magnification" />
<java-symbol type="drawable" name="ic_accessibility_reduce_bright_colors" />
<java-symbol type="drawable" name="ic_accessibility_one_handed" />
+ <java-symbol type="string" name="hearing_aids_feature_name" />
<java-symbol type="string" name="reduce_bright_colors_feature_name" />
-
<java-symbol type="string" name="one_handed_mode_feature_name" />
<!-- com.android.internal.widget.RecyclerView -->
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
index 6baf305..a68c392 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
@@ -21,6 +21,12 @@
import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY;
+import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME;
+import static com.android.internal.accessibility.AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME;
+import static com.android.internal.accessibility.AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME;
+import static com.android.internal.accessibility.AccessibilityShortcutController.ONE_HANDED_COMPONENT_NAME;
+import static com.android.internal.accessibility.AccessibilityShortcutController.REDUCE_BRIGHT_COLORS_COMPONENT_NAME;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
@@ -90,7 +96,6 @@
import java.util.Map;
import java.util.Set;
-
@RunWith(AndroidJUnit4.class)
public class AccessibilityShortcutControllerTest {
private static final String SERVICE_NAME_STRING = "fake.package/fake.service.name";
@@ -429,7 +434,7 @@
@Test
public void getFrameworkFeatureMap_shouldBeNonNullAndUnmodifiable() {
- Map<ComponentName, AccessibilityShortcutController.ToggleableFrameworkFeatureInfo>
+ Map<ComponentName, AccessibilityShortcutController.FrameworkFeatureInfo>
frameworkFeatureMap =
AccessibilityShortcutController.getFrameworkShortcutFeaturesMap();
assertTrue("Framework features not supported", frameworkFeatureMap.size() > 0);
@@ -443,6 +448,19 @@
}
@Test
+ public void getFrameworkFeatureMap_containsExpectedKey() {
+ Map<ComponentName, AccessibilityShortcutController.FrameworkFeatureInfo>
+ frameworkFeatureMap =
+ AccessibilityShortcutController.getFrameworkShortcutFeaturesMap();
+
+ assertTrue(frameworkFeatureMap.containsKey(COLOR_INVERSION_COMPONENT_NAME));
+ assertTrue(frameworkFeatureMap.containsKey(DALTONIZER_COMPONENT_NAME));
+ assertTrue(frameworkFeatureMap.containsKey(ONE_HANDED_COMPONENT_NAME));
+ assertTrue(frameworkFeatureMap.containsKey(REDUCE_BRIGHT_COLORS_COMPONENT_NAME));
+ assertTrue(frameworkFeatureMap.containsKey(ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME));
+ }
+
+ @Test
public void testOnAccessibilityShortcut_forServiceWithNoSummary_doesNotCrash()
throws Exception {
configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
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/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml
index c410d64..5bd15a9 100644
--- a/packages/CompanionDeviceManager/res/values/strings.xml
+++ b/packages/CompanionDeviceManager/res/values/strings.xml
@@ -158,7 +158,7 @@
<!-- Apps permission will be granted of corresponding profile [CHAR LIMIT=30] -->
<string name="permission_app_streaming">Apps</string>
- <!-- Nearby_device_streaming permission will be granted to the corresponding profile [CHAR LIMIT=30] -->
+ <!-- Nearby_device_streaming permission will be granted to the corresponding profile [CHAR LIMIT=45] -->
<string name="permission_nearby_device_streaming">Nearby Device Streaming</string>
<!-- Description of phone permission of corresponding profile [CHAR LIMIT=NONE] -->
diff --git a/packages/CredentialManager/res/values-ar/strings.xml b/packages/CredentialManager/res/values-ar/strings.xml
index e99e357..c0ff69d 100644
--- a/packages/CredentialManager/res/values-ar/strings.xml
+++ b/packages/CredentialManager/res/values-ar/strings.xml
@@ -10,44 +10,32 @@
<string name="string_use_another_device" msgid="8754514926121520445">"استخدام جهاز آخر"</string>
<string name="string_save_to_another_device" msgid="1959562542075194458">"الحفظ على جهاز آخر"</string>
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"توفير المزيد من الأمان باستخدام مفاتيح المرور"</string>
- <!-- no translation found for passkey_creation_intro_body_password (8825872426579958200) -->
- <skip />
- <!-- no translation found for passkey_creation_intro_body_fingerprint (7331338631826254055) -->
- <skip />
- <!-- no translation found for passkey_creation_intro_body_device (1203796455762131631) -->
- <skip />
+ <string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"باستخدام مفاتيح المرور، لا حاجة لإنشاء كلمات مرور معقدة أو تذكّرها."</string>
+ <string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"مفاتيح المرور هي مفاتيح رقمية مشفّرة يمكنك إنشاؤها باستخدام بصمة الإصبع أو التعرّف على الوجه أو قفل الشاشة."</string>
+ <string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"يتم حفظها في مدير كلمات مرور، حتى تتمكن من تسجيل الدخول على أجهزة أخرى."</string>
<string name="choose_provider_title" msgid="7245243990139698508">"اختيار مكان <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="create_your_passkeys" msgid="8901224153607590596">"إنشاء مفاتيح مرورك"</string>
<string name="save_your_password" msgid="6597736507991704307">"حفظ كلمة المرور"</string>
<string name="save_your_sign_in_info" msgid="7213978049817076882">"حفظ معلومات تسجيل الدخول"</string>
- <!-- no translation found for choose_provider_body (4384188171872005547) -->
- <skip />
- <!-- no translation found for choose_create_option_passkey_title (5220979185879006862) -->
- <skip />
- <!-- no translation found for choose_create_option_password_title (7097275038523578687) -->
- <skip />
- <!-- no translation found for choose_create_option_sign_in_title (4124872317613421249) -->
- <skip />
- <!-- no translation found for choose_create_option_description (5531335144879100664) -->
- <skip />
+ <string name="choose_provider_body" msgid="4384188171872005547">"اختَر مدير كلمة مرور لحفظ معلوماتك وتسجيل الدخول بشكل أسرع في المرة القادمة."</string>
+ <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"إنشاء مفتاح مرور لتطبيق \"<xliff:g id="APPNAME">%1$s</xliff:g>\""</string>
+ <string name="choose_create_option_password_title" msgid="7097275038523578687">"هل تريد حفظ كلمة المرور لتطبيق \"<xliff:g id="APPNAME">%1$s</xliff:g>\"؟"</string>
+ <string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"هل تريد حفظ معلومات تسجيل الدخول لتطبيق \"<xliff:g id="APPNAME">%1$s</xliff:g>\"؟"</string>
+ <string name="choose_create_option_description" msgid="5531335144879100664">"يمكنك استخدام <xliff:g id="CREDENTIALTYPES">%2$s</xliff:g> لتطبيق \"<xliff:g id="APPDOMAINNAME">%1$s</xliff:g>\" على أي جهاز. يتم حفظه في \"<xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>\" للحساب \"<xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>\"."</string>
<string name="passkey" msgid="632353688396759522">"مفتاح مرور"</string>
<string name="password" msgid="6738570945182936667">"كلمة المرور"</string>
<string name="sign_ins" msgid="4710739369149469208">"عمليات تسجيل الدخول"</string>
- <!-- no translation found for sign_in_info (2627704710674232328) -->
- <skip />
- <!-- no translation found for save_credential_to_title (3172811692275634301) -->
- <skip />
+ <string name="sign_in_info" msgid="2627704710674232328">"معلومات تسجيل الدخول"</string>
+ <string name="save_credential_to_title" msgid="3172811692275634301">"حفظ <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> في"</string>
<string name="create_passkey_in_other_device_title" msgid="6372952459932674632">"هل تريد إنشاء مفتاح مرور في جهاز آخر؟"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"هل تريد استخدام \"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>\" لكل عمليات تسجيل الدخول؟"</string>
<string name="use_provider_for_all_description" msgid="6560593199974037820">"ستخزِّن خدمة إدارة كلمات المرور هذه كلمات المرور ومفاتيح المرور لمساعدتك في تسجيل الدخول بسهولة."</string>
<string name="set_as_default" msgid="4415328591568654603">"ضبط الخيار كتلقائي"</string>
<string name="use_once" msgid="9027366575315399714">"الاستخدام مرة واحدة"</string>
- <!-- no translation found for more_options_usage_passwords_passkeys (3470113942332934279) -->
- <skip />
+ <string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> كلمة مرور • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> مفتاح مرور"</string>
<string name="more_options_usage_passwords" msgid="1632047277723187813">"عدد كلمات المرور: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>"</string>
<string name="more_options_usage_passkeys" msgid="5390320437243042237">"عدد مفاتيح المرور: <xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>"</string>
- <!-- no translation found for more_options_usage_credentials (1785697001787193984) -->
- <skip />
+ <string name="more_options_usage_credentials" msgid="1785697001787193984">"بيانات الاعتماد: <xliff:g id="TOTALCREDENTIALSNUMBER">%1$s</xliff:g>"</string>
<string name="passkey_before_subtitle" msgid="2448119456208647444">"مفتاح مرور"</string>
<string name="another_device" msgid="5147276802037801217">"جهاز آخر"</string>
<string name="other_password_manager" msgid="565790221427004141">"خدمات مدراء كلمات المرور الأخرى"</string>
@@ -58,8 +46,7 @@
<string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"هل تريد استخدام بيانات اعتماد تسجيل الدخول المحفوظة لتطبيق \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"؟"</string>
<string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"اختيار بيانات اعتماد تسجيل دخول محفوظة لـ \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
<string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"تسجيل الدخول بطريقة أخرى"</string>
- <!-- no translation found for snackbar_action (37373514216505085) -->
- <skip />
+ <string name="snackbar_action" msgid="37373514216505085">"عرض الخيارات"</string>
<string name="get_dialog_button_label_continue" msgid="6446201694794283870">"متابعة"</string>
<string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"خيارات تسجيل الدخول"</string>
<string name="get_dialog_heading_for_username" msgid="3456868514554204776">"معلومات تسجيل دخول \"<xliff:g id="USERNAME">%1$s</xliff:g>\""</string>
diff --git a/packages/CredentialManager/res/values-ja/strings.xml b/packages/CredentialManager/res/values-ja/strings.xml
index f2a91f5..18681fe 100644
--- a/packages/CredentialManager/res/values-ja/strings.xml
+++ b/packages/CredentialManager/res/values-ja/strings.xml
@@ -10,44 +10,32 @@
<string name="string_use_another_device" msgid="8754514926121520445">"別のデバイスを使用"</string>
<string name="string_save_to_another_device" msgid="1959562542075194458">"他のデバイスに保存"</string>
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"パスキーでより安全に"</string>
- <!-- no translation found for passkey_creation_intro_body_password (8825872426579958200) -->
- <skip />
- <!-- no translation found for passkey_creation_intro_body_fingerprint (7331338631826254055) -->
- <skip />
- <!-- no translation found for passkey_creation_intro_body_device (1203796455762131631) -->
- <skip />
+ <string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"パスキーがあれば、複雑なパスワードを作成したり覚えたりする必要はありません"</string>
+ <string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"パスキーは、指紋認証、顔認証、または画面ロックを使って作成される暗号化されたデジタルキーです"</string>
+ <string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"パスワード マネージャーに保存され、他のデバイスでもログインできます"</string>
<string name="choose_provider_title" msgid="7245243990139698508">"<xliff:g id="CREATETYPES">%1$s</xliff:g> の保存場所の選択"</string>
<string name="create_your_passkeys" msgid="8901224153607590596">"パスキーの作成"</string>
<string name="save_your_password" msgid="6597736507991704307">"パスワードを保存"</string>
<string name="save_your_sign_in_info" msgid="7213978049817076882">"ログイン情報を保存"</string>
- <!-- no translation found for choose_provider_body (4384188171872005547) -->
- <skip />
- <!-- no translation found for choose_create_option_passkey_title (5220979185879006862) -->
- <skip />
- <!-- no translation found for choose_create_option_password_title (7097275038523578687) -->
- <skip />
- <!-- no translation found for choose_create_option_sign_in_title (4124872317613421249) -->
- <skip />
- <!-- no translation found for choose_create_option_description (5531335144879100664) -->
- <skip />
+ <string name="choose_provider_body" msgid="4384188171872005547">"パスワード マネージャーを選択して情報を保存しておくと、次回からすばやくログインできます。"</string>
+ <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g> のパスキーを作成しますか?"</string>
+ <string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g> のパスワードを保存しますか?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g> のログイン情報を保存しますか?"</string>
+ <string name="choose_create_option_description" msgid="5531335144879100664">"<xliff:g id="APPDOMAINNAME">%1$s</xliff:g> の<xliff:g id="CREDENTIALTYPES">%2$s</xliff:g>はどのデバイスでも使用できます。<xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> の <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>に保存されます。"</string>
<string name="passkey" msgid="632353688396759522">"パスキー"</string>
<string name="password" msgid="6738570945182936667">"パスワード"</string>
<string name="sign_ins" msgid="4710739369149469208">"ログイン"</string>
- <!-- no translation found for sign_in_info (2627704710674232328) -->
- <skip />
- <!-- no translation found for save_credential_to_title (3172811692275634301) -->
- <skip />
+ <string name="sign_in_info" msgid="2627704710674232328">"ログイン情報"</string>
+ <string name="save_credential_to_title" msgid="3172811692275634301">"<xliff:g id="CREDENTIALTYPES">%1$s</xliff:g>の保存先"</string>
<string name="create_passkey_in_other_device_title" msgid="6372952459932674632">"他のデバイスでパスキーを作成しますか?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"ログインのたびに <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> を使用しますか?"</string>
<string name="use_provider_for_all_description" msgid="6560593199974037820">"このパスワード マネージャーに、パスワードやパスキーが保存され、簡単にログインできるようになります。"</string>
<string name="set_as_default" msgid="4415328591568654603">"デフォルトに設定"</string>
<string name="use_once" msgid="9027366575315399714">"1 回使用"</string>
- <!-- no translation found for more_options_usage_passwords_passkeys (3470113942332934279) -->
- <skip />
+ <string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> 件のパスワード • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> 件のパスキー"</string>
<string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> 件のパスワード"</string>
<string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> 件のパスキー"</string>
- <!-- no translation found for more_options_usage_credentials (1785697001787193984) -->
- <skip />
+ <string name="more_options_usage_credentials" msgid="1785697001787193984">"<xliff:g id="TOTALCREDENTIALSNUMBER">%1$s</xliff:g> 件の認証情報"</string>
<string name="passkey_before_subtitle" msgid="2448119456208647444">"パスキー"</string>
<string name="another_device" msgid="5147276802037801217">"別のデバイス"</string>
<string name="other_password_manager" msgid="565790221427004141">"他のパスワード マネージャー"</string>
@@ -58,8 +46,7 @@
<string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> の保存したログイン情報を使用しますか?"</string>
<string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> の保存したログイン情報の選択"</string>
<string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"別の方法でログイン"</string>
- <!-- no translation found for snackbar_action (37373514216505085) -->
- <skip />
+ <string name="snackbar_action" msgid="37373514216505085">"オプションを表示"</string>
<string name="get_dialog_button_label_continue" msgid="6446201694794283870">"続行"</string>
<string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"ログイン オプション"</string>
<string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> 用"</string>
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/ProgressBar.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/ProgressBar.kt
index 5d8502d..0f5e935 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/ProgressBar.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/ProgressBar.kt
@@ -30,9 +30,11 @@
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.CornerRadius
+import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.DrawScope
+import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
/**
@@ -62,13 +64,15 @@
}
private fun DrawScope.drawLinearBar(
- endFraction: Float,
+ progress: Float,
color: Color,
roundedCorner: Boolean
) {
- val width = endFraction * size.width
+ val isLtr = layoutDirection == LayoutDirection.Ltr
+ val width = progress * size.width
drawRoundRect(
color = color,
+ topLeft = if (isLtr) Offset.Zero else Offset((1 - progress) * size.width, 0f),
size = Size(width, size.height),
cornerRadius = if (roundedCorner) CornerRadius(
size.height / 2,
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 63dbc7c..a2c59ff 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -564,10 +564,8 @@
<string name="user_add_user_title" msgid="5457079143694924885">"新しいユーザーを追加しますか?"</string>
<string name="user_add_user_message_long" msgid="1527434966294733380">"追加ユーザーを作成して、このデバイスを他のユーザーと共有できます。各ユーザーは各自のスペースを所有して、アプリや壁紙などのカスタマイズを行うことができます。Wi-Fi など、すべてのユーザーに影響するデバイス設定を変更することもできます。\n\n新しく追加したユーザーは各自でスペースをセットアップする必要があります。\n\nすべてのユーザーがアプリを更新でき、その影響は他のユーザーにも及びます。ユーザー補助機能の設定とサービスは新しいユーザーに適用されないことがあります。"</string>
<string name="user_add_user_message_short" msgid="3295959985795716166">"新しく追加したユーザーは各自でスペースをセットアップする必要があります。\n\nすべてのユーザーがアプリを更新でき、その影響は他のユーザーにも及びます。"</string>
- <!-- no translation found for user_grant_admin_title (5565796912475193314) -->
- <skip />
- <!-- no translation found for user_grant_admin_message (7925257971286380976) -->
- <skip />
+ <string name="user_grant_admin_title" msgid="5565796912475193314">"このユーザーに管理者権限を付与しますか?"</string>
+ <string name="user_grant_admin_message" msgid="7925257971286380976">"管理者は、他のユーザーの管理、デバイスの設定の変更、デバイスの出荷時設定へのリセットを行えます。"</string>
<string name="user_setup_dialog_title" msgid="8037342066381939995">"ユーザーを今すぐセットアップ"</string>
<string name="user_setup_dialog_message" msgid="269931619868102841">"ユーザーがデバイスを使って各自のスペースをセットアップできるようにします"</string>
<string name="user_setup_profile_dialog_message" msgid="4788197052296962620">"プロファイルを今すぐセットアップしますか?"</string>
@@ -599,10 +597,8 @@
<string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"新しいゲスト セッションが開始し、現在のセッションのすべてのアプリとデータが削除されます"</string>
<string name="guest_exit_dialog_title" msgid="1846494656849381804">"ゲストモードを終了しますか?"</string>
<string name="guest_exit_dialog_message" msgid="1743218864242719783">"現在のゲスト セッションからすべてのアプリとデータが削除されます"</string>
- <!-- no translation found for grant_admin (4273077214151417783) -->
- <skip />
- <!-- no translation found for not_grant_admin (6985027675930546850) -->
- <skip />
+ <string name="grant_admin" msgid="4273077214151417783">"このユーザーに管理者権限を付与します"</string>
+ <string name="not_grant_admin" msgid="6985027675930546850">"ユーザーに管理者権限を付与しません"</string>
<string name="guest_exit_dialog_button" msgid="1736401897067442044">"終了"</string>
<string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"ゲストアクティビティの保存"</string>
<string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"現在のセッションのアクティビティの保存や、すべてのアプリとデータの削除を行えます"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index c9b1f33..81c2a00 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -564,7 +564,7 @@
<string name="user_add_user_title" msgid="5457079143694924885">"要新增使用者嗎?"</string>
<string name="user_add_user_message_long" msgid="1527434966294733380">"你可以建立其他使用者,藉此與他人共用這個裝置。每位使用者都有自己的專屬空間,並可使用應用程式、桌布等項目自訂個人空間。此外,使用者也可以調整會影響所有人的裝置設定,例如 Wi‑Fi 設定。\n\n新增的使用者需要自行設定個人空間。\n\n任何使用者都可以為所有其他使用者更新應用程式。無障礙設定和服務可能無法轉移到新的使用者。"</string>
<string name="user_add_user_message_short" msgid="3295959985795716166">"新增的使用者需要自行設定個人空間。\n\n任何使用者皆可為其他所有使用者更新應用程式。"</string>
- <string name="user_grant_admin_title" msgid="5565796912475193314">"要授予這位使用者管理員權限嗎?"</string>
+ <string name="user_grant_admin_title" msgid="5565796912475193314">"要將管理員權限授予此使用者嗎?"</string>
<string name="user_grant_admin_message" msgid="7925257971286380976">"取得管理員權限後,就能管理其他使用者、修改裝置設定,以及將裝置恢復原廠設定。"</string>
<string name="user_setup_dialog_title" msgid="8037342066381939995">"立即設定使用者?"</string>
<string name="user_setup_dialog_message" msgid="269931619868102841">"請確保對方可以使用裝置並設定自己的空間"</string>
@@ -597,8 +597,8 @@
<string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"如果重設,系統會開始新的訪客工作階段,並刪除目前工作階段中的所有應用程式和資料"</string>
<string name="guest_exit_dialog_title" msgid="1846494656849381804">"要結束訪客模式嗎?"</string>
<string name="guest_exit_dialog_message" msgid="1743218864242719783">"如果結束,系統會刪除目前訪客工作階段中的所有應用程式和資料"</string>
- <string name="grant_admin" msgid="4273077214151417783">"授予這位使用者管理員權限"</string>
- <string name="not_grant_admin" msgid="6985027675930546850">"不授予這位使用者管理員權限"</string>
+ <string name="grant_admin" msgid="4273077214151417783">"將管理員權限授予這位使用者"</string>
+ <string name="not_grant_admin" msgid="6985027675930546850">"不要將管理員權限授予使用者"</string>
<string name="guest_exit_dialog_button" msgid="1736401897067442044">"結束"</string>
<string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"要儲存訪客活動嗎?"</string>
<string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"你可以儲存目前工作階段中的活動,也可以刪除所有應用程式和資料"</string>
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/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index 5c97f37..aec1767 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -92,8 +92,6 @@
@VisibleForTesting
List<MediaDevice> mDisconnectedMediaDevices = new CopyOnWriteArrayList<>();
@VisibleForTesting
- MediaDevice mPhoneDevice;
- @VisibleForTesting
MediaDevice mCurrentConnectedDevice;
@VisibleForTesting
DeviceAttributeChangeCallback mDeviceAttributeChangeCallback =
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/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
index c8d186f..d87fc54 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
@@ -266,18 +266,15 @@
final MediaDevice device1 = mock(MediaDevice.class);
final MediaDevice device2 = mock(MediaDevice.class);
final MediaDevice device3 = mock(MediaDevice.class);
- mLocalMediaManager.mPhoneDevice = mock(PhoneMediaDevice.class);
devices.add(device1);
devices.add(device2);
mLocalMediaManager.mMediaDevices.add(device3);
- mLocalMediaManager.mMediaDevices.add(mLocalMediaManager.mPhoneDevice);
when(device1.getId()).thenReturn(TEST_DEVICE_ID_1);
when(device2.getId()).thenReturn(TEST_DEVICE_ID_2);
when(device3.getId()).thenReturn(TEST_DEVICE_ID_3);
- when(mLocalMediaManager.mPhoneDevice.getId()).thenReturn("test_phone_id");
- assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
+ assertThat(mLocalMediaManager.mMediaDevices).hasSize(1);
mLocalMediaManager.registerCallback(mCallback);
mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices);
@@ -290,14 +287,12 @@
final List<MediaDevice> devices = new ArrayList<>();
final MediaDevice device1 = mock(MediaDevice.class);
final MediaDevice device2 = mock(MediaDevice.class);
- mLocalMediaManager.mPhoneDevice = mock(PhoneMediaDevice.class);
devices.add(device1);
devices.add(device2);
mLocalMediaManager.mMediaDevices.add(device1);
mLocalMediaManager.mMediaDevices.add(device2);
- mLocalMediaManager.mMediaDevices.add(mLocalMediaManager.mPhoneDevice);
- assertThat(mLocalMediaManager.mMediaDevices).hasSize(3);
+ assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
mLocalMediaManager.registerCallback(mCallback);
mLocalMediaManager.mMediaDeviceCallback
.onDeviceListRemoved(mLocalMediaManager.mMediaDevices);
@@ -312,19 +307,17 @@
final MediaDevice device1 = mock(MediaDevice.class);
final MediaDevice device2 = mock(MediaDevice.class);
final MediaDevice device3 = mock(MediaDevice.class);
- mLocalMediaManager.mPhoneDevice = mock(PhoneMediaDevice.class);
devices.add(device1);
devices.add(device3);
mLocalMediaManager.mMediaDevices.add(device1);
mLocalMediaManager.mMediaDevices.add(device2);
mLocalMediaManager.mMediaDevices.add(device3);
- mLocalMediaManager.mMediaDevices.add(mLocalMediaManager.mPhoneDevice);
- assertThat(mLocalMediaManager.mMediaDevices).hasSize(4);
+ assertThat(mLocalMediaManager.mMediaDevices).hasSize(3);
mLocalMediaManager.registerCallback(mCallback);
mLocalMediaManager.mMediaDeviceCallback.onDeviceListRemoved(devices);
- assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
+ assertThat(mLocalMediaManager.mMediaDevices).hasSize(1);
verify(mCallback).onDeviceListUpdate(any());
}
@@ -452,9 +445,7 @@
public void onDeviceListAdded_haveMutingExpectedDevice_addMutingExpectedDevice() {
final List<MediaDevice> devices = new ArrayList<>();
final MediaDevice device1 = mock(MediaDevice.class);
- mLocalMediaManager.mPhoneDevice = mock(PhoneMediaDevice.class);
devices.add(device1);
- devices.add(mLocalMediaManager.mPhoneDevice);
final List<LocalBluetoothProfile> profiles = new ArrayList<>();
final A2dpProfile a2dpProfile = mock(A2dpProfile.class);
@@ -483,13 +474,12 @@
when(device1.getId()).thenReturn(TEST_DEVICE_ID_1);
when(device1.getDeviceType()).thenReturn(MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE);
- when(mLocalMediaManager.mPhoneDevice.getId()).thenReturn("test_phone_id");
assertThat(mLocalMediaManager.mMediaDevices).hasSize(0);
mLocalMediaManager.registerCallback(mCallback);
mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices);
- assertThat(mLocalMediaManager.mMediaDevices).hasSize(3);
+ assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
verify(mCallback).onDeviceListUpdate(any());
}
@@ -536,17 +526,13 @@
final MediaDevice device1 = mock(MediaDevice.class);
final MediaDevice device2 = mock(MediaDevice.class);
final MediaDevice device3 = mock(MediaDevice.class);
- mLocalMediaManager.mPhoneDevice = mock(PhoneMediaDevice.class);
devices.add(device1);
devices.add(device2);
mLocalMediaManager.mMediaDevices.add(device3);
- mLocalMediaManager.mMediaDevices.add(mLocalMediaManager.mPhoneDevice);
mShadowBluetoothAdapter = null;
- when(mLocalMediaManager.mPhoneDevice.getId()).thenReturn("test_phone_id");
-
- assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
+ assertThat(mLocalMediaManager.mMediaDevices).hasSize(1);
mLocalMediaManager.registerCallback(mCallback);
mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices);
diff --git a/packages/SystemUI/res/layout/window_magnification_settings_view.xml b/packages/SystemUI/res/layout/window_magnification_settings_view.xml
index 6d8847c..a3c0554 100644
--- a/packages/SystemUI/res/layout/window_magnification_settings_view.xml
+++ b/packages/SystemUI/res/layout/window_magnification_settings_view.xml
@@ -37,6 +37,7 @@
<Button
android:id="@+id/magnifier_edit_button"
+ android:background="@drawable/accessibility_magnification_setting_view_btn_bg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/accessibility_magnifier_edit"
@@ -57,21 +58,36 @@
android:layout_height="56dp"
android:scaleType="center"
android:layout_weight="1"
- android:layout_marginStart="12dp"/>
+ android:layout_marginStart="12dp"
+ android:background="@drawable/accessibility_magnification_setting_view_btn_bg"
+ android:padding="@dimen/magnification_switch_button_padding"
+ android:src="@drawable/ic_magnification_menu_small"
+ android:tint="@color/accessibility_magnifier_icon_color"
+ android:tintMode="src_atop" />
<ImageButton
android:id="@+id/magnifier_medium_button"
android:layout_width="0dp"
android:layout_height="56dp"
android:scaleType="center"
- android:layout_weight="1"/>
+ android:layout_weight="1"
+ android:background="@drawable/accessibility_magnification_setting_view_btn_bg"
+ android:padding="@dimen/magnification_switch_button_padding"
+ android:src="@drawable/ic_magnification_menu_medium"
+ android:tint="@color/accessibility_magnifier_icon_color"
+ android:tintMode="src_atop" />
<ImageButton
android:id="@+id/magnifier_large_button"
android:layout_width="0dp"
android:layout_height="56dp"
android:scaleType="center"
- android:layout_weight="1"/>
+ android:layout_weight="1"
+ android:background="@drawable/accessibility_magnification_setting_view_btn_bg"
+ android:padding="@dimen/magnification_switch_button_padding"
+ android:src="@drawable/ic_magnification_menu_large"
+ android:tint="@color/accessibility_magnifier_icon_color"
+ android:tintMode="src_atop" />
<ImageButton
android:id="@+id/magnifier_full_button"
@@ -79,7 +95,12 @@
android:layout_height="56dp"
android:scaleType="center"
android:layout_weight="1"
- android:layout_marginEnd="12dp"/>
+ android:layout_marginEnd="12dp"
+ android:background="@drawable/accessibility_magnification_setting_view_btn_bg"
+ android:padding="@dimen/magnification_switch_button_padding"
+ android:src="@drawable/ic_open_in_new_fullscreen"
+ android:tint="@color/accessibility_magnifier_icon_color"
+ android:tintMode="src_atop" />
</LinearLayout>
<LinearLayout
@@ -141,6 +162,7 @@
<Button
android:id="@+id/magnifier_close_button"
+ android:background="@drawable/accessibility_magnification_setting_view_btn_bg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/accessibility_magnification_close"
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index ec15d1a..a3dbaad 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -440,6 +440,7 @@
final int configDiff = newConfig.diff(mConfiguration);
mConfiguration.setTo(newConfig);
onConfigurationChanged(configDiff);
+ mWindowMagnificationSettings.onConfigurationChanged(configDiff);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
index 069c0f6..9f857a8 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
@@ -27,8 +27,6 @@
import android.content.pm.ActivityInfo;
import android.graphics.Insets;
import android.graphics.PixelFormat;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.UserHandle;
@@ -105,14 +103,14 @@
/** Denotes the Magnification size type. */
public @interface MagnificationSize {
int NONE = 0;
- int SMALL = 1;
+ int SMALL = 1;
int MEDIUM = 2;
int LARGE = 3;
}
@VisibleForTesting
WindowMagnificationSettings(Context context, WindowMagnificationSettingsCallback callback,
- SfVsyncFrameCallbackProvider sfVsyncFrameProvider) {
+ SfVsyncFrameCallbackProvider sfVsyncFrameProvider) {
mContext = context;
mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
mWindowManager = mContext.getSystemService(WindowManager.class);
@@ -123,66 +121,10 @@
Settings.Secure.ACCESSIBILITY_ALLOW_DIAGONAL_SCROLLING, 0,
UserHandle.USER_CURRENT) == 1;
- float scale = Settings.Secure.getFloatForUser(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, 0,
- UserHandle.USER_CURRENT);
-
- mSettingView = (LinearLayout) View.inflate(context,
- R.layout.window_magnification_settings_view, null);
-
- mSettingView.setClickable(true);
- mSettingView.setFocusable(true);
- mSettingView.setOnTouchListener(this::onTouch);
-
- mPanelView = mSettingView.findViewById(R.id.magnifier_panel_view);
- mSmallButton = mSettingView.findViewById(R.id.magnifier_small_button);
- mMediumButton = mSettingView.findViewById(R.id.magnifier_medium_button);
- mLargeButton = mSettingView.findViewById(R.id.magnifier_large_button);
- mCloseButton = mSettingView.findViewById(R.id.magnifier_close_button);
- mEditButton = mSettingView.findViewById(R.id.magnifier_edit_button);
- mChangeModeButton = mSettingView.findViewById(R.id.magnifier_full_button);
-
- mZoomSeekbar = mSettingView.findViewById(R.id.magnifier_zoom_seekbar);
- mZoomSeekbar.setOnSeekBarChangeListener(new ZoomSeekbarChangeListener());
- setSeekbarProgress(scale);
- mAllowDiagonalScrollingSwitch =
- (Switch) mSettingView.findViewById(R.id.magnifier_horizontal_lock_switch);
- mAllowDiagonalScrollingSwitch.setChecked(mAllowDiagonalScrolling);
- mAllowDiagonalScrollingSwitch.setOnCheckedChangeListener((view, checked) -> {
- toggleDiagonalScrolling();
- });
+ inflateView();
mParams = createLayoutParams(context);
- applyResourcesValues();
-
- mSmallButton.setAccessibilityDelegate(mButtonDelegate);
- mSmallButton.setOnClickListener(mButtonClickListener);
-
- mMediumButton.setAccessibilityDelegate(mButtonDelegate);
- mMediumButton.setOnClickListener(mButtonClickListener);
-
- mLargeButton.setAccessibilityDelegate(mButtonDelegate);
- mLargeButton.setOnClickListener(mButtonClickListener);
-
- mCloseButton.setAccessibilityDelegate(mButtonDelegate);
- mCloseButton.setOnClickListener(mButtonClickListener);
-
- mChangeModeButton.setAccessibilityDelegate(mButtonDelegate);
- mChangeModeButton.setOnClickListener(mButtonClickListener);
-
- mEditButton.setAccessibilityDelegate(mButtonDelegate);
- mEditButton.setOnClickListener(mButtonClickListener);
-
mWindowInsetChangeRunnable = this::onWindowInsetChanged;
- mSettingView.setOnApplyWindowInsetsListener((v, insets) -> {
- // Adds a pending post check to avoiding redundant calculation because this callback
- // is sent frequently when the switch icon window dragged by the users.
- if (!mSettingView.getHandler().hasCallbacks(mWindowInsetChangeRunnable)) {
- mSettingView.getHandler().post(mWindowInsetChangeRunnable);
- }
- return v.onApplyWindowInsets(insets);
- });
-
mGestureDetector = new MagnificationGestureDetector(context,
context.getMainThreadHandler(), this);
}
@@ -230,43 +172,6 @@
}
}
- private void applyResourcesValues() {
- final int padding = mContext.getResources().getDimensionPixelSize(
- R.dimen.magnification_switch_button_padding);
-
- PorterDuffColorFilter filter = new PorterDuffColorFilter(mContext.getColor(
- R.color.accessibility_magnifier_icon_color), PorterDuff.Mode.SRC_ATOP);
-
- mSmallButton.setImageResource(R.drawable.ic_magnification_menu_small);
- mSmallButton.setColorFilter(filter);
- mSmallButton.setBackgroundResource(
- R.drawable.accessibility_magnification_setting_view_btn_bg);
- mSmallButton.setPadding(padding, padding, padding, padding);
-
- mMediumButton.setImageResource(R.drawable.ic_magnification_menu_medium);
- mMediumButton.setColorFilter(filter);
- mMediumButton.setBackgroundResource(
- R.drawable.accessibility_magnification_setting_view_btn_bg);
- mMediumButton.setPadding(padding, padding, padding, padding);
-
- mLargeButton.setImageResource(R.drawable.ic_magnification_menu_large);
- mLargeButton.setColorFilter(filter);
- mLargeButton.setBackgroundResource(
- R.drawable.accessibility_magnification_setting_view_btn_bg);
- mLargeButton.setPadding(padding, padding, padding, padding);
-
- mChangeModeButton.setImageResource(R.drawable.ic_open_in_new_fullscreen);
- mChangeModeButton.setColorFilter(filter);
- mChangeModeButton.setBackgroundResource(
- R.drawable.accessibility_magnification_setting_view_btn_bg);
- mChangeModeButton.setPadding(padding, padding, padding, padding);
-
- mCloseButton.setBackgroundResource(
- R.drawable.accessibility_magnification_setting_view_btn_bg);
- mEditButton.setBackgroundResource(
- R.drawable.accessibility_magnification_setting_view_btn_bg);
- }
-
private final AccessibilityDelegate mButtonDelegate = new AccessibilityDelegate() {
@Override
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
@@ -394,6 +299,10 @@
}
public void hideSettingPanel() {
+ hideSettingPanel(true);
+ }
+
+ public void hideSettingPanel(boolean resetPosition) {
if (!mIsVisible) {
return;
}
@@ -401,8 +310,10 @@
// Reset button status.
mWindowManager.removeView(mSettingView);
mIsVisible = false;
- mParams.x = 0;
- mParams.y = 0;
+ if (resetPosition) {
+ mParams.x = 0;
+ mParams.y = 0;
+ }
mContext.unregisterReceiver(mScreenOffReceiver);
}
@@ -458,9 +369,72 @@
mZoomSeekbar.setProgress(index);
}
+ void inflateView() {
+ mSettingView = (LinearLayout) View.inflate(mContext,
+ R.layout.window_magnification_settings_view, null);
+
+ mSettingView.setClickable(true);
+ mSettingView.setFocusable(true);
+ mSettingView.setOnTouchListener(this::onTouch);
+
+ mPanelView = mSettingView.findViewById(R.id.magnifier_panel_view);
+ mSmallButton = mSettingView.findViewById(R.id.magnifier_small_button);
+ mMediumButton = mSettingView.findViewById(R.id.magnifier_medium_button);
+ mLargeButton = mSettingView.findViewById(R.id.magnifier_large_button);
+ mCloseButton = mSettingView.findViewById(R.id.magnifier_close_button);
+ mEditButton = mSettingView.findViewById(R.id.magnifier_edit_button);
+ mChangeModeButton = mSettingView.findViewById(R.id.magnifier_full_button);
+
+ mZoomSeekbar = mSettingView.findViewById(R.id.magnifier_zoom_seekbar);
+ mZoomSeekbar.setOnSeekBarChangeListener(new ZoomSeekbarChangeListener());
+
+ float scale = Settings.Secure.getFloatForUser(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, 0,
+ UserHandle.USER_CURRENT);
+ setSeekbarProgress(scale);
+ mAllowDiagonalScrollingSwitch =
+ (Switch) mSettingView.findViewById(R.id.magnifier_horizontal_lock_switch);
+ mAllowDiagonalScrollingSwitch.setChecked(mAllowDiagonalScrolling);
+ mAllowDiagonalScrollingSwitch.setOnCheckedChangeListener((view, checked) -> {
+ toggleDiagonalScrolling();
+ });
+
+ mSmallButton.setAccessibilityDelegate(mButtonDelegate);
+ mSmallButton.setOnClickListener(mButtonClickListener);
+
+ mMediumButton.setAccessibilityDelegate(mButtonDelegate);
+ mMediumButton.setOnClickListener(mButtonClickListener);
+
+ mLargeButton.setAccessibilityDelegate(mButtonDelegate);
+ mLargeButton.setOnClickListener(mButtonClickListener);
+
+ mCloseButton.setAccessibilityDelegate(mButtonDelegate);
+ mCloseButton.setOnClickListener(mButtonClickListener);
+
+ mChangeModeButton.setAccessibilityDelegate(mButtonDelegate);
+ mChangeModeButton.setOnClickListener(mButtonClickListener);
+
+ mEditButton.setAccessibilityDelegate(mButtonDelegate);
+ mEditButton.setOnClickListener(mButtonClickListener);
+
+ mSettingView.setOnApplyWindowInsetsListener((v, insets) -> {
+ // Adds a pending post check to avoiding redundant calculation because this callback
+ // is sent frequently when the switch icon window dragged by the users.
+ if (!mSettingView.getHandler().hasCallbacks(mWindowInsetChangeRunnable)) {
+ mSettingView.getHandler().post(mWindowInsetChangeRunnable);
+ }
+ return v.onApplyWindowInsets(insets);
+ });
+ }
+
void onConfigurationChanged(int configDiff) {
if ((configDiff & ActivityInfo.CONFIG_UI_MODE) != 0) {
- applyResourcesValues();
+ boolean showSettingPanelAfterThemeChange = mIsVisible;
+ hideSettingPanel(/* resetPosition= */ false);
+ inflateView();
+ if (showSettingPanelAfterThemeChange) {
+ showSettingPanel(/* resetPosition= */ false);
+ }
return;
}
if ((configDiff & ActivityInfo.CONFIG_ORIENTATION) != 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index d85ab8e..3d4347e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -1459,16 +1459,16 @@
public void maybeHandlePendingLock() {
if (mPendingLock) {
- // The screen off animation is playing, so if we lock now, the foreground app will
- // vanish and the keyguard will jump-cut in. Delay it, until either:
+ // The screen off animation is playing or is about to be, so if we lock now, the
+ // foreground app will vanish and the keyguard will jump-cut in. Delay it, until either:
// - The screen off animation ends. We will call maybeHandlePendingLock from
// the end action in UnlockedScreenOffAnimationController#animateInKeyguard.
// - The screen off animation is cancelled by the device waking back up. We will call
// maybeHandlePendingLock from KeyguardViewMediator#onStartedWakingUp.
- if (mScreenOffAnimationController.isKeyguardShowDelayed()) {
+ if (mScreenOffAnimationController.shouldDelayKeyguardShow()) {
if (DEBUG) {
Log.d(TAG, "#maybeHandlePendingLock: not handling because the screen off "
- + "animation's isKeyguardShowDelayed() returned true. This should be "
+ + "animation's shouldDelayKeyguardShow() returned true. This should be "
+ "handled soon by #onStartedWakingUp, or by the end actions of the "
+ "screen off animation.");
}
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/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 5196f49..9b0d8db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -444,6 +444,45 @@
TestableLooper.get(this).processAllMessages();
}
+ @Test
+ @TestableLooper.RunWithLooper(setAsMainLooper = true)
+ public void testKeyguardDelayedOnGoingToSleep_ifScreenOffAnimationWillPlayButIsntPlayingYet() {
+ mViewMediator.onSystemReady();
+ TestableLooper.get(this).processAllMessages();
+
+ mViewMediator.setShowingLocked(false);
+ TestableLooper.get(this).processAllMessages();
+
+ mViewMediator.onStartedGoingToSleep(OFF_BECAUSE_OF_USER);
+ TestableLooper.get(this).processAllMessages();
+
+ when(mScreenOffAnimationController.shouldDelayKeyguardShow()).thenReturn(true);
+ when(mScreenOffAnimationController.isKeyguardShowDelayed()).thenReturn(false);
+ mViewMediator.onFinishedGoingToSleep(OFF_BECAUSE_OF_USER, false);
+ TestableLooper.get(this).processAllMessages();
+
+ assertFalse(mViewMediator.isShowingAndNotOccluded());
+ }
+
+ @Test
+ @TestableLooper.RunWithLooper(setAsMainLooper = true)
+ public void testKeyguardNotDelayedOnGoingToSleep_ifScreenOffAnimationWillNotPlay() {
+ mViewMediator.onSystemReady();
+ TestableLooper.get(this).processAllMessages();
+
+ mViewMediator.setShowingLocked(false);
+ TestableLooper.get(this).processAllMessages();
+
+ mViewMediator.onStartedGoingToSleep(OFF_BECAUSE_OF_USER);
+ TestableLooper.get(this).processAllMessages();
+
+ when(mScreenOffAnimationController.shouldDelayKeyguardShow()).thenReturn(false);
+ mViewMediator.onFinishedGoingToSleep(OFF_BECAUSE_OF_USER, false);
+ TestableLooper.get(this).processAllMessages();
+
+ assertTrue(mViewMediator.isShowingAndNotOccluded());
+ }
+
private void createAndStartViewMediator() {
mViewMediator = new KeyguardViewMediator(
mContext,
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/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index c5598cb..5e3e97d 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -33,6 +33,7 @@
import static android.view.accessibility.AccessibilityManager.CONTRAST_NOT_SET;
import static android.view.accessibility.AccessibilityManager.ShortcutType;
+import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME;
@@ -131,7 +132,8 @@
import com.android.internal.R;
import com.android.internal.accessibility.AccessibilityShortcutController;
-import com.android.internal.accessibility.AccessibilityShortcutController.ToggleableFrameworkFeatureInfo;
+import com.android.internal.accessibility.AccessibilityShortcutController.FrameworkFeatureInfo;
+import com.android.internal.accessibility.AccessibilityShortcutController.LaunchableFrameworkFeatureInfo;
import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity;
import com.android.internal.accessibility.dialog.AccessibilityShortcutChooserActivity;
import com.android.internal.annotations.GuardedBy;
@@ -1810,6 +1812,18 @@
}
}
+ private void launchAccessibilitySubSettings(int displayId, ComponentName name) {
+ final Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_DETAILS_SETTINGS);
+ final Bundle bundle = ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle();
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(Intent.EXTRA_COMPONENT_NAME, name.flattenToString());
+ try {
+ mContext.startActivityAsUser(intent, bundle, UserHandle.of(mCurrentUserId));
+ } catch (ActivityNotFoundException ignore) {
+ // ignore the exception
+ }
+ }
+
private void notifyAccessibilityButtonVisibilityChangedLocked(boolean available) {
final AccessibilityUserState state = getCurrentUserStateLocked();
mIsAccessibilityButtonShown = available;
@@ -3245,7 +3259,7 @@
return;
}
// In case user assigned an accessibility framework feature to the given shortcut.
- if (performAccessibilityFrameworkFeature(targetComponentName, shortcutType)) {
+ if (performAccessibilityFrameworkFeature(displayId, targetComponentName, shortcutType)) {
return;
}
// In case user assigned an accessibility shortcut target to the given shortcut.
@@ -3260,17 +3274,24 @@
}
}
- private boolean performAccessibilityFrameworkFeature(ComponentName assignedTarget,
- @ShortcutType int shortcutType) {
- final Map<ComponentName, ToggleableFrameworkFeatureInfo> frameworkFeatureMap =
+ private boolean performAccessibilityFrameworkFeature(int displayId,
+ ComponentName assignedTarget, @ShortcutType int shortcutType) {
+ final Map<ComponentName, FrameworkFeatureInfo> frameworkFeatureMap =
AccessibilityShortcutController.getFrameworkShortcutFeaturesMap();
if (!frameworkFeatureMap.containsKey(assignedTarget)) {
return false;
}
- // Toggle the requested framework feature
- final ToggleableFrameworkFeatureInfo featureInfo = frameworkFeatureMap.get(assignedTarget);
+ final FrameworkFeatureInfo featureInfo = frameworkFeatureMap.get(assignedTarget);
final SettingStringHelper setting = new SettingStringHelper(mContext.getContentResolver(),
featureInfo.getSettingKey(), mCurrentUserId);
+
+ if (featureInfo instanceof LaunchableFrameworkFeatureInfo) {
+ logAccessibilityShortcutActivated(mContext, assignedTarget, shortcutType,
+ /* serviceEnabled= */ true);
+ launchAccessibilityFrameworkFeature(displayId, assignedTarget);
+ return true;
+ }
+
// Assuming that the default state will be to have the feature off
if (!TextUtils.equals(featureInfo.getSettingOnValue(), setting.read())) {
logAccessibilityShortcutActivated(mContext, assignedTarget, shortcutType,
@@ -3374,6 +3395,12 @@
}
}
+ private void launchAccessibilityFrameworkFeature(int displayId, ComponentName assignedTarget) {
+ if (assignedTarget.equals(ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME)) {
+ launchAccessibilitySubSettings(displayId, ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME);
+ }
+ }
+
@Override
public List<String> getAccessibilityShortcutTargets(@ShortcutType int shortcutType) {
if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
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/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index a53d0f9..65acdc1 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -4300,9 +4300,7 @@
case KeyEvent.KEYCODE_DEMO_APP_3:
case KeyEvent.KEYCODE_DEMO_APP_4: {
// TODO(b/254604589): Dispatch KeyEvent to System UI.
- if (!mStylusButtonsDisabled) {
- sendSystemKeyToStatusBarAsync(keyCode);
- }
+ sendSystemKeyToStatusBarAsync(keyCode);
// Just drop if keys are not intercepted for direct key.
result &= ~ACTION_PASS_TO_USER;
@@ -4312,7 +4310,9 @@
case KeyEvent.KEYCODE_STYLUS_BUTTON_SECONDARY:
case KeyEvent.KEYCODE_STYLUS_BUTTON_TERTIARY:
case KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL: {
- // TODO(go/android-stylus-buttons): Handle stylus button presses.
+ if (!mStylusButtonsDisabled) {
+ sendSystemKeyToStatusBarAsync(keyCode);
+ }
result &= ~ACTION_PASS_TO_USER;
break;
}
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/Session.java b/services/core/java/com/android/server/wm/Session.java
index b5c82a8..15a5ebf 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -910,6 +910,22 @@
}
@Override
+ public boolean transferEmbeddedTouchFocusToHost(IWindow embeddedWindow) {
+ if (embeddedWindow == null) {
+ return false;
+ }
+
+ final long identity = Binder.clearCallingIdentity();
+ boolean didTransfer = false;
+ try {
+ didTransfer = mService.transferEmbeddedTouchFocusToHost(embeddedWindow);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ return didTransfer;
+ }
+
+ @Override
public void generateDisplayHash(IWindow window, Rect boundsInWindow, String hashAlgorithm,
RemoteCallback callback) {
final long origId = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d058892c..9b09d94 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8682,6 +8682,38 @@
clientChannel.copyTo(outInputChannel);
}
+ boolean transferEmbeddedTouchFocusToHost(IWindow embeddedWindow) {
+ final IBinder windowBinder = embeddedWindow.asBinder();
+ final IBinder hostInputChannel, embeddedInputChannel;
+ synchronized (mGlobalLock) {
+ final EmbeddedWindowController.EmbeddedWindow ew =
+ mEmbeddedWindowController.getByWindowToken(windowBinder);
+ if (ew == null) {
+ Slog.w(TAG, "Attempt to transfer touch focus from non-existent embedded window");
+ return false;
+ }
+ final WindowState hostWindowState = ew.getWindowState();
+ if (hostWindowState == null) {
+ Slog.w(TAG, "Attempt to transfer touch focus from embedded window with no" +
+ " associated host");
+ return false;
+ }
+ embeddedInputChannel = ew.getInputChannelToken();
+ if (embeddedInputChannel == null) {
+ Slog.w(TAG, "Attempt to transfer touch focus from embedded window with no input" +
+ " channel");
+ return false;
+ }
+ hostInputChannel = hostWindowState.mInputChannelToken;
+ if (hostInputChannel == null) {
+ Slog.w(TAG, "Attempt to transfer touch focus to a host window with no" +
+ " input channel");
+ return false;
+ }
+ return mInputManager.transferTouchFocus(embeddedInputChannel, hostInputChannel);
+ }
+ }
+
private void updateInputChannel(IBinder channelToken, int callingUid, int callingPid,
int displayId, SurfaceControl surface, String name,
InputApplicationHandle applicationHandle, int flags,
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/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 52a550b..d056348 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -21,10 +21,12 @@
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
+import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -51,7 +53,9 @@
import android.content.pm.ServiceInfo;
import android.graphics.drawable.Icon;
import android.hardware.display.DisplayManagerGlobal;
+import android.os.Bundle;
import android.os.IBinder;
+import android.os.UserHandle;
import android.provider.Settings;
import android.testing.TestableContext;
import android.view.Display;
@@ -61,6 +65,7 @@
import android.view.accessibility.AccessibilityWindowAttributes;
import androidx.test.InstrumentationRegistry;
+import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.SmallTest;
import com.android.compatibility.common.util.TestUtils;
@@ -76,12 +81,12 @@
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatchers;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
@@ -92,13 +97,16 @@
* APCT tests for {@link AccessibilityManagerService}.
*/
public class AccessibilityManagerServiceTest {
- private static final String TAG = "A11Y_MANAGER_SERVICE_TEST";
+ @Rule
+ public final A11yTestableContext mTestableContext = new A11yTestableContext(
+ ApplicationProvider.getApplicationContext());
+
private static final int ACTION_ID = 20;
private static final String LABEL = "label";
private static final String INTENT_ACTION = "TESTACTION";
private static final String DESCRIPTION = "description";
private static final PendingIntent TEST_PENDING_INTENT = PendingIntent.getBroadcast(
- InstrumentationRegistry.getTargetContext(), 0, new Intent(INTENT_ACTION),
+ ApplicationProvider.getApplicationContext(), 0, new Intent(INTENT_ACTION),
PendingIntent.FLAG_MUTABLE_UNAUDITED);
private static final RemoteAction TEST_ACTION = new RemoteAction(
Icon.createWithContentUri("content://test"),
@@ -130,11 +138,7 @@
@Mock private MagnificationController mMockMagnificationController;
@Mock private FullScreenMagnificationController mMockFullScreenMagnificationController;
@Mock private ProxyManager mProxyManager;
-
- @Rule
- public final TestableContext mTestableContext = new TestableContext(
- getInstrumentation().getTargetContext(), null);
-
+ @Captor private ArgumentCaptor<Intent> mIntentArgumentCaptor;
private MessageCapturingHandler mHandler = new MessageCapturingHandler(null);
private AccessibilityServiceConnection mAccessibilityServiceConnection;
private AccessibilityInputFilter mInputFilter;
@@ -228,11 +232,8 @@
doThrow(SecurityException.class).when(mMockSecurityPolicy)
.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
- try {
- mA11yms.registerSystemAction(TEST_ACTION, ACTION_ID);
- Assert.fail();
- } catch (SecurityException expected) {
- }
+ assertThrows(SecurityException.class,
+ () -> mA11yms.registerSystemAction(TEST_ACTION, ACTION_ID));
verify(mMockSystemActionPerformer, never()).registerSystemAction(ACTION_ID, TEST_ACTION);
}
@@ -248,11 +249,8 @@
doThrow(SecurityException.class).when(mMockSecurityPolicy)
.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
- try {
- mA11yms.unregisterSystemAction(ACTION_ID);
- Assert.fail();
- } catch (SecurityException expected) {
- }
+ assertThrows(SecurityException.class,
+ () -> mA11yms.unregisterSystemAction(ACTION_ID));
verify(mMockSystemActionPerformer, never()).unregisterSystemAction(ACTION_ID);
}
@@ -290,11 +288,9 @@
public void testRegisterProxyWithoutPermission() throws Exception {
doThrow(SecurityException.class).when(mMockSecurityPolicy)
.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
- try {
- mA11yms.registerProxyForDisplay(mMockServiceClient, TEST_DISPLAY);
- Assert.fail();
- } catch (SecurityException expected) {
- }
+
+ assertThrows(SecurityException.class,
+ () -> mA11yms.registerProxyForDisplay(mMockServiceClient, TEST_DISPLAY));
verify(mProxyManager, never()).registerProxy(any(), anyInt(), any(), anyInt(), any(), any(),
any(), any(), any(), any());
}
@@ -302,11 +298,8 @@
@SmallTest
@Test
public void testRegisterProxyForDefaultDisplay() throws Exception {
- try {
- mA11yms.registerProxyForDisplay(mMockServiceClient, Display.DEFAULT_DISPLAY);
- Assert.fail();
- } catch (IllegalArgumentException expected) {
- }
+ assertThrows(IllegalArgumentException.class,
+ () -> mA11yms.registerProxyForDisplay(mMockServiceClient, Display.DEFAULT_DISPLAY));
verify(mProxyManager, never()).registerProxy(any(), anyInt(), any(), anyInt(), any(), any(),
any(), any(), any(), any());
}
@@ -314,11 +307,8 @@
@SmallTest
@Test
public void testRegisterProxyForInvalidDisplay() throws Exception {
- try {
- mA11yms.registerProxyForDisplay(mMockServiceClient, Display.INVALID_DISPLAY);
- Assert.fail();
- } catch (IllegalArgumentException expected) {
- }
+ assertThrows(IllegalArgumentException.class,
+ () -> mA11yms.registerProxyForDisplay(mMockServiceClient, Display.INVALID_DISPLAY));
verify(mProxyManager, never()).registerProxy(any(), anyInt(), any(), anyInt(), any(), any(),
any(), any(), any(), any());
}
@@ -337,11 +327,9 @@
public void testUnRegisterProxyWithoutPermission() throws Exception {
doThrow(SecurityException.class).when(mMockSecurityPolicy)
.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
- try {
- mA11yms.unregisterProxyForDisplay(TEST_DISPLAY);
- Assert.fail();
- } catch (SecurityException expected) {
- }
+
+ assertThrows(SecurityException.class,
+ () -> mA11yms.unregisterProxyForDisplay(TEST_DISPLAY));
verify(mProxyManager, never()).unregisterProxy(TEST_DISPLAY);
}
@@ -357,8 +345,9 @@
mA11yms.onMagnificationTransitionEndedLocked(Display.DEFAULT_DISPLAY, false);
- Assert.assertEquals(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW,
- userState.getMagnificationModeLocked(Display.DEFAULT_DISPLAY));
+ assertThat(userState.getMagnificationModeLocked(Display.DEFAULT_DISPLAY)).isEqualTo(
+ ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
+
}
@SmallTest
@@ -376,7 +365,7 @@
ArgumentCaptor<Display> displayCaptor = ArgumentCaptor.forClass(Display.class);
verify(mInputFilter, timeout(100)).refreshMagnificationMode(displayCaptor.capture());
- Assert.assertEquals(Display.DEFAULT_DISPLAY, displayCaptor.getValue().getDisplayId());
+ assertThat(displayCaptor.getValue().getDisplayId()).isEqualTo(Display.DEFAULT_DISPLAY);
}
@SmallTest
@@ -506,10 +495,59 @@
attributes);
}
+ @SmallTest
+ @Test
+ public void testPerformAccessibilityShortcut_hearingAids_startActivityWithExpectedComponent() {
+ final AccessibilityUserState userState = mA11yms.mUserStates.get(
+ mA11yms.getCurrentUserIdLocked());
+ mockManageAccessibilityGranted(mTestableContext);
+ userState.mAccessibilityShortcutKeyTargets.add(
+ ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString());
+
+ mA11yms.performAccessibilityShortcut(
+ ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString());
+ mHandler.sendAllMessages();
+
+ assertStartActivityWithExpectedComponentName(mTestableContext.getMockContext(),
+ ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString());
+ }
+
+ private void mockManageAccessibilityGranted(TestableContext context) {
+ context.getTestablePermissions().setPermission(Manifest.permission.MANAGE_ACCESSIBILITY,
+ PackageManager.PERMISSION_GRANTED);
+ }
+
+ private void assertStartActivityWithExpectedComponentName(Context mockContext,
+ String componentName) {
+ verify(mockContext).startActivityAsUser(mIntentArgumentCaptor.capture(),
+ any(Bundle.class), any(UserHandle.class));
+ assertThat(mIntentArgumentCaptor.getValue().getStringExtra(
+ Intent.EXTRA_COMPONENT_NAME)).isEqualTo(componentName);
+ }
+
public static class FakeInputFilter extends AccessibilityInputFilter {
FakeInputFilter(Context context,
AccessibilityManagerService service) {
super(context, service);
}
}
+
+ private static class A11yTestableContext extends TestableContext {
+
+ private final Context mMockContext;
+
+ A11yTestableContext(Context base) {
+ super(base);
+ mMockContext = Mockito.mock(Context.class);
+ }
+
+ @Override
+ public void startActivityAsUser(Intent intent, Bundle options, UserHandle user) {
+ mMockContext.startActivityAsUser(intent, options, user);
+ }
+
+ Context getMockContext() {
+ return mMockContext;
+ }
+ }
}
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);