diff options
137 files changed, 2578 insertions, 1288 deletions
diff --git a/StubLibraries.bp b/StubLibraries.bp index d1ad189c3cdf..49433f16f572 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -116,7 +116,7 @@ droidstubs { last_released: { api_file: ":android-non-updatable.api.public.latest", removed_api_file: ":android-non-updatable-removed.api.public.latest", - baseline_file: ":public-api-incompatibilities-with-last-released", + baseline_file: ":android-incompatibilities.api.public.latest", }, api_lint: { enabled: true, @@ -168,7 +168,7 @@ droidstubs { last_released: { api_file: ":android-non-updatable.api.system.latest", removed_api_file: ":android-non-updatable-removed.api.system.latest", - baseline_file: ":system-api-incompatibilities-with-last-released" + baseline_file: ":android-incompatibilities.api.system.latest" }, api_lint: { enabled: true, diff --git a/apex/Android.bp b/apex/Android.bp index 77ff6dbd8b8b..1876110c9355 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -101,6 +101,10 @@ java_defaults { sdk_version: "module_current", }, + // installable implies we'll create a non-apex (platform) variant, which + // we shouldn't ordinarily need (and it can create issues), so disable that. + installable: false, + // Configure framework module specific metalava options. droiddoc_options: [mainline_stubs_args], diff --git a/boot/Android.bp b/boot/Android.bp new file mode 100644 index 000000000000..dd4066a7d151 --- /dev/null +++ b/boot/Android.bp @@ -0,0 +1,18 @@ +// Copyright (C) 2021 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +boot_image { + name: "framework-boot-image", + image_name: "boot", +} diff --git a/core/api/current.txt b/core/api/current.txt index f49ce1fd48a5..61d7aadce928 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -24225,7 +24225,8 @@ package android.media.session { method public void setCallback(@Nullable android.media.session.MediaSession.Callback, @Nullable android.os.Handler); method public void setExtras(@Nullable android.os.Bundle); method public void setFlags(int); - method public void setMediaButtonReceiver(@Nullable android.app.PendingIntent); + method public void setMediaButtonBroadcastReceiver(@Nullable android.content.ComponentName); + method @Deprecated public void setMediaButtonReceiver(@Nullable android.app.PendingIntent); method public void setMetadata(@Nullable android.media.MediaMetadata); method public void setPlaybackState(@Nullable android.media.session.PlaybackState); method public void setPlaybackToLocal(android.media.AudioAttributes); @@ -30191,6 +30192,8 @@ package android.os { field @Deprecated public static final String RADIO; field @Deprecated public static final String SERIAL; field @NonNull public static final String SKU; + field @NonNull public static final String SOC_MANUFACTURER; + field @NonNull public static final String SOC_MODEL; field public static final String[] SUPPORTED_32_BIT_ABIS; field public static final String[] SUPPORTED_64_BIT_ABIS; field public static final String[] SUPPORTED_ABIS; @@ -31878,6 +31881,8 @@ package android.os.strictmode { } public final class UnsafeIntentLaunchViolation extends android.os.strictmode.Violation { + ctor public UnsafeIntentLaunchViolation(@NonNull android.content.Intent); + method @Nullable public android.content.Intent getIntent(); } public final class UntaggedSocketViolation extends android.os.strictmode.Violation { @@ -36957,6 +36962,7 @@ package android.security.keystore { field public static final int ORIGIN_IMPORTED = 2; // 0x2 field public static final int ORIGIN_SECURELY_IMPORTED = 8; // 0x8 field public static final int ORIGIN_UNKNOWN = 4; // 0x4 + field public static final int PURPOSE_AGREE_KEY = 64; // 0x40 field public static final int PURPOSE_DECRYPT = 2; // 0x2 field public static final int PURPOSE_ENCRYPT = 1; // 0x1 field public static final int PURPOSE_SIGN = 4; // 0x4 @@ -38570,6 +38576,7 @@ package android.speech { public class SpeechRecognizer { method public void cancel(); + method @NonNull public static android.speech.SpeechRecognizer createOnDeviceSpeechRecognizer(@NonNull android.content.Context); method public static android.speech.SpeechRecognizer createSpeechRecognizer(android.content.Context); method public static android.speech.SpeechRecognizer createSpeechRecognizer(android.content.Context, android.content.ComponentName); method public void destroy(); diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 2a2b799816c3..7c426be80fa4 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -2370,6 +2370,7 @@ package android.content.pm { field public static final int FLAG_PERMISSION_REVIEW_REQUIRED = 64; // 0x40 field public static final int FLAG_PERMISSION_REVOKED_COMPAT = 8; // 0x8 field @Deprecated public static final int FLAG_PERMISSION_REVOKE_ON_UPGRADE = 8; // 0x8 + field public static final int FLAG_PERMISSION_SELECTED_LOCATION_ACCURACY = 524288; // 0x80000 field public static final int FLAG_PERMISSION_SYSTEM_FIXED = 16; // 0x10 field public static final int FLAG_PERMISSION_USER_FIXED = 2; // 0x2 field public static final int FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED = 512; // 0x200 @@ -5606,12 +5607,12 @@ package android.media.tv.tuner.filter { public class Filter implements java.lang.AutoCloseable { method public void close(); method public int configure(@NonNull android.media.tv.tuner.filter.FilterConfiguration); - method public int configureMonitorEvent(int); method public int flush(); method public int getId(); method public long getId64Bit(); method public int read(@NonNull byte[], long, long); method public int setDataSource(@Nullable android.media.tv.tuner.filter.Filter); + method public int setMonitorEventMask(int); method public int start(); method public int stop(); field public static final int MONITOR_EVENT_IP_CID_CHANGE = 2; // 0x2 diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index f366df55be4c..bdd541a2f0ac 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -7881,6 +7881,10 @@ public class Activity extends ContextThemeWrapper @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) final void performCreate(Bundle icicle, PersistableBundle persistentState) { + if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) { + Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "performCreate:" + + mComponent.getClassName()); + } dispatchActivityPreCreated(icicle); mCanEnterPictureInPicture = true; // initialize mIsInMultiWindowMode and mIsInPictureInPictureMode before onCreate @@ -7903,6 +7907,7 @@ public class Activity extends ContextThemeWrapper mFragments.dispatchActivityCreated(); mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions()); dispatchActivityPostCreated(icicle); + Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); } final void performNewIntent(@NonNull Intent intent) { @@ -8008,6 +8013,10 @@ public class Activity extends ContextThemeWrapper } final void performResume(boolean followedByPause, String reason) { + if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) { + Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "performResume:" + + mComponent.getClassName()); + } dispatchActivityPreResumed(); performRestart(true /* start */, reason); @@ -8059,9 +8068,14 @@ public class Activity extends ContextThemeWrapper " did not call through to super.onPostResume()"); } dispatchActivityPostResumed(); + Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); } final void performPause() { + if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) { + Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "performPause:" + + mComponent.getClassName()); + } dispatchActivityPrePaused(); mDoReportFullyDrawn = false; mFragments.dispatchPause(); @@ -8077,6 +8091,7 @@ public class Activity extends ContextThemeWrapper " did not call through to super.onPause()"); } dispatchActivityPostPaused(); + Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); } final void performUserLeaving() { @@ -8085,6 +8100,10 @@ public class Activity extends ContextThemeWrapper } final void performStop(boolean preserveWindow, String reason) { + if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) { + Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "performStop:" + + mComponent.getClassName()); + } mDoReportFullyDrawn = false; mFragments.doLoaderStop(mChangingConfigurations /*retain*/); @@ -8130,9 +8149,14 @@ public class Activity extends ContextThemeWrapper dispatchActivityPostStopped(); } mResumed = false; + Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); } final void performDestroy() { + if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) { + Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "performDestroy:" + + mComponent.getClassName()); + } dispatchActivityPreDestroyed(); mDestroyed = true; mWindow.destroy(); @@ -8145,6 +8169,7 @@ public class Activity extends ContextThemeWrapper mVoiceInteractor.detachActivity(); } dispatchActivityPostDestroyed(); + Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); } final void dispatchMultiWindowModeChanged(boolean isInMultiWindowMode, diff --git a/core/java/android/app/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl index 9127ebb4eb83..c2c62c11b9a6 100644 --- a/core/java/android/app/IActivityClientController.aidl +++ b/core/java/android/app/IActivityClientController.aidl @@ -36,7 +36,13 @@ interface IActivityClientController { oneway void activityIdle(in IBinder token, in Configuration config, in boolean stopProfiling); oneway void activityResumed(in IBinder token); oneway void activityTopResumedStateLost(); - oneway void activityPaused(in IBinder token); + /** + * Notifies that the activity has completed paused. This call is not one-way because it can make + * consecutive launch in the same process more coherent. About the order of binder call, it + * should be fine with other one-way calls because if pause hasn't completed on the server side, + * there won't be other lifecycle changes. + */ + void activityPaused(in IBinder token); oneway void activityStopped(in IBinder token, in Bundle state, in PersistableBundle persistentState, in CharSequence description); oneway void activityDestroyed(in IBinder token); diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index c5206d77ea50..0c50446e0a4e 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -64,7 +64,6 @@ import android.os.HandlerExecutor; import android.os.IBinder; import android.os.Looper; import android.os.StatFs; -import android.os.StrictMode; import android.os.UserHandle; import android.os.UserManager; import android.os.storage.StorageManager; @@ -3575,6 +3574,7 @@ public abstract class Context { LIGHTS_SERVICE, //@hide: PEOPLE_SERVICE, //@hide: DEVICE_STATE_SERVICE, + //@hide: SPEECH_RECOGNITION_SERVICE, UWB_SERVICE, MEDIA_METRICS_SERVICE, }) @@ -5410,6 +5410,14 @@ public abstract class Context { public static final String MEDIA_METRICS_SERVICE = "media_metrics"; /** + * Use with {@link #getSystemService(String)} to access system speech recognition service. + * + * @see #getSystemService(String) + * @hide + */ + public static final String SPEECH_RECOGNITION_SERVICE = "speech_recognition"; + + /** * Determine whether the given permission is allowed for a particular * process and user ID running in the system. * @@ -6469,7 +6477,7 @@ public abstract class Context { * {@link WindowManager}, {@link android.view.LayoutInflater LayoutInflater} or * {@link android.app.WallpaperManager WallpaperManager}. Accessing UI components from non-UI * contexts throws {@link android.os.strictmode.Violation} if - * {@link StrictMode.VmPolicy.Builder#detectIncorrectContextUse()} is enabled. + * {@link android.os.StrictMode.VmPolicy.Builder#detectIncorrectContextUse()} is enabled. * <p> * Examples of UI contexts are * an {@link android.app.Activity Activity}, a context created from @@ -6479,7 +6487,7 @@ public abstract class Context { * * @see #getDisplay() * @see #getSystemService(String) - * @see StrictMode.VmPolicy.Builder#detectIncorrectContextUse() + * @see android.os.StrictMode.VmPolicy.Builder#detectIncorrectContextUse() */ public static boolean isUiContext(@NonNull Context context) { return context.isUiContext(); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index fcd573ea3b65..68792b2f47de 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -3971,6 +3971,15 @@ public abstract class PackageManager { public static final int FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT = 1 << 18; /** + * Permission flag: This location permission is selected as the level of granularity of + * location accuracy. + * + * @hide + */ + @SystemApi + public static final int FLAG_PERMISSION_SELECTED_LOCATION_ACCURACY = 1 << 19; + + /** * Permission flags: Reserved for use by the permission controller. The platform and any * packages besides the permission controller should not assume any definition about these * flags. diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index 5a03adee4eab..95f1d124b9a1 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -465,4 +465,46 @@ public abstract class DisplayManagerInternal { public interface DisplayTransactionListener { void onDisplayTransaction(Transaction t); } + + /** + * Called when there are changes to {@link com.android.server.display.DisplayGroup + * DisplayGroups}. + */ + public interface DisplayGroupListener { + /** + * A new display group with the provided {@code groupId} was added. + * + * <ol> + * <li>The {@code groupId} is applied to all appropriate {@link Display displays}. + * <li>This method is called. + * <li>{@link android.hardware.display.DisplayManager.DisplayListener DisplayListeners} + * are informed of any corresponding changes. + * </ol> + */ + void onDisplayGroupAdded(int groupId); + + /** + * The display group with the provided {@code groupId} was removed. + * + * <ol> + * <li>All affected {@link Display displays} have their group IDs updated appropriately. + * <li>{@link android.hardware.display.DisplayManager.DisplayListener DisplayListeners} + * are informed of any corresponding changes. + * <li>This method is called. + * </ol> + */ + void onDisplayGroupRemoved(int groupId); + + /** + * The display group with the provided {@code groupId} has changed. + * + * <ol> + * <li>All affected {@link Display displays} have their group IDs updated appropriately. + * <li>{@link android.hardware.display.DisplayManager.DisplayListener DisplayListeners} + * are informed of any corresponding changes. + * <li>This method is called. + * </ol> + */ + void onDisplayGroupChanged(int groupId); + } } diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index 0a895b98f9fd..3843b9ab93c2 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -204,6 +204,7 @@ public final class NetworkCapabilities implements Parcelable { NET_CAPABILITY_TEMPORARILY_NOT_METERED, NET_CAPABILITY_OEM_PRIVATE, NET_CAPABILITY_VEHICLE_INTERNAL, + NET_CAPABILITY_NOT_VCN_MANAGED, }) public @interface NetCapability { } @@ -399,8 +400,16 @@ public final class NetworkCapabilities implements Parcelable { @SystemApi public static final int NET_CAPABILITY_VEHICLE_INTERNAL = 27; + /** + * Indicates that this network is not managed by a Virtual Carrier Network (VCN). + * + * TODO(b/177299683): Add additional clarifying javadoc. + * @hide + */ + public static final int NET_CAPABILITY_NOT_VCN_MANAGED = 28; + private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS; - private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_VEHICLE_INTERNAL; + private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_NOT_VCN_MANAGED; /** * Network capabilities that are expected to be mutable, i.e., can change while a particular @@ -417,7 +426,8 @@ public final class NetworkCapabilities implements Parcelable { | (1 << NET_CAPABILITY_NOT_CONGESTED) | (1 << NET_CAPABILITY_NOT_SUSPENDED) | (1 << NET_CAPABILITY_PARTIAL_CONNECTIVITY) - | (1 << NET_CAPABILITY_TEMPORARILY_NOT_METERED); + | (1 << NET_CAPABILITY_TEMPORARILY_NOT_METERED) + | (1 << NET_CAPABILITY_NOT_VCN_MANAGED); /** * Network capabilities that are not allowed in NetworkRequests. This exists because the @@ -426,16 +436,21 @@ public final class NetworkCapabilities implements Parcelable { * can get into a cycle where the NetworkFactory endlessly churns out NetworkAgents that then * get immediately torn down because they do not have the requested capability. */ + // Note that as a historical exception, the TRUSTED and NOT_VCN_MANAGED capabilities + // are mutable but requestable. Factories are responsible for not getting + // in an infinite loop about these. private static final long NON_REQUESTABLE_CAPABILITIES = - MUTABLE_CAPABILITIES & ~(1 << NET_CAPABILITY_TRUSTED); + MUTABLE_CAPABILITIES + & ~(1 << NET_CAPABILITY_TRUSTED) + & ~(1 << NET_CAPABILITY_NOT_VCN_MANAGED); /** * Capabilities that are set by default when the object is constructed. */ private static final long DEFAULT_CAPABILITIES = - (1 << NET_CAPABILITY_NOT_RESTRICTED) | - (1 << NET_CAPABILITY_TRUSTED) | - (1 << NET_CAPABILITY_NOT_VPN); + (1 << NET_CAPABILITY_NOT_RESTRICTED) + | (1 << NET_CAPABILITY_TRUSTED) + | (1 << NET_CAPABILITY_NOT_VPN); /** * Capabilities that suggest that a network is restricted. @@ -495,7 +510,8 @@ public final class NetworkCapabilities implements Parcelable { | (1 << NET_CAPABILITY_NOT_VPN) | (1 << NET_CAPABILITY_NOT_ROAMING) | (1 << NET_CAPABILITY_NOT_CONGESTED) - | (1 << NET_CAPABILITY_NOT_SUSPENDED); + | (1 << NET_CAPABILITY_NOT_SUSPENDED) + | (1 << NET_CAPABILITY_NOT_VCN_MANAGED); /** * Adds the given capability to this {@code NetworkCapability} instance. @@ -1982,6 +1998,7 @@ public final class NetworkCapabilities implements Parcelable { case NET_CAPABILITY_TEMPORARILY_NOT_METERED: return "TEMPORARILY_NOT_METERED"; case NET_CAPABILITY_OEM_PRIVATE: return "OEM_PRIVATE"; case NET_CAPABILITY_VEHICLE_INTERNAL: return "NET_CAPABILITY_VEHICLE_INTERNAL"; + case NET_CAPABILITY_NOT_VCN_MANAGED: return "NOT_VCN_MANAGED"; default: return Integer.toString(capability); } } diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index e50494615f0c..74df1b2b9194 100755 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -27,6 +27,7 @@ import android.app.ActivityThread; import android.app.Application; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; +import android.sysprop.SocProperties; import android.sysprop.TelephonyProperties; import android.text.TextUtils; import android.util.Slog; @@ -88,6 +89,14 @@ public class Build { /** The end-user-visible name for the end product. */ public static final String MODEL = getString("ro.product.model"); + /** The manufacturer of the device's primary system-on-chip. */ + @NonNull + public static final String SOC_MANUFACTURER = SocProperties.soc_manufacturer().orElse(UNKNOWN); + + /** The model name of the device's primary system-on-chip. */ + @NonNull + public static final String SOC_MODEL = SocProperties.soc_model().orElse(UNKNOWN); + /** The system bootloader version number. */ public static final String BOOTLOADER = getString("ro.bootloader"); diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java index 1c1f5c034cd9..102f52566590 100644 --- a/core/java/android/os/Bundle.java +++ b/core/java/android/os/Bundle.java @@ -45,6 +45,7 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable { @VisibleForTesting static final int FLAG_ALLOW_FDS = 1 << 10; + /** An unmodifiable {@code Bundle} that is always {@link #isEmpty() empty}. */ public static final Bundle EMPTY; /** diff --git a/core/java/android/os/PersistableBundle.java b/core/java/android/os/PersistableBundle.java index 5e3a34d8848a..e5e9b5f6f53c 100644 --- a/core/java/android/os/PersistableBundle.java +++ b/core/java/android/os/PersistableBundle.java @@ -47,6 +47,8 @@ import java.util.ArrayList; public final class PersistableBundle extends BaseBundle implements Cloneable, Parcelable, XmlUtils.WriteMapCallback { private static final String TAG_PERSISTABLEMAP = "pbundle_as_map"; + + /** An unmodifiable {@code PersistableBundle} that is always {@link #isEmpty() empty}. */ public static final PersistableBundle EMPTY; static { diff --git a/core/java/android/os/strictmode/UnsafeIntentLaunchViolation.java b/core/java/android/os/strictmode/UnsafeIntentLaunchViolation.java index 891fb59326df..f0f3cef656f0 100644 --- a/core/java/android/os/strictmode/UnsafeIntentLaunchViolation.java +++ b/core/java/android/os/strictmode/UnsafeIntentLaunchViolation.java @@ -17,10 +17,13 @@ package android.os.strictmode; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.PendingIntent; import android.content.Intent; import android.net.Uri; +import java.util.Objects; + /** * Violation raised when your app launches an {@link Intent} which originated * from outside your app. @@ -46,8 +49,20 @@ import android.net.Uri; * not protected, your app is likely vulnerable to malicious apps. */ public final class UnsafeIntentLaunchViolation extends Violation { - /** @hide */ + private transient Intent mIntent; + public UnsafeIntentLaunchViolation(@NonNull Intent intent) { super("Launch of unsafe intent: " + intent); + mIntent = Objects.requireNonNull(intent); + } + + /** + * Return the {@link Intent} which caused this violation to be raised. Note + * that this value is not available if this violation has been serialized + * since intents cannot be serialized. + */ + @SuppressWarnings("IntentBuilderName") + public @Nullable Intent getIntent() { + return mIntent; } } diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java index 017f40521a81..f994d2930cd9 100644 --- a/core/java/android/security/keymaster/KeymasterDefs.java +++ b/core/java/android/security/keymaster/KeymasterDefs.java @@ -177,6 +177,7 @@ public final class KeymasterDefs { public static final int KM_PURPOSE_SIGN = KeyPurpose.SIGN; public static final int KM_PURPOSE_VERIFY = KeyPurpose.VERIFY; public static final int KM_PURPOSE_WRAP = KeyPurpose.WRAP_KEY; + public static final int KM_PURPOSE_AGREE_KEY = KeyPurpose.AGREE_KEY; // Key formats. public static final int KM_KEY_FORMAT_X509 = KeyFormat.X509; diff --git a/core/java/android/speech/IRecognitionServiceManager.aidl b/core/java/android/speech/IRecognitionServiceManager.aidl new file mode 100644 index 000000000000..7158ba2f9f63 --- /dev/null +++ b/core/java/android/speech/IRecognitionServiceManager.aidl @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.speech; + +import android.speech.IRecognitionServiceManagerCallback; + +/** + * Binder service allowing speech recognition proxied by the system. + * + * {@hide} + */ +interface IRecognitionServiceManager { + void createSession(in IRecognitionServiceManagerCallback callback); +} diff --git a/core/java/android/speech/IRecognitionServiceManagerCallback.aidl b/core/java/android/speech/IRecognitionServiceManagerCallback.aidl new file mode 100644 index 000000000000..d760810deda8 --- /dev/null +++ b/core/java/android/speech/IRecognitionServiceManagerCallback.aidl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.speech; + +import android.speech.IRecognitionService; + +/** + * Callback for the service allowing speech recognition proxied by the system. + * + * {@hide} + */ +oneway interface IRecognitionServiceManagerCallback { + void onSuccess(in IRecognitionService service); + void onError(); +} diff --git a/core/java/android/speech/RecognitionService.java b/core/java/android/speech/RecognitionService.java index 5fd192afed00..c97dbfe38ead 100644 --- a/core/java/android/speech/RecognitionService.java +++ b/core/java/android/speech/RecognitionService.java @@ -28,6 +28,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; +import android.os.Process; import android.os.RemoteException; import android.util.Log; @@ -50,7 +51,9 @@ public abstract class RecognitionService extends Service { /** * Name under which a RecognitionService component publishes information about itself. * This meta-data should reference an XML resource containing a - * <code><{@link android.R.styleable#RecognitionService recognition-service}></code> tag. + * <code><{@link android.R.styleable#RecognitionService recognition-service}></code> or + * <code><{@link android.R.styleable#RecognitionService on-device-recognition-service} + * ></code> tag. */ public static final String SERVICE_META_DATA = "android.speech"; @@ -182,6 +185,13 @@ public abstract class RecognitionService extends Service { private boolean checkPermissions(IRecognitionListener listener, boolean forDataDelivery, @NonNull String packageName, @Nullable String featureId) { if (DBG) Log.d(TAG, "checkPermissions"); + + final int callingUid = Binder.getCallingUid(); + if (callingUid == Process.SYSTEM_UID) { + // Assuming system has verified permissions of the caller. + return true; + } + if (forDataDelivery) { if (PermissionChecker.checkCallingOrSelfPermissionForDataDelivery(this, android.Manifest.permission.RECORD_AUDIO, packageName, featureId, @@ -342,6 +352,7 @@ public abstract class RecognitionService extends Service { * Return the Linux uid assigned to the process that sent you the current transaction that * is being processed. This is obtained from {@link Binder#getCallingUid()}. */ + // TODO(b/176578753): need to make sure this is fixed when proxied through system. public int getCallingUid() { return mCallingUid; } diff --git a/core/java/android/speech/SpeechRecognizer.java b/core/java/android/speech/SpeechRecognizer.java index aea94bfb1bbb..de879c63a1a6 100644 --- a/core/java/android/speech/SpeechRecognizer.java +++ b/core/java/android/speech/SpeechRecognizer.java @@ -16,6 +16,7 @@ package android.speech; +import android.annotation.NonNull; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -27,6 +28,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.RemoteException; +import android.os.ServiceManager; import android.provider.Settings; import android.text.TextUtils; import android.util.Log; @@ -38,8 +40,9 @@ import java.util.Queue; /** * This class provides access to the speech recognition service. This service allows access to the * speech recognizer. Do not instantiate this class directly, instead, call - * {@link SpeechRecognizer#createSpeechRecognizer(Context)}. This class's methods must be - * invoked only from the main application thread. + * {@link SpeechRecognizer#createSpeechRecognizer(Context)}, or + * {@link SpeechRecognizer#createOnDeviceSpeechRecognizer(Context)}. This class's methods must be + * invoked only from the main application thread. * * <p>The implementation of this API is likely to stream audio to remote servers to perform speech * recognition. As such this API is not intended to be used for continuous recognition, which would @@ -122,8 +125,13 @@ public class SpeechRecognizer { /** Component to direct service intent to */ private final ComponentName mServiceComponent; + /** Whether to use on-device speech recognizer. */ + private final boolean mOnDevice; + + private IRecognitionServiceManager mManagerService; + /** Handler that will execute the main tasks */ - private Handler mHandler = new Handler() { + private Handler mHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { switch (msg.what) { @@ -159,6 +167,17 @@ public class SpeechRecognizer { private SpeechRecognizer(final Context context, final ComponentName serviceComponent) { mContext = context; mServiceComponent = serviceComponent; + mOnDevice = false; + } + + /** + * The right way to create a {@code SpeechRecognizer} is by using + * {@link #createOnDeviceSpeechRecognizer} static factory method + */ + private SpeechRecognizer(final Context context, boolean onDevice) { + mContext = context; + mServiceComponent = null; + mOnDevice = onDevice; } /** @@ -194,6 +213,7 @@ public class SpeechRecognizer { * @return {@code true} if recognition is available, {@code false} otherwise */ public static boolean isRecognitionAvailable(final Context context) { + // TODO(b/176578753): make sure this works well with system speech recognizers. final List<ResolveInfo> list = context.getPackageManager().queryIntentServices( new Intent(RecognitionService.SERVICE_INTERFACE), 0); return list != null && list.size() != 0; @@ -231,13 +251,32 @@ public class SpeechRecognizer { public static SpeechRecognizer createSpeechRecognizer(final Context context, final ComponentName serviceComponent) { if (context == null) { - throw new IllegalArgumentException("Context cannot be null)"); + throw new IllegalArgumentException("Context cannot be null"); } checkIsCalledFromMainThread(); return new SpeechRecognizer(context, serviceComponent); } /** + * Factory method to create a new {@code SpeechRecognizer}. + * + * <p>Please note that {@link #setRecognitionListener(RecognitionListener)} should be called + * before dispatching any command to the created {@code SpeechRecognizer}, otherwise no + * notifications will be received. + * + * @param context in which to create {@code SpeechRecognizer} + * @return a new on-device {@code SpeechRecognizer}. + */ + @NonNull + public static SpeechRecognizer createOnDeviceSpeechRecognizer(@NonNull final Context context) { + if (context == null) { + throw new IllegalArgumentException("Context cannot be null"); + } + checkIsCalledFromMainThread(); + return new SpeechRecognizer(context, /* onDevice */ true); + } + + /** * Sets the listener that will receive all the callbacks. The previous unfinished commands will * be executed with the old listener, while any following command will be executed with the new * listener. @@ -265,34 +304,72 @@ public class SpeechRecognizer { } checkIsCalledFromMainThread(); if (mConnection == null) { // first time connection - mConnection = new Connection(); - - Intent serviceIntent = new Intent(RecognitionService.SERVICE_INTERFACE); - - if (mServiceComponent == null) { - String serviceComponent = Settings.Secure.getString(mContext.getContentResolver(), - Settings.Secure.VOICE_RECOGNITION_SERVICE); - - if (TextUtils.isEmpty(serviceComponent)) { - Log.e(TAG, "no selected voice recognition service"); - mListener.onError(ERROR_CLIENT); - return; - } - - serviceIntent.setComponent(ComponentName.unflattenFromString(serviceComponent)); + // TODO(b/176578753): both flows should go through system service. + if (mOnDevice) { + connectToSystemService(); } else { - serviceIntent.setComponent(mServiceComponent); + connectToService(); } - if (!mContext.bindService(serviceIntent, mConnection, - Context.BIND_AUTO_CREATE | Context.BIND_INCLUDE_CAPABILITIES)) { - Log.e(TAG, "bind to recognition service failed"); - mConnection = null; - mService = null; + } + putMessage(Message.obtain(mHandler, MSG_START, recognizerIntent)); + } + + private void connectToSystemService() { + mManagerService = IRecognitionServiceManager.Stub.asInterface( + ServiceManager.getService(Context.SPEECH_RECOGNITION_SERVICE)); + + if (mManagerService == null) { + mListener.onError(ERROR_CLIENT); + return; + } + + try { + // TODO(b/176578753): this has to supply information on whether to use on-device impl. + mManagerService.createSession(new IRecognitionServiceManagerCallback.Stub(){ + @Override + public void onSuccess(IRecognitionService service) throws RemoteException { + mService = service; + } + + @Override + public void onError() throws RemoteException { + Log.e(TAG, "Bind to system recognition service failed"); + mListener.onError(ERROR_CLIENT); + } + }); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + private void connectToService() { + mConnection = new Connection(); + + Intent serviceIntent = new Intent(RecognitionService.SERVICE_INTERFACE); + + if (mServiceComponent == null) { + String serviceComponent = Settings.Secure.getString(mContext.getContentResolver(), + Settings.Secure.VOICE_RECOGNITION_SERVICE); + + if (TextUtils.isEmpty(serviceComponent)) { + Log.e(TAG, "no selected voice recognition service"); mListener.onError(ERROR_CLIENT); return; } + + serviceIntent.setComponent( + ComponentName.unflattenFromString(serviceComponent)); + } else { + serviceIntent.setComponent(mServiceComponent); + } + if (!mContext.bindService(serviceIntent, mConnection, + Context.BIND_AUTO_CREATE | Context.BIND_INCLUDE_CAPABILITIES)) { + Log.e(TAG, "bind to recognition service failed"); + mConnection = null; + mService = null; + mListener.onError(ERROR_CLIENT); + return; } - putMessage(Message.obtain(mHandler, MSG_START, recognizerIntent)); } /** @@ -378,7 +455,7 @@ public class SpeechRecognizer { mListener.onError(ERROR_CLIENT); } } - + private boolean checkOpenConnection() { if (mService != null) { return true; @@ -433,7 +510,7 @@ public class SpeechRecognizer { private final static int MSG_RMS_CHANGED = 8; private final static int MSG_ON_EVENT = 9; - private final Handler mInternalHandler = new Handler() { + private final Handler mInternalHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { if (mInternalListener == null) { diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 0e5fb2cad08f..c664ccba4ca7 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -121,6 +121,19 @@ public final class Display { public static final int INVALID_DISPLAY = -1; /** + * The default display group id, which is the display group id of the primary display assuming + * there is one. + * @hide + */ + public static final int DEFAULT_DISPLAY_GROUP = 0; + + /** + * Invalid display group id. + * @hide + */ + public static final int INVALID_DISPLAY_GROUP = -1; + + /** * Display flag: Indicates that the display supports compositing content * that is stored in protected graphics buffers. * <p> diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java index ab35af89f0b3..d200a328773b 100644 --- a/core/java/android/view/DisplayInfo.java +++ b/core/java/android/view/DisplayInfo.java @@ -66,6 +66,11 @@ public final class DisplayInfo implements Parcelable { public int displayId; /** + * Display Group identifier. + */ + public int displayGroupId; + + /** * Display address, or null if none. * Interpretation varies by display type. */ @@ -331,6 +336,7 @@ public final class DisplayInfo implements Parcelable { && flags == other.flags && type == other.type && displayId == other.displayId + && displayGroupId == other.displayGroupId && Objects.equals(address, other.address) && Objects.equals(deviceProductInfo, other.deviceProductInfo) && Objects.equals(uniqueId, other.uniqueId) @@ -376,6 +382,7 @@ public final class DisplayInfo implements Parcelable { flags = other.flags; type = other.type; displayId = other.displayId; + displayGroupId = other.displayGroupId; address = other.address; deviceProductInfo = other.deviceProductInfo; name = other.name; @@ -418,6 +425,7 @@ public final class DisplayInfo implements Parcelable { flags = source.readInt(); type = source.readInt(); displayId = source.readInt(); + displayGroupId = source.readInt(); address = source.readParcelable(null); deviceProductInfo = source.readParcelable(null); name = source.readString8(); @@ -468,6 +476,7 @@ public final class DisplayInfo implements Parcelable { dest.writeInt(this.flags); dest.writeInt(type); dest.writeInt(displayId); + dest.writeInt(displayGroupId); dest.writeParcelable(address, flags); dest.writeParcelable(deviceProductInfo, flags); dest.writeString8(name); @@ -662,6 +671,8 @@ public final class DisplayInfo implements Parcelable { sb.append(name); sb.append("\", displayId "); sb.append(displayId); + sb.append("\", displayGroupId "); + sb.append(displayGroupId); sb.append(flagsToString(flags)); sb.append(", real "); sb.append(logicalWidth); diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 9932b2a473dc..106e3927656f 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -339,8 +339,6 @@ public final class SurfaceControl implements Parcelable { */ public long mNativeObject; private long mNativeHandle; - private boolean mDebugRelease = false; - private Throwable mReleaseStack = null; // TODO: Move width/height to native and fix locking through out. private final Object mLock = new Object(); @@ -588,13 +586,6 @@ public final class SurfaceControl implements Parcelable { } mNativeObject = nativeObject; mNativeHandle = mNativeObject != 0 ? nativeGetHandle(nativeObject) : 0; - if (mNativeObject == 0) { - if (mDebugRelease) { - mReleaseStack = new Throwable("assigned zero nativeObject here"); - } - } else { - mReleaseStack = null; - } } /** @@ -605,7 +596,6 @@ public final class SurfaceControl implements Parcelable { mWidth = other.mWidth; mHeight = other.mHeight; mLocalOwnerView = other.mLocalOwnerView; - mDebugRelease = other.mDebugRelease; assignNativeObject(nativeCopyFromSurfaceControl(other.mNativeObject), callsite); } @@ -1435,7 +1425,6 @@ public final class SurfaceControl implements Parcelable { mName = in.readString8(); mWidth = in.readInt(); mHeight = in.readInt(); - mDebugRelease = in.readBoolean(); long object = 0; if (in.readInt() != 0) { @@ -1454,12 +1443,8 @@ public final class SurfaceControl implements Parcelable { dest.writeString8(mName); dest.writeInt(mWidth); dest.writeInt(mHeight); - dest.writeBoolean(mDebugRelease); if (mNativeObject == 0) { dest.writeInt(0); - if (mReleaseStack != null) { - Log.w(TAG, "Sending invalid " + this + " caused by:", mReleaseStack); - } } else { dest.writeInt(1); } @@ -1471,13 +1456,6 @@ public final class SurfaceControl implements Parcelable { } /** - * @hide - */ - public void setDebugRelease(boolean debug) { - mDebugRelease = debug; - } - - /** * Checks whether two {@link SurfaceControl} objects represent the same surface. * * @param other The other object to check @@ -1547,9 +1525,6 @@ public final class SurfaceControl implements Parcelable { nativeRelease(mNativeObject); mNativeObject = 0; mNativeHandle = 0; - if (mDebugRelease) { - mReleaseStack = new Throwable("released here"); - } mCloseGuard.close(); } } @@ -1565,11 +1540,8 @@ public final class SurfaceControl implements Parcelable { } private void checkNotReleased() { - if (mNativeObject == 0) { - Log.wtf(TAG, "Invalid " + this + " caused by:", mReleaseStack); - throw new NullPointerException( - "mNativeObject of " + this + " is null. Have you called release() already?"); - } + if (mNativeObject == 0) throw new NullPointerException( + "Invalid " + this + ", mNativeObject is null. Have you called release() already?"); } /** @@ -2417,7 +2389,6 @@ public final class SurfaceControl implements Parcelable { public static SurfaceControl mirrorSurface(SurfaceControl mirrorOf) { long nativeObj = nativeMirrorSurface(mirrorOf.mNativeObject); SurfaceControl sc = new SurfaceControl(); - sc.mDebugRelease = mirrorOf.mDebugRelease; sc.assignNativeObject(nativeObj, "mirrorSurface"); return sc; } diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java index 2168dd01089d..7a5561c4fac9 100644 --- a/core/java/android/view/WindowInsets.java +++ b/core/java/android/view/WindowInsets.java @@ -240,7 +240,7 @@ public final class WindowInsets { /** * Creates a indexOf(type) -> inset map for which the {@code insets} is just mapped to - * {@link InsetsType#statusBars()} and {@link InsetsType#navigationBars()}, depending on the + * {@link Type#statusBars()} and {@link Type#navigationBars()}, depending on the * location of the inset. */ private static Insets[] createCompatTypeMap(@Nullable Rect insets) { @@ -321,9 +321,9 @@ public final class WindowInsets { /** * Returns the insets of a specific set of windows causing insets, denoted by the - * {@code typeMask} bit mask of {@link InsetsType}s. + * {@code typeMask} bit mask of {@link Type}s. * - * @param typeMask Bit mask of {@link InsetsType}s to query the insets for. + * @param typeMask Bit mask of {@link Type}s to query the insets for. * @return The insets. */ @NonNull @@ -333,7 +333,7 @@ public final class WindowInsets { /** * Returns the insets a specific set of windows can cause, denoted by the - * {@code typeMask} bit mask of {@link InsetsType}s, regardless of whether that type is + * {@code typeMask} bit mask of {@link Type}s, regardless of whether that type is * currently visible or not. * * <p>The insets represents the area of a a window that that <b>may</b> be partially @@ -342,7 +342,7 @@ public final class WindowInsets { * normally shown, but temporarily hidden, the inset returned here will still provide the inset * associated with the status bar being shown.</p> * - * @param typeMask Bit mask of {@link InsetsType}s to query the insets for. + * @param typeMask Bit mask of {@link Type}s to query the insets for. * @return The insets. * * @throws IllegalArgumentException If the caller tries to query {@link Type#ime()}. Insets are @@ -362,7 +362,7 @@ public final class WindowInsets { * Returns whether a set of windows that may cause insets is currently visible on screen, * regardless of whether it actually overlaps with this window. * - * @param typeMask Bit mask of {@link Type.InsetsType}s to query visibility status. + * @param typeMask Bit mask of {@link Type}s to query visibility status. * @return {@code true} if and only if all windows included in {@code typeMask} are currently * visible on screen. */ @@ -1148,7 +1148,7 @@ public final class WindowInsets { * * @see #getInsets(int) * - * @param typeMask The bitmask of {@link InsetsType} to set the insets for. + * @param typeMask The bitmask of {@link Type} to set the insets for. * @param insets The insets to set. * * @return itself @@ -1172,7 +1172,7 @@ public final class WindowInsets { * * @see #getInsetsIgnoringVisibility(int) * - * @param typeMask The bitmask of {@link InsetsType} to set the insets for. + * @param typeMask The bitmask of {@link Type} to set the insets for. * @param insets The insets to set. * * @return itself @@ -1201,7 +1201,7 @@ public final class WindowInsets { * * @see #isVisible(int) * - * @param typeMask The bitmask of {@link InsetsType} to set the visibility for. + * @param typeMask The bitmask of {@link Type} to set the visibility for. * @param visible Whether to mark the windows as visible or not. * * @return itself diff --git a/core/java/android/view/WindowInsetsAnimation.java b/core/java/android/view/WindowInsetsAnimation.java index cf5e7e3d3e26..ab5b5ba51a6e 100644 --- a/core/java/android/view/WindowInsetsAnimation.java +++ b/core/java/android/view/WindowInsetsAnimation.java @@ -61,7 +61,7 @@ public final class WindowInsetsAnimation { } /** - * @return The bitmask of {@link WindowInsets.Type.InsetsType}s that are animating. + * @return The bitmask of {@link WindowInsets.Type}s that are animating. */ @WindowInsets.Type.InsetsType public int getTypeMask() { @@ -140,7 +140,7 @@ public final class WindowInsetsAnimation { } /** - * Set fraction of the progress if {@link WindowInsets.Type.InsetsType} animation is + * Set fraction of the progress if {@link WindowInsets.Type} animation is * controlled by the app. * <p> * Note: This should only be used for testing, as the system fills in the fraction for the @@ -159,7 +159,7 @@ public final class WindowInsetsAnimation { /** * Retrieves the translucency of the windows that are animating. * - * @return Alpha of windows that cause insets of type {@link WindowInsets.Type.InsetsType}. + * @return Alpha of windows that cause insets of type {@link WindowInsets.Type}. */ @FloatRange(from = 0f, to = 1f) public float getAlpha() { @@ -174,8 +174,7 @@ public final class WindowInsetsAnimation { * {@link WindowInsetsAnimationController#setInsetsAndAlpha(Insets, float, float)} is being * used. * </p> - * @param alpha Alpha of windows that cause insets of type - * {@link WindowInsets.Type.InsetsType}. + * @param alpha Alpha of windows that cause insets of type {@link WindowInsets.Type}. * @see #getAlpha() */ public void setAlpha(@FloatRange(from = 0f, to = 1f) float alpha) { diff --git a/core/java/android/view/WindowInsetsAnimationControlListener.java b/core/java/android/view/WindowInsetsAnimationControlListener.java index b61fa36f0e2a..140a9a8c8970 100644 --- a/core/java/android/view/WindowInsetsAnimationControlListener.java +++ b/core/java/android/view/WindowInsetsAnimationControlListener.java @@ -46,8 +46,8 @@ public interface WindowInsetsAnimationControlListener { * to redraw because of an {@link EditorInfo} change, or when the window is starting up. * * @param controller The controller to control the inset animation. - * @param types The {@link InsetsType}s it was able to gain control over. Note that this may be - * different than the types passed into + * @param types The {@link WindowInsets.Type}s it was able to gain control over. Note that this + * may be different than the types passed into * {@link WindowInsetsController#controlWindowInsetsAnimation} in case the window * wasn't able to gain the controls because it wasn't the IME target or not * currently the window that's controlling the system bars. diff --git a/core/java/android/view/WindowInsetsAnimationController.java b/core/java/android/view/WindowInsetsAnimationController.java index 792b974558bb..6578e9b6c20c 100644 --- a/core/java/android/view/WindowInsetsAnimationController.java +++ b/core/java/android/view/WindowInsetsAnimationController.java @@ -99,7 +99,7 @@ public interface WindowInsetsAnimationController { float getCurrentAlpha(); /** - * @return The {@link InsetsType}s this object is currently controlling. + * @return The {@link WindowInsets.Type}s this object is currently controlling. */ @InsetsType int getTypes(); diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java index fb9bcbd2fcb2..991ed5518003 100644 --- a/core/java/android/view/WindowInsetsController.java +++ b/core/java/android/view/WindowInsetsController.java @@ -142,7 +142,7 @@ public interface WindowInsetsController { * change as soon as the window gains control. The app can listen to the event by observing * {@link View#onApplyWindowInsets} and checking visibility with {@link WindowInsets#isVisible}. * - * @param types A bitmask of {@link InsetsType} specifying what windows the app + * @param types A bitmask of {@link WindowInsets.Type} specifying what windows the app * would like to make appear on screen. */ void show(@InsetsType int types); @@ -154,7 +154,7 @@ public interface WindowInsetsController { * change as soon as the window gains control. The app can listen to the event by observing * {@link View#onApplyWindowInsets} and checking visibility with {@link WindowInsets#isVisible}. * - * @param types A bitmask of {@link InsetsType} specifying what windows the app + * @param types A bitmask of {@link WindowInsets.Type} specifying what windows the app * would like to make disappear. */ void hide(@InsetsType int types); @@ -163,7 +163,7 @@ public interface WindowInsetsController { * Lets the application control window inset animations in a frame-by-frame manner by modifying * the position of the windows in the system causing insets directly. * - * @param types The {@link InsetsType}s the application has requested to control. + * @param types The {@link WindowInsets.Type}s the application has requested to control. * @param durationMillis Duration of animation in * {@link java.util.concurrent.TimeUnit#MILLISECONDS}, or -1 if the * animation doesn't have a predetermined duration. This value will be @@ -199,7 +199,7 @@ public interface WindowInsetsController { * setSystemBarsAppearance(0, APPEARANCE_LIGHT_STATUS_BARS) * </pre> * - * @param appearance Bitmask of {@link Appearance} flags. + * @param appearance Bitmask of appearance flags. * @param mask Specifies which flags of appearance should be changed. * @see #getSystemBarsAppearance */ @@ -280,11 +280,11 @@ public interface WindowInsetsController { @NonNull OnControllableInsetsChangedListener listener); /** - * Listener to be notified when the set of controllable {@link InsetsType} controlled by a - * {@link WindowInsetsController} changes. + * Listener to be notified when the set of controllable {@link WindowInsets.Type} controlled by + * a {@link WindowInsetsController} changes. * <p> - * Once a {@link InsetsType} becomes controllable, the app will be able to control the window - * that is causing this type of insets by calling {@link #controlWindowInsetsAnimation}. + * Once a {@link WindowInsets.Type} becomes controllable, the app will be able to control the + * window that is causing this type of insets by calling {@link #controlWindowInsetsAnimation}. * <p> * Note: When listening to controllability of the {@link Type#ime}, * {@link #controlWindowInsetsAnimation} may still fail in case the {@link InputMethodService} @@ -297,12 +297,12 @@ public interface WindowInsetsController { interface OnControllableInsetsChangedListener { /** - * Called when the set of controllable {@link InsetsType} changes. + * Called when the set of controllable {@link WindowInsets.Type} changes. * - * @param controller The controller for which the set of controllable {@link InsetsType}s - * are changing. - * @param typeMask Bitwise type-mask of the {@link InsetsType}s the controller is currently - * able to control. + * @param controller The controller for which the set of controllable + * {@link WindowInsets.Type}s are changing. + * @param typeMask Bitwise type-mask of the {@link WindowInsets.Type}s the controller is + * currently able to control. */ void onControllableInsetsChanged(@NonNull WindowInsetsController controller, @InsetsType int typeMask); diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 1327f9c6e827..7faa222dd51f 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -3326,8 +3326,8 @@ public interface WindowManager extends ViewManager { /** * Specifies types of insets that this window should avoid overlapping during layout. * - * @param types which types of insets that this window should avoid. The initial value of - * this object includes all system bars. + * @param types which {@link WindowInsets.Type}s of insets that this window should avoid. + * The initial value of this object includes all system bars. */ public void setFitInsetsTypes(@InsetsType int types) { mFitInsetsTypes = types; @@ -3401,7 +3401,7 @@ public interface WindowManager extends ViewManager { } /** - * @return the insets types that this window is avoiding overlapping. + * @return the {@link WindowInsets.Type}s that this window is avoiding overlapping. */ public @InsetsType int getFitInsetsTypes() { return mFitInsetsTypes; diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index d475c658a3a0..2b034b0667d6 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -1099,16 +1099,6 @@ public class BatteryStatsImpl extends BatteryStats { private long[] mCpuFreqs; /** - * Times spent by the system server process grouped by cluster and CPU speed. - */ - private LongSamplingCounterArray mSystemServerCpuTimesUs; - - /** - * Times spent by the system server threads grouped by cluster and CPU speed. - */ - private LongSamplingCounterArray mSystemServerThreadCpuTimesUs; - - /** * Times spent by the system server threads handling incoming binder requests. */ private LongSamplingCounterArray mBinderThreadCpuTimesUs; @@ -10856,6 +10846,14 @@ public class BatteryStatsImpl extends BatteryStats { } } + /** + * Starts tracking CPU time-in-state for threads of the system server process, + * keeping a separate account of threads receiving incoming binder calls. + */ + public void startTrackingSystemServerCpuTime() { + mSystemServerCpuThreadReader.startTrackingThreadCpuTime(); + } + public void setCallback(BatteryCallback cb) { mCallback = cb; } @@ -11508,8 +11506,6 @@ public class BatteryStatsImpl extends BatteryStats { MeasuredEnergyStats.resetIfNotNull(mGlobalMeasuredEnergyStats); - resetIfNotNull(mSystemServerCpuTimesUs, false, elapsedRealtimeUs); - resetIfNotNull(mSystemServerThreadCpuTimesUs, false, elapsedRealtimeUs); resetIfNotNull(mBinderThreadCpuTimesUs, false, elapsedRealtimeUs); mLastHistoryStepDetails = null; @@ -12695,27 +12691,17 @@ public class BatteryStatsImpl extends BatteryStats { return; } - if (mSystemServerCpuTimesUs == null) { - mSystemServerCpuTimesUs = new LongSamplingCounterArray(mOnBatteryTimeBase); - mSystemServerThreadCpuTimesUs = new LongSamplingCounterArray(mOnBatteryTimeBase); + if (mBinderThreadCpuTimesUs == null) { mBinderThreadCpuTimesUs = new LongSamplingCounterArray(mOnBatteryTimeBase); } - mSystemServerCpuTimesUs.addCountLocked(systemServiceCpuThreadTimes.processCpuTimesUs); - mSystemServerThreadCpuTimesUs.addCountLocked(systemServiceCpuThreadTimes.threadCpuTimesUs); mBinderThreadCpuTimesUs.addCountLocked(systemServiceCpuThreadTimes.binderThreadCpuTimesUs); if (DEBUG_BINDER_STATS) { - Slog.d(TAG, "System server threads per CPU cluster (binder threads/total threads/%)"); - long totalCpuTimeMs = 0; - long totalThreadTimeMs = 0; + Slog.d(TAG, "System server threads per CPU cluster (incoming binder threads)"); long binderThreadTimeMs = 0; int cpuIndex = 0; - final long[] systemServerCpuTimesUs = - mSystemServerCpuTimesUs.getCountsLocked(0); - final long[] systemServerThreadCpuTimesUs = - mSystemServerThreadCpuTimesUs.getCountsLocked(0); - final long[] binderThreadCpuTimesUs = - mBinderThreadCpuTimesUs.getCountsLocked(0); + final long[] binderThreadCpuTimesUs = mBinderThreadCpuTimesUs.getCountsLocked( + BatteryStats.STATS_SINCE_CHARGED); int index = 0; int numCpuClusters = mPowerProfile.getNumCpuClusters(); for (int cluster = 0; cluster < numCpuClusters; cluster++) { @@ -12726,28 +12712,15 @@ public class BatteryStatsImpl extends BatteryStats { if (speed != 0) { sb.append(", "); } - long totalCountMs = systemServerThreadCpuTimesUs[index] / 1000; long binderCountMs = binderThreadCpuTimesUs[index] / 1000; - sb.append(String.format("%d/%d(%.1f%%)", - binderCountMs, - totalCountMs, - totalCountMs != 0 ? (double) binderCountMs * 100 / totalCountMs : 0)); + sb.append(TextUtils.formatSimple("%10d", binderCountMs)); - totalCpuTimeMs += systemServerCpuTimesUs[index] / 1000; - totalThreadTimeMs += totalCountMs; binderThreadTimeMs += binderCountMs; index++; } cpuIndex += mPowerProfile.getNumCoresInCpuCluster(cluster); Slog.d(TAG, sb.toString()); } - - Slog.d(TAG, "Total system server CPU time (ms): " + totalCpuTimeMs); - Slog.d(TAG, "Total system server thread time (ms): " + totalThreadTimeMs); - Slog.d(TAG, String.format("Total Binder thread time (ms): %d (%.1f%%)", - binderThreadTimeMs, - binderThreadTimeMs != 0 - ? (double) binderThreadTimeMs * 100 / totalThreadTimeMs : 0)); } } @@ -14022,60 +13995,16 @@ public class BatteryStatsImpl extends BatteryStats { } + /** + * Estimates the time spent by the system server handling incoming binder requests. + */ @Override public long[] getSystemServiceTimeAtCpuSpeeds() { - // Estimates the time spent by the system server handling incoming binder requests. - // - // The data that we can get from the kernel is this: - // - CPU duration for a (thread - cluster - CPU speed) combination - // - CPU duration for a (UID - cluster - CPU speed) combination - // - // The configuration we have in the Power Profile is this: - // - Average CPU power for a (cluster - CPU speed) combination. - // - // The model used by BatteryStats can be illustrated with this example: - // - // - Let's say the system server has 10 threads. - // - These 10 threads spent 1000 ms of CPU time in aggregate - // - Of the 10 threads 4 were execute exclusively incoming binder calls. - // - These 4 "binder" threads consumed 600 ms of CPU time in aggregate - // - The real time spent by the system server process doing all of this is, say, 200 ms. - // - // We will assume that power consumption is proportional to the time spent by the CPU - // across all threads. This is a crude assumption, but we don't have more detailed data. - // Thus, - // binderRealTime = realTime * aggregateBinderThreadTime / aggregateAllThreadTime - // - // In our example, - // binderRealTime = 200 * 600 / 1000 = 120ms - // - // We can then multiply this estimated time by the average power to obtain an estimate - // of the total power consumed by incoming binder calls for the given cluster/speed - // combination. - - if (mSystemServerCpuTimesUs == null) { + if (mBinderThreadCpuTimesUs == null) { return null; } - final long[] systemServerCpuTimesUs = mSystemServerCpuTimesUs.getCountsLocked( - BatteryStats.STATS_SINCE_CHARGED); - final long [] systemServerThreadCpuTimesUs = mSystemServerThreadCpuTimesUs.getCountsLocked( - BatteryStats.STATS_SINCE_CHARGED); - final long[] binderThreadCpuTimesUs = mBinderThreadCpuTimesUs.getCountsLocked( - BatteryStats.STATS_SINCE_CHARGED); - - final int size = systemServerCpuTimesUs.length; - final long[] results = new long[size]; - - for (int i = 0; i < size; i++) { - if (systemServerThreadCpuTimesUs[i] == 0) { - continue; - } - - results[i] = systemServerCpuTimesUs[i] * binderThreadCpuTimesUs[i] - / systemServerThreadCpuTimesUs[i]; - } - return results; + return mBinderThreadCpuTimesUs.getCountsLocked(BatteryStats.STATS_SINCE_CHARGED); } /** @@ -14506,7 +14435,7 @@ public class BatteryStatsImpl extends BatteryStats { } updateSystemServiceCallStats(); - if (mSystemServerThreadCpuTimesUs != null) { + if (mBinderThreadCpuTimesUs != null) { pw.println("Per UID System server binder time in ms:"); long[] systemServiceTimeAtCpuSpeeds = getSystemServiceTimeAtCpuSpeeds(); for (int i = 0; i < size; i++) { @@ -16097,9 +16026,6 @@ public class BatteryStatsImpl extends BatteryStats { mUidStats.append(uid, u); } - mSystemServerCpuTimesUs = LongSamplingCounterArray.readFromParcel(in, mOnBatteryTimeBase); - mSystemServerThreadCpuTimesUs = LongSamplingCounterArray.readFromParcel(in, - mOnBatteryTimeBase); mBinderThreadCpuTimesUs = LongSamplingCounterArray.readFromParcel(in, mOnBatteryTimeBase); } @@ -16308,8 +16234,6 @@ public class BatteryStatsImpl extends BatteryStats { } else { out.writeInt(0); } - LongSamplingCounterArray.writeToParcel(out, mSystemServerCpuTimesUs); - LongSamplingCounterArray.writeToParcel(out, mSystemServerThreadCpuTimesUs); LongSamplingCounterArray.writeToParcel(out, mBinderThreadCpuTimesUs); } diff --git a/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java b/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java index e6a962312a00..4d2a08a4bcf3 100644 --- a/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java +++ b/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java @@ -16,23 +16,12 @@ package com.android.internal.os; -import static android.os.Process.PROC_OUT_LONG; -import static android.os.Process.PROC_SPACE_TERM; - import android.annotation.Nullable; -import android.os.Process; -import android.system.Os; -import android.system.OsConstants; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import java.io.IOException; -import java.nio.file.DirectoryIteratorException; -import java.nio.file.DirectoryStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; import java.util.Arrays; /** @@ -45,93 +34,65 @@ public class KernelSingleProcessCpuThreadReader { private static final String TAG = "KernelSingleProcCpuThreadRdr"; private static final boolean DEBUG = false; - private static final boolean NATIVE_ENABLED = true; - - /** - * The name of the file to read CPU statistics from, must be found in {@code - * /proc/$PID/task/$TID} - */ - private static final String CPU_STATISTICS_FILENAME = "time_in_state"; - - private static final String PROC_STAT_FILENAME = "stat"; - - /** Directory under /proc/$PID containing CPU stats files for threads */ - public static final String THREAD_CPU_STATS_DIRECTORY = "task"; - - /** Default mount location of the {@code proc} filesystem */ - private static final Path DEFAULT_PROC_PATH = Paths.get("/proc"); - - /** The initial {@code time_in_state} file for {@link ProcTimeInStateReader} */ - private static final Path INITIAL_TIME_IN_STATE_PATH = Paths.get("self/time_in_state"); - - /** See https://man7.org/linux/man-pages/man5/proc.5.html */ - private static final int[] PROCESS_FULL_STATS_FORMAT = new int[] { - PROC_SPACE_TERM, - PROC_SPACE_TERM, - PROC_SPACE_TERM, - PROC_SPACE_TERM, - PROC_SPACE_TERM, - PROC_SPACE_TERM, - PROC_SPACE_TERM, - PROC_SPACE_TERM, - PROC_SPACE_TERM, - PROC_SPACE_TERM, - PROC_SPACE_TERM, - PROC_SPACE_TERM, - PROC_SPACE_TERM, - PROC_SPACE_TERM | PROC_OUT_LONG, // 14: utime - PROC_SPACE_TERM | PROC_OUT_LONG, // 15: stime - // Ignore remaining fields - }; - - private final long[] mProcessFullStatsData = new long[2]; - - private static final int PROCESS_FULL_STAT_UTIME = 0; - private static final int PROCESS_FULL_STAT_STIME = 1; - - /** Used to read and parse {@code time_in_state} files */ - private final ProcTimeInStateReader mProcTimeInStateReader; private final int mPid; - /** Where the proc filesystem is mounted */ - private final Path mProcPath; + private final CpuTimeInStateReader mCpuTimeInStateReader; - // How long a CPU jiffy is in milliseconds. - private final long mJiffyMillis; + private int[] mSelectedThreadNativeTids = new int[0]; // Sorted - // Path: /proc/<pid>/stat - private final String mProcessStatFilePath; + /** + * Count of frequencies read from the {@code time_in_state} file. + */ + private int mFrequencyCount; - // Path: /proc/<pid>/task - private final Path mThreadsDirectoryPath; + private boolean mIsTracking; /** - * Count of frequencies read from the {@code time_in_state} file. Read from {@link - * #mProcTimeInStateReader#getCpuFrequenciesKhz()}. + * A CPU time-in-state provider for testing. Imitates the behavior of the corresponding + * methods in frameworks/native/libs/cputimeinstate/cputimeinstate.c */ - private int mFrequencyCount; + @VisibleForTesting + public interface CpuTimeInStateReader { + /** + * Returns the overall number of cluster-frequency combinations. + */ + int getCpuFrequencyCount(); + + /** + * Returns true to indicate success. + * + * Called from native. + */ + boolean startTrackingProcessCpuTimes(int tgid); + + /** + * Returns true to indicate success. + * + * Called from native. + */ + boolean startAggregatingTaskCpuTimes(int pid, int aggregationKey); + + /** + * Must return an array of strings formatted like this: + * "aggKey:t0_0 t0_1...:t1_0 t1_1..." + * Times should be provided in nanoseconds. + * + * Called from native. + */ + String[] getAggregatedTaskCpuFreqTimes(int pid); + } /** * Create with a path where `proc` is mounted. Used primarily for testing * * @param pid PID of the process whose threads are to be read. - * @param procPath where `proc` is mounted (to find, see {@code mount | grep ^proc}) */ @VisibleForTesting - public KernelSingleProcessCpuThreadReader( - int pid, - Path procPath) throws IOException { + public KernelSingleProcessCpuThreadReader(int pid, + @Nullable CpuTimeInStateReader cpuTimeInStateReader) throws IOException { mPid = pid; - mProcPath = procPath; - mProcTimeInStateReader = new ProcTimeInStateReader( - mProcPath.resolve(INITIAL_TIME_IN_STATE_PATH)); - long jiffyHz = Os.sysconf(OsConstants._SC_CLK_TCK); - mJiffyMillis = 1000 / jiffyHz; - mProcessStatFilePath = - mProcPath.resolve(String.valueOf(mPid)).resolve(PROC_STAT_FILENAME).toString(); - mThreadsDirectoryPath = - mProcPath.resolve(String.valueOf(mPid)).resolve(THREAD_CPU_STATS_DIRECTORY); + mCpuTimeInStateReader = cpuTimeInStateReader; } /** @@ -142,7 +103,7 @@ public class KernelSingleProcessCpuThreadReader { @Nullable public static KernelSingleProcessCpuThreadReader create(int pid) { try { - return new KernelSingleProcessCpuThreadReader(pid, DEFAULT_PROC_PATH); + return new KernelSingleProcessCpuThreadReader(pid, null); } catch (IOException e) { Slog.e(TAG, "Failed to initialize KernelSingleProcessCpuThreadReader", e); return null; @@ -150,146 +111,98 @@ public class KernelSingleProcessCpuThreadReader { } /** - * Get the CPU frequencies that correspond to the times reported in {@link - * ProcessCpuUsage#processCpuTimesMillis} etc. + * Starts tracking aggregated CPU time-in-state of all threads of the process with the PID + * supplied in the constructor. + */ + public void startTrackingThreadCpuTimes() { + if (!mIsTracking) { + if (!startTrackingProcessCpuTimes(mPid, mCpuTimeInStateReader)) { + Slog.e(TAG, "Failed to start tracking process CPU times for " + mPid); + } + if (mSelectedThreadNativeTids.length > 0) { + if (!startAggregatingThreadCpuTimes(mSelectedThreadNativeTids, + mCpuTimeInStateReader)) { + Slog.e(TAG, "Failed to start tracking aggregated thread CPU times for " + + Arrays.toString(mSelectedThreadNativeTids)); + } + } + mIsTracking = true; + } + } + + /** + * @param nativeTids an array of native Thread IDs whose CPU times should + * be aggregated as a group. This is expected to be a subset + * of all thread IDs owned by the process. + */ + public void setSelectedThreadIds(int[] nativeTids) { + mSelectedThreadNativeTids = nativeTids.clone(); + if (mIsTracking) { + startAggregatingThreadCpuTimes(mSelectedThreadNativeTids, mCpuTimeInStateReader); + } + } + + /** + * Get the CPU frequencies that correspond to the times reported in {@link ProcessCpuUsage}. */ public int getCpuFrequencyCount() { if (mFrequencyCount == 0) { - mFrequencyCount = mProcTimeInStateReader.getFrequenciesKhz().length; + mFrequencyCount = getCpuFrequencyCount(mCpuTimeInStateReader); } return mFrequencyCount; } /** - * Get the total and per-thread CPU usage of the process with the PID specified in the - * constructor. - * - * @param selectedThreadIds a SORTED array of native Thread IDs whose CPU times should - * be aggregated as a group. This is expected to be a subset - * of all thread IDs owned by the process. + * Get the total CPU usage of the process with the PID specified in the + * constructor. The CPU usage time is aggregated across all threads and may + * exceed the time the entire process has been running. */ @Nullable - public ProcessCpuUsage getProcessCpuUsage(int[] selectedThreadIds) { + public ProcessCpuUsage getProcessCpuUsage() { if (DEBUG) { - Slog.d(TAG, "Reading CPU thread usages with directory " + mProcPath + " process ID " - + mPid); - } - - int cpuFrequencyCount = getCpuFrequencyCount(); - ProcessCpuUsage processCpuUsage = new ProcessCpuUsage(cpuFrequencyCount); - - if (NATIVE_ENABLED) { - boolean result = readProcessCpuUsage(mProcPath.toString(), mPid, - selectedThreadIds, processCpuUsage.processCpuTimesMillis, - processCpuUsage.threadCpuTimesMillis, - processCpuUsage.selectedThreadCpuTimesMillis); - if (!result) { - return null; - } - return processCpuUsage; + Slog.d(TAG, "Reading CPU thread usages for PID " + mPid); } - if (!isSorted(selectedThreadIds)) { - throw new IllegalArgumentException("selectedThreadIds is not sorted: " - + Arrays.toString(selectedThreadIds)); - } - - if (!Process.readProcFile(mProcessStatFilePath, PROCESS_FULL_STATS_FORMAT, null, - mProcessFullStatsData, null)) { - Slog.e(TAG, "Failed to read process stat file " + mProcessStatFilePath); - return null; - } + ProcessCpuUsage processCpuUsage = new ProcessCpuUsage(getCpuFrequencyCount()); - long utime = mProcessFullStatsData[PROCESS_FULL_STAT_UTIME]; - long stime = mProcessFullStatsData[PROCESS_FULL_STAT_STIME]; - - long processCpuTimeMillis = (utime + stime) * mJiffyMillis; - - try (DirectoryStream<Path> threadPaths = Files.newDirectoryStream(mThreadsDirectoryPath)) { - for (Path threadDirectory : threadPaths) { - readThreadCpuUsage(processCpuUsage, selectedThreadIds, threadDirectory); - } - } catch (IOException | DirectoryIteratorException e) { - // Expected when a process finishes + boolean result = readProcessCpuUsage(mPid, + processCpuUsage.threadCpuTimesMillis, + processCpuUsage.selectedThreadCpuTimesMillis, + mCpuTimeInStateReader); + if (!result) { return null; } - // Estimate per cluster per frequency CPU time for the entire process - // by distributing the total process CPU time proportionately to how much - // CPU time its threads took on those clusters/frequencies. This algorithm - // works more accurately when when we have equally distributed concurrency. - // TODO(b/169279846): obtain actual process CPU times from the kernel - long totalCpuTimeAllThreads = 0; - for (int i = cpuFrequencyCount - 1; i >= 0; i--) { - totalCpuTimeAllThreads += processCpuUsage.threadCpuTimesMillis[i]; - } - - for (int i = cpuFrequencyCount - 1; i >= 0; i--) { - processCpuUsage.processCpuTimesMillis[i] = - processCpuTimeMillis * processCpuUsage.threadCpuTimesMillis[i] - / totalCpuTimeAllThreads; + if (DEBUG) { + Slog.d(TAG, "threadCpuTimesMillis = " + + Arrays.toString(processCpuUsage.threadCpuTimesMillis)); + Slog.d(TAG, "selectedThreadCpuTimesMillis = " + + Arrays.toString(processCpuUsage.selectedThreadCpuTimesMillis)); } return processCpuUsage; } - /** - * Reads a thread's CPU usage and aggregates the per-cluster per-frequency CPU times. - * - * @param threadDirectory the {@code /proc} directory of the thread - */ - private void readThreadCpuUsage(ProcessCpuUsage processCpuUsage, int[] selectedThreadIds, - Path threadDirectory) { - // Get the thread ID from the directory name - final int threadId; - try { - final String directoryName = threadDirectory.getFileName().toString(); - threadId = Integer.parseInt(directoryName); - } catch (NumberFormatException e) { - Slog.w(TAG, "Failed to parse thread ID when iterating over /proc/*/task", e); - return; - } - - // Get the CPU statistics from the directory - final Path threadCpuStatPath = threadDirectory.resolve(CPU_STATISTICS_FILENAME); - final long[] cpuUsages = mProcTimeInStateReader.getUsageTimesMillis(threadCpuStatPath); - if (cpuUsages == null) { - return; - } - - final int cpuFrequencyCount = getCpuFrequencyCount(); - final boolean isSelectedThread = Arrays.binarySearch(selectedThreadIds, threadId) >= 0; - for (int i = cpuFrequencyCount - 1; i >= 0; i--) { - processCpuUsage.threadCpuTimesMillis[i] += cpuUsages[i]; - if (isSelectedThread) { - processCpuUsage.selectedThreadCpuTimesMillis[i] += cpuUsages[i]; - } - } - } - /** CPU usage of a process, all of its threads and a selected subset of its threads */ public static class ProcessCpuUsage { - public long[] processCpuTimesMillis; public long[] threadCpuTimesMillis; public long[] selectedThreadCpuTimesMillis; public ProcessCpuUsage(int cpuFrequencyCount) { - processCpuTimesMillis = new long[cpuFrequencyCount]; threadCpuTimesMillis = new long[cpuFrequencyCount]; selectedThreadCpuTimesMillis = new long[cpuFrequencyCount]; } } - private static boolean isSorted(int[] array) { - for (int i = 0; i < array.length - 1; i++) { - if (array[i] > array[i + 1]) { - return false; - } - } - return true; - } + private native int getCpuFrequencyCount(CpuTimeInStateReader reader); + + private native boolean startTrackingProcessCpuTimes(int pid, CpuTimeInStateReader reader); + + private native boolean startAggregatingThreadCpuTimes(int[] selectedThreadIds, + CpuTimeInStateReader reader); - private native boolean readProcessCpuUsage(String procPath, int pid, int[] selectedThreadIds, - long[] processCpuTimesMillis, long[] threadCpuTimesMillis, - long[] selectedThreadCpuTimesMillis); + private native boolean readProcessCpuUsage(int pid, + long[] threadCpuTimesMillis, + long[] selectedThreadCpuTimesMillis, + CpuTimeInStateReader reader); } diff --git a/core/java/com/android/internal/os/SystemServerCpuThreadReader.java b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java index fbbee94feacc..fbad75e93a17 100644 --- a/core/java/com/android/internal/os/SystemServerCpuThreadReader.java +++ b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java @@ -22,8 +22,6 @@ import android.os.Process; import com.android.internal.annotations.VisibleForTesting; import java.io.IOException; -import java.nio.file.Path; -import java.util.Arrays; /** * Reads /proc/UID/task/TID/time_in_state files to obtain statistics on CPU usage @@ -31,9 +29,7 @@ import java.util.Arrays; */ public class SystemServerCpuThreadReader { private final KernelSingleProcessCpuThreadReader mKernelCpuThreadReader; - private int[] mBinderThreadNativeTids = new int[0]; // Sorted - private long[] mLastProcessCpuTimeUs; private long[] mLastThreadCpuTimesUs; private long[] mLastBinderThreadCpuTimesUs; @@ -41,8 +37,6 @@ public class SystemServerCpuThreadReader { * Times (in microseconds) spent by the system server UID. */ public static class SystemServiceCpuThreadTimes { - // The entire process - public long[] processCpuTimesUs; // All threads public long[] threadCpuTimesUs; // Just the threads handling incoming binder calls @@ -61,8 +55,10 @@ public class SystemServerCpuThreadReader { } @VisibleForTesting - public SystemServerCpuThreadReader(Path procPath, int pid) throws IOException { - this(new KernelSingleProcessCpuThreadReader(pid, procPath)); + public SystemServerCpuThreadReader(int pid, + KernelSingleProcessCpuThreadReader.CpuTimeInStateReader cpuTimeInStateReader) + throws IOException { + this(new KernelSingleProcessCpuThreadReader(pid, cpuTimeInStateReader)); } @VisibleForTesting @@ -70,9 +66,15 @@ public class SystemServerCpuThreadReader { mKernelCpuThreadReader = kernelCpuThreadReader; } + /** + * Start tracking CPU time-in-state for the process specified in the constructor. + */ + public void startTrackingThreadCpuTime() { + mKernelCpuThreadReader.startTrackingThreadCpuTimes(); + } + public void setBinderThreadNativeTids(int[] nativeTids) { - mBinderThreadNativeTids = nativeTids.clone(); - Arrays.sort(mBinderThreadNativeTids); + mKernelCpuThreadReader.setSelectedThreadIds(nativeTids); } /** @@ -81,33 +83,27 @@ public class SystemServerCpuThreadReader { @Nullable public SystemServiceCpuThreadTimes readDelta() { final int numCpuFrequencies = mKernelCpuThreadReader.getCpuFrequencyCount(); - if (mLastProcessCpuTimeUs == null) { - mLastProcessCpuTimeUs = new long[numCpuFrequencies]; + if (mLastThreadCpuTimesUs == null) { mLastThreadCpuTimesUs = new long[numCpuFrequencies]; mLastBinderThreadCpuTimesUs = new long[numCpuFrequencies]; - mDeltaCpuThreadTimes.processCpuTimesUs = new long[numCpuFrequencies]; mDeltaCpuThreadTimes.threadCpuTimesUs = new long[numCpuFrequencies]; mDeltaCpuThreadTimes.binderThreadCpuTimesUs = new long[numCpuFrequencies]; } final KernelSingleProcessCpuThreadReader.ProcessCpuUsage processCpuUsage = - mKernelCpuThreadReader.getProcessCpuUsage(mBinderThreadNativeTids); + mKernelCpuThreadReader.getProcessCpuUsage(); if (processCpuUsage == null) { return null; } for (int i = numCpuFrequencies - 1; i >= 0; i--) { - long processCpuTimesUs = processCpuUsage.processCpuTimesMillis[i] * 1000; long threadCpuTimesUs = processCpuUsage.threadCpuTimesMillis[i] * 1000; long binderThreadCpuTimesUs = processCpuUsage.selectedThreadCpuTimesMillis[i] * 1000; - mDeltaCpuThreadTimes.processCpuTimesUs[i] = - Math.max(0, processCpuTimesUs - mLastProcessCpuTimeUs[i]); mDeltaCpuThreadTimes.threadCpuTimesUs[i] = Math.max(0, threadCpuTimesUs - mLastThreadCpuTimesUs[i]); mDeltaCpuThreadTimes.binderThreadCpuTimesUs[i] = Math.max(0, binderThreadCpuTimesUs - mLastBinderThreadCpuTimesUs[i]); - mLastProcessCpuTimeUs[i] = processCpuTimesUs; mLastThreadCpuTimesUs[i] = threadCpuTimesUs; mLastBinderThreadCpuTimesUs[i] = binderThreadCpuTimesUs; } diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java index 1e9801f5ef30..30cd94c21169 100644 --- a/core/java/com/android/internal/util/LatencyTracker.java +++ b/core/java/com/android/internal/util/LatencyTracker.java @@ -106,7 +106,6 @@ public class LatencyTracker { private static LatencyTracker sLatencyTracker; private final SparseLongArray mStartRtc = new SparseLongArray(); - private final Context mContext; private volatile int mSamplingInterval; private volatile boolean mEnabled; @@ -114,15 +113,14 @@ public class LatencyTracker { if (sLatencyTracker == null) { synchronized (LatencyTracker.class) { if (sLatencyTracker == null) { - sLatencyTracker = new LatencyTracker(context); + sLatencyTracker = new LatencyTracker(); } } } return sLatencyTracker; } - public LatencyTracker(Context context) { - mContext = context; + private LatencyTracker() { mEnabled = DEFAULT_ENABLED; mSamplingInterval = DEFAULT_SAMPLING_INTERVAL; @@ -173,8 +171,8 @@ public class LatencyTracker { } } - private String getTraceNameOfAcion(int action) { - return "L<" + getNameOfAction(action) + ">"; + private static String getTraceNameOfAction(int action) { + return "L<" + getNameOfAction(STATSD_ACTION[action]) + ">"; } public static boolean isEnabled(Context ctx) { @@ -194,7 +192,7 @@ public class LatencyTracker { if (!isEnabled()) { return; } - Trace.asyncTraceBegin(Trace.TRACE_TAG_APP, getTraceNameOfAcion(action), 0); + Trace.asyncTraceBegin(Trace.TRACE_TAG_APP, getTraceNameOfAction(action), 0); mStartRtc.put(action, SystemClock.elapsedRealtime()); } @@ -213,7 +211,7 @@ public class LatencyTracker { return; } mStartRtc.delete(action); - Trace.asyncTraceEnd(Trace.TRACE_TAG_APP, getTraceNameOfAcion(action), 0); + Trace.asyncTraceEnd(Trace.TRACE_TAG_APP, getTraceNameOfAction(action), 0); logAction(action, (int) (endRtc - startRtc)); } @@ -236,7 +234,7 @@ public class LatencyTracker { * @param writeToStatsLog Whether to write the measured latency to FrameworkStatsLog. */ public static void logActionDeprecated(int action, int duration, boolean writeToStatsLog) { - Log.i(TAG, "action=" + action + " latency=" + duration); + Log.i(TAG, getNameOfAction(STATSD_ACTION[action]) + " latency=" + duration); EventLog.writeEvent(EventLogTags.SYSUI_LATENCY, action, duration); if (writeToStatsLog) { diff --git a/core/jni/com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp b/core/jni/com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp index 52bed6bcfce3..dfae68429f6d 100644 --- a/core/jni/com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp +++ b/core/jni/com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp @@ -26,244 +26,308 @@ #include <android_runtime/Log.h> #include <nativehelper/ScopedPrimitiveArray.h> -#include <nativehelper/ScopedUtfChars.h> namespace android { +static constexpr uint16_t DEFAULT_THREAD_AGGREGATION_KEY = 0; +static constexpr uint16_t SELECTED_THREAD_AGGREGATION_KEY = 1; + +static constexpr uint64_t NSEC_PER_MSEC = 1000000; + // Number of milliseconds in a jiffy - the unit of time measurement for processes and threads static const uint32_t gJiffyMillis = (uint32_t)(1000 / sysconf(_SC_CLK_TCK)); -// Given a PID, returns a vector of all TIDs for the process' tasks. Thread IDs are -// file names in the /proc/<pid>/task directory. -static bool getThreadIds(const std::string &procPath, const pid_t pid, - std::vector<pid_t> &outThreadIds) { - std::string taskPath = android::base::StringPrintf("%s/%u/task", procPath.c_str(), pid); +// Abstract class for readers of CPU time-in-state. There are two implementations of +// this class: BpfCpuTimeInStateReader and MockCpuTimeInStateReader. The former is used +// by the production code. The latter is used by unit tests to provide mock +// CPU time-in-state data via a Java implementation. +class ICpuTimeInStateReader { +public: + virtual ~ICpuTimeInStateReader() {} + + // Returns the overall number of cluser-frequency combinations + virtual size_t getCpuFrequencyCount(); + + // Marks the CPU time-in-state tracking for threads of the specified TGID + virtual bool startTrackingProcessCpuTimes(pid_t) = 0; + + // Marks the thread specified by its PID for CPU time-in-state tracking. + virtual bool startAggregatingTaskCpuTimes(pid_t, uint16_t) = 0; + + // Retrieves the accumulated time-in-state data, which is organized as a map + // from aggregation keys to vectors of vectors using the format: + // { aggKey0 -> [[t0_0_0, t0_0_1, ...], [t0_1_0, t0_1_1, ...], ...], + // aggKey1 -> [[t1_0_0, t1_0_1, ...], [t1_1_0, t1_1_1, ...], ...], ... } + // where ti_j_k is the ns tid i spent running on the jth cluster at the cluster's kth lowest + // freq. + virtual std::optional<std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>>> + getAggregatedTaskCpuFreqTimes(pid_t, const std::vector<uint16_t> &); +}; - struct dirent **dirlist; - int threadCount = scandir(taskPath.c_str(), &dirlist, NULL, NULL); - if (threadCount == -1) { - ALOGE("Cannot read directory %s", taskPath.c_str()); - return false; +// ICpuTimeInStateReader that uses eBPF to provide a map of aggregated CPU time-in-state values. +// See cputtimeinstate.h/.cpp +class BpfCpuTimeInStateReader : public ICpuTimeInStateReader { +public: + size_t getCpuFrequencyCount() { + std::optional<std::vector<std::vector<uint32_t>>> cpuFreqs = android::bpf::getCpuFreqs(); + if (!cpuFreqs) { + ALOGE("Cannot obtain CPU frequency count"); + return 0; + } + + size_t freqCount = 0; + for (auto cluster : *cpuFreqs) { + freqCount += cluster.size(); + } + + return freqCount; } - outThreadIds.reserve(threadCount); + bool startTrackingProcessCpuTimes(pid_t tgid) { + return android::bpf::startTrackingProcessCpuTimes(tgid); + } - for (int i = 0; i < threadCount; i++) { - pid_t tid; - if (android::base::ParseInt<pid_t>(dirlist[i]->d_name, &tid)) { - outThreadIds.push_back(tid); - } - free(dirlist[i]); + bool startAggregatingTaskCpuTimes(pid_t pid, uint16_t aggregationKey) { + return android::bpf::startAggregatingTaskCpuTimes(pid, aggregationKey); } - free(dirlist); - return true; -} + std::optional<std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>>> + getAggregatedTaskCpuFreqTimes(pid_t pid, const std::vector<uint16_t> &aggregationKeys) { + return android::bpf::getAggregatedTaskCpuFreqTimes(pid, aggregationKeys); + } +}; -// Reads contents of a time_in_state file and returns times as a vector of times per frequency -// A time_in_state file contains pairs of frequency - time (in jiffies): -// -// cpu0 -// 300000 30 -// 403200 0 -// cpu4 -// 710400 10 -// 825600 20 -// 940800 30 -// -static bool getThreadTimeInState(const std::string &procPath, const pid_t pid, const pid_t tid, - const size_t frequencyCount, - std::vector<uint64_t> &outThreadTimeInState) { - std::string timeInStateFilePath = - android::base::StringPrintf("%s/%u/task/%u/time_in_state", procPath.c_str(), pid, tid); - std::string data; - - if (!android::base::ReadFileToString(timeInStateFilePath, &data)) { - ALOGE("Cannot read file: %s", timeInStateFilePath.c_str()); - return false; +// ICpuTimeInStateReader that uses JNI to provide a map of aggregated CPU time-in-state +// values. +// This version of CpuTimeInStateReader is used exclusively for providing mock data in tests. +class MockCpuTimeInStateReader : public ICpuTimeInStateReader { +private: + JNIEnv *mEnv; + jobject mCpuTimeInStateReader; + +public: + MockCpuTimeInStateReader(JNIEnv *env, jobject cpuTimeInStateReader) + : mEnv(env), mCpuTimeInStateReader(cpuTimeInStateReader) {} + + size_t getCpuFrequencyCount(); + + bool startTrackingProcessCpuTimes(pid_t tgid); + + bool startAggregatingTaskCpuTimes(pid_t pid, uint16_t aggregationKey); + + std::optional<std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>>> + getAggregatedTaskCpuFreqTimes(pid_t tgid, const std::vector<uint16_t> &aggregationKeys); +}; + +static ICpuTimeInStateReader *getCpuTimeInStateReader(JNIEnv *env, + jobject cpuTimeInStateReaderObject) { + if (cpuTimeInStateReaderObject) { + return new MockCpuTimeInStateReader(env, cpuTimeInStateReaderObject); + } else { + return new BpfCpuTimeInStateReader(); } +} - auto lines = android::base::Split(data, "\n"); - size_t index = 0; - for (const auto &line : lines) { - if (line.empty()) { - continue; - } +static jint getCpuFrequencyCount(JNIEnv *env, jclass, jobject cpuTimeInStateReaderObject) { + std::unique_ptr<ICpuTimeInStateReader> cpuTimeInStateReader( + getCpuTimeInStateReader(env, cpuTimeInStateReaderObject)); + return cpuTimeInStateReader->getCpuFrequencyCount(); +} - auto numbers = android::base::Split(line, " "); - if (numbers.size() != 2) { - continue; - } - uint64_t timeInState; - if (!android::base::ParseUint<uint64_t>(numbers[1], &timeInState)) { - ALOGE("Invalid time_in_state file format: %s", timeInStateFilePath.c_str()); +static jboolean startTrackingProcessCpuTimes(JNIEnv *env, jclass, jint tgid, + jobject cpuTimeInStateReaderObject) { + std::unique_ptr<ICpuTimeInStateReader> cpuTimeInStateReader( + getCpuTimeInStateReader(env, cpuTimeInStateReaderObject)); + return cpuTimeInStateReader->startTrackingProcessCpuTimes(tgid); +} + +static jboolean startAggregatingThreadCpuTimes(JNIEnv *env, jclass, jintArray selectedThreadIdArray, + jobject cpuTimeInStateReaderObject) { + ScopedIntArrayRO selectedThreadIds(env, selectedThreadIdArray); + std::unique_ptr<ICpuTimeInStateReader> cpuTimeInStateReader( + getCpuTimeInStateReader(env, cpuTimeInStateReaderObject)); + + for (int i = 0; i < selectedThreadIds.size(); i++) { + if (!cpuTimeInStateReader->startAggregatingTaskCpuTimes(selectedThreadIds[i], + SELECTED_THREAD_AGGREGATION_KEY)) { return false; } - if (index < frequencyCount) { - outThreadTimeInState[index] = timeInState; - } - index++; } + return true; +} +// Converts time-in-state data from a vector of vectors to a flat array. +// Also converts from nanoseconds to milliseconds. +static bool flattenTimeInStateData(ScopedLongArrayRW &cpuTimesMillis, + const std::vector<std::vector<uint64_t>> &data) { + size_t frequencyCount = cpuTimesMillis.size(); + size_t index = 0; + for (const auto &cluster : data) { + for (const uint64_t &timeNanos : cluster) { + if (index < frequencyCount) { + cpuTimesMillis[index] = timeNanos / NSEC_PER_MSEC; + } + index++; + } + } if (index != frequencyCount) { - ALOGE("Incorrect number of frequencies %u in %s. Expected %u", - (uint32_t)outThreadTimeInState.size(), timeInStateFilePath.c_str(), - (uint32_t)frequencyCount); + ALOGE("CPU time-in-state reader returned data for %zu frequencies; expected: %zu", index, + frequencyCount); return false; } return true; } -static int pidCompare(const void *a, const void *b) { - return (*(pid_t *)a - *(pid_t *)b); -} - -static inline bool isSelectedThread(const pid_t tid, const pid_t *selectedThreadIds, - const size_t selectedThreadCount) { - return bsearch(&tid, selectedThreadIds, selectedThreadCount, sizeof(pid_t), pidCompare) != NULL; -} - -// Reads all /proc/<pid>/task/*/time_in_state files and aggregates per-frequency +// Reads all CPU time-in-state data accumulated by BPF and aggregates per-frequency // time in state data for all threads. Also, separately aggregates time in state for // selected threads whose TIDs are passes as selectedThreadIds. -static void aggregateThreadCpuTimes(const std::string &procPath, const pid_t pid, - const std::vector<pid_t> &threadIds, - const size_t frequencyCount, const pid_t *selectedThreadIds, - const size_t selectedThreadCount, - uint64_t *threadCpuTimesMillis, - uint64_t *selectedThreadCpuTimesMillis) { - for (size_t j = 0; j < frequencyCount; j++) { - threadCpuTimesMillis[j] = 0; - selectedThreadCpuTimesMillis[j] = 0; - } +static jboolean readProcessCpuUsage(JNIEnv *env, jclass, jint pid, + jlongArray threadCpuTimesMillisArray, + jlongArray selectedThreadCpuTimesMillisArray, + jobject cpuTimeInStateReaderObject) { + ScopedLongArrayRW threadCpuTimesMillis(env, threadCpuTimesMillisArray); + ScopedLongArrayRW selectedThreadCpuTimesMillis(env, selectedThreadCpuTimesMillisArray); + std::unique_ptr<ICpuTimeInStateReader> cpuTimeInStateReader( + getCpuTimeInStateReader(env, cpuTimeInStateReaderObject)); - for (size_t i = 0; i < threadIds.size(); i++) { - pid_t tid = threadIds[i]; - std::vector<uint64_t> timeInState(frequencyCount); - if (!getThreadTimeInState(procPath, pid, tid, frequencyCount, timeInState)) { - continue; - } + const size_t frequencyCount = cpuTimeInStateReader->getCpuFrequencyCount(); - bool selectedThread = isSelectedThread(tid, selectedThreadIds, selectedThreadCount); - for (size_t j = 0; j < frequencyCount; j++) { - threadCpuTimesMillis[j] += timeInState[j]; - if (selectedThread) { - selectedThreadCpuTimesMillis[j] += timeInState[j]; - } - } + if (threadCpuTimesMillis.size() != frequencyCount) { + ALOGE("Invalid threadCpuTimesMillis array length: %zu frequencies; expected: %zu", + threadCpuTimesMillis.size(), frequencyCount); + return false; } + + if (selectedThreadCpuTimesMillis.size() != frequencyCount) { + ALOGE("Invalid selectedThreadCpuTimesMillis array length: %zu frequencies; expected: %zu", + selectedThreadCpuTimesMillis.size(), frequencyCount); + return false; + } + for (size_t i = 0; i < frequencyCount; i++) { - threadCpuTimesMillis[i] *= gJiffyMillis; - selectedThreadCpuTimesMillis[i] *= gJiffyMillis; + threadCpuTimesMillis[i] = 0; + selectedThreadCpuTimesMillis[i] = 0; } -} -// Reads process utime and stime from the /proc/<pid>/stat file. -// Format of this file is described in https://man7.org/linux/man-pages/man5/proc.5.html. -static bool getProcessCpuTime(const std::string &procPath, const pid_t pid, - uint64_t &outTimeMillis) { - std::string statFilePath = android::base::StringPrintf("%s/%u/stat", procPath.c_str(), pid); - std::string data; - if (!android::base::ReadFileToString(statFilePath, &data)) { + std::optional<std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>>> data = + cpuTimeInStateReader->getAggregatedTaskCpuFreqTimes(pid, + {DEFAULT_THREAD_AGGREGATION_KEY, + SELECTED_THREAD_AGGREGATION_KEY}); + if (!data) { + ALOGE("Cannot read thread CPU times for PID %d", pid); return false; } - auto fields = android::base::Split(data, " "); - uint64_t utime, stime; - - // Field 14 (counting from 1) is utime - process time in user space, in jiffies - // Field 15 (counting from 1) is stime - process time in system space, in jiffies - if (fields.size() < 15 || !android::base::ParseUint(fields[13], &utime) || - !android::base::ParseUint(fields[14], &stime)) { - ALOGE("Invalid file format %s", statFilePath.c_str()); + if (!flattenTimeInStateData(threadCpuTimesMillis, (*data)[DEFAULT_THREAD_AGGREGATION_KEY])) { return false; } - outTimeMillis = (utime + stime) * gJiffyMillis; - return true; -} + if (!flattenTimeInStateData(selectedThreadCpuTimesMillis, + (*data)[SELECTED_THREAD_AGGREGATION_KEY])) { + return false; + } -// Estimates per cluster per frequency CPU time for the entire process -// by distributing the total process CPU time proportionately to how much -// CPU time its threads took on those clusters/frequencies. This algorithm -// works more accurately when when we have equally distributed concurrency. -// TODO(b/169279846): obtain actual process CPU times from the kernel -static void estimateProcessTimeInState(const uint64_t processCpuTimeMillis, - const uint64_t *threadCpuTimesMillis, - const size_t frequencyCount, - uint64_t *processCpuTimesMillis) { - uint64_t totalCpuTimeAllThreads = 0; + // threadCpuTimesMillis returns CPU times for _all_ threads, including the selected ones for (size_t i = 0; i < frequencyCount; i++) { - totalCpuTimeAllThreads += threadCpuTimesMillis[i]; + threadCpuTimesMillis[i] += selectedThreadCpuTimesMillis[i]; } - if (totalCpuTimeAllThreads != 0) { - for (size_t i = 0; i < frequencyCount; i++) { - processCpuTimesMillis[i] = - processCpuTimeMillis * threadCpuTimesMillis[i] / totalCpuTimeAllThreads; - } - } else { - for (size_t i = 0; i < frequencyCount; i++) { - processCpuTimesMillis[i] = 0; - } - } + return true; } -static jboolean readProcessCpuUsage(JNIEnv *env, jclass, jstring procPath, jint pid, - jintArray selectedThreadIdArray, - jlongArray processCpuTimesMillisArray, - jlongArray threadCpuTimesMillisArray, - jlongArray selectedThreadCpuTimesMillisArray) { - ScopedUtfChars procPathChars(env, procPath); - ScopedIntArrayRO selectedThreadIds(env, selectedThreadIdArray); - ScopedLongArrayRW processCpuTimesMillis(env, processCpuTimesMillisArray); - ScopedLongArrayRW threadCpuTimesMillis(env, threadCpuTimesMillisArray); - ScopedLongArrayRW selectedThreadCpuTimesMillis(env, selectedThreadCpuTimesMillisArray); +static const JNINativeMethod g_single_methods[] = { + {"getCpuFrequencyCount", + "(Lcom/android/internal/os/KernelSingleProcessCpuThreadReader$CpuTimeInStateReader;)I", + (void *)getCpuFrequencyCount}, + {"startTrackingProcessCpuTimes", + "(ILcom/android/internal/os/KernelSingleProcessCpuThreadReader$CpuTimeInStateReader;)Z", + (void *)startTrackingProcessCpuTimes}, + {"startAggregatingThreadCpuTimes", + "([ILcom/android/internal/os/KernelSingleProcessCpuThreadReader$CpuTimeInStateReader;)Z", + (void *)startAggregatingThreadCpuTimes}, + {"readProcessCpuUsage", + "(I[J[J" + "Lcom/android/internal/os/KernelSingleProcessCpuThreadReader$CpuTimeInStateReader;)Z", + (void *)readProcessCpuUsage}, +}; - std::string procPathStr(procPathChars.c_str()); +int register_com_android_internal_os_KernelSingleProcessCpuThreadReader(JNIEnv *env) { + return RegisterMethodsOrDie(env, "com/android/internal/os/KernelSingleProcessCpuThreadReader", + g_single_methods, NELEM(g_single_methods)); +} - // Get all thread IDs for the process. - std::vector<pid_t> threadIds; - if (!getThreadIds(procPathStr, pid, threadIds)) { - ALOGE("Could not obtain thread IDs from: %s", procPathStr.c_str()); +size_t MockCpuTimeInStateReader::getCpuFrequencyCount() { + jclass cls = mEnv->GetObjectClass(mCpuTimeInStateReader); + jmethodID mid = mEnv->GetMethodID(cls, "getCpuFrequencyCount", "()I"); + if (mid == 0) { + ALOGE("Couldn't find the method getCpuFrequencyCount"); return false; } + return (size_t)mEnv->CallIntMethod(mCpuTimeInStateReader, mid); +} - size_t frequencyCount = processCpuTimesMillis.size(); - - if (threadCpuTimesMillis.size() != frequencyCount) { - ALOGE("Invalid array length: threadCpuTimesMillis"); +bool MockCpuTimeInStateReader::startTrackingProcessCpuTimes(pid_t tgid) { + jclass cls = mEnv->GetObjectClass(mCpuTimeInStateReader); + jmethodID mid = mEnv->GetMethodID(cls, "startTrackingProcessCpuTimes", "(I)Z"); + if (mid == 0) { + ALOGE("Couldn't find the method startTrackingProcessCpuTimes"); return false; } - if (selectedThreadCpuTimesMillis.size() != frequencyCount) { - ALOGE("Invalid array length: selectedThreadCpuTimesMillisArray"); + return mEnv->CallBooleanMethod(mCpuTimeInStateReader, mid, tgid); +} + +bool MockCpuTimeInStateReader::startAggregatingTaskCpuTimes(pid_t pid, uint16_t aggregationKey) { + jclass cls = mEnv->GetObjectClass(mCpuTimeInStateReader); + jmethodID mid = mEnv->GetMethodID(cls, "startAggregatingTaskCpuTimes", "(II)Z"); + if (mid == 0) { + ALOGE("Couldn't find the method startAggregatingTaskCpuTimes"); return false; } + return mEnv->CallBooleanMethod(mCpuTimeInStateReader, mid, pid, aggregationKey); +} - aggregateThreadCpuTimes(procPathStr, pid, threadIds, frequencyCount, selectedThreadIds.get(), - selectedThreadIds.size(), - reinterpret_cast<uint64_t *>(threadCpuTimesMillis.get()), - reinterpret_cast<uint64_t *>(selectedThreadCpuTimesMillis.get())); - - uint64_t processCpuTime; - bool ret = getProcessCpuTime(procPathStr, pid, processCpuTime); - if (ret) { - estimateProcessTimeInState(processCpuTime, - reinterpret_cast<uint64_t *>(threadCpuTimesMillis.get()), - frequencyCount, - reinterpret_cast<uint64_t *>(processCpuTimesMillis.get())); +std::optional<std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>>> +MockCpuTimeInStateReader::getAggregatedTaskCpuFreqTimes( + pid_t pid, const std::vector<uint16_t> &aggregationKeys) { + jclass cls = mEnv->GetObjectClass(mCpuTimeInStateReader); + jmethodID mid = + mEnv->GetMethodID(cls, "getAggregatedTaskCpuFreqTimes", "(I)[Ljava/lang/String;"); + if (mid == 0) { + ALOGE("Couldn't find the method getAggregatedTaskCpuFreqTimes"); + return {}; } - return ret; -} -static const JNINativeMethod g_single_methods[] = { - {"readProcessCpuUsage", "(Ljava/lang/String;I[I[J[J[J)Z", (void *)readProcessCpuUsage}, -}; + std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>> map; -int register_com_android_internal_os_KernelSingleProcessCpuThreadReader(JNIEnv *env) { - return RegisterMethodsOrDie(env, "com/android/internal/os/KernelSingleProcessCpuThreadReader", - g_single_methods, NELEM(g_single_methods)); + jobjectArray stringArray = + (jobjectArray)mEnv->CallObjectMethod(mCpuTimeInStateReader, mid, pid); + int size = mEnv->GetArrayLength(stringArray); + for (int i = 0; i < size; i++) { + ScopedUtfChars line(mEnv, (jstring)mEnv->GetObjectArrayElement(stringArray, i)); + uint16_t aggregationKey; + std::vector<std::vector<uint64_t>> times; + + // Each string is formatted like this: "aggKey:t0_0 t0_1...:t1_0 t1_1..." + auto fields = android::base::Split(line.c_str(), ":"); + android::base::ParseUint(fields[0], &aggregationKey); + + for (int j = 1; j < fields.size(); j++) { + auto numbers = android::base::Split(fields[j], " "); + + std::vector<uint64_t> chunk; + for (int k = 0; k < numbers.size(); k++) { + uint64_t time; + android::base::ParseUint(numbers[k], &time); + chunk.emplace_back(time); + } + times.emplace_back(chunk); + } + + map.emplace(aggregationKey, times); + } + + return map; } } // namespace android diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 1b9455cb9f44..396f95446bf6 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -5912,7 +5912,7 @@ <!-- AOSP configures a default secondary LocationTimeZoneProvider that uses an on-device data set from the com.android.geotz APEX. --> - <service android:name="com.android.timezone.geotz.provider.OfflineLocationTimeZoneProviderService" + <service android:name="com.android.timezone.location.provider.OfflineLocationTimeZoneProviderService" android:enabled="@bool/config_enableSecondaryLocationTimeZoneProvider" android:permission="android.permission.BIND_TIME_ZONE_PROVIDER_SERVICE" android:exported="false"> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 243c2448a3b1..415a0a2ed595 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -8288,6 +8288,23 @@ </declare-styleable> <!-- =============================== --> + <!-- System Speech Recognition attributes --> + <!-- =============================== --> + <eat-comment /> + + <!-- Use <code>on-device-recognition-service</code> as the root tag of the XML resource that + describes a {@link android.service.speech.RecognitionService}, which is referenced + from its {@link android.service.speech.RecognitionService#SERVICE_META_DATA} meta-data + entry. + @hide @SystemApi + --> + <declare-styleable name="OnDeviceRecognitionService"> + <!-- Fully qualified class name of an activity that allows the user to modify + the settings for this service. --> + <attr name="settingsActivity" /> + </declare-styleable> + + <!-- =============================== --> <!-- Content Capture attributes --> <!-- =============================== --> <eat-comment /> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 4e272f110a6e..a928408f68ed 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3800,6 +3800,12 @@ --> <string name="config_defaultSearchUiService" translatable="false"></string> + <!-- The package name for the system's speech recognition service. + This service must be trusted, as it can be activated without explicit consent of the user. + Example: "com.android.speech/.RecognitionService" + --> + <string name="config_defaultOnDeviceSpeechRecognitionService" translatable="false"></string> + <string name="config_defaultMusicRecognitionService" translatable="false"></string> <!-- The package name for the default retail demo app. diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index e92eb9722d96..bcef68072079 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3489,6 +3489,7 @@ <java-symbol type="string" name="notification_channel_do_not_disturb" /> <java-symbol type="string" name="notification_channel_accessibility_magnification" /> <java-symbol type="string" name="config_defaultAutofillService" /> + <java-symbol type="string" name="config_defaultOnDeviceSpeechRecognitionService" /> <java-symbol type="string" name="config_defaultTextClassifierPackage" /> <java-symbol type="string" name="config_defaultWellbeingPackage" /> <java-symbol type="string" name="config_defaultContentCaptureService" /> diff --git a/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java index b5720a262b3d..2de800bb6d00 100644 --- a/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java +++ b/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java @@ -19,122 +19,87 @@ package com.android.internal.os; import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assert.assertTrue; - -import android.content.Context; -import android.os.FileUtils; - -import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import org.junit.After; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import java.io.File; import java.io.IOException; -import java.io.OutputStream; -import java.nio.file.Files; -import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; @SmallTest @RunWith(AndroidJUnit4.class) public class KernelSingleProcessCpuThreadReaderTest { - private File mProcDirectory; - - @Before - public void setUp() { - Context context = InstrumentationRegistry.getContext(); - mProcDirectory = context.getDir("proc", Context.MODE_PRIVATE); - } - - @After - public void tearDown() throws Exception { - FileUtils.deleteContents(mProcDirectory); - } - @Test public void getProcessCpuUsage() throws IOException { - setupDirectory(42, - new int[] {42, 1, 2, 3}, - new int[] {1000, 2000}, - // Units are 10ms aka 10000Us - new int[][] {{100, 200}, {0, 200}, {100, 300}, {0, 600}}, - new int[] {4500, 500}); + // Units are nanoseconds + MockCpuTimeInStateReader mockReader = new MockCpuTimeInStateReader(4, new String[] { + "0:1000000000 2000000000 3000000000:4000000000", + "1:100000000 200000000 300000000:400000000", + }); KernelSingleProcessCpuThreadReader reader = new KernelSingleProcessCpuThreadReader(42, - mProcDirectory.toPath()); + mockReader); + reader.setSelectedThreadIds(new int[] {2, 3}); + reader.startTrackingThreadCpuTimes(); KernelSingleProcessCpuThreadReader.ProcessCpuUsage processCpuUsage = - reader.getProcessCpuUsage(new int[] {2, 3}); - assertThat(processCpuUsage.threadCpuTimesMillis).isEqualTo(new long[] {2000, 13000}); - assertThat(processCpuUsage.selectedThreadCpuTimesMillis).isEqualTo(new long[] {1000, 9000}); - assertThat(processCpuUsage.processCpuTimesMillis).isEqualTo(new long[] {6666, 43333}); + reader.getProcessCpuUsage(); + assertThat(mockReader.mTrackedTgid).isEqualTo(42); + // The strings are formatted as <TID TGID AGG_KEY>, where AGG_KEY is 1 for binder + // threads and 0 for all other threads. + assertThat(mockReader.mTrackedTasks).containsExactly( + "2 1", + "3 1"); + assertThat(processCpuUsage.threadCpuTimesMillis).isEqualTo( + new long[] {1100, 2200, 3300, 4400}); + assertThat(processCpuUsage.selectedThreadCpuTimesMillis).isEqualTo( + new long[] {100, 200, 300, 400}); } @Test public void getCpuFrequencyCount() throws IOException { - setupDirectory(13, - new int[] {13}, - new int[] {1000, 2000, 3000}, - new int[][] {{100, 200, 300}}, - new int[] {14, 15}); + MockCpuTimeInStateReader mockReader = new MockCpuTimeInStateReader(3, new String[0]); KernelSingleProcessCpuThreadReader reader = new KernelSingleProcessCpuThreadReader(13, - mProcDirectory.toPath()); + mockReader); int cpuFrequencyCount = reader.getCpuFrequencyCount(); assertThat(cpuFrequencyCount).isEqualTo(3); } - private void setupDirectory(int pid, int[] threadIds, int[] cpuFrequencies, - int[][] threadCpuTimes, int[] processCpuTimes) - throws IOException { - - assertTrue(mProcDirectory.toPath().resolve("self").toFile().mkdirs()); - - try (OutputStream timeInStateStream = - Files.newOutputStream( - mProcDirectory.toPath().resolve("self").resolve("time_in_state"))) { - for (int i = 0; i < cpuFrequencies.length; i++) { - final String line = cpuFrequencies[i] + " 0\n"; - timeInStateStream.write(line.getBytes()); - } + public static class MockCpuTimeInStateReader implements + KernelSingleProcessCpuThreadReader.CpuTimeInStateReader { + private final int mCpuFrequencyCount; + private final String[] mAggregatedTaskCpuFreqTimes; + public int mTrackedTgid; + public List<String> mTrackedTasks = new ArrayList<>(); + + public MockCpuTimeInStateReader(int cpuFrequencyCount, + String[] aggregatedTaskCpuFreqTimes) { + mCpuFrequencyCount = cpuFrequencyCount; + mAggregatedTaskCpuFreqTimes = aggregatedTaskCpuFreqTimes; } - Path processPath = mProcDirectory.toPath().resolve(String.valueOf(pid)); + @Override + public int getCpuFrequencyCount() { + return mCpuFrequencyCount; + } - // Make /proc/$PID - assertTrue(processPath.toFile().mkdirs()); + @Override + public boolean startTrackingProcessCpuTimes(int tgid) { + mTrackedTgid = tgid; + return true; + } - // Write /proc/$PID/stat. Only the fields 14-17 matter. - try (OutputStream timeInStateStream = Files.newOutputStream(processPath.resolve("stat"))) { - timeInStateStream.write( - (pid + " (test) S 4 5 6 7 8 9 10 11 12 13 " - + processCpuTimes[0] + " " - + processCpuTimes[1] + " " - + "16 17 18 19 20 ...").getBytes()); + public boolean startAggregatingTaskCpuTimes(int pid, int aggregationKey) { + mTrackedTasks.add(pid + " " + aggregationKey); + return true; } - // Make /proc/$PID/task - final Path selfThreadsPath = processPath.resolve("task"); - assertTrue(selfThreadsPath.toFile().mkdirs()); - - // Make thread directories - for (int i = 0; i < threadIds.length; i++) { - // Make /proc/$PID/task/$TID - final Path threadPath = selfThreadsPath.resolve(String.valueOf(threadIds[i])); - assertTrue(threadPath.toFile().mkdirs()); - - // Make /proc/$PID/task/$TID/time_in_state - try (OutputStream timeInStateStream = - Files.newOutputStream(threadPath.resolve("time_in_state"))) { - for (int j = 0; j < cpuFrequencies.length; j++) { - final String line = cpuFrequencies[j] + " " + threadCpuTimes[i][j] + "\n"; - timeInStateStream.write(line.getBytes()); - } - } + public String[] getAggregatedTaskCpuFreqTimes(int pid) { + return mAggregatedTaskCpuFreqTimes; } } } diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java index 121c637310c6..d116d4dcbe92 100644 --- a/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java +++ b/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java @@ -16,146 +16,86 @@ package com.android.internal.os; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertTrue; +import static com.google.common.truth.Truth.assertThat; -import android.content.Context; -import android.os.FileUtils; - -import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import org.junit.After; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import java.io.File; import java.io.IOException; -import java.io.OutputStream; -import java.nio.file.Files; -import java.nio.file.Path; @SmallTest @RunWith(AndroidJUnit4.class) public class SystemServerCpuThreadReaderTest { - private File mProcDirectory; - - @Before - public void setUp() { - Context context = InstrumentationRegistry.getContext(); - mProcDirectory = context.getDir("proc", Context.MODE_PRIVATE); - } - - @After - public void tearDown() throws Exception { - FileUtils.deleteContents(mProcDirectory); - } @Test - public void testReaderDelta_firstTime() throws IOException { + public void testReadDelta() throws IOException { int pid = 42; - setupDirectory( - pid, - new int[] {42, 1, 2, 3}, - new int[] {1000, 2000}, - // Units are 10ms aka 10000Us - new int[][] {{100, 200}, {0, 200}, {0, 300}, {0, 400}}, - new int[] {1400, 1500}); - - SystemServerCpuThreadReader reader = new SystemServerCpuThreadReader( - mProcDirectory.toPath(), pid); + + MockCpuTimeInStateReader mockReader = new MockCpuTimeInStateReader(4); + // Units are nanoseconds + mockReader.setAggregatedTaskCpuFreqTimes(new String[] { + "0:1000000000 2000000000 3000000000:4000000000", + "1:100000000 200000000 300000000:400000000", + }); + + SystemServerCpuThreadReader reader = new SystemServerCpuThreadReader(pid, mockReader); reader.setBinderThreadNativeTids(new int[] {1, 3}); + + // The first invocation of readDelta populates the "last" snapshot SystemServerCpuThreadReader.SystemServiceCpuThreadTimes systemServiceCpuThreadTimes = reader.readDelta(); - assertArrayEquals(new long[] {100 * 10000, 1100 * 10000}, - systemServiceCpuThreadTimes.threadCpuTimesUs); - assertArrayEquals(new long[] {0, 600 * 10000}, - systemServiceCpuThreadTimes.binderThreadCpuTimesUs); - } - - @Test - public void testReaderDelta_nextTime() throws IOException { - int pid = 42; - setupDirectory( - pid, - new int[] {42, 1, 2, 3}, - new int[] {1000, 2000}, - new int[][] {{100, 200}, {0, 200}, {0, 300}, {0, 400}}, - new int[] {1400, 1500}); - - SystemServerCpuThreadReader reader = new SystemServerCpuThreadReader( - mProcDirectory.toPath(), pid); - reader.setBinderThreadNativeTids(new int[] {1, 3}); - // First time, populate "last" snapshot - reader.readDelta(); + assertThat(systemServiceCpuThreadTimes.threadCpuTimesUs) + .isEqualTo(new long[] {1100000, 2200000, 3300000, 4400000}); + assertThat(systemServiceCpuThreadTimes.binderThreadCpuTimesUs) + .isEqualTo(new long[] {100000, 200000, 300000, 400000}); - FileUtils.deleteContents(mProcDirectory); - setupDirectory( - pid, - new int[] {42, 1, 2, 3}, - new int[] {1000, 2000}, - new int[][] {{500, 600}, {700, 800}, {900, 1000}, {1100, 1200}}, - new int[] {2400, 2500}); + mockReader.setAggregatedTaskCpuFreqTimes(new String[] { + "0:1010000000 2020000000 3030000000:4040000000", + "1:101000000 202000000 303000000:404000000", + }); - // Second time, get the actual delta - SystemServerCpuThreadReader.SystemServiceCpuThreadTimes systemServiceCpuThreadTimes = - reader.readDelta(); + // The second invocation gets the actual delta + systemServiceCpuThreadTimes = reader.readDelta(); - assertArrayEquals(new long[] {3100 * 10000, 2500 * 10000}, - systemServiceCpuThreadTimes.threadCpuTimesUs); - assertArrayEquals(new long[] {1800 * 10000, 1400 * 10000}, - systemServiceCpuThreadTimes.binderThreadCpuTimesUs); + assertThat(systemServiceCpuThreadTimes.threadCpuTimesUs) + .isEqualTo(new long[] {11000, 22000, 33000, 44000}); + assertThat(systemServiceCpuThreadTimes.binderThreadCpuTimesUs) + .isEqualTo(new long[] {1000, 2000, 3000, 4000}); } - private void setupDirectory(int pid, int[] threadIds, int[] cpuFrequencies, int[][] cpuTimes, - int[] processCpuTimes) - throws IOException { + public static class MockCpuTimeInStateReader implements + KernelSingleProcessCpuThreadReader.CpuTimeInStateReader { + private final int mCpuFrequencyCount; + private String[] mAggregatedTaskCpuFreqTimes; - assertTrue(mProcDirectory.toPath().resolve("self").toFile().mkdirs()); + MockCpuTimeInStateReader(int frequencyCount) { + mCpuFrequencyCount = frequencyCount; + } + + @Override + public int getCpuFrequencyCount() { + return mCpuFrequencyCount; + } + + @Override + public boolean startTrackingProcessCpuTimes(int tgid) { + return true; + } - try (OutputStream timeInStateStream = - Files.newOutputStream( - mProcDirectory.toPath().resolve("self").resolve("time_in_state"))) { - for (int i = 0; i < cpuFrequencies.length; i++) { - final String line = cpuFrequencies[i] + " 0\n"; - timeInStateStream.write(line.getBytes()); - } + public boolean startAggregatingTaskCpuTimes(int pid, int aggregationKey) { + return true; } - Path processPath = mProcDirectory.toPath().resolve(String.valueOf(pid)); - // Make /proc/$PID - assertTrue(processPath.toFile().mkdirs()); - - // Write /proc/$PID/stat. Only the fields 14-17 matter. - try (OutputStream timeInStateStream = Files.newOutputStream(processPath.resolve("stat"))) { - timeInStateStream.write( - (pid + " (test) S 4 5 6 7 8 9 10 11 12 13 " - + processCpuTimes[0] + " " - + processCpuTimes[1] + " " - + "16 17 18 19 20 ...").getBytes()); + public void setAggregatedTaskCpuFreqTimes(String[] mAggregatedTaskCpuFreqTimes) { + this.mAggregatedTaskCpuFreqTimes = mAggregatedTaskCpuFreqTimes; } - // Make /proc/$PID/task - final Path selfThreadsPath = processPath.resolve("task"); - assertTrue(selfThreadsPath.toFile().mkdirs()); - - // Make thread directories - for (int i = 0; i < threadIds.length; i++) { - // Make /proc/$PID/task/$TID - final Path threadPath = selfThreadsPath.resolve(String.valueOf(threadIds[i])); - assertTrue(threadPath.toFile().mkdirs()); - - // Make /proc/$PID/task/$TID/time_in_state - try (OutputStream timeInStateStream = - Files.newOutputStream(threadPath.resolve("time_in_state"))) { - for (int j = 0; j < cpuFrequencies.length; j++) { - final String line = cpuFrequencies[j] + " " + cpuTimes[i][j] + "\n"; - timeInStateStream.write(line.getBytes()); - } - } + public String[] getAggregatedTaskCpuFreqTimes(int pid) { + return mAggregatedTaskCpuFreqTimes; } } } diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java index a5cafb974b0e..24741fe110ce 100644 --- a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java @@ -62,7 +62,6 @@ public class SystemServicePowerCalculatorTest { public void testCalculateApp() { // Test Power Profile has two CPU clusters with 3 and 4 speeds, thus 7 freq times total mMockSystemServerCpuThreadReader.setCpuTimes( - new long[] {10000, 15000, 20000, 25000, 30000, 35000, 40000}, new long[] {30000, 40000, 50000, 60000, 70000, 80000, 90000}, new long[] {20000, 30000, 40000, 50000, 60000, 70000, 80000}); @@ -144,9 +143,7 @@ public class SystemServicePowerCalculatorTest { super(null); } - public void setCpuTimes(long[] processCpuTimesUs, long[] threadCpuTimesUs, - long[] binderThreadCpuTimesUs) { - mThreadTimes.processCpuTimesUs = processCpuTimesUs; + public void setCpuTimes(long[] threadCpuTimesUs, long[] binderThreadCpuTimesUs) { mThreadTimes.threadCpuTimesUs = threadCpuTimesUs; mThreadTimes.binderThreadCpuTimesUs = binderThreadCpuTimesUs; } diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java index ebf7cea98a81..dd64327ebfd8 100644 --- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java @@ -570,6 +570,13 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 { } } + @Override + protected void onBoundsChange(Rect bounds) { + if (mState.mNativePtr != 0) { + nSetBounds(mState.mNativePtr, bounds); + } + } + private static native long nCreate(long nativeImageDecoder, @Nullable ImageDecoder decoder, int width, int height, long colorSpaceHandle, @@ -601,4 +608,6 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 { private static native long nNativeByteSize(long nativePtr); @FastNative private static native void nSetMirrored(long nativePtr, boolean mirror); + @FastNative + private static native void nSetBounds(long nativePtr, Rect rect); } diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java index 5928540b19bf..014d6882be8d 100644 --- a/keystore/java/android/security/keystore/KeyProperties.java +++ b/keystore/java/android/security/keystore/KeyProperties.java @@ -67,6 +67,7 @@ public abstract class KeyProperties { PURPOSE_SIGN, PURPOSE_VERIFY, PURPOSE_WRAP_KEY, + PURPOSE_AGREE_KEY, }) public @interface PurposeEnum {} @@ -96,6 +97,11 @@ public abstract class KeyProperties { public static final int PURPOSE_WRAP_KEY = 1 << 5; /** + * Purpose of key: creating a shared ECDH secret through key agreement. + */ + public static final int PURPOSE_AGREE_KEY = 1 << 6; + + /** * @hide */ public static abstract class Purpose { @@ -113,6 +119,8 @@ public abstract class KeyProperties { return KeymasterDefs.KM_PURPOSE_VERIFY; case PURPOSE_WRAP_KEY: return KeymasterDefs.KM_PURPOSE_WRAP; + case PURPOSE_AGREE_KEY: + return KeymasterDefs.KM_PURPOSE_AGREE_KEY; default: throw new IllegalArgumentException("Unknown purpose: " + purpose); } @@ -130,6 +138,8 @@ public abstract class KeyProperties { return PURPOSE_VERIFY; case KeymasterDefs.KM_PURPOSE_WRAP: return PURPOSE_WRAP_KEY; + case KeymasterDefs.KM_PURPOSE_AGREE_KEY: + return PURPOSE_AGREE_KEY; default: throw new IllegalArgumentException("Unknown purpose: " + purpose); } diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java new file mode 100644 index 000000000000..fc963a88c4d1 --- /dev/null +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.keystore2; + +import android.hardware.security.keymint.Algorithm; +import android.hardware.security.keymint.KeyParameter; +import android.hardware.security.keymint.KeyPurpose; +import android.hardware.security.keymint.Tag; +import android.security.KeyStoreException; +import android.security.KeyStoreOperation; +import android.security.keystore.KeyStoreCryptoOperation; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.ProviderException; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import java.util.ArrayList; +import java.util.List; + +import javax.crypto.KeyAgreementSpi; +import javax.crypto.SecretKey; +import javax.crypto.ShortBufferException; +import javax.crypto.spec.SecretKeySpec; + +/** + * {@link KeyAgreementSpi} which provides an ECDH implementation backed by Android KeyStore. + * + * @hide + */ +public class AndroidKeyStoreKeyAgreementSpi extends KeyAgreementSpi + implements KeyStoreCryptoOperation { + + private static final String TAG = "AndroidKeyStoreKeyAgreementSpi"; + + /** + * ECDH implementation. + * + * @hide + */ + public static class ECDH extends AndroidKeyStoreKeyAgreementSpi { + public ECDH() { + super(Algorithm.EC); + } + } + + private final int mKeymintAlgorithm; + + // Fields below are populated by engineInit and should be preserved after engineDoFinal. + private AndroidKeyStorePrivateKey mKey; + private PublicKey mOtherPartyKey; + + // Fields below are reset when engineDoFinal succeeds. + private KeyStoreOperation mOperation; + private long mOperationHandle; + + protected AndroidKeyStoreKeyAgreementSpi(int keymintAlgorithm) { + resetAll(); + + mKeymintAlgorithm = keymintAlgorithm; + } + + @Override + protected void engineInit(Key key, SecureRandom random) throws InvalidKeyException { + resetAll(); + + if (key == null) { + throw new InvalidKeyException("key == null"); + } else if (!(key instanceof AndroidKeyStorePrivateKey)) { + throw new InvalidKeyException( + "Only Android KeyStore private keys supported. Key: " + key); + } + // Checking the correct KEY_PURPOSE and algorithm is done by the Keymint implementation in + // ensureKeystoreOperationInitialized() below. + mKey = (AndroidKeyStorePrivateKey) key; + + boolean success = false; + try { + ensureKeystoreOperationInitialized(); + success = true; + } finally { + if (!success) { + resetAll(); + } + } + } + + @Override + protected void engineInit(Key key, AlgorithmParameterSpec params, SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException { + if (params != null) { + throw new InvalidAlgorithmParameterException( + "Unsupported algorithm parameters: " + params); + } + engineInit(key, random); + } + + @Override + protected Key engineDoPhase(Key key, boolean lastPhase) + throws InvalidKeyException, IllegalStateException { + ensureKeystoreOperationInitialized(); + + if (key == null) { + throw new InvalidKeyException("key == null"); + } else if (!(key instanceof PublicKey)) { + throw new InvalidKeyException("Only public keys supported. Key: " + key); + } else if (!lastPhase) { + throw new IllegalStateException( + "Only one other party supported. lastPhase must be set to true."); + } else if (mOtherPartyKey != null) { + throw new IllegalStateException( + "Only one other party supported. doPhase() must only be called exactly once."); + } + // The other party key will be passed as part of the doFinal() call, to prevent an + // additional IPC. + mOtherPartyKey = (PublicKey) key; + + return null; // No intermediate key + } + + @Override + protected byte[] engineGenerateSecret() throws IllegalStateException { + try { + ensureKeystoreOperationInitialized(); + } catch (InvalidKeyException e) { + throw new IllegalStateException("Not initialized", e); + } + + if (mOtherPartyKey == null) { + throw new IllegalStateException("Other party key not provided. Call doPhase() first."); + } + byte[] otherPartyKeyEncoded = mOtherPartyKey.getEncoded(); + + try { + return mOperation.finish(otherPartyKeyEncoded, null); + } catch (KeyStoreException e) { + throw new ProviderException("Keystore operation failed", e); + } finally { + resetWhilePreservingInitState(); + } + } + + @Override + protected SecretKey engineGenerateSecret(String algorithm) + throws IllegalStateException, NoSuchAlgorithmException, InvalidKeyException { + byte[] generatedSecret = engineGenerateSecret(); + + return new SecretKeySpec(generatedSecret, algorithm); + } + + @Override + protected int engineGenerateSecret(byte[] sharedSecret, int offset) + throws IllegalStateException, ShortBufferException { + byte[] generatedSecret = engineGenerateSecret(); + + if (generatedSecret.length > sharedSecret.length - offset) { + throw new ShortBufferException("Needed: " + generatedSecret.length); + } + System.arraycopy(generatedSecret, 0, sharedSecret, offset, generatedSecret.length); + return generatedSecret.length; + } + + @Override + public long getOperationHandle() { + return mOperationHandle; + } + + @Override + protected void finalize() throws Throwable { + try { + resetAll(); + } finally { + super.finalize(); + } + } + + private void resetWhilePreservingInitState() { + KeyStoreCryptoOperationUtils.abortOperation(mOperation); + mOperationHandle = 0; + mOperation = null; + mOtherPartyKey = null; + } + + private void resetAll() { + resetWhilePreservingInitState(); + mKey = null; + } + + private void ensureKeystoreOperationInitialized() + throws InvalidKeyException, IllegalStateException { + if (mKey == null) { + throw new IllegalStateException("Not initialized"); + } + if (mOperation != null) { + return; + } + + // We don't need to explicitly pass in any other parameters here, as they're part of the + // private key that is available to Keymint. + List<KeyParameter> parameters = new ArrayList<>(); + parameters.add(KeyStore2ParameterUtils.makeEnum( + Tag.PURPOSE, KeyPurpose.AGREE_KEY + )); + + try { + mOperation = + mKey.getSecurityLevel().createOperation(mKey.getKeyIdDescriptor(), parameters); + } catch (KeyStoreException keyStoreException) { + // If necessary, throw an exception due to KeyStore operation having failed. + InvalidKeyException e = + KeyStoreCryptoOperationUtils.getInvalidKeyException(mKey, keyStoreException); + if (e != null) { + throw e; + } + } + + // Set the operation handle. This will be a random number, or the operation challenge if + // user authentication is required. If we got a challenge we check if the authorization can + // possibly succeed. + mOperationHandle = + KeyStoreCryptoOperationUtils.getOrMakeOperationChallenge(mOperation, mKey); + } +} diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java index 403da189262d..164bc8669525 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java @@ -94,6 +94,9 @@ public class AndroidKeyStoreProvider extends Provider { put("KeyGenerator.DESede", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$DESede"); } + // javax.crypto.KeyAgreement + put("KeyAgreement.ECDH", PACKAGE_NAME + ".AndroidKeyStoreKeyAgreementSpi$ECDH"); + // java.security.SecretKeyFactory putSecretKeyFactoryImpl("AES"); if (supports3DES) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java index 9e1ea53c8f01..a2cd683f2fdd 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java @@ -47,7 +47,6 @@ import com.android.internal.inputmethod.ResultCallbacks; import com.android.internal.view.IInputMethodManager; import java.util.ArrayList; -import java.util.Objects; import java.util.concurrent.Executor; /** @@ -209,23 +208,21 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged } protected void insetsChanged(InsetsState insetsState) { - mMainExecutor.execute(() -> { - if (mInsetsState.equals(insetsState)) { - return; - } + if (mInsetsState.equals(insetsState)) { + return; + } - mImeShowing = insetsState.getSourceOrDefaultVisibility(InsetsState.ITYPE_IME); + mImeShowing = insetsState.getSourceOrDefaultVisibility(InsetsState.ITYPE_IME); - final InsetsSource newSource = insetsState.getSource(InsetsState.ITYPE_IME); - final Rect newFrame = newSource.getFrame(); - final Rect oldFrame = mInsetsState.getSource(InsetsState.ITYPE_IME).getFrame(); + final InsetsSource newSource = insetsState.getSource(InsetsState.ITYPE_IME); + final Rect newFrame = newSource.getFrame(); + final Rect oldFrame = mInsetsState.getSource(InsetsState.ITYPE_IME).getFrame(); - mInsetsState.set(insetsState, true /* copySources */); - if (mImeShowing && !newFrame.equals(oldFrame) && newSource.isVisible()) { - if (DEBUG) Slog.d(TAG, "insetsChanged when IME showing, restart animation"); - startAnimation(mImeShowing, true /* forceRestart */); - } - }); + mInsetsState.set(insetsState, true /* copySources */); + if (mImeShowing && !newFrame.equals(oldFrame) && newSource.isVisible()) { + if (DEBUG) Slog.d(TAG, "insetsChanged when IME showing, restart animation"); + startAnimation(mImeShowing, true /* forceRestart */); + } } @VisibleForTesting @@ -238,27 +235,25 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged continue; } if (activeControl.getType() == InsetsState.ITYPE_IME) { - mMainExecutor.execute(() -> { - final Point lastSurfacePosition = mImeSourceControl != null - ? mImeSourceControl.getSurfacePosition() : null; - final boolean positionChanged = - !activeControl.getSurfacePosition().equals(lastSurfacePosition); - final boolean leashChanged = - !haveSameLeash(mImeSourceControl, activeControl); - mImeSourceControl = activeControl; - if (mAnimation != null) { - if (positionChanged) { - startAnimation(mImeShowing, true /* forceRestart */); - } - } else { - if (leashChanged) { - applyVisibilityToLeash(); - } - if (!mImeShowing) { - removeImeSurface(); - } + final Point lastSurfacePosition = mImeSourceControl != null + ? mImeSourceControl.getSurfacePosition() : null; + final boolean positionChanged = + !activeControl.getSurfacePosition().equals(lastSurfacePosition); + final boolean leashChanged = + !haveSameLeash(mImeSourceControl, activeControl); + mImeSourceControl = activeControl; + if (mAnimation != null) { + if (positionChanged) { + startAnimation(mImeShowing, true /* forceRestart */); + } + } else { + if (leashChanged) { + applyVisibilityToLeash(); + } + if (!mImeShowing) { + removeImeSurface(); } - }); + } } } } @@ -283,7 +278,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged return; } if (DEBUG) Slog.d(TAG, "Got showInsets for ime"); - mMainExecutor.execute(() -> startAnimation(true /* show */, false /* forceRestart */)); + startAnimation(true /* show */, false /* forceRestart */); } @@ -292,7 +287,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged return; } if (DEBUG) Slog.d(TAG, "Got hideInsets for ime"); - mMainExecutor.execute(() -> startAnimation(false /* show */, false /* forceRestart */)); + startAnimation(false /* show */, false /* forceRestart */); } public void topFocusedWindowChanged(String packageName) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java index f8397277eb0a..a8961ea3d8a8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java @@ -30,6 +30,8 @@ import android.util.TypedValue; import android.view.DisplayInfo; import android.view.Gravity; +import com.android.wm.shell.common.DisplayLayout; + import java.io.PrintWriter; /** @@ -190,9 +192,9 @@ public class PipBoundsAlgorithm { size = adjustSizeToAspectRatio(overrideMinSize, aspectRatio); } else { // Calculate the default size using the display size and default min edge size. - final DisplayInfo displayInfo = mPipBoundsState.getDisplayInfo(); + final DisplayLayout displayLayout = mPipBoundsState.getDisplayLayout(); size = getSizeForAspectRatio(aspectRatio, mDefaultMinSize, - displayInfo.logicalWidth, displayInfo.logicalHeight); + displayLayout.width(), displayLayout.height()); } } @@ -232,7 +234,7 @@ public class PipBoundsAlgorithm { final Size defaultSize; final Rect insetBounds = new Rect(); getInsetBounds(insetBounds); - final DisplayInfo displayInfo = mPipBoundsState.getDisplayInfo(); + final DisplayLayout displayLayout = mPipBoundsState.getDisplayLayout(); final Size overrideMinSize = mPipBoundsState.getOverrideMinSize(); if (overrideMinSize != null) { // The override minimal size is set, use that as the default size making sure it's @@ -241,7 +243,7 @@ public class PipBoundsAlgorithm { } else { // Calculate the default size using the display size and default min edge size. defaultSize = getSizeForAspectRatio(mDefaultAspectRatio, - mDefaultMinSize, displayInfo.logicalWidth, displayInfo.logicalHeight); + mDefaultMinSize, displayLayout.width(), displayLayout.height()); } // Now that we have the default size, apply the snap fraction if valid or position the @@ -264,12 +266,12 @@ public class PipBoundsAlgorithm { * Populates the bounds on the screen that the PIP can be visible in. */ public void getInsetBounds(Rect outRect) { - final DisplayInfo displayInfo = mPipBoundsState.getDisplayInfo(); + final DisplayLayout displayLayout = mPipBoundsState.getDisplayLayout(); Rect insets = mPipBoundsState.getDisplayLayout().stableInsets(); outRect.set(insets.left + mScreenEdgeInsets.x, insets.top + mScreenEdgeInsets.y, - displayInfo.logicalWidth - insets.right - mScreenEdgeInsets.x, - displayInfo.logicalHeight - insets.bottom - mScreenEdgeInsets.y); + displayLayout.width() - insets.right - mScreenEdgeInsets.x, + displayLayout.height() - insets.bottom - mScreenEdgeInsets.y); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java index 9595b5a3b0a9..b112c51455d2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java @@ -24,6 +24,7 @@ import android.content.Context; import android.graphics.Point; import android.graphics.Rect; import android.util.Size; +import android.view.Display; import android.view.DisplayInfo; import com.android.internal.annotations.VisibleForTesting; @@ -68,7 +69,7 @@ public final class PipBoundsState { private int mStashOffset; private @Nullable PipReentryState mPipReentryState; private @Nullable ComponentName mLastPipComponentName; - private final @NonNull DisplayInfo mDisplayInfo = new DisplayInfo(); + private int mDisplayId = Display.DEFAULT_DISPLAY; private final @NonNull DisplayLayout mDisplayLayout = new DisplayLayout(); /** The current minimum edge size of PIP. */ private int mMinEdgeSize; @@ -238,26 +239,20 @@ public final class PipBoundsState { return mLastPipComponentName; } - /** Get the current display info. */ - @NonNull - public DisplayInfo getDisplayInfo() { - return mDisplayInfo; - } - - /** Update the display info. */ - public void setDisplayInfo(@NonNull DisplayInfo displayInfo) { - mDisplayInfo.copyFrom(displayInfo); + /** Get the current display id. */ + public int getDisplayId() { + return mDisplayId; } - /** Set the rotation of the display. */ - public void setDisplayRotation(int rotation) { - mDisplayInfo.rotation = rotation; + /** Set the current display id for the associated display layout. */ + public void setDisplayId(int displayId) { + mDisplayId = displayId; } /** Returns the display's bounds. */ @NonNull public Rect getDisplayBounds() { - return new Rect(0, 0, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight); + return new Rect(0, 0, mDisplayLayout.width(), mDisplayLayout.height()); } /** Update the display layout. */ @@ -474,7 +469,7 @@ public final class PipBoundsState { pw.println(innerPrefix + "mExpandedMovementBounds=" + mExpandedMovementBounds); pw.println(innerPrefix + "mLastPipComponentName=" + mLastPipComponentName); pw.println(innerPrefix + "mAspectRatio=" + mAspectRatio); - pw.println(innerPrefix + "mDisplayInfo=" + mDisplayInfo); + pw.println(innerPrefix + "mDisplayId=" + mDisplayId); pw.println(innerPrefix + "mDisplayLayout=" + mDisplayLayout); pw.println(innerPrefix + "mStashedState=" + mStashedState); pw.println(innerPrefix + "mStashOffset=" + mStashOffset); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index b80f285c3eb4..b7958b7a7077 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -55,6 +55,7 @@ import android.os.RemoteException; import android.util.Log; import android.util.Rational; import android.util.Size; +import android.view.Display; import android.view.SurfaceControl; import android.window.TaskOrganizer; import android.window.WindowContainerToken; @@ -324,7 +325,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mPipUiEventLoggerLogger.log( PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN); final boolean orientationDiffers = initialConfig.windowConfiguration.getRotation() - != mPipBoundsState.getDisplayInfo().rotation; + != mPipBoundsState.getDisplayLayout().rotation(); final WindowContainerTransaction wct = new WindowContainerTransaction(); final Rect destinationBounds = initialConfig.windowConfiguration.getBounds(); final int direction = syncWithSplitScreenBounds(destinationBounds) @@ -437,7 +438,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, // If the displayId of the task is different than what PipBoundsHandler has, then update // it. This is possible if we entered PiP on an external display. - if (info.displayId != mPipBoundsState.getDisplayInfo().displayId + if (info.displayId != mPipBoundsState.getDisplayId() && mOnDisplayIdChangeCallback != null) { mOnDisplayIdChangeCallback.accept(info.displayId); } @@ -605,6 +606,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mState = State.UNDEFINED; mPipUiEventLoggerLogger.setTaskInfo(null); mPipMenuController.detach(); + + if (info.displayId != Display.DEFAULT_DISPLAY && mOnDisplayIdChangeCallback != null) { + mOnDisplayIdChangeCallback.accept(Display.DEFAULT_DISPLAY); + } } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index cefeb93998f9..c06f9d03cdf7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -19,8 +19,6 @@ package com.android.wm.shell.pip.phone; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE; -import static android.view.Surface.ROTATION_0; -import static android.view.Surface.ROTATION_180; import static android.view.WindowManager.INPUT_CONSUMER_PIP; import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection; @@ -86,7 +84,6 @@ public class PipController implements PipTaskOrganizer.PipTransitionCallback { private PipTouchHandler mTouchHandler; protected final PipImpl mImpl = new PipImpl(); - private final DisplayInfo mTmpDisplayInfo = new DisplayInfo(); private final Rect mTmpInsetBounds = new Rect(); private boolean mIsInFixedRotation; @@ -145,7 +142,7 @@ public class PipController implements PipTaskOrganizer.PipTransitionCallback { } }; - private final DisplayController.OnDisplaysChangedListener mFixedRotationListener = + private final DisplayController.OnDisplaysChangedListener mDisplaysChangedListener = new DisplayController.OnDisplaysChangedListener() { @Override public void onFixedRotationStarted(int displayId, int newRotation) { @@ -159,8 +156,20 @@ public class PipController implements PipTaskOrganizer.PipTransitionCallback { @Override public void onDisplayAdded(int displayId) { - mPipBoundsState.setDisplayLayout( - mDisplayController.getDisplayLayout(displayId)); + if (displayId != mPipBoundsState.getDisplayId()) { + return; + } + onDisplayChanged(mDisplayController.getDisplayLayout(displayId), + false /* saveRestoreSnapFraction */); + } + + @Override + public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) { + if (displayId != mPipBoundsState.getDisplayId()) { + return; + } + onDisplayChanged(mDisplayController.getDisplayLayout(displayId), + true /* saveRestoreSnapFraction */); } }; @@ -261,12 +270,9 @@ public class PipController implements PipTaskOrganizer.PipTransitionCallback { INPUT_CONSUMER_PIP, mainExecutor); mPipTaskOrganizer.registerPipTransitionCallback(this); mPipTaskOrganizer.registerOnDisplayIdChangeCallback((int displayId) -> { - final DisplayInfo newDisplayInfo = new DisplayInfo(); - displayController.getDisplay(displayId).getDisplayInfo(newDisplayInfo); - mPipBoundsState.setDisplayInfo(newDisplayInfo); - updateMovementBounds(null /* toBounds */, false /* fromRotation */, - false /* fromImeAdjustment */, false /* fromShelfAdjustment */, - null /* wct */); + mPipBoundsState.setDisplayId(displayId); + onDisplayChanged(displayController.getDisplayLayout(displayId), + false /* saveRestoreSnapFraction */); }); mPipBoundsState.setOnMinimalSizeChangeCallback( () -> { @@ -291,13 +297,12 @@ public class PipController implements PipTaskOrganizer.PipTransitionCallback { mPipInputConsumer.setRegistrationListener(mTouchHandler::onRegistrationChanged); } displayController.addDisplayChangingController(mRotationController); - displayController.addDisplayWindowListener(mFixedRotationListener); + displayController.addDisplayWindowListener(mDisplaysChangedListener); // Ensure that we have the display info in case we get calls to update the bounds before the // listener calls back - final DisplayInfo displayInfo = new DisplayInfo(); - context.getDisplay().getDisplayInfo(displayInfo); - mPipBoundsState.setDisplayInfo(displayInfo); + mPipBoundsState.setDisplayId(context.getDisplayId()); + mPipBoundsState.setDisplayLayout(new DisplayLayout(context, context.getDisplay())); try { mWindowManagerShellWrapper.addPinnedStackListener(mPinnedStackListener); @@ -363,11 +368,42 @@ public class PipController implements PipTaskOrganizer.PipTransitionCallback { } private void onOverlayChanged() { - mPipBoundsState.setDisplayLayout(new DisplayLayout(mContext, mContext.getDisplay())); - updateMovementBounds(null /* toBounds */, - false /* fromRotation */, false /* fromImeAdjustment */, - false /* fromShelfAdjustment */, - null /* windowContainerTransaction */); + onDisplayChanged(new DisplayLayout(mContext, mContext.getDisplay()), + false /* saveRestoreSnapFraction */); + } + + private void onDisplayChanged(DisplayLayout layout, boolean saveRestoreSnapFraction) { + Runnable updateDisplayLayout = () -> { + mPipBoundsState.setDisplayLayout(layout); + updateMovementBounds(null /* toBounds */, + false /* fromRotation */, false /* fromImeAdjustment */, + false /* fromShelfAdjustment */, + null /* windowContainerTransaction */); + }; + + if (saveRestoreSnapFraction) { + // Calculate the snap fraction of the current stack along the old movement bounds + final PipSnapAlgorithm pipSnapAlgorithm = mPipBoundsAlgorithm.getSnapAlgorithm(); + final Rect postChangeStackBounds = new Rect(mPipBoundsState.getBounds()); + final float snapFraction = pipSnapAlgorithm.getSnapFraction(postChangeStackBounds, + mPipBoundsAlgorithm.getMovementBounds(postChangeStackBounds), + mPipBoundsState.getStashedState()); + + updateDisplayLayout.run(); + + // Calculate the stack bounds in the new orientation based on same fraction along the + // rotated movement bounds. + final Rect postChangeMovementBounds = mPipBoundsAlgorithm.getMovementBounds( + postChangeStackBounds, false /* adjustForIme */); + pipSnapAlgorithm.applySnapFraction(postChangeStackBounds, postChangeMovementBounds, + snapFraction, mPipBoundsState.getStashedState(), + mPipBoundsState.getStashOffset(), + mPipBoundsState.getDisplayBounds()); + + mTouchHandler.getMotionHelper().movePip(postChangeStackBounds); + } else { + updateDisplayLayout.run(); + } } private void registerSessionListenerForCurrentUser() { @@ -500,7 +536,7 @@ public class PipController implements PipTaskOrganizer.PipTransitionCallback { // Populate inset / normal bounds and DisplayInfo from mPipBoundsHandler before // passing to mTouchHandler/mPipTaskOrganizer final Rect outBounds = new Rect(toBounds); - mTmpDisplayInfo.copyFrom(mPipBoundsState.getDisplayInfo()); + final int rotation = mPipBoundsState.getDisplayLayout().rotation(); mPipBoundsAlgorithm.getInsetBounds(mTmpInsetBounds); mPipBoundsState.setNormalBounds(mPipBoundsAlgorithm.getNormalBounds()); @@ -512,8 +548,7 @@ public class PipController implements PipTaskOrganizer.PipTransitionCallback { mPipTaskOrganizer.onMovementBoundsChanged(outBounds, fromRotation, fromImeAdjustment, fromShelfAdjustment, wct); mTouchHandler.onMovementBoundsChanged(mTmpInsetBounds, mPipBoundsState.getNormalBounds(), - outBounds, fromImeAdjustment, fromShelfAdjustment, - mTmpDisplayInfo.rotation); + outBounds, fromImeAdjustment, fromShelfAdjustment, rotation); } /** @@ -525,13 +560,6 @@ public class PipController implements PipTaskOrganizer.PipTransitionCallback { // Update the display layout, note that we have to do this on every rotation even if we // aren't in PIP since we need to update the display layout to get the right resources mPipBoundsState.getDisplayLayout().rotateTo(context.getResources(), toRotation); - - // Populate the new {@link #mDisplayInfo}. - // The {@link DisplayInfo} queried from DisplayManager would be the one before rotation, - // therefore, the width/height may require a swap first. - // Moving forward, we should get the new dimensions after rotation from DisplayLayout. - mPipBoundsState.setDisplayRotation(toRotation); - updateDisplayInfoIfNeeded(); } /** @@ -543,9 +571,8 @@ public class PipController implements PipTaskOrganizer.PipTransitionCallback { private boolean onDisplayRotationChanged(Context context, Rect outBounds, Rect oldBounds, Rect outInsetBounds, int displayId, int fromRotation, int toRotation, WindowContainerTransaction t) { - // Bail early if the event is not sent to current {@link #mDisplayInfo} - if ((displayId != mPipBoundsState.getDisplayInfo().displayId) - || (fromRotation == toRotation)) { + // Bail early if the event is not sent to current display + if ((displayId != mPipBoundsState.getDisplayId()) || (fromRotation == toRotation)) { return false; } @@ -570,13 +597,6 @@ public class PipController implements PipTaskOrganizer.PipTransitionCallback { // Update the display layout mPipBoundsState.getDisplayLayout().rotateTo(context.getResources(), toRotation); - // Populate the new {@link #mDisplayInfo}. - // The {@link DisplayInfo} queried from DisplayManager would be the one before rotation, - // therefore, the width/height may require a swap first. - // Moving forward, we should get the new dimensions after rotation from DisplayLayout. - mPipBoundsState.getDisplayInfo().rotation = toRotation; - updateDisplayInfoIfNeeded(); - // Calculate the stack bounds in the new orientation based on same fraction along the // rotated movement bounds. final Rect postChangeMovementBounds = mPipBoundsAlgorithm.getMovementBounds( @@ -591,21 +611,6 @@ public class PipController implements PipTaskOrganizer.PipTransitionCallback { return true; } - private void updateDisplayInfoIfNeeded() { - final DisplayInfo displayInfo = mPipBoundsState.getDisplayInfo(); - final boolean updateNeeded; - if ((displayInfo.rotation == ROTATION_0) || (displayInfo.rotation == ROTATION_180)) { - updateNeeded = (displayInfo.logicalWidth > displayInfo.logicalHeight); - } else { - updateNeeded = (displayInfo.logicalWidth < displayInfo.logicalHeight); - } - if (updateNeeded) { - final int newLogicalHeight = displayInfo.logicalWidth; - displayInfo.logicalWidth = displayInfo.logicalHeight; - displayInfo.logicalHeight = newLogicalHeight; - } - } - private void dump(PrintWriter pw) { final String innerPrefix = " "; pw.println(TAG); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java index 61cf22b7164b..75fc9f5a4ecf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java @@ -36,6 +36,7 @@ import android.view.DisplayInfo; import com.android.wm.shell.R; import com.android.wm.shell.WindowManagerShellWrapper; +import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TaskStackListenerCallback; import com.android.wm.shell.common.TaskStackListenerImpl; @@ -138,7 +139,8 @@ public class TvPipController implements PipTaskOrganizer.PipTransitionCallback, mMainExecutor = mainExecutor; mPipBoundsState = pipBoundsState; - mPipBoundsState.setDisplayInfo(getDisplayInfo()); + mPipBoundsState.setDisplayId(context.getDisplayId()); + mPipBoundsState.setDisplayLayout(new DisplayLayout(context, context.getDisplay())); mPipBoundsAlgorithm = pipBoundsAlgorithm; mPipMediaController = pipMediaController; diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt index 292d0efeaeea..5e0760ceeda7 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt @@ -127,7 +127,7 @@ class PipOrientationTest( hasVisibleRegion(pipApp.defaultWindowName, startingBounds) isInvisible(testApp.defaultWindowName) } - end("testApp layer covers fullscreen") { + end("testApp layer covers fullscreen", enabled = false) { hasVisibleRegion(testApp.defaultWindowName, endingBounds) } navBarLayerIsAlwaysVisible(bugId = 140855415) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java index 47104ce6afc0..4cedc483fc21 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java @@ -19,11 +19,13 @@ package com.android.wm.shell.common; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.InsetsState.ITYPE_IME; import static android.view.Surface.ROTATION_0; +import static android.view.WindowInsets.Type.ime; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; @@ -40,18 +42,22 @@ import com.android.internal.view.IInputMethodManager; import org.junit.Before; import org.junit.Test; +import java.util.concurrent.Executor; + @SmallTest public class DisplayImeControllerTest { private SurfaceControl.Transaction mT; private DisplayImeController.PerDisplay mPerDisplay; private IInputMethodManager mMock; + private Executor mExecutor; @Before public void setUp() throws Exception { mT = mock(SurfaceControl.Transaction.class); mMock = mock(IInputMethodManager.class); - mPerDisplay = new DisplayImeController(null, null, Runnable::run, new TransactionPool() { + mExecutor = spy(Runnable::run); + mPerDisplay = new DisplayImeController(null, null, mExecutor, new TransactionPool() { @Override public SurfaceControl.Transaction acquire() { return mT; @@ -71,6 +77,30 @@ public class DisplayImeControllerTest { } @Test + public void insetsControlChanged_schedulesNoWorkOnExecutor() { + mPerDisplay.insetsControlChanged(insetsStateWithIme(false), insetsSourceControl()); + verifyZeroInteractions(mExecutor); + } + + @Test + public void insetsChanged_schedulesNoWorkOnExecutor() { + mPerDisplay.insetsChanged(insetsStateWithIme(false)); + verifyZeroInteractions(mExecutor); + } + + @Test + public void showInsets_schedulesNoWorkOnExecutor() { + mPerDisplay.showInsets(ime(), true); + verifyZeroInteractions(mExecutor); + } + + @Test + public void hideInsets_schedulesNoWorkOnExecutor() { + mPerDisplay.hideInsets(ime(), true); + verifyZeroInteractions(mExecutor); + } + + @Test public void reappliesVisibilityToChangedLeash() { verifyZeroInteractions(mT); mPerDisplay.mImeShowing = true; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java index ef9923550fc5..babfc5ca20cf 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java @@ -24,12 +24,14 @@ import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableResources; import android.util.Size; +import android.view.Display; import android.view.DisplayInfo; import android.view.Gravity; import androidx.test.filters.SmallTest; import com.android.wm.shell.ShellTestCase; +import com.android.wm.shell.common.DisplayLayout; import org.junit.Before; import org.junit.Test; @@ -62,7 +64,8 @@ public class PipBoundsAlgorithmTest extends ShellTestCase { mPipBoundsState = new PipBoundsState(mContext); mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState); - mPipBoundsState.setDisplayInfo(mDefaultDisplayInfo); + mPipBoundsState.setDisplayLayout( + new DisplayLayout(mDefaultDisplayInfo, mContext.getResources(), true, true)); } private void initializeMockResources() { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java index 245858d14b49..7a810a1742d7 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java @@ -38,6 +38,7 @@ import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.util.Rational; import android.util.Size; +import android.view.Display; import android.view.DisplayInfo; import android.window.WindowContainerToken; @@ -45,6 +46,7 @@ import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestShellExecutor; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.pip.phone.PhonePipMenuController; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; @@ -196,11 +198,11 @@ public class PipTaskOrganizerTest extends ShellTestCase { private void preparePipTaskOrg() { final DisplayInfo info = new DisplayInfo(); - mPipBoundsState.setDisplayInfo(info); + mPipBoundsState.setDisplayLayout(new DisplayLayout(info, + mContext.getResources(), true, true)); when(mMockPipBoundsAlgorithm.getEntryDestinationBounds()).thenReturn(new Rect()); when(mMockPipBoundsAlgorithm.getAdjustedDestinationBounds(any(), anyFloat())) .thenReturn(new Rect()); - mPipBoundsState.setDisplayInfo(info); mSpiedPipTaskOrganizer.setOneShotAnimationType(PipAnimationController.ANIM_TYPE_ALPHA); doNothing().when(mSpiedPipTaskOrganizer).enterPipWithAlphaAnimation(any(), anyLong()); doNothing().when(mSpiedPipTaskOrganizer).scheduleAnimateResizePip(any(), anyInt(), any()); diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 1ebc48900349..1a8d9eb1f74f 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -700,11 +700,14 @@ void SkiaCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& chunk, floa NinePatchUtils::SetLatticeFlags(&lattice, flags.get(), numFlags, chunk, colors.get()); } + SkFilterMode filter = paint && paint->isFilterBitmap() ? SkFilterMode::kLinear + : SkFilterMode::kNearest; + lattice.fBounds = nullptr; SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom); auto image = bitmap.makeImage(); apply_looper(paint, [&](const SkPaint& p) { - mCanvas->drawImageLattice(image.get(), lattice, dst, &p); + mCanvas->drawImageLattice(image.get(), lattice, dst, filter, &p); }); } diff --git a/libs/hwui/canvas/CanvasOps.h b/libs/hwui/canvas/CanvasOps.h index fa0c45b5221c..cceba59cc639 100644 --- a/libs/hwui/canvas/CanvasOps.h +++ b/libs/hwui/canvas/CanvasOps.h @@ -408,21 +408,24 @@ struct CanvasOp<CanvasOpType::DrawImageLattice> { const sk_sp<Bitmap>& bitmap, SkRect dst, SkCanvas::Lattice lattice, + SkFilterMode filter, SkPaint paint ): dst(dst), lattice(lattice), + filter(filter), bitmap(bitmap), image(bitmap->makeImage()), paint(std::move(paint)) {} SkRect dst; SkCanvas::Lattice lattice; + SkFilterMode filter; const sk_sp<Bitmap> bitmap; const sk_sp<SkImage> image; SkPaint paint; void draw(SkCanvas* canvas) const { - canvas->drawImageLattice(image.get(), lattice, dst, &paint); + canvas->drawImageLattice(image.get(), lattice, dst, filter, &paint); } ASSERT_DRAWABLE() }; diff --git a/libs/hwui/hwui/AnimatedImageDrawable.cpp b/libs/hwui/hwui/AnimatedImageDrawable.cpp index 638de850a6c5..0d3d3e3f38fd 100644 --- a/libs/hwui/hwui/AnimatedImageDrawable.cpp +++ b/libs/hwui/hwui/AnimatedImageDrawable.cpp @@ -20,6 +20,7 @@ #endif #include "utils/TraceUtils.h" +#include "pipeline/skia/SkiaUtils.h" #include <SkPicture.h> #include <SkRefCnt.h> @@ -31,6 +32,7 @@ namespace android { AnimatedImageDrawable::AnimatedImageDrawable(sk_sp<SkAnimatedImage> animatedImage, size_t bytesUsed) : mSkAnimatedImage(std::move(animatedImage)), mBytesUsed(bytesUsed) { mTimeToShowNextSnapshot = ms2ns(mSkAnimatedImage->currentFrameDuration()); + setStagingBounds(mSkAnimatedImage->getBounds()); } void AnimatedImageDrawable::syncProperties() { @@ -127,21 +129,38 @@ AnimatedImageDrawable::Snapshot AnimatedImageDrawable::reset() { return snap; } +// Update the matrix to map from the intrinsic bounds of the SkAnimatedImage to +// the bounds specified by Drawable#setBounds. +static void handleBounds(SkMatrix* matrix, const SkRect& intrinsicBounds, const SkRect& bounds) { + matrix->preTranslate(bounds.left(), bounds.top()); + matrix->preScale(bounds.width() / intrinsicBounds.width(), + bounds.height() / intrinsicBounds.height()); +} + // Only called on the RenderThread. void AnimatedImageDrawable::onDraw(SkCanvas* canvas) { + // Store the matrix used to handle bounds and mirroring separate from the + // canvas. We may need to invert the matrix to determine the proper bounds + // to pass to saveLayer, and this matrix (as opposed to, potentially, the + // canvas' matrix) only uses scale and translate, so it must be invertible. + SkMatrix matrix; + SkAutoCanvasRestore acr(canvas, true); + handleBounds(&matrix, mSkAnimatedImage->getBounds(), mProperties.mBounds); + + if (mProperties.mMirrored) { + matrix.preTranslate(mSkAnimatedImage->getBounds().width(), 0); + matrix.preScale(-1, 1); + } + std::optional<SkPaint> lazyPaint; - SkAutoCanvasRestore acr(canvas, false); if (mProperties.mAlpha != SK_AlphaOPAQUE || mProperties.mColorFilter.get()) { lazyPaint.emplace(); lazyPaint->setAlpha(mProperties.mAlpha); lazyPaint->setColorFilter(mProperties.mColorFilter); lazyPaint->setFilterQuality(kLow_SkFilterQuality); } - if (mProperties.mMirrored) { - canvas->save(); - canvas->translate(mSkAnimatedImage->getBounds().width(), 0); - canvas->scale(-1, 1); - } + + canvas->concat(matrix); const bool starting = mStarting; mStarting = false; @@ -151,7 +170,11 @@ void AnimatedImageDrawable::onDraw(SkCanvas* canvas) { // The image is not animating, and never was. Draw directly from // mSkAnimatedImage. if (lazyPaint) { - canvas->saveLayer(mSkAnimatedImage->getBounds(), &*lazyPaint); + SkMatrix inverse; + (void) matrix.invert(&inverse); + SkRect r = mProperties.mBounds; + inverse.mapRect(&r); + canvas->saveLayer(r, &*lazyPaint); } std::unique_lock lock{mImageLock}; @@ -211,17 +234,31 @@ void AnimatedImageDrawable::onDraw(SkCanvas* canvas) { } int AnimatedImageDrawable::drawStaging(SkCanvas* canvas) { - SkAutoCanvasRestore acr(canvas, false); + // Store the matrix used to handle bounds and mirroring separate from the + // canvas. We may need to invert the matrix to determine the proper bounds + // to pass to saveLayer, and this matrix (as opposed to, potentially, the + // canvas' matrix) only uses scale and translate, so it must be invertible. + SkMatrix matrix; + SkAutoCanvasRestore acr(canvas, true); + handleBounds(&matrix, mSkAnimatedImage->getBounds(), mStagingProperties.mBounds); + + if (mStagingProperties.mMirrored) { + matrix.preTranslate(mSkAnimatedImage->getBounds().width(), 0); + matrix.preScale(-1, 1); + } + + canvas->concat(matrix); + if (mStagingProperties.mAlpha != SK_AlphaOPAQUE || mStagingProperties.mColorFilter.get()) { SkPaint paint; paint.setAlpha(mStagingProperties.mAlpha); paint.setColorFilter(mStagingProperties.mColorFilter); - canvas->saveLayer(mSkAnimatedImage->getBounds(), &paint); - } - if (mStagingProperties.mMirrored) { - canvas->save(); - canvas->translate(mSkAnimatedImage->getBounds().width(), 0); - canvas->scale(-1, 1); + + SkMatrix inverse; + (void) matrix.invert(&inverse); + SkRect r = mStagingProperties.mBounds; + inverse.mapRect(&r); + canvas->saveLayer(r, &paint); } if (!mRunning) { @@ -294,4 +331,10 @@ int AnimatedImageDrawable::drawStaging(SkCanvas* canvas) { return ns2ms(mTimeToShowNextSnapshot - mCurrentTime); } +SkRect AnimatedImageDrawable::onGetBounds() { + // This must return a bounds that is valid for all possible states, + // including if e.g. the client calls setBounds. + return SkRectMakeLargest(); +} + } // namespace android diff --git a/libs/hwui/hwui/AnimatedImageDrawable.h b/libs/hwui/hwui/AnimatedImageDrawable.h index f81a5a40b44e..8ca3c7e125f1 100644 --- a/libs/hwui/hwui/AnimatedImageDrawable.h +++ b/libs/hwui/hwui/AnimatedImageDrawable.h @@ -67,9 +67,10 @@ public: mStagingProperties.mColorFilter = filter; } void setStagingMirrored(bool mirrored) { mStagingProperties.mMirrored = mirrored; } + void setStagingBounds(const SkRect& bounds) { mStagingProperties.mBounds = bounds; } void syncProperties(); - virtual SkRect onGetBounds() override { return mSkAnimatedImage->getBounds(); } + SkRect onGetBounds() override; // Draw to software canvas, and return time to next draw. // 0 means the animation is not running. @@ -109,7 +110,7 @@ public: size_t byteSize() const { return sizeof(*this) + mBytesUsed; } protected: - virtual void onDraw(SkCanvas* canvas) override; + void onDraw(SkCanvas* canvas) override; private: sk_sp<SkAnimatedImage> mSkAnimatedImage; @@ -145,6 +146,7 @@ private: int mAlpha = SK_AlphaOPAQUE; sk_sp<SkColorFilter> mColorFilter; bool mMirrored = false; + SkRect mBounds; Properties() = default; Properties(Properties&) = default; diff --git a/libs/hwui/jni/AnimatedImageDrawable.cpp b/libs/hwui/jni/AnimatedImageDrawable.cpp index 1ff156593c41..c9433ec8a9da 100644 --- a/libs/hwui/jni/AnimatedImageDrawable.cpp +++ b/libs/hwui/jni/AnimatedImageDrawable.cpp @@ -244,6 +244,14 @@ static void AnimatedImageDrawable_nSetMirrored(JNIEnv* env, jobject /*clazz*/, j drawable->setStagingMirrored(mirrored); } +static void AnimatedImageDrawable_nSetBounds(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, + jobject jrect) { + auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); + SkRect rect; + GraphicsJNI::jrect_to_rect(env, jrect, &rect); + drawable->setStagingBounds(rect); +} + static const JNINativeMethod gAnimatedImageDrawableMethods[] = { { "nCreate", "(JLandroid/graphics/ImageDecoder;IIJZLandroid/graphics/Rect;)J",(void*) AnimatedImageDrawable_nCreate }, { "nGetNativeFinalizer", "()J", (void*) AnimatedImageDrawable_nGetNativeFinalizer }, @@ -259,6 +267,7 @@ static const JNINativeMethod gAnimatedImageDrawableMethods[] = { { "nSetOnAnimationEndListener", "(JLandroid/graphics/drawable/AnimatedImageDrawable;)V", (void*) AnimatedImageDrawable_nSetOnAnimationEndListener }, { "nNativeByteSize", "(J)J", (void*) AnimatedImageDrawable_nNativeByteSize }, { "nSetMirrored", "(JZ)V", (void*) AnimatedImageDrawable_nSetMirrored }, + { "nSetBounds", "(JLandroid/graphics/Rect;)V", (void*) AnimatedImageDrawable_nSetBounds }, }; int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv* env) { diff --git a/libs/hwui/jni/Typeface.cpp b/libs/hwui/jni/Typeface.cpp index 10c80774fd16..8f455fe4ab43 100644 --- a/libs/hwui/jni/Typeface.cpp +++ b/libs/hwui/jni/Typeface.cpp @@ -25,12 +25,17 @@ #include <hwui/Typeface.h> #include <minikin/FontCollection.h> #include <minikin/FontFamily.h> +#include <minikin/FontFileParser.h> #include <minikin/SystemFonts.h> #include <utils/TraceUtils.h> #include <mutex> #include <unordered_map> +#ifdef __ANDROID__ +#include <sys/stat.h> +#endif + using namespace android; using android::uirenderer::TraceUtils; @@ -155,12 +160,43 @@ static void Typeface_registerGenericFamily(JNIEnv *env, jobject, jstring familyN toTypeface(ptr)->fFontCollection); } -static sk_sp<SkData> makeSkDataCached(const std::string& path) { +#ifdef __ANDROID__ + +static bool getVerity(const std::string& path) { + struct statx out = {}; + if (statx(AT_FDCWD, path.c_str(), 0 /* flags */, STATX_ALL, &out) != 0) { + ALOGE("statx failed for %s, errno = %d", path.c_str(), errno); + return false; + } + + // Validity check. + if ((out.stx_attributes_mask & STATX_ATTR_VERITY) == 0) { + // STATX_ATTR_VERITY not supported by kernel. + return false; + } + + return (out.stx_attributes & STATX_ATTR_VERITY) != 0; +} + +#else + +static bool getVerity(const std::string&) { + // verity check is not enabled on desktop. + return false; +} + +#endif // __ANDROID__ + +static sk_sp<SkData> makeSkDataCached(const std::string& path, bool hasVerity) { // We don't clear cache as Typeface objects created by Typeface_readTypefaces() will be stored // in a static field and will not be garbage collected. static std::unordered_map<std::string, sk_sp<SkData>> cache; static std::mutex mutex; ALOG_ASSERT(!path.empty()); + if (hasVerity && !getVerity(path)) { + LOG_ALWAYS_FATAL("verity bit was removed from %s", path.c_str()); + return nullptr; + } std::lock_guard lock{mutex}; sk_sp<SkData>& entry = cache[path]; if (entry.get() == nullptr) { @@ -171,15 +207,34 @@ static sk_sp<SkData> makeSkDataCached(const std::string& path) { static std::function<std::shared_ptr<minikin::MinikinFont>()> readMinikinFontSkia( minikin::BufferReader* reader) { - std::string_view fontPath = reader->readString(); - int fontIndex = reader->read<int>(); - const minikin::FontVariation* axesPtr; - uint32_t axesCount; - std::tie(axesPtr, axesCount) = reader->readArray<minikin::FontVariation>(); - return [fontPath, fontIndex, axesPtr, axesCount]() -> std::shared_ptr<minikin::MinikinFont> { + const void* buffer = reader->data(); + size_t pos = reader->pos(); + // Advance reader's position. + reader->skipString(); // fontPath + reader->skip<int>(); // fontIndex + reader->skipArray<minikin::FontVariation>(); // axesPtr, axesCount + bool hasVerity = static_cast<bool>(reader->read<int8_t>()); + if (hasVerity) { + reader->skip<uint32_t>(); // expectedFontRevision + reader->skipString(); // expectedPostScriptName + } + return [buffer, pos]() -> std::shared_ptr<minikin::MinikinFont> { + minikin::BufferReader fontReader(buffer, pos); + std::string_view fontPath = fontReader.readString(); std::string path(fontPath.data(), fontPath.size()); ATRACE_FORMAT("Loading font %s", path.c_str()); - sk_sp<SkData> data = makeSkDataCached(path); + int fontIndex = fontReader.read<int>(); + const minikin::FontVariation* axesPtr; + uint32_t axesCount; + std::tie(axesPtr, axesCount) = fontReader.readArray<minikin::FontVariation>(); + bool hasVerity = static_cast<bool>(fontReader.read<int8_t>()); + uint32_t expectedFontRevision; + std::string_view expectedPostScriptName; + if (hasVerity) { + expectedFontRevision = fontReader.read<uint32_t>(); + expectedPostScriptName = fontReader.readString(); + } + sk_sp<SkData> data = makeSkDataCached(path, hasVerity); if (data.get() == nullptr) { // This may happen if: // 1. When the process failed to open the file (e.g. invalid path or permission). @@ -189,6 +244,20 @@ static std::function<std::shared_ptr<minikin::MinikinFont>()> readMinikinFontSki } const void* fontPtr = data->data(); size_t fontSize = data->size(); + if (hasVerity) { + // Verify font metadata if verity is enabled. + minikin::FontFileParser parser(fontPtr, fontSize, fontIndex); + std::optional<uint32_t> revision = parser.getFontRevision(); + if (!revision.has_value() || revision.value() != expectedFontRevision) { + LOG_ALWAYS_FATAL("Wrong font revision: %s", path.c_str()); + return nullptr; + } + std::optional<std::string> psName = parser.getPostScriptName(); + if (!psName.has_value() || psName.value() != expectedPostScriptName) { + LOG_ALWAYS_FATAL("Wrong PostScript name: %s", path.c_str()); + return nullptr; + } + } std::vector<minikin::FontVariation> axes(axesPtr, axesPtr + axesCount); std::shared_ptr<minikin::MinikinFont> minikinFont = fonts::createMinikinFontSkia(std::move(data), fontPath, fontPtr, fontSize, @@ -203,10 +272,24 @@ static std::function<std::shared_ptr<minikin::MinikinFont>()> readMinikinFontSki static void writeMinikinFontSkia(minikin::BufferWriter* writer, const minikin::MinikinFont* typeface) { - writer->writeString(typeface->GetFontPath()); + const std::string& path = typeface->GetFontPath(); + writer->writeString(path); writer->write<int>(typeface->GetFontIndex()); const std::vector<minikin::FontVariation>& axes = typeface->GetAxes(); writer->writeArray<minikin::FontVariation>(axes.data(), axes.size()); + bool hasVerity = getVerity(path); + writer->write<int8_t>(static_cast<int8_t>(hasVerity)); + if (hasVerity) { + // Write font metadata for verification only when verity is enabled. + minikin::FontFileParser parser(typeface->GetFontData(), typeface->GetFontSize(), + typeface->GetFontIndex()); + std::optional<uint32_t> revision = parser.getFontRevision(); + LOG_ALWAYS_FATAL_IF(!revision.has_value()); + writer->write<uint32_t>(revision.value()); + std::optional<std::string> psName = parser.getPostScriptName(); + LOG_ALWAYS_FATAL_IF(!psName.has_value()); + writer->writeString(psName.value()); + } } static jint Typeface_writeTypefaces(JNIEnv *env, jobject, jobject buffer, jlongArray faceHandles) { diff --git a/libs/hwui/tests/unit/CanvasOpTests.cpp b/libs/hwui/tests/unit/CanvasOpTests.cpp index a09e7429b4be..c9e8d808f995 100644 --- a/libs/hwui/tests/unit/CanvasOpTests.cpp +++ b/libs/hwui/tests/unit/CanvasOpTests.cpp @@ -528,6 +528,7 @@ TEST(CanvasOp, simpleDrawImageLattice) { bitmap, SkRect::MakeWH(5, 1), lattice, + SkFilterMode::kNearest, SkPaint{} } ); diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl index 77f7b54368f8..31fb8d03c4a0 100644 --- a/media/java/android/media/session/ISession.aidl +++ b/media/java/android/media/session/ISession.aidl @@ -16,6 +16,7 @@ package android.media.session; import android.app.PendingIntent; +import android.content.ComponentName; import android.content.pm.ParceledListSlice; import android.media.AudioAttributes; import android.media.MediaMetadata; @@ -35,6 +36,7 @@ interface ISession { void setFlags(int flags); void setActive(boolean active); void setMediaButtonReceiver(in PendingIntent mbr); + void setMediaButtonBroadcastReceiver(in ComponentName broadcastReceiver); void setLaunchPendingIntent(in PendingIntent pi); void destroySession(); diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java index 14b236890e4a..24118b086c24 100644 --- a/media/java/android/media/session/MediaSession.java +++ b/media/java/android/media/session/MediaSession.java @@ -23,6 +23,7 @@ import android.annotation.SystemApi; import android.app.Activity; import android.app.PendingIntent; import android.compat.annotation.UnsupportedAppUsage; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.media.AudioAttributes; @@ -131,6 +132,7 @@ public final class MediaSession { public @interface SessionFlags { } private final Object mLock = new Object(); + private Context mContext; private final int mMaxBitmapSize; private final Token mSessionToken; @@ -194,6 +196,7 @@ public final class MediaSession { + "parcelables"); } + mContext = context; mMaxBitmapSize = context.getResources().getDimensionPixelSize( com.android.internal.R.dimen.config_mediaMetadataBitmapMaxSize); mCbStub = new CallbackStub(this); @@ -277,7 +280,10 @@ public final class MediaSession { * * @param mbr The {@link PendingIntent} to send the media button event to. * @see PendingIntent#getActivity + * + * @deprecated Use {@link #setMediaButtonBroadcastReceiver(ComponentName)} instead. */ + @Deprecated public void setMediaButtonReceiver(@Nullable PendingIntent mbr) { try { mBinder.setMediaButtonReceiver(mbr); @@ -287,6 +293,32 @@ public final class MediaSession { } /** + * Set the component name of the manifest-declared {@link android.content.BroadcastReceiver} + * class that should receive media buttons. This allows restarting playback after the session + * has been stopped. If your app is started in this way an {@link Intent#ACTION_MEDIA_BUTTON} + * intent will be sent to the broadcast receiver. + * <p> + * Note: The given {@link android.content.BroadcastReceiver} should belong to the same package + * as the context that was given when creating {@link MediaSession}. + * + * @param broadcastReceiver the component name of the BroadcastReceiver class + */ + public void setMediaButtonBroadcastReceiver(@Nullable ComponentName broadcastReceiver) { + try { + if (broadcastReceiver != null) { + if (!TextUtils.equals(broadcastReceiver.getPackageName(), + mContext.getPackageName())) { + throw new IllegalArgumentException("broadcastReceiver should belong to the same" + + " package as the context given when creating MediaSession."); + } + } + mBinder.setMediaButtonBroadcastReceiver(broadcastReceiver); + } catch (RemoteException e) { + Log.wtf(TAG, "Failure in setMediaButtonBroadcastReceiver.", e); + } + } + + /** * Set any flags for the session. * * @param flags The flags to set for this session. diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java index 96371e505a77..9f327c93f63b 100644 --- a/media/java/android/media/tv/tuner/filter/Filter.java +++ b/media/java/android/media/tv/tuner/filter/Filter.java @@ -209,7 +209,7 @@ public class Filter implements AutoCloseable { prefix = "MONITOR_EVENT_", value = {MONITOR_EVENT_SCRAMBLING_STATUS, MONITOR_EVENT_IP_CID_CHANGE}) @Retention(RetentionPolicy.SOURCE) - public @interface MonitorEventTypeMask {} + public @interface MonitorEventMask {} /** * Monitor scrambling status change. @@ -239,7 +239,7 @@ public class Filter implements AutoCloseable { int type, int subType, FilterConfiguration settings); private native int nativeGetId(); private native long nativeGetId64Bit(); - private native int nativeConfigureMonitorEvent(int monitorEventTypesMask); + private native int nativeConfigureMonitorEvent(int monitorEventMask); private native int nativeSetDataSource(Filter source); private native int nativeStartFilter(); private native int nativeStopFilter(); @@ -344,19 +344,19 @@ public class Filter implements AutoCloseable { * will cause no-op. Use {@link TunerVersionChecker#getTunerVersion()} to get the version * information. * - * @param monitorEventTypesMask Types of event to be monitored. Set corresponding bit to - * monitor it. Reset to stop monitoring. + * @param monitorEventMask Types of event to be monitored. Set corresponding bit to + * monitor it. Reset to stop monitoring. * @return result status of the operation. */ @Result - public int configureMonitorEvent(@MonitorEventTypeMask int monitorEventTypesMask) { + public int setMonitorEventMask(@MonitorEventMask int monitorEventMask) { synchronized (mLock) { TunerUtils.checkResourceState(TAG, mIsClosed); if (!TunerVersionChecker.checkHigherOrEqualVersionTo( - TunerVersionChecker.TUNER_VERSION_1_1, "configureMonitorEvent")) { + TunerVersionChecker.TUNER_VERSION_1_1, "setMonitorEventMask")) { return Tuner.RESULT_UNAVAILABLE; } - return nativeConfigureMonitorEvent(monitorEventTypesMask); + return nativeConfigureMonitorEvent(monitorEventMask); } } diff --git a/packages/SystemUI/res/drawable/stat_sys_roaming_large.xml b/packages/SystemUI/res/drawable/stat_sys_roaming_large.xml new file mode 100644 index 000000000000..1511659ea42f --- /dev/null +++ b/packages/SystemUI/res/drawable/stat_sys_roaming_large.xml @@ -0,0 +1,24 @@ +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="@dimen/signal_icon_size" + android:height="@dimen/signal_icon_size" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FF000000" + android:pathData="M14.21,12.81c0.36,-0.16 0.69,-0.36 0.97,-0.61c0.41,-0.38 0.72,-0.83 0.94,-1.37c0.21,-0.54 0.32,-1.14 0.32,-1.79c0,-0.92 -0.16,-1.7 -0.49,-2.33c-0.32,-0.64 -0.79,-1.12 -1.43,-1.45c-0.62,-0.33 -1.4,-0.49 -2.32,-0.49H8.23V19h1.8v-5.76h2.5L15.06,19h1.92v-0.12L14.21,12.81zM10.03,11.71V6.32h2.18c0.59,0 1.06,0.11 1.42,0.34c0.36,0.22 0.62,0.54 0.78,0.95c0.16,0.41 0.24,0.89 0.24,1.44c0,0.49 -0.09,0.93 -0.27,1.34c-0.18,0.4 -0.46,0.73 -0.82,0.97c-0.36,0.23 -0.82,0.35 -1.37,0.35H10.03z"/> +</vector> diff --git a/packages/SystemUI/res/layout/mobile_signal_group.xml b/packages/SystemUI/res/layout/mobile_signal_group.xml index bfd079b59054..5552020f22cb 100644 --- a/packages/SystemUI/res/layout/mobile_signal_group.xml +++ b/packages/SystemUI/res/layout/mobile_signal_group.xml @@ -77,4 +77,11 @@ android:contentDescription="@string/data_connection_roaming" android:visibility="gone" /> </FrameLayout> + <ImageView + android:id="@+id/mobile_roaming_large" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/stat_sys_roaming_large" + android:contentDescription="@string/data_connection_roaming" + android:visibility="gone" /> </com.android.keyguard.AlphaOptimizedLinearLayout> diff --git a/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml b/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml index 42d541e3afc9..10d49b38ae75 100644 --- a/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml +++ b/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml @@ -85,6 +85,13 @@ android:contentDescription="@string/data_connection_roaming" android:visibility="gone" /> </FrameLayout> + <ImageView + android:id="@+id/mobile_roaming_large" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/stat_sys_roaming_large" + android:contentDescription="@string/data_connection_roaming" + android:visibility="gone" /> </com.android.keyguard.AlphaOptimizedLinearLayout> </com.android.systemui.statusbar.StatusBarMobileView> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 0a17828bda8e..09710d7e3fad 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -351,7 +351,7 @@ <bool name="config_showNotificationGear">true</bool> <!-- Whether or not a background should be drawn behind a notification. --> - <bool name="config_drawNotificationBackground">false</bool> + <bool name="config_drawNotificationBackground">true</bool> <!-- Whether or the notifications can be shown and dismissed with a drag. --> <bool name="config_enableNotificationShadeDrag">true</bool> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 79cb236ff34a..e5109307c4f4 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -155,7 +155,7 @@ <dimen name="notification_max_heads_up_height_increased">188dp</dimen> <!-- Side padding on the lockscreen on the side of notifications --> - <dimen name="notification_side_paddings">16dp</dimen> + <dimen name="notification_side_paddings">4dp</dimen> <!-- padding between the heads up and the statusbar --> <dimen name="heads_up_status_bar_padding">8dp</dimen> @@ -177,7 +177,10 @@ <dimen name="notification_min_interaction_height">40dp</dimen> <!-- Radius for notifications corners without adjacent notifications --> - <dimen name="notification_corner_radius">28dp</dimen> + <dimen name="notification_corner_radius">8dp</dimen> + + <!-- Radius for notifications corners with adjacent notifications --> + <dimen name="notification_corner_radius_small">0dp</dimen> <!-- the padding of the shelf icon container --> <dimen name="shelf_icon_container_padding">13dp</dimen> @@ -619,7 +622,7 @@ <dimen name="z_distance_between_notifications">0.5dp</dimen> <!-- The height of the divider between the individual notifications. --> - <dimen name="notification_divider_height">2dp</dimen> + <dimen name="notification_divider_height">1dp</dimen> <!-- The corner radius of the shadow behind the notification. --> <dimen name="notification_shadow_radius">0dp</dimen> @@ -632,7 +635,7 @@ <dimen name="notification_children_container_divider_height">0.5dp</dimen> <!-- The horizontal margin of the content in the notification shade --> - <dimen name="notification_shade_content_margin_horizontal">16dp</dimen> + <dimen name="notification_shade_content_margin_horizontal">4dp</dimen> <!-- The top margin for the notification children container in its non-expanded form. --> <dimen name="notification_children_container_margin_top"> diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml index 6ff589c13419..49f91096543a 100644 --- a/packages/SystemUI/res/values/flags.xml +++ b/packages/SystemUI/res/values/flags.xml @@ -20,6 +20,8 @@ <bool name="flag_notification_pipeline2">false</bool> <bool name="flag_notification_pipeline2_rendering">false</bool> + <bool name="flag_notif_updates">false</bool> + <bool name="flag_shade_is_opaque">false</bool> <!-- b/171917882 --> diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java index 39b92dc4f662..7d8d86fe691e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java +++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java @@ -20,6 +20,7 @@ import android.content.Context; import android.content.res.ColorStateList; import android.text.TextUtils; import android.util.AttributeSet; +import android.util.FeatureFlagUtils; import android.view.View; import android.widget.ImageView; import android.widget.LinearLayout; @@ -65,10 +66,13 @@ public class QSCarrier extends LinearLayout { super.onFinishInflate(); mDualToneHandler = new DualToneHandler(getContext()); mMobileGroup = findViewById(R.id.mobile_combo); + if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) { + mMobileRoaming = findViewById(R.id.mobile_roaming_large); + } else { + mMobileRoaming = findViewById(R.id.mobile_roaming); + } mMobileSignal = findViewById(R.id.mobile_signal); - mMobileRoaming = findViewById(R.id.mobile_roaming); mCarrierText = findViewById(R.id.qs_carrier_text); - mMobileSignal.setImageDrawable(new SignalDrawable(mContext)); int colorForeground = Utils.getColorAttrDefaultColor(mContext, diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java index ebdcc0006dce..77200ccaf5cb 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java @@ -71,7 +71,7 @@ public class QSCarrierGroupController { boolean activityIn, boolean activityOut, CharSequence typeContentDescription, CharSequence typeContentDescriptionHtml, CharSequence description, - boolean isWide, int subId, boolean roaming) { + boolean isWide, int subId, boolean roaming, boolean showTriangle) { int slotIndex = getSlotIndex(subId); if (slotIndex >= SIM_SLOTS) { Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java index f742752d80be..720c5dc7026f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java @@ -268,7 +268,7 @@ public class CellularTile extends QSTileImpl<SignalState> { int qsType, boolean activityIn, boolean activityOut, CharSequence typeContentDescription, CharSequence typeContentDescriptionHtml, CharSequence description, - boolean isWide, int subId, boolean roaming) { + boolean isWide, int subId, boolean roaming, boolean showTriangle) { if (qsIcon == null) { // Not data sim, don't display. return; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java index 15232789cc64..1b2ad4c8b5bb 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java @@ -34,6 +34,7 @@ import android.widget.Switch; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settingslib.graph.SignalDrawable; +import com.android.settingslib.mobile.TelephonyIcons; import com.android.settingslib.net.DataUsageController; import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Background; @@ -178,6 +179,7 @@ public class InternetTile extends QSTileImpl<SignalState> { CharSequence mDataSubscriptionName; CharSequence mDataContentDescription; int mMobileSignalIconId; + int mQsTypeIcon; boolean mActivityIn; boolean mActivityOut; boolean mNoSim; @@ -194,6 +196,7 @@ public class InternetTile extends QSTileImpl<SignalState> { .append(",mDataSubscriptionName=").append(mDataSubscriptionName) .append(",mDataContentDescription=").append(mDataContentDescription) .append(",mMobileSignalIconId=").append(mMobileSignalIconId) + .append(",mQsTypeIcon=").append(mQsTypeIcon) .append(",mActivityIn=").append(mActivityIn) .append(",mActivityOut=").append(mActivityOut) .append(",mNoSim=").append(mNoSim) @@ -249,7 +252,7 @@ public class InternetTile extends QSTileImpl<SignalState> { int qsType, boolean activityIn, boolean activityOut, CharSequence typeContentDescription, CharSequence typeContentDescriptionHtml, CharSequence description, - boolean isWide, int subId, boolean roaming) { + boolean isWide, int subId, boolean roaming, boolean showTriangle) { if (DEBUG) { Log.d(TAG, "setMobileDataIndicators: " + "statusIcon = " + (statusIcon == null ? "" : statusIcon.toString()) + "," @@ -263,7 +266,8 @@ public class InternetTile extends QSTileImpl<SignalState> { + "description = " + description + "," + "isWide = " + isWide + "," + "subId = " + subId + "," - + "roaming = " + roaming); + + "roaming = " + roaming + "," + + "showTriangle = " + showTriangle); } if (qsIcon == null) { // Not data sim, don't display. @@ -274,6 +278,7 @@ public class InternetTile extends QSTileImpl<SignalState> { mCellularInfo.mDataContentDescription = (description != null) ? typeContentDescriptionHtml : null; mCellularInfo.mMobileSignalIconId = qsIcon.icon; + mCellularInfo.mQsTypeIcon = qsType; mCellularInfo.mActivityIn = activityIn; mCellularInfo.mActivityOut = activityOut; mCellularInfo.mRoaming = roaming; @@ -292,6 +297,7 @@ public class InternetTile extends QSTileImpl<SignalState> { if (mCellularInfo.mNoSim) { // Make sure signal gets cleared out when no sims. mCellularInfo.mMobileSignalIconId = 0; + mCellularInfo.mQsTypeIcon = 0; } refreshState(mCellularInfo); } @@ -374,6 +380,7 @@ public class InternetTile extends QSTileImpl<SignalState> { state.label = r.getString(R.string.quick_settings_internet_label); if (cb.mAirplaneModeEnabled) { if (!state.value) { + state.state = Tile.STATE_INACTIVE; state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_airplane); state.secondaryLabel = r.getString(R.string.status_bar_airplane); } else if (!wifiConnected) { @@ -443,7 +450,8 @@ public class InternetTile extends QSTileImpl<SignalState> { state.activityOut = mobileDataEnabled && cb.mActivityOut; state.expandedAccessibilityClassName = Switch.class.getName(); - if (cb.mAirplaneModeEnabled && cb.mNoDefaultNetwork) { + if (cb.mAirplaneModeEnabled && cb.mQsTypeIcon != TelephonyIcons.ICON_CWF) { + state.state = Tile.STATE_INACTIVE; state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_airplane); state.secondaryLabel = r.getString(R.string.status_bar_airplane); } else if (cb.mNoDefaultNetwork && cb.mNoNetworksAvailable) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java index fdb793e6da95..924eb263de50 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java @@ -131,6 +131,7 @@ public class RemoteInputController { */ public void removeRemoteInput(NotificationEntry entry, Object token) { Objects.requireNonNull(entry); + if (entry.mRemoteEditImeVisible) return; pruneWeakThenRemoveAndContains(null /* contains */, entry /* remove */, token); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java index 239addd4ee1d..d562726681f1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java @@ -27,6 +27,7 @@ import android.content.res.ColorStateList; import android.graphics.Color; import android.graphics.Rect; import android.util.AttributeSet; +import android.util.FeatureFlagUtils; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; @@ -64,7 +65,6 @@ public class StatusBarMobileView extends FrameLayout implements DarkReceiver, LayoutInflater inflater = LayoutInflater.from(context); StatusBarMobileView v = (StatusBarMobileView) inflater.inflate(R.layout.status_bar_mobile_signal_group, null); - v.setSlot(slot); v.init(); v.setVisibleState(STATE_ICON); @@ -104,7 +104,11 @@ public class StatusBarMobileView extends FrameLayout implements DarkReceiver, mMobileGroup = findViewById(R.id.mobile_group); mMobile = findViewById(R.id.mobile_signal); mMobileType = findViewById(R.id.mobile_type); - mMobileRoaming = findViewById(R.id.mobile_roaming); + if (FeatureFlagUtils.isEnabled(getContext(), FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) { + mMobileRoaming = findViewById(R.id.mobile_roaming_large); + } else { + mMobileRoaming = findViewById(R.id.mobile_roaming); + } mMobileRoamingSpace = findViewById(R.id.mobile_roaming_space); mIn = findViewById(R.id.mobile_in); mOut = findViewById(R.id.mobile_out); @@ -160,7 +164,7 @@ public class StatusBarMobileView extends FrameLayout implements DarkReceiver, } else { mMobileType.setVisibility(View.GONE); } - + mMobile.setVisibility(mState.showTriangle ? View.VISIBLE : View.GONE); mMobileRoaming.setVisibility(mState.roaming ? View.VISIBLE : View.GONE); mMobileRoamingSpace.setVisibility(mState.roaming ? View.VISIBLE : View.GONE); mIn.setVisibility(mState.activityIn ? View.VISIBLE : View.GONE); @@ -191,6 +195,7 @@ public class StatusBarMobileView extends FrameLayout implements DarkReceiver, } } + mMobile.setVisibility(state.showTriangle ? View.VISIBLE : View.GONE); mMobileRoaming.setVisibility(state.roaming ? View.VISIBLE : View.GONE); mMobileRoamingSpace.setVisibility(state.roaming ? View.VISIBLE : View.GONE); mIn.setVisibility(state.activityIn ? View.VISIBLE : View.GONE); @@ -200,7 +205,8 @@ public class StatusBarMobileView extends FrameLayout implements DarkReceiver, needsLayout |= state.roaming != mState.roaming || state.activityIn != mState.activityIn - || state.activityOut != mState.activityOut; + || state.activityOut != mState.activityOut + || state.showTriangle != mState.showTriangle; mState = state; return needsLayout; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java index bafa4a254d79..8a22b9f6891f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java @@ -179,6 +179,8 @@ public final class NotificationEntry extends ListEntry { private boolean mShelfIconVisible; private boolean mIsAlerting; + public boolean mRemoteEditImeVisible; + /** * @param sbn the StatusBarNotification from system server * @param ranking also from system server diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java index 596aea0f1493..2b12119ca0d6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java @@ -302,8 +302,12 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl } else { mMenuContainer = new FrameLayout(mContext); } - final boolean newFlowHideShelf = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.SHOW_NEW_NOTIF_DISMISS, 1 /* on by default */) == 1; + // The setting can win (which is needed for tests) but if not set, then use the flag + final int showDismissSetting = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.SHOW_NEW_NOTIF_DISMISS, -1); + final boolean newFlowHideShelf = showDismissSetting == -1 + ? mContext.getResources().getBoolean(R.bool.flag_notif_updates) + : showDismissSetting == 1; if (newFlowHideShelf) { return; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java index 079cf77767c0..45f5b3136b9b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java @@ -16,8 +16,10 @@ package com.android.systemui.statusbar.notification.stack; +import android.content.res.Resources; import android.util.MathUtils; +import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -45,11 +47,6 @@ public class NotificationRoundnessManager { private ExpandableNotificationRow mTrackedHeadsUp; private float mAppearFraction; - // Radius for notification corners WITH adjacent notifications - // as percent of radius WITHOUT adjacent notifications. - // TODO(b/175710408) pull from dimens and hide from beta builds. - static final float SMALL_CORNER_RADIUS = 4f/28; - @Inject NotificationRoundnessManager( KeyguardBypassController keyguardBypassController, @@ -128,7 +125,9 @@ public class NotificationRoundnessManager { if (view.showingPulsing() && !mBypassController.getBypassEnabled()) { return 1.0f; } - return SMALL_CORNER_RADIUS; + final Resources resources = view.getResources(); + return resources.getDimension(R.dimen.notification_corner_radius_small) + / resources.getDimension(R.dimen.notification_corner_radius); } public void setExpanded(float expandedHeight, float appearFraction) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index f07d8740c3e9..3f3be4465bb9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -41,8 +41,6 @@ import android.graphics.Color; import android.graphics.Outline; import android.graphics.Paint; import android.graphics.PointF; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.os.Bundle; import android.os.UserHandle; @@ -503,7 +501,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mSections = mSectionsManager.createSectionsForBuckets(); mAmbientState = new AmbientState(context, mSectionsManager); - mBgColor = Utils.getColorAttr(mContext, android.R.attr.colorBackground).getDefaultColor(); + mBgColor = Utils.getColorAttr(mContext, android.R.attr.colorBackgroundFloating) + .getDefaultColor(); int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height); int maxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height); mExpandHelper = new ExpandHelper(getContext(), mExpandHelperCallback, @@ -623,7 +622,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } void updateBgColor() { - mBgColor = Utils.getColorAttr(mContext, android.R.attr.colorBackground).getDefaultColor(); + mBgColor = Utils.getColorAttr(mContext, android.R.attr.colorBackgroundFloating) + .getDefaultColor(); updateBackgroundDimming(); mShelf.onUiModeChanged(); } @@ -2282,7 +2282,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable if (child.getVisibility() != View.GONE && !(child instanceof StackScrollerDecorView) && child != mShelf - && mSwipeHelper.getSwipedView() != child) { + && (mSwipeHelper.getSwipedView() != child + || !child.getResources().getBoolean(R.bool.flag_notif_updates))) { children.add(child); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java index d11e8641f987..5f90077640ec 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java @@ -221,7 +221,7 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba int qsType, boolean activityIn, boolean activityOut, CharSequence typeContentDescription, CharSequence typeContentDescriptionHtml, CharSequence description, - boolean isWide, int subId, boolean roaming) { + boolean isWide, int subId, boolean roaming, boolean showTriangle) { if (DEBUG) { Log.d(TAG, "setMobileDataIndicators: " + "statusIcon = " + (statusIcon == null ? "" : statusIcon.toString()) + "," @@ -235,7 +235,8 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba + "description = " + description + "," + "isWide = " + isWide + "," + "subId = " + subId + "," - + "roaming = " + roaming); + + "roaming = " + roaming + "," + + "showTriangle = " + showTriangle); } MobileIconState state = getState(subId); if (state == null) { @@ -250,6 +251,7 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba state.typeId = statusType; state.contentDescription = statusIcon.contentDescription; state.typeContentDescription = typeContentDescription; + state.showTriangle = showTriangle; state.roaming = roaming; state.activityIn = activityIn && mActivityEnabled; state.activityOut = activityOut && mActivityEnabled; @@ -551,6 +553,7 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba public int subId; public int strengthId; public int typeId; + public boolean showTriangle; public boolean roaming; public boolean needsLeadingPadding; public CharSequence typeContentDescription; @@ -569,20 +572,21 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba return false; } MobileIconState that = (MobileIconState) o; - return subId == that.subId && - strengthId == that.strengthId && - typeId == that.typeId && - roaming == that.roaming && - needsLeadingPadding == that.needsLeadingPadding && - Objects.equals(typeContentDescription, that.typeContentDescription); + return subId == that.subId + && strengthId == that.strengthId + && typeId == that.typeId + && showTriangle == that.showTriangle + && roaming == that.roaming + && needsLeadingPadding == that.needsLeadingPadding + && Objects.equals(typeContentDescription, that.typeContentDescription); } @Override public int hashCode() { return Objects - .hash(super.hashCode(), subId, strengthId, typeId, roaming, needsLeadingPadding, - typeContentDescription); + .hash(super.hashCode(), subId, strengthId, typeId, showTriangle, roaming, + needsLeadingPadding, typeContentDescription); } public MobileIconState copy() { @@ -596,6 +600,7 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba other.subId = subId; other.strengthId = strengthId; other.typeId = typeId; + other.showTriangle = showTriangle; other.roaming = roaming; other.needsLeadingPadding = needsLeadingPadding; other.typeContentDescription = typeContentDescription; @@ -613,8 +618,9 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba } @Override public String toString() { - return "MobileIconState(subId=" + subId + ", strengthId=" + strengthId + ", roaming=" - + roaming + ", typeId=" + typeId + ", visible=" + visible + ")"; + return "MobileIconState(subId=" + subId + ", strengthId=" + strengthId + + ", showTriangle=" + showTriangle + ", roaming=" + roaming + + ", typeId=" + typeId + ", visible=" + visible + ")"; } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java index 5e88cd5c2423..08a4492fc15d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java @@ -124,12 +124,13 @@ public class CallbackHandler extends Handler implements EmergencyListener, Signa final int statusType, final int qsType, final boolean activityIn, final boolean activityOut, final CharSequence typeContentDescription, CharSequence typeContentDescriptionHtml, final CharSequence description, - final boolean isWide, final int subId, boolean roaming) { + final boolean isWide, final int subId, boolean roaming, boolean showTriangle) { post(() -> { for (SignalCallback signalCluster : mSignalCallbacks) { signalCluster.setMobileDataIndicators(statusIcon, qsIcon, statusType, qsType, activityIn, activityOut, typeContentDescription, - typeContentDescriptionHtml, description, isWide, subId, roaming); + typeContentDescriptionHtml, description, isWide, subId, roaming, + showTriangle); } }); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java index 2f66508dafee..39472ded4a3f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java @@ -284,13 +284,15 @@ public class MobileSignalController extends SignalController<MobileState, Mobile && !mCurrentState.carrierNetworkChangeMode && mCurrentState.activityOut; showDataIcon &= mCurrentState.dataSim && mCurrentState.isDefault; + boolean showTriangle = showDataIcon && !mCurrentState.airplaneMode; + int typeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.dataType : 0; + showDataIcon |= mCurrentState.roaming; IconState statusIcon = new IconState(showDataIcon && !mCurrentState.airplaneMode, getCurrentIconId(), contentDescription); - int typeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.dataType : 0; callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon, activityIn, activityOut, dataContentDescription, dataContentDescriptionHtml, description, icons.isWide, mSubscriptionInfo.getSubscriptionId(), - mCurrentState.roaming); + mCurrentState.roaming, showTriangle); } else { boolean showDataIcon = mCurrentState.dataConnected || dataDisabled; IconState statusIcon = new IconState( @@ -316,10 +318,11 @@ public class MobileSignalController extends SignalController<MobileState, Mobile && mCurrentState.activityOut; showDataIcon &= mCurrentState.isDefault || dataDisabled; int typeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.dataType : 0; + boolean showTriangle = mCurrentState.enabled && !mCurrentState.airplaneMode; callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon, activityIn, activityOut, dataContentDescription, dataContentDescriptionHtml, description, icons.isWide, mSubscriptionInfo.getSubscriptionId(), - mCurrentState.roaming); + mCurrentState.roaming, showTriangle); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java index f2b0d762b6fc..e60d5c5f2fa8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java @@ -66,12 +66,13 @@ public interface NetworkController extends CallbackController<SignalCallback>, D * @param isWide //TODO: unused? * @param subId subscription ID for which to update the UI * @param roaming indicates roaming + * @param showTriangle whether to show the mobile triangle the in status bar */ default void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType, int qsType, boolean activityIn, boolean activityOut, CharSequence typeContentDescription, CharSequence typeContentDescriptionHtml, CharSequence description, - boolean isWide, int subId, boolean roaming) { + boolean isWide, int subId, boolean roaming, boolean showTriangle) { } default void setSubs(List<SubscriptionInfo> subs) {} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java index 6c5251b02291..9380d9110c35 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.policy; +import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.annotation.Nullable; @@ -50,6 +52,8 @@ import android.view.OnReceiveContentListener; import android.view.View; import android.view.ViewAnimationUtils; import android.view.ViewGroup; +import android.view.WindowInsets; +import android.view.WindowInsetsAnimation; import android.view.accessibility.AccessibilityEvent; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.EditorInfo; @@ -61,6 +65,8 @@ import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.TextView; +import androidx.annotation.NonNull; + import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; import com.android.internal.statusbar.IStatusBarService; @@ -76,6 +82,7 @@ import com.android.systemui.statusbar.phone.LightBarController; import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.function.Consumer; /** @@ -135,6 +142,27 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene mEditText = (RemoteEditText) getChildAt(0); mEditText.setInnerFocusable(false); + mEditText.setWindowInsetsAnimationCallback( + new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) { + @NonNull + @Override + public WindowInsets onProgress(@NonNull WindowInsets insets, + @NonNull List<WindowInsetsAnimation> runningAnimations) { + return insets; + } + + @Override + public void onEnd(@NonNull WindowInsetsAnimation animation) { + super.onEnd(animation); + if (animation.getTypeMask() == WindowInsets.Type.ime()) { + mEntry.mRemoteEditImeVisible = + mEditText.getRootWindowInsets().isVisible(WindowInsets.Type.ime()); + if (!mEntry.mRemoteEditImeVisible && !mEditText.mShowImeOnInputConnection) { + mController.removeRemoteInput(mEntry, mToken); + } + } + } + }); } protected Intent prepareRemoteInputFromText() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java index 7042e2f9e102..9669522675ac 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java @@ -150,7 +150,7 @@ public class WifiSignalController extends callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon, mCurrentState.activityIn, mCurrentState.activityOut, dataContentDescription, dataContentDescriptionHtml, description, icons.isWide, - mCurrentState.subId, /* roaming= */ false); + mCurrentState.subId, /* roaming= */ false, /* showTriangle= */ true); } private int getCurrentIconIdForCarrierWifi() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java index 8a412bff03ac..b452d3a79814 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java @@ -218,7 +218,7 @@ public class QSCarrierGroupControllerTest extends LeakCheckedTest { mSignalCallback.setMobileDataIndicators( mock(NetworkController.IconState.class), mock(NetworkController.IconState.class), - 0, 0, true, true, "", "", "", true, 0, true); + 0, 0, true, true, "", "", "", true, 0, true, true); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java index 63bfd6ab25cc..919ddcb488c2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java @@ -16,8 +16,6 @@ package com.android.systemui.statusbar.notification.stack; -import static com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager.SMALL_CORNER_RADIUS; - import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -25,12 +23,14 @@ import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.content.res.Resources; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import androidx.test.filters.SmallTest; +import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -61,10 +61,14 @@ public class NotificationRoundnessManagerTest extends SysuiTestCase { private ExpandableNotificationRow mSecond; @Mock private KeyguardBypassController mBypassController; + private float mSmallRadiusRatio; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + final Resources resources = mContext.getResources(); + mSmallRadiusRatio = resources.getDimension(R.dimen.notification_corner_radius_small) + / resources.getDimension(R.dimen.notification_corner_radius); mRoundnessManager = new NotificationRoundnessManager( mBypassController, new NotificationSectionsFeatureManager(new DeviceConfigProxy(), mContext)); @@ -141,7 +145,7 @@ public class NotificationRoundnessManagerTest extends SysuiTestCase { createSection(null, null) }); Assert.assertEquals(1.0f, mSecond.getCurrentBottomRoundness(), 0.0f); - Assert.assertEquals(SMALL_CORNER_RADIUS, mSecond.getCurrentTopRoundness(), 0.0f); + Assert.assertEquals(mSmallRadiusRatio, mSecond.getCurrentTopRoundness(), 0.0f); } @Test @@ -168,8 +172,8 @@ public class NotificationRoundnessManagerTest extends SysuiTestCase { row.setHeadsUp(false); mRoundnessManager.updateView(entry.getRow(), false); - Assert.assertEquals(SMALL_CORNER_RADIUS, row.getCurrentBottomRoundness(), 0.0f); - Assert.assertEquals(SMALL_CORNER_RADIUS, row.getCurrentTopRoundness(), 0.0f); + Assert.assertEquals(mSmallRadiusRatio, row.getCurrentBottomRoundness(), 0.0f); + Assert.assertEquals(mSmallRadiusRatio, row.getCurrentTopRoundness(), 0.0f); } @Test @@ -179,7 +183,7 @@ public class NotificationRoundnessManagerTest extends SysuiTestCase { createSection(null, mSecond) }); Assert.assertEquals(1.0f, mSecond.getCurrentBottomRoundness(), 0.0f); - Assert.assertEquals(SMALL_CORNER_RADIUS, mSecond.getCurrentTopRoundness(), 0.0f); + Assert.assertEquals(mSmallRadiusRatio, mSecond.getCurrentTopRoundness(), 0.0f); } @Test @@ -188,7 +192,7 @@ public class NotificationRoundnessManagerTest extends SysuiTestCase { createSection(mFirst, mFirst), createSection(mSecond, null) }); - Assert.assertEquals(SMALL_CORNER_RADIUS, mSecond.getCurrentBottomRoundness(), 0.0f); + Assert.assertEquals(mSmallRadiusRatio, mSecond.getCurrentBottomRoundness(), 0.0f); Assert.assertEquals(1.0f, mSecond.getCurrentTopRoundness(), 0.0f); } @@ -198,7 +202,7 @@ public class NotificationRoundnessManagerTest extends SysuiTestCase { createSection(mFirst, null), createSection(null, null) }); - Assert.assertEquals(SMALL_CORNER_RADIUS, mFirst.getCurrentBottomRoundness(), 0.0f); + Assert.assertEquals(mSmallRadiusRatio, mFirst.getCurrentBottomRoundness(), 0.0f); Assert.assertEquals(1.0f, mFirst.getCurrentTopRoundness(), 0.0f); } @@ -208,8 +212,8 @@ public class NotificationRoundnessManagerTest extends SysuiTestCase { createSection(mSecond, mSecond), createSection(null, null) }); - Assert.assertEquals(SMALL_CORNER_RADIUS, mFirst.getCurrentBottomRoundness(), 0.0f); - Assert.assertEquals(SMALL_CORNER_RADIUS, mFirst.getCurrentTopRoundness(), 0.0f); + Assert.assertEquals(mSmallRadiusRatio, mFirst.getCurrentBottomRoundness(), 0.0f); + Assert.assertEquals(mSmallRadiusRatio, mFirst.getCurrentTopRoundness(), 0.0f); } @Test @@ -255,8 +259,8 @@ public class NotificationRoundnessManagerTest extends SysuiTestCase { createSection(mSecond, mSecond), createSection(null, null) }); - Assert.assertEquals(SMALL_CORNER_RADIUS, mFirst.getCurrentBottomRoundness(), 0.0f); - Assert.assertEquals(SMALL_CORNER_RADIUS, mFirst.getCurrentTopRoundness(), 0.0f); + Assert.assertEquals(mSmallRadiusRatio, mFirst.getCurrentBottomRoundness(), 0.0f); + Assert.assertEquals(mSmallRadiusRatio, mFirst.getCurrentTopRoundness(), 0.0f); } @Test @@ -305,8 +309,8 @@ public class NotificationRoundnessManagerTest extends SysuiTestCase { }); mFirst.setHeadsUpAnimatingAway(true); mFirst.setHeadsUpAnimatingAway(false); - Assert.assertEquals(SMALL_CORNER_RADIUS, mFirst.getCurrentBottomRoundness(), 0.0f); - Assert.assertEquals(SMALL_CORNER_RADIUS, mFirst.getCurrentTopRoundness(), 0.0f); + Assert.assertEquals(mSmallRadiusRatio, mFirst.getCurrentBottomRoundness(), 0.0f); + Assert.assertEquals(mSmallRadiusRatio, mFirst.getCurrentTopRoundness(), 0.0f); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java index ebc45f41ce61..c212cf3ee769 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java @@ -125,7 +125,7 @@ public class CallbackHandlerTest extends SysuiTestCase { int subId = 5; boolean roaming = true; mHandler.setMobileDataIndicators(status, qs, type, qsType, in, out, typeDescription, - typeDescriptionHtml, description, wide, subId, roaming); + typeDescriptionHtml, description, wide, subId, roaming, true); waitForCallbacks(); ArgumentCaptor<IconState> statusArg = ArgumentCaptor.forClass(IconState.class); @@ -143,7 +143,7 @@ public class CallbackHandlerTest extends SysuiTestCase { Mockito.verify(mSignalCallback).setMobileDataIndicators(statusArg.capture(), qsArg.capture(), typeIconArg.capture(), qsTypeIconArg.capture(), inArg.capture(), outArg.capture(), typeContentArg.capture(), typeContentHtmlArg.capture(), - descArg.capture(), wideArg.capture(), subIdArg.capture(), eq(roaming)); + descArg.capture(), wideArg.capture(), subIdArg.capture(), eq(roaming), eq(true)); assertEquals(status, statusArg.getValue()); assertEquals(qs, qsArg.getValue()); assertEquals(type, (int) typeIconArg.getValue()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java index deabcbe520b6..da1f5d392872 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java @@ -489,7 +489,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase { anyInt(), typeIconArg.capture(), dataInArg.capture(), dataOutArg.capture(), any(CharSequence.class), any(CharSequence.class), any(CharSequence.class), - anyBoolean(), anyInt(), anyBoolean()); + anyBoolean(), anyInt(), anyBoolean(), anyBoolean()); IconState iconState = iconArg.getValue(); int state = SignalDrawable.getState(icon, CellSignalStrength.getNumSignalStrengthLevels(), false); @@ -523,7 +523,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase { typeIconArg.capture(), anyInt(), anyBoolean(), anyBoolean(), any(CharSequence.class), any(CharSequence.class), any(), - anyBoolean(), anyInt(), eq(roaming)); + anyBoolean(), anyInt(), eq(roaming), anyBoolean()); IconState iconState = iconArg.getValue(); int state = icon == -1 ? 0 : SignalDrawable.getState(icon, CellSignalStrength.getNumSignalStrengthLevels(), @@ -544,7 +544,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase { typeIconArg.capture(), anyInt(), anyBoolean(), anyBoolean(), any(CharSequence.class), any(CharSequence.class), any(), - anyBoolean(), anyInt(), anyBoolean()); + anyBoolean(), anyInt(), anyBoolean(), anyBoolean()); IconState iconState = iconArg.getValue(); int state = SignalDrawable.getState( level, CellSignalStrength.getNumSignalStrengthLevels(), !inet); @@ -591,7 +591,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase { dataOutArg.capture(), typeContentDescriptionArg.capture(), typeContentDescriptionHtmlArg.capture(), - any(), anyBoolean(), anyInt(), anyBoolean()); + any(), anyBoolean(), anyInt(), anyBoolean(), anyBoolean()); IconState iconState = iconArg.getValue(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java index 2b82f66cb4c2..10166cb0c43f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java @@ -134,7 +134,7 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest { // Still be on wifi though. setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, true, true); setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_CELLULAR, false, false); - verifyLastMobileDataIndicators(false, DEFAULT_LEVEL, 0, true); + verifyLastMobileDataIndicators(true, DEFAULT_LEVEL, 0, true); } @Test diff --git a/services/Android.bp b/services/Android.bp index b11a2e8bfa4f..f6bb72a46b87 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -28,6 +28,7 @@ filegroup { ":services.profcollect-sources", ":services.restrictions-sources", ":services.searchui-sources", + ":services.speech-sources", ":services.startop.iorap-sources", ":services.systemcaptions-sources", ":services.translation-sources", @@ -75,6 +76,7 @@ java_library { "services.profcollect", "services.restrictions", "services.searchui", + "services.speech", "services.startop", "services.systemcaptions", "services.translation", @@ -139,7 +141,7 @@ droidstubs { last_released: { api_file: ":android.api.system-server.latest", removed_api_file: ":removed.api.system-server.latest", - baseline_file: ":system-server-api-incompatibilities-with-last-released" + baseline_file: ":android-incompatibilities.api.system-server.latest" }, api_lint: { enabled: true, diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags index b595eb15ee15..9f91dd6b982b 100644 --- a/services/core/java/com/android/server/EventLogTags.logtags +++ b/services/core/java/com/android/server/EventLogTags.logtags @@ -1,4 +1,4 @@ -# See system/core/logcat/event.logtags for a description of the format of this file. +# See system/logging/logcat/event.logtags for a description of the format of this file. option java_package com.android.server @@ -173,6 +173,10 @@ option java_package com.android.server 3120 pm_critical_info (msg|3) # Disk usage stats for verifying quota correctness 3121 pm_package_stats (manual_time|2|3),(quota_time|2|3),(manual_data|2|2),(quota_data|2|2),(manual_cache|2|2),(quota_cache|2|2) +# Snapshot statistics +3130 pm_snapshot_stats (build_count|1|1),(reuse_count|1|1),(big_builds|1|1),(quick_rebuilds|1|1),(max_build_time|1|3),(cumm_build_time|1|3) +# Snapshot rebuild instance +3131 pm_snapshot_rebuild (build_time|1|3),(elapsed|1|3) # --------------------------- # InputMethodManagerService.java diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java index e8687e57a07b..a08d066513c7 100644 --- a/services/core/java/com/android/server/TestNetworkService.java +++ b/services/core/java/com/android/server/TestNetworkService.java @@ -242,6 +242,7 @@ class TestNetworkService extends ITestNetworkManager.Stub { nc.addTransportType(NetworkCapabilities.TRANSPORT_TEST); nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED); nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); + nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED); nc.setNetworkSpecifier(new StringNetworkSpecifier(iface)); nc.setAdministratorUids(administratorUids); if (!isMetered) { diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java index 3e80709d03d6..97e313e13f6d 100644 --- a/services/core/java/com/android/server/VibratorService.java +++ b/services/core/java/com/android/server/VibratorService.java @@ -102,7 +102,10 @@ public class VibratorService extends IVibratorService.Stub { private VibrationScaler mVibrationScaler; private InputDeviceDelegate mInputDeviceDelegate; - private volatile VibrationThread mThread; + @GuardedBy("mLock") + private VibrationThread mThread; + @GuardedBy("mLock") + private VibrationThread mNextVibrationThread; @GuardedBy("mLock") private Vibration mCurrentVibration; @@ -132,6 +135,10 @@ public class VibratorService extends IVibratorService.Stub { if (mCurrentVibration != null && mCurrentVibration.id == vibrationId) { mThread = null; reportFinishVibrationLocked(status); + if (mNextVibrationThread != null) { + startVibrationThreadLocked(mNextVibrationThread); + mNextVibrationThread = null; + } } } } @@ -258,18 +265,14 @@ public class VibratorService extends IVibratorService.Stub { @VisibleForTesting public void onVibrationComplete(int vibratorId, long vibrationId) { synchronized (mLock) { - if (mCurrentVibration != null && mCurrentVibration.id == vibrationId) { + if (mCurrentVibration != null && mCurrentVibration.id == vibrationId + && mThread != null) { if (DEBUG) { Slog.d(TAG, "Vibration onComplete callback, notifying VibrationThread"); } - if (mThread != null) { - // Let the thread playing the vibration handle the callback, since it might be - // expecting the vibrator to turn off multiple times during a single vibration. - mThread.vibratorComplete(vibratorId); - } else { - // No vibration is playing in the thread, but clean up service just in case. - doCancelVibrateLocked(Vibration.Status.FINISHED); - } + // Let the thread playing the vibration handle the callback, since it might be + // expecting the vibrator to turn off multiple times during a single vibration. + mThread.vibratorComplete(vibratorId); } } } @@ -462,8 +465,10 @@ public class VibratorService extends IVibratorService.Stub { try { doCancelVibrateLocked(Vibration.Status.CANCELLED); startVibrationLocked(vib); + boolean isNextVibration = mNextVibrationThread != null + && vib.equals(mNextVibrationThread.getVibration()); - if (!vib.hasEnded() && mCurrentVibration.id != vib.id) { + if (!vib.hasEnded() && !vib.equals(mCurrentVibration) && !isNextVibration) { // Vibration was unexpectedly ignored: add to list for debugging endVibrationLocked(vib, Vibration.Status.IGNORED); } @@ -532,6 +537,7 @@ public class VibratorService extends IVibratorService.Stub { } final long ident = Binder.clearCallingIdentity(); try { + mNextVibrationThread = null; doCancelVibrateLocked(Vibration.Status.CANCELLED); } finally { Binder.restoreCallingIdentity(ident); @@ -546,16 +552,14 @@ public class VibratorService extends IVibratorService.Stub { try { if (mThread != null) { mThread.cancel(); - mThread = null; } + mInputDeviceDelegate.cancelVibrateIfAvailable(); if (mCurrentExternalVibration != null) { endVibrationLocked(mCurrentExternalVibration, status); mCurrentExternalVibration.externalVibration.mute(); mCurrentExternalVibration = null; mVibratorController.setExternalControl(false); } - doVibratorOff(); - reportFinishVibrationLocked(status); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); } @@ -579,28 +583,30 @@ public class VibratorService extends IVibratorService.Stub { private void startVibrationInnerLocked(Vibration vib) { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationInnerLocked"); try { - // Set current vibration before starting it, so callback will work. - mCurrentVibration = vib; - VibrationEffect effect = getEffect(vib); - Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); boolean inputDevicesAvailable = mInputDeviceDelegate.vibrateIfAvailable( vib.uid, vib.opPkg, vib.getEffect(), vib.reason, vib.attrs); if (inputDevicesAvailable) { - // The set current vibration is no longer being played by this service, so drop it. - mCurrentVibration = null; endVibrationLocked(vib, Vibration.Status.FORWARDED_TO_INPUT_DEVICES); + } else if (mThread == null) { + startVibrationThreadLocked(new VibrationThread(vib, mVibratorController, mWakeLock, + mBatteryStatsService, mVibrationCallbacks)); } else { - // mThread better be null here. doCancelVibrate should always be - // called before startVibrationInnerLocked - mThread = new VibrationThread(vib, mVibratorController, mWakeLock, + mNextVibrationThread = new VibrationThread(vib, mVibratorController, mWakeLock, mBatteryStatsService, mVibrationCallbacks); - mThread.start(); } } finally { Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); } } + @GuardedBy("mLock") + private void startVibrationThreadLocked(VibrationThread thread) { + Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); + mCurrentVibration = thread.getVibration(); + mThread = thread; + mThread.start(); + } + /** Scale the vibration effect by the intensity as appropriate based its intent. */ private void applyVibrationIntensityScalingLocked(Vibration vib) { vib.updateEffect(mVibrationScaler.scale(vib.getEffect(), vib.attrs.getUsage())); @@ -665,13 +671,14 @@ public class VibratorService extends IVibratorService.Stub { @GuardedBy("mLock") private void reportFinishVibrationLocked(Vibration.Status status) { - Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "reportFinishVibrationLocked"); try { if (mCurrentVibration != null) { endVibrationLocked(mCurrentVibration, status); mAppOps.finishOp(AppOpsManager.OP_VIBRATE, mCurrentVibration.uid, mCurrentVibration.opPkg); + + Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); mCurrentVibration = null; } } finally { @@ -697,21 +704,6 @@ public class VibratorService extends IVibratorService.Stub { } } - private void doVibratorOff() { - Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOff"); - try { - if (DEBUG) { - Slog.d(TAG, "Turning vibrator off."); - } - boolean inputDevicesAvailable = mInputDeviceDelegate.cancelVibrateIfAvailable(); - if (!inputDevicesAvailable) { - mVibratorController.off(); - } - } finally { - Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); - } - } - private boolean isSystemHapticFeedback(Vibration vib) { if (vib.attrs.getUsage() != VibrationAttributes.USAGE_TOUCH) { return false; @@ -844,6 +836,7 @@ public class VibratorService extends IVibratorService.Stub { // haptic feedback as part of the transition. So we don't cancel // system vibrations. if (mCurrentVibration != null && !isSystemHapticFeedback(mCurrentVibration)) { + mNextVibrationThread = null; doCancelVibrateLocked(Vibration.Status.CANCELLED); } } @@ -910,6 +903,8 @@ public class VibratorService extends IVibratorService.Stub { return IExternalVibratorService.SCALE_MUTE; } + VibrationThread cancelingVibration = null; + int scale; synchronized (mLock) { if (mCurrentExternalVibration != null && mCurrentExternalVibration.externalVibration.equals(vib)) { @@ -920,11 +915,9 @@ public class VibratorService extends IVibratorService.Stub { if (mCurrentExternalVibration == null) { // If we're not under external control right now, then cancel any normal // vibration that may be playing and ready the vibrator for external control. - if (DEBUG) { - Slog.d(TAG, "Vibrator going under external control."); - } + mNextVibrationThread = null; doCancelVibrateLocked(Vibration.Status.CANCELLED); - mVibratorController.setExternalControl(true); + cancelingVibration = mThread; } else { endVibrationLocked(mCurrentExternalVibration, Vibration.Status.CANCELLED); } @@ -941,11 +934,24 @@ public class VibratorService extends IVibratorService.Stub { vib.linkToDeath(mCurrentExternalDeathRecipient); mCurrentExternalVibration.scale = mVibrationScaler.getExternalVibrationScale( vib.getVibrationAttributes().getUsage()); - if (DEBUG) { - Slog.e(TAG, "Playing external vibration: " + vib); + scale = mCurrentExternalVibration.scale; + } + if (cancelingVibration != null) { + try { + cancelingVibration.join(); + } catch (InterruptedException e) { + Slog.w("Interrupted while waiting current vibration to be cancelled before " + + "starting external vibration", e); } - return mCurrentExternalVibration.scale; } + if (DEBUG) { + Slog.d(TAG, "Vibrator going under external control."); + } + mVibratorController.setExternalControl(true); + if (DEBUG) { + Slog.e(TAG, "Playing external vibration: " + vib); + } + return scale; } @Override diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 50e55579dd74..c2872405ae18 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -254,6 +254,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub mStats.setRadioScanningTimeoutLocked(mContext.getResources().getInteger( com.android.internal.R.integer.config_radioScanningTimeout) * 1000L); mStats.setPowerProfileLocked(new PowerProfile(context)); + mStats.startTrackingSystemServerCpuTime(); + mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mStats); } diff --git a/services/core/java/com/android/server/display/DisplayGroup.java b/services/core/java/com/android/server/display/DisplayGroup.java index 2ba875813734..a11a745d0b7e 100644 --- a/services/core/java/com/android/server/display/DisplayGroup.java +++ b/services/core/java/com/android/server/display/DisplayGroup.java @@ -26,8 +26,6 @@ import java.util.List; */ public class DisplayGroup { - public static final int DEFAULT = 0; - private final List<LogicalDisplay> mDisplays = new ArrayList<>(); private final int mGroupId; diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java index 5bf83db4ee34..86de159b5824 100644 --- a/services/core/java/com/android/server/display/LogicalDisplay.java +++ b/services/core/java/com/android/server/display/LogicalDisplay.java @@ -71,6 +71,9 @@ final class LogicalDisplay { private final int mDisplayId; private final int mLayerStack; + + private int mDisplayGroupId = Display.INVALID_DISPLAY_GROUP; + /** * Override information set by the window manager. Will be reported instead of {@link #mInfo} * if not null. @@ -265,6 +268,19 @@ final class LogicalDisplay { } /** + * Updates the {@link DisplayGroup} to which the logical display belongs. + * + * @param groupId Identifier for the {@link DisplayGroup}. + */ + public void updateDisplayGroupIdLocked(int groupId) { + if (groupId != mDisplayGroupId) { + mDisplayGroupId = groupId; + mBaseDisplayInfo.displayGroupId = groupId; + mInfo.set(null); + } + } + + /** * Updates the state of the logical display based on the available display devices. * The logical display might become invalid if it is attached to a display device * that no longer exists. @@ -365,6 +381,7 @@ final class LogicalDisplay { (deviceInfo.flags & DisplayDeviceInfo.FLAG_MASK_DISPLAY_CUTOUT) != 0; mBaseDisplayInfo.displayCutout = maskCutout ? null : deviceInfo.displayCutout; mBaseDisplayInfo.displayId = mDisplayId; + mBaseDisplayInfo.displayGroupId = mDisplayGroupId; updateFrameRateOverrides(deviceInfo); mBaseDisplayInfo.brightnessMinimum = deviceInfo.brightnessMinimum; mBaseDisplayInfo.brightnessMaximum = deviceInfo.brightnessMaximum; diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java index bb2fbed354aa..e7388787ecf9 100644 --- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java +++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java @@ -96,7 +96,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { private final SparseArray<LogicalDisplay> mLogicalDisplays = new SparseArray<LogicalDisplay>(); private int mNextNonDefaultDisplayId = Display.DEFAULT_DISPLAY + 1; - private int mNextNonDefaultGroupId = DisplayGroup.DEFAULT + 1; + private int mNextNonDefaultGroupId = Display.DEFAULT_DISPLAY_GROUP + 1; /** A mapping from logical display id to display group. */ private final SparseArray<DisplayGroup> mDisplayGroups = new SparseArray<>(); @@ -313,7 +313,18 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { final int displayId = assignDisplayIdLocked(isDefault); final int layerStack = assignLayerStackLocked(displayId); + final DisplayGroup displayGroup; + final boolean addNewDisplayGroup = + isDefault || (deviceInfo.flags & DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP) != 0; + if (addNewDisplayGroup) { + final int groupId = assignDisplayGroupIdLocked(isDefault); + displayGroup = new DisplayGroup(groupId); + } else { + displayGroup = mDisplayGroups.get(Display.DEFAULT_DISPLAY); + } + LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device); + display.updateDisplayGroupIdLocked(displayGroup.getGroupId()); display.updateLocked(mDisplayDeviceRepo); if (!display.isValidLocked()) { // This should never happen currently. @@ -324,13 +335,6 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { mLogicalDisplays.put(displayId, display); - final DisplayGroup displayGroup; - if (isDefault || (deviceInfo.flags & DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP) != 0) { - final int groupId = assignDisplayGroupIdLocked(isDefault); - displayGroup = new DisplayGroup(groupId); - } else { - displayGroup = mDisplayGroups.get(Display.DEFAULT_DISPLAY); - } displayGroup.addDisplay(display); mDisplayGroups.append(displayId, displayGroup); @@ -369,6 +373,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { final DisplayGroup displayGroup = new DisplayGroup(groupId); displayGroup.addDisplay(display); mDisplayGroups.append(display.getDisplayIdLocked(), displayGroup); + display.updateDisplayGroupIdLocked(groupId); } } else { // The display should be a part of the default DisplayGroup. @@ -377,6 +382,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { displayGroup.removeDisplay(display); defaultDisplayGroup.addDisplay(display); mDisplayGroups.put(displayId, defaultDisplayGroup); + display.updateDisplayGroupIdLocked(defaultDisplayGroup.getGroupId()); } } @@ -406,7 +412,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { } private int assignDisplayGroupIdLocked(boolean isDefault) { - return isDefault ? DisplayGroup.DEFAULT : mNextNonDefaultGroupId++; + return isDefault ? Display.DEFAULT_DISPLAY_GROUP : mNextNonDefaultGroupId++; } private int assignLayerStackLocked(int displayId) { diff --git a/services/core/java/com/android/server/location/timezone/OWNERS b/services/core/java/com/android/server/location/timezone/OWNERS deleted file mode 100644 index 28aff188dbd8..000000000000 --- a/services/core/java/com/android/server/location/timezone/OWNERS +++ /dev/null @@ -1,3 +0,0 @@ -# Bug component: 847766 -nfuller@google.com -include /core/java/android/app/timedetector/OWNERS diff --git a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java index 0a4d17f20aec..e2e5046d98bf 100644 --- a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java +++ b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java @@ -141,6 +141,11 @@ final class MediaButtonReceiverHolder { packageName != null ? packageName : ""); } + public static MediaButtonReceiverHolder create(int userId, ComponentName broadcastReceiver) { + return new MediaButtonReceiverHolder(userId, null, broadcastReceiver, + COMPONENT_TYPE_BROADCAST); + } + private MediaButtonReceiverHolder(int userId, PendingIntent pendingIntent, ComponentName componentName, @ComponentType int componentType) { mUserId = userId; diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index ea6e7d7d0bf6..ae58d4c40622 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -18,6 +18,7 @@ package com.android.server.media; import android.annotation.Nullable; import android.app.PendingIntent; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ParceledListSlice; @@ -858,6 +859,21 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR } @Override + public void setMediaButtonBroadcastReceiver(ComponentName receiver) throws RemoteException { + final long token = Binder.clearCallingIdentity(); + try { + if ((mPolicies & SessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_RECEIVER) + != 0) { + return; + } + mMediaButtonReceiverHolder = MediaButtonReceiverHolder.create(mUserId, receiver); + mService.onMediaButtonReceiverChanged(MediaSessionRecord.this); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override public void setLaunchPendingIntent(PendingIntent pi) throws RemoteException { mLaunchIntent = pi; } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 50aacd677fdc..c27e670c4c99 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -518,8 +518,7 @@ public class PackageManagerService extends IPackageManager.Stub public static final boolean DEBUG_PERMISSIONS = false; private static final boolean DEBUG_SHARED_LIBRARIES = false; public static final boolean DEBUG_COMPRESSION = Build.IS_DEBUGGABLE; - public static final boolean DEBUG_CACHES = false; - public static final boolean TRACE_CACHES = false; + public static final boolean TRACE_SNAPSHOTS = false; private static final boolean DEBUG_PER_UID_READ_TIMEOUTS = false; // Debug output for dexopting. This is shared between PackageManagerService, OtaDexoptService @@ -2015,7 +2014,7 @@ public class PackageManagerService extends IPackageManager.Stub } /** - * A computer provides the functional interface to the cache + * A computer provides the functional interface to the snapshot. */ private interface Computer { @@ -2181,13 +2180,11 @@ public class PackageManagerService extends IPackageManager.Stub private final ResolveInfo mInstantAppInstallerInfo; private final InstantAppRegistry mInstantAppRegistry; private final ApplicationInfo mLocalAndroidApplication; + private final AppsFilter mAppsFilter; // Immutable service attribute private final String mAppPredictionServicePackage; - // TODO: create cache copies of the following attributes - private final AppsFilter mAppsFilter; - // The following are not cloned since changes to these have never // been guarded by the PMS lock. private final Context mContext; @@ -4738,11 +4735,11 @@ public class PackageManagerService extends IPackageManager.Stub private final Computer mLiveComputer; // A lock-free cache for frequently called functions. private volatile Computer mSnapshotComputer; - // If true, the cached computer object is invalid (the cache is stale). - // The attribute is static since it may be set from outside classes. + // If true, the snapshot is invalid (stale). The attribute is static since it may be + // set from outside classes. private static volatile boolean sSnapshotInvalid = true; - // If true, the cache is corked. Do not create a new cache but continue to use the - // existing one. This throttles cache creation during periods of churn in Package + // If true, the snapshot is corked. Do not create a new snapshot but use the live + // computer. This throttles snapshot creation during periods of churn in Package // Manager. private static volatile boolean sSnapshotCorked = false; @@ -4754,13 +4751,121 @@ public class PackageManagerService extends IPackageManager.Stub */ private final Object mSnapshotLock = new Object(); - // A counter of all queries that hit the cache. - private AtomicInteger mSnapshotHits = new AtomicInteger(0); + // A counter of all queries that hit the current snapshot. + @GuardedBy("mSnapshotLock") + private int mSnapshotHits = 0; + + // A class to record snapshot statistics. + private static class SnapshotStatistics { + // A build time is "big" if it takes longer than 5ms. + private static final long SNAPSHOT_BIG_BUILD_TIME_NS = TimeUnit.MILLISECONDS.toNanos(5); + + // A snapshot is in quick succession to the previous snapshot if it less than + // 100ms since the previous snapshot. + private static final long SNAPSHOT_QUICK_REBUILD_INTERVAL_NS = + TimeUnit.MILLISECONDS.toNanos(100); + + // The interval between snapshot statistics logging, in ns. + private static final long SNAPSHOT_LOG_INTERVAL_NS = TimeUnit.MINUTES.toNanos(10); + + // The throttle parameters for big build reporting. Do not report more than this + // many events in a single log interval. + private static final int SNAPSHOT_BUILD_REPORT_LIMIT = 10; + + // The time the snapshot statistics were last logged. + private long mStatisticsSent = 0; + + // The number of build events logged since the last periodic log. + private int mLoggedBuilds = 0; + + // The time of the last build. + private long mLastBuildTime = 0; + + // The number of times the snapshot has been rebuilt since the statistics were + // last logged. + private int mRebuilds = 0; + + // The number of times the snapshot has been used since it was rebuilt. + private int mReused = 0; + + // The number of "big" build times since the last log. "Big" is defined by + // SNAPSHOT_BIG_BUILD_TIME. + private int mBigBuilds = 0; + + // The number of quick rebuilds. "Quick" is defined by + // SNAPSHOT_QUICK_REBUILD_INTERVAL_NS. + private int mQuickRebuilds = 0; + + // The time take to build a snapshot. This is cumulative over the rebuilds recorded + // in mRebuilds, so the average time to build a snapshot is given by + // mBuildTimeNs/mRebuilds. + private int mBuildTimeNs = 0; + + // The maximum build time since the last log. + private long mMaxBuildTimeNs = 0; + + // The constant that converts ns to ms. This is the divisor. + private final long NS_TO_MS = TimeUnit.MILLISECONDS.toNanos(1); - // The number of queries at the last miss. This is updated when the cache is rebuilt - // (guarded by mLock) and is used to report the hit run-length. + // Convert ns to an int ms. The maximum range of this method is about 24 days. + // There is no expectation that an event will take longer than that. + private int nsToMs(long ns) { + return (int) (ns / NS_TO_MS); + } + + // The single method records a rebuild. The "now" parameter is passed in because + // the caller needed it to computer the duration, so pass it in to avoid + // recomputing it. + private void rebuild(long now, long done, int hits) { + if (mStatisticsSent == 0) { + mStatisticsSent = now; + } + final long elapsed = now - mLastBuildTime; + final long duration = done - now; + mLastBuildTime = now; + + if (mMaxBuildTimeNs < duration) { + mMaxBuildTimeNs = duration; + } + mRebuilds++; + mReused += hits; + mBuildTimeNs += duration; + + boolean log_build = false; + if (duration > SNAPSHOT_BIG_BUILD_TIME_NS) { + log_build = true; + mBigBuilds++; + } + if (elapsed < SNAPSHOT_QUICK_REBUILD_INTERVAL_NS) { + log_build = true; + mQuickRebuilds++; + } + if (log_build && mLoggedBuilds < SNAPSHOT_BUILD_REPORT_LIMIT) { + EventLogTags.writePmSnapshotRebuild(nsToMs(duration), nsToMs(elapsed)); + mLoggedBuilds++; + } + + final long log_interval = now - mStatisticsSent; + if (log_interval >= SNAPSHOT_LOG_INTERVAL_NS) { + EventLogTags.writePmSnapshotStats(mRebuilds, mReused, + mBigBuilds, mQuickRebuilds, + nsToMs(mMaxBuildTimeNs), + nsToMs(mBuildTimeNs)); + mStatisticsSent = now; + mRebuilds = 0; + mReused = 0; + mBuildTimeNs = 0; + mMaxBuildTimeNs = 0; + mBigBuilds = 0; + mQuickRebuilds = 0; + mLoggedBuilds = 0; + } + } + } + + // Snapshot statistics. @GuardedBy("mLock") - private int mSnapshotRebuilt = 0; + private final SnapshotStatistics mSnapshotStatistics = new SnapshotStatistics(); // The snapshot disable/enable switch. An image with the flag set true uses snapshots // and an image with the flag set false does not use snapshots. @@ -4786,23 +4891,21 @@ public class PackageManagerService extends IPackageManager.Stub // yet invalidated the snapshot. Always give the thread the live computer. return mLiveComputer; } - int hits = 0; - if (TRACE_CACHES) { - hits = mSnapshotHits.incrementAndGet(); - } synchronized (mSnapshotLock) { Computer c = mSnapshotComputer; if (sSnapshotCorked && (c != null)) { // Snapshots are corked, which means new ones should not be built right now. return c; } + // Deliberately capture the value pre-increment + final int hits = mSnapshotHits++; if (sSnapshotInvalid || (c == null)) { // The snapshot is invalid if it is marked as invalid or if it is null. If it // is null, then it is currently being rebuilt by rebuildSnapshot(). synchronized (mLock) { // Rebuild the snapshot if it is invalid. Note that the snapshot might be // invalidated as it is rebuilt. However, the snapshot is still - // self-consistent (the lock is being held)and is current as of the time + // self-consistent (the lock is being held) and is current as of the time // this function is entered. if (sSnapshotInvalid) { rebuildSnapshot(hits); @@ -4820,22 +4923,20 @@ public class PackageManagerService extends IPackageManager.Stub } /** - * Rebuild the cached computer. mSnapshotComputer is temporarily set to null to block - * other threads from using the invalid computer until it is rebuilt. + * Rebuild the cached computer. mSnapshotComputer is temporarily set to null to block other + * threads from using the invalid computer until it is rebuilt. */ @GuardedBy("mLock") private void rebuildSnapshot(int hits) { + final long now = System.nanoTime(); mSnapshotComputer = null; sSnapshotInvalid = false; final Snapshot args = new Snapshot(Snapshot.SNAPPED); mSnapshotComputer = new ComputerEngine(args); + final long done = System.nanoTime(); - // Still guarded by mLock - final int run = hits - mSnapshotRebuilt; - mSnapshotRebuilt = hits; - if (TRACE_CACHES) { - Log.w(TAG, "computer: rebuild after " + run + " hits"); - } + mSnapshotStatistics.rebuild(now, done, hits); + mSnapshotHits = 0; } /** @@ -4852,8 +4953,8 @@ public class PackageManagerService extends IPackageManager.Stub * @hide */ public static void onChange(@Nullable Watchable what) { - if (TRACE_CACHES) { - Log.e(TAG, "computer: onChange(" + what + ")"); + if (TRACE_SNAPSHOTS) { + Log.e(TAG, "snapshot: onChange(" + what + ")"); } sSnapshotInvalid = true; } diff --git a/services/core/java/com/android/server/power/DisplayPowerRequestMapper.java b/services/core/java/com/android/server/power/DisplayPowerRequestMapper.java index 6477552eb550..2fc3e40acd4d 100644 --- a/services/core/java/com/android/server/power/DisplayPowerRequestMapper.java +++ b/services/core/java/com/android/server/power/DisplayPowerRequestMapper.java @@ -25,7 +25,6 @@ import android.util.SparseIntArray; import android.view.Display; import com.android.internal.annotations.GuardedBy; -import com.android.server.display.DisplayGroup; /** * Responsible for creating {@link DisplayPowerRequest}s and associating them with @@ -110,8 +109,8 @@ class DisplayPowerRequestMapper { DisplayManagerInternal displayManagerInternal, Handler handler) { mDisplayManagerInternal = displayManagerInternal; displayManager.registerDisplayListener(mDisplayListener, handler); - mDisplayPowerRequests.append(DisplayGroup.DEFAULT, new DisplayPowerRequest()); - mDisplayGroupIds.append(Display.DEFAULT_DISPLAY, DisplayGroup.DEFAULT); + mDisplayPowerRequests.append(Display.DEFAULT_DISPLAY_GROUP, new DisplayPowerRequest()); + mDisplayGroupIds.append(Display.DEFAULT_DISPLAY, Display.DEFAULT_DISPLAY_GROUP); } DisplayPowerRequest get(int displayId) { diff --git a/services/core/java/com/android/server/speech/Android.bp b/services/core/java/com/android/server/speech/Android.bp new file mode 100644 index 000000000000..379b0754b82a --- /dev/null +++ b/services/core/java/com/android/server/speech/Android.bp @@ -0,0 +1,13 @@ +filegroup { + name: "services.speech-sources", + srcs: ["java/**/*.java"], + path: "java", + visibility: ["//frameworks/base/services"], +} + +java_library_static { + name: "services.speech", + defaults: ["platform_service_defaults"], + srcs: [":services.speech-sources"], + libs: ["services.core"], +} diff --git a/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java b/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java new file mode 100644 index 000000000000..96248c3711e3 --- /dev/null +++ b/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.speech; + +import static com.android.internal.infra.AbstractRemoteService.PERMANENT_BOUND_TIMEOUT_MS; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.os.RemoteException; +import android.speech.IRecognitionListener; +import android.speech.IRecognitionService; +import android.speech.RecognitionService; +import android.util.Slog; + +import com.android.internal.infra.ServiceConnector; + +final class RemoteSpeechRecognitionService extends ServiceConnector.Impl<IRecognitionService> { + private static final String TAG = RemoteSpeechRecognitionService.class.getSimpleName(); + private static final boolean DEBUG = true; + + RemoteSpeechRecognitionService(Context context, ComponentName serviceName, int userId) { + super(context, + new Intent(RecognitionService.SERVICE_INTERFACE).setComponent(serviceName), + Context.BIND_AUTO_CREATE + | Context.BIND_FOREGROUND_SERVICE + | Context.BIND_INCLUDE_CAPABILITIES + | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS, + userId, + IRecognitionService.Stub::asInterface); + + if (DEBUG) { + Slog.i(TAG, "Bound to recognition service at: " + serviceName.flattenToString()); + } + } + + void startListening(Intent recognizerIntent, IRecognitionListener listener, String packageName, + String featureId) throws RemoteException { + if (DEBUG) { + Slog.i(TAG, "#startListening for package: " + packageName + ", feature=" + featureId); + } + run(service -> service.startListening(recognizerIntent, listener, packageName, featureId)); + } + + void stopListening(IRecognitionListener listener, String packageName, String featureId) + throws RemoteException { + if (DEBUG) { + Slog.i(TAG, "#stopListening for package: " + packageName + ", feature=" + featureId); + } + run(service -> service.stopListening(listener, packageName, featureId)); + } + + void cancel(IRecognitionListener listener, String packageName, String featureId) + throws RemoteException { + if (DEBUG) { + Slog.i(TAG, "#cancel for package: " + packageName + ", feature=" + featureId); + } + run(service -> service.cancel(listener, packageName, featureId)); + } + + @Override // from ServiceConnector.Impl + protected void onServiceConnectionStatusChanged( + IRecognitionService service, boolean connected) { + if (!DEBUG) { + return; + } + + if (connected) { + Slog.i(TAG, "Connected to ASR service"); + } else { + Slog.w(TAG, "Disconnected from ASR service"); + } + } + + @Override // from AbstractRemoteService + protected long getAutoDisconnectTimeoutMs() { + return PERMANENT_BOUND_TIMEOUT_MS; + } +} diff --git a/services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java b/services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java new file mode 100644 index 000000000000..592ba9e616b8 --- /dev/null +++ b/services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.speech; + +import android.annotation.NonNull; +import android.annotation.UserIdInt; +import android.content.Context; +import android.os.UserHandle; +import android.speech.IRecognitionServiceManager; +import android.speech.IRecognitionServiceManagerCallback; + +import com.android.internal.R; +import com.android.server.infra.AbstractMasterSystemService; +import com.android.server.infra.FrameworkResourcesServiceNameResolver; + +/** + * System service implementation for Speech Recognizer. + * + * <p>This service uses RemoteSpeechRecognitionService to bind to a default implementation of + * ISpeechRecognition. It relays all the requests from the client to the default system impl of + * ISpeechRecognition service (denoted by {@code + * R.string.config_defaultOnDeviceSpeechRecognitionService}). + */ +public final class SpeechRecognitionManagerService extends + AbstractMasterSystemService<SpeechRecognitionManagerService, + SpeechRecognitionManagerServiceImpl> { + private static final String TAG = SpeechRecognitionManagerService.class.getSimpleName(); + + public SpeechRecognitionManagerService(@NonNull Context context) { + super(context, + new FrameworkResourcesServiceNameResolver( + context, + R.string.config_defaultOnDeviceSpeechRecognitionService), + /*disallowProperty=*/ null); + } + + @Override // from SystemService + public void onStart() { + SpeechRecognitionManagerServiceStub serviceStub = new SpeechRecognitionManagerServiceStub(); + publishBinderService(Context.SPEECH_RECOGNITION_SERVICE, serviceStub); + } + + @Override + protected SpeechRecognitionManagerServiceImpl newServiceLocked( + @UserIdInt int resolvedUserId, boolean disabled) { + return new SpeechRecognitionManagerServiceImpl(this, mLock, resolvedUserId, disabled); + } + + final class SpeechRecognitionManagerServiceStub extends IRecognitionServiceManager.Stub { + + @Override + public void createSession(IRecognitionServiceManagerCallback callback) { + int userId = UserHandle.getCallingUserId(); + synchronized (mLock) { + SpeechRecognitionManagerServiceImpl service = getServiceForUserLocked(userId); + service.createSessionLocked(callback); + } + } + } +} diff --git a/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java b/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java new file mode 100644 index 000000000000..bcaf174b1d92 --- /dev/null +++ b/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.speech; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.app.AppGlobals; +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ServiceInfo; +import android.os.RemoteException; +import android.speech.IRecognitionListener; +import android.speech.IRecognitionService; +import android.speech.IRecognitionServiceManagerCallback; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; +import com.android.server.infra.AbstractPerUserSystemService; + +final class SpeechRecognitionManagerServiceImpl extends + AbstractPerUserSystemService<SpeechRecognitionManagerServiceImpl, + SpeechRecognitionManagerService> { + + private static final String TAG = SpeechRecognitionManagerServiceImpl.class.getSimpleName(); + + @GuardedBy("mLock") + @Nullable + private RemoteSpeechRecognitionService mRemoteService; + + SpeechRecognitionManagerServiceImpl( + @NonNull SpeechRecognitionManagerService master, + @NonNull Object lock, @UserIdInt int userId, boolean disabled) { + super(master, lock, userId); + updateRemoteServiceLocked(); + } + + @GuardedBy("mLock") + @Override // from PerUserSystemService + protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent) + throws PackageManager.NameNotFoundException { + try { + return AppGlobals.getPackageManager().getServiceInfo(serviceComponent, + PackageManager.GET_META_DATA, mUserId); + } catch (RemoteException e) { + throw new PackageManager.NameNotFoundException( + "Could not get service for " + serviceComponent); + } + } + + @GuardedBy("mLock") + @Override // from PerUserSystemService + protected boolean updateLocked(boolean disabled) { + final boolean enabledChanged = super.updateLocked(disabled); + updateRemoteServiceLocked(); + return enabledChanged; + } + + /** + * Updates the reference to the remote service. + */ + @GuardedBy("mLock") + private void updateRemoteServiceLocked() { + if (mRemoteService != null) { + if (mMaster.debug) { + Slog.d(TAG, "updateRemoteService(): destroying old remote service"); + } + mRemoteService.unbind(); + mRemoteService = null; + } + } + + void createSessionLocked(IRecognitionServiceManagerCallback callback) { + // TODO(b/176578753): check clients have record audio permission. + // TODO(b/176578753): verify caller package is the one supplied + + RemoteSpeechRecognitionService service = ensureRemoteServiceLocked(); + + if (service == null) { + tryRespondWithError(callback); + return; + } + + service.connect().thenAccept(binderService -> { + if (binderService != null) { + try { + callback.onSuccess(new IRecognitionService.Stub() { + @Override + public void startListening(Intent recognizerIntent, + IRecognitionListener listener, + String packageName, String featureId) throws RemoteException { + service.startListening( + recognizerIntent, listener, packageName, featureId); + } + + @Override + public void stopListening(IRecognitionListener listener, + String packageName, + String featureId) throws RemoteException { + service.stopListening(listener, packageName, featureId); + } + + @Override + public void cancel(IRecognitionListener listener, + String packageName, + String featureId) throws RemoteException { + service.cancel(listener, packageName, featureId); + } + }); + } catch (RemoteException e) { + Slog.e(TAG, "Error creating a speech recognition session", e); + tryRespondWithError(callback); + } + } else { + tryRespondWithError(callback); + } + }); + } + + @GuardedBy("mLock") + @Nullable + private RemoteSpeechRecognitionService ensureRemoteServiceLocked() { + if (mRemoteService == null) { + final String serviceName = getComponentNameLocked(); + if (serviceName == null) { + if (mMaster.verbose) { + Slog.v(TAG, "ensureRemoteServiceLocked(): no service component name."); + } + return null; + } + final ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName); + mRemoteService = + new RemoteSpeechRecognitionService(getContext(), serviceComponent, mUserId); + } + return mRemoteService; + } + + private static void tryRespondWithError(IRecognitionServiceManagerCallback callback) { + try { + callback.onError(); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to respond with error"); + } + } +} diff --git a/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java b/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java index 210fb5c0a1ab..c0c9e6d58622 100644 --- a/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java +++ b/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java @@ -14,15 +14,15 @@ * limitations under the License. */ -package com.android.server.location.timezone; - -import static com.android.server.location.timezone.LocationTimeZoneManagerService.debugLog; -import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED; -import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED; -import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN; -import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING; -import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN; -import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED; +package com.android.server.timezonedetector.location; + +import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.debugLog; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED; import android.annotation.NonNull; import android.annotation.Nullable; diff --git a/services/core/java/com/android/server/location/timezone/ControllerCallbackImpl.java b/services/core/java/com/android/server/timezonedetector/location/ControllerCallbackImpl.java index cd9aa2fd6a5b..46eaad075b54 100644 --- a/services/core/java/com/android/server/location/timezone/ControllerCallbackImpl.java +++ b/services/core/java/com/android/server/timezonedetector/location/ControllerCallbackImpl.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.location.timezone; +package com.android.server.timezonedetector.location; import android.annotation.NonNull; diff --git a/services/core/java/com/android/server/location/timezone/ControllerEnvironmentImpl.java b/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java index d896f6e441d8..83b33ee75de9 100644 --- a/services/core/java/com/android/server/location/timezone/ControllerEnvironmentImpl.java +++ b/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.location.timezone; +package com.android.server.timezonedetector.location; import android.annotation.NonNull; diff --git a/services/core/java/com/android/server/location/timezone/ControllerImpl.java b/services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java index 0d284fc2de4f..fb2a18493b7d 100644 --- a/services/core/java/com/android/server/location/timezone/ControllerImpl.java +++ b/services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java @@ -14,20 +14,20 @@ * limitations under the License. */ -package com.android.server.location.timezone; - -import static com.android.server.location.timezone.LocationTimeZoneManagerService.debugLog; -import static com.android.server.location.timezone.LocationTimeZoneManagerService.warnLog; -import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState; -import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED; -import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED; -import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN; -import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING; -import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN; -import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED; -import static com.android.server.location.timezone.TimeZoneProviderEvent.EVENT_TYPE_PERMANENT_FAILURE; -import static com.android.server.location.timezone.TimeZoneProviderEvent.EVENT_TYPE_SUGGESTION; -import static com.android.server.location.timezone.TimeZoneProviderEvent.EVENT_TYPE_UNCERTAIN; +package com.android.server.timezonedetector.location; + +import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.debugLog; +import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.warnLog; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED; +import static com.android.server.timezonedetector.location.TimeZoneProviderEvent.EVENT_TYPE_PERMANENT_FAILURE; +import static com.android.server.timezonedetector.location.TimeZoneProviderEvent.EVENT_TYPE_SUGGESTION; +import static com.android.server.timezonedetector.location.TimeZoneProviderEvent.EVENT_TYPE_UNCERTAIN; import android.annotation.DurationMillisLong; import android.annotation.NonNull; @@ -36,9 +36,9 @@ import android.os.RemoteCallback; import android.util.IndentingPrintWriter; import com.android.internal.annotations.GuardedBy; -import com.android.server.location.timezone.ThreadingDomain.SingleRunnableQueue; import com.android.server.timezonedetector.ConfigurationInternal; import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion; +import com.android.server.timezonedetector.location.ThreadingDomain.SingleRunnableQueue; import java.time.Duration; import java.util.List; diff --git a/services/core/java/com/android/server/location/timezone/HandlerThreadingDomain.java b/services/core/java/com/android/server/timezonedetector/location/HandlerThreadingDomain.java index 3055ff8a2b59..0dd2922bb240 100644 --- a/services/core/java/com/android/server/location/timezone/HandlerThreadingDomain.java +++ b/services/core/java/com/android/server/timezonedetector/location/HandlerThreadingDomain.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.location.timezone; +package com.android.server.timezonedetector.location; import android.annotation.DurationMillisLong; import android.annotation.NonNull; diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java index 54535eb50130..5bee7ee9d4b2 100644 --- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java +++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.location.timezone; +package com.android.server.timezonedetector.location; import static android.app.time.LocationTimeZoneManager.PRIMARY_PROVIDER_NAME; import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_DISABLED; diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerServiceState.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerServiceState.java index b1dd55f3d4fd..113926a265f5 100644 --- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerServiceState.java +++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerServiceState.java @@ -14,13 +14,13 @@ * limitations under the License. */ -package com.android.server.location.timezone; +package com.android.server.timezonedetector.location; import android.annotation.NonNull; import android.annotation.Nullable; -import com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState; import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion; +import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState; import java.util.ArrayList; import java.util.Collections; diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerShellCommand.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java index 6f9863c9bd09..b53150c729bc 100644 --- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerShellCommand.java +++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.server.location.timezone; +package com.android.server.timezonedetector.location; import static android.app.time.LocationTimeZoneManager.DUMP_STATE_OPTION_PROTO; import static android.app.time.LocationTimeZoneManager.PRIMARY_PROVIDER_NAME; @@ -28,13 +28,13 @@ import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_SET_PROVIDE import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_START; import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_STOP; -import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED; -import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED; -import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN; -import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING; -import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN; -import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED; -import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_UNKNOWN; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_UNKNOWN; import android.annotation.NonNull; import android.app.time.GeolocationTimeZoneSuggestionProto; @@ -47,8 +47,8 @@ import android.util.IndentingPrintWriter; import android.util.proto.ProtoOutputStream; import com.android.internal.util.dump.DualDumpOutputStream; -import com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.ProviderStateEnum; import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion; +import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.ProviderStateEnum; import java.io.FileDescriptor; import java.io.PrintWriter; diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProvider.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java index 9a7b7750659c..ef2f357b8c3e 100644 --- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProvider.java +++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java @@ -14,22 +14,22 @@ * limitations under the License. */ -package com.android.server.location.timezone; +package com.android.server.timezonedetector.location; import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_ERROR_KEY; import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_SUCCESS_KEY; -import static com.android.server.location.timezone.LocationTimeZoneManagerService.debugLog; -import static com.android.server.location.timezone.LocationTimeZoneManagerService.warnLog; -import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED; -import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED; -import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN; -import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING; -import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN; -import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED; -import static com.android.server.location.timezone.TimeZoneProviderEvent.EVENT_TYPE_PERMANENT_FAILURE; -import static com.android.server.location.timezone.TimeZoneProviderEvent.EVENT_TYPE_SUGGESTION; -import static com.android.server.location.timezone.TimeZoneProviderEvent.EVENT_TYPE_UNCERTAIN; +import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.debugLog; +import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.warnLog; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED; +import static com.android.server.timezonedetector.location.TimeZoneProviderEvent.EVENT_TYPE_PERMANENT_FAILURE; +import static com.android.server.timezonedetector.location.TimeZoneProviderEvent.EVENT_TYPE_SUGGESTION; +import static com.android.server.timezonedetector.location.TimeZoneProviderEvent.EVENT_TYPE_UNCERTAIN; import android.annotation.ElapsedRealtimeLong; import android.annotation.IntDef; @@ -42,11 +42,11 @@ import android.os.SystemClock; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.ProviderStateEnum; -import com.android.server.location.timezone.ThreadingDomain.SingleRunnableQueue; import com.android.server.timezonedetector.ConfigurationInternal; import com.android.server.timezonedetector.Dumpable; import com.android.server.timezonedetector.ReferenceWithHistory; +import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.ProviderStateEnum; +import com.android.server.timezonedetector.location.ThreadingDomain.SingleRunnableQueue; import java.time.Duration; import java.util.ArrayList; diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderController.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderController.java index ec2bc13b8a16..b4aff3e005ad 100644 --- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderController.java +++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderController.java @@ -14,17 +14,17 @@ * limitations under the License. */ -package com.android.server.location.timezone; +package com.android.server.timezonedetector.location; import android.annotation.DurationMillisLong; import android.annotation.NonNull; import android.os.Handler; import com.android.internal.annotations.VisibleForTesting; -import com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState; import com.android.server.timezonedetector.ConfigurationInternal; import com.android.server.timezonedetector.Dumpable; import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion; +import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState; import java.time.Duration; import java.util.Objects; diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderProxy.java index 8368b5ed5d75..43b1b5f017b2 100644 --- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderProxy.java +++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderProxy.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.location.timezone; +package com.android.server.timezonedetector.location; import android.annotation.NonNull; import android.annotation.Nullable; diff --git a/services/core/java/com/android/server/location/timezone/NullLocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/timezonedetector/location/NullLocationTimeZoneProviderProxy.java index c2abbf9a1b8c..1f45e828aad4 100644 --- a/services/core/java/com/android/server/location/timezone/NullLocationTimeZoneProviderProxy.java +++ b/services/core/java/com/android/server/timezonedetector/location/NullLocationTimeZoneProviderProxy.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.location.timezone; +package com.android.server.timezonedetector.location; import android.annotation.NonNull; import android.annotation.Nullable; diff --git a/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java index 0904ba419b3d..38211efc1c63 100644 --- a/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java +++ b/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java @@ -14,13 +14,13 @@ * limitations under the License. */ -package com.android.server.location.timezone; +package com.android.server.timezonedetector.location; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_ERROR_KEY; import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_SUCCESS_KEY; -import static com.android.server.location.timezone.LocationTimeZoneManagerService.warnLog; +import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.warnLog; import android.Manifest; import android.annotation.NonNull; diff --git a/services/core/java/com/android/server/location/timezone/SimulatedLocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/timezonedetector/location/SimulatedLocationTimeZoneProviderProxy.java index 66ccaed25e32..02b0a849c1b1 100644 --- a/services/core/java/com/android/server/location/timezone/SimulatedLocationTimeZoneProviderProxy.java +++ b/services/core/java/com/android/server/timezonedetector/location/SimulatedLocationTimeZoneProviderProxy.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.location.timezone; +package com.android.server.timezonedetector.location; import static android.app.time.LocationTimeZoneManager.SIMULATED_PROVIDER_TEST_COMMAND_ON_BIND; import static android.app.time.LocationTimeZoneManager.SIMULATED_PROVIDER_TEST_COMMAND_ON_UNBIND; diff --git a/services/core/java/com/android/server/location/timezone/TestCommand.java b/services/core/java/com/android/server/timezonedetector/location/TestCommand.java index 0df3ca087fc7..21482ea6ff79 100644 --- a/services/core/java/com/android/server/location/timezone/TestCommand.java +++ b/services/core/java/com/android/server/timezonedetector/location/TestCommand.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.location.timezone; +package com.android.server.timezonedetector.location; import android.annotation.NonNull; import android.net.Uri; diff --git a/services/core/java/com/android/server/location/timezone/ThreadingDomain.java b/services/core/java/com/android/server/timezonedetector/location/ThreadingDomain.java index 4ada6f50b40e..9e3497f92dc0 100644 --- a/services/core/java/com/android/server/location/timezone/ThreadingDomain.java +++ b/services/core/java/com/android/server/timezonedetector/location/ThreadingDomain.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.location.timezone; +package com.android.server.timezonedetector.location; import android.annotation.DurationMillisLong; import android.annotation.NonNull; diff --git a/services/core/java/com/android/server/location/timezone/TimeZoneProviderEvent.java b/services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderEvent.java index 2d6f8ad446f0..3e224e03fda0 100644 --- a/services/core/java/com/android/server/location/timezone/TimeZoneProviderEvent.java +++ b/services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderEvent.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.location.timezone; +package com.android.server.timezonedetector.location; import android.annotation.IntDef; import android.annotation.NonNull; diff --git a/services/core/java/com/android/server/location/timezone/TimeZoneProviderRequest.java b/services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderRequest.java index 649a74bbed49..14820319d9df 100644 --- a/services/core/java/com/android/server/location/timezone/TimeZoneProviderRequest.java +++ b/services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderRequest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.location.timezone; +package com.android.server.timezonedetector.location; import android.annotation.NonNull; import android.annotation.Nullable; diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java index c36375ef0af5..53552526c936 100644 --- a/services/core/java/com/android/server/vibrator/VibrationThread.java +++ b/services/core/java/com/android/server/vibrator/VibrationThread.java @@ -74,6 +74,7 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi void onVibrationEnded(long vibrationId, Vibration.Status status); } + private final Object mLock = new Object(); private final WorkSource mWorkSource = new WorkSource(); private final PowerManager.WakeLock mWakeLock; private final IBatteryStats mBatteryStatsService; @@ -81,10 +82,10 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi private final VibrationCallbacks mCallbacks; private final SparseArray<VibratorController> mVibrators; - @GuardedBy("this") + @GuardedBy("mLock") @Nullable private VibrateStep mCurrentVibrateStep; - @GuardedBy("this") + @GuardedBy("mLock") private boolean mForceStop; // TODO(b/159207608): Remove this constructor once VibratorService is removed @@ -113,6 +114,10 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi } } + public Vibration getVibration() { + return mVibration; + } + @Override public void binderDied() { cancel(); @@ -136,15 +141,15 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi /** Cancel current vibration and shuts down the thread gracefully. */ public void cancel() { - synchronized (this) { + synchronized (mLock) { mForceStop = true; - notify(); + mLock.notify(); } } /** Notify current vibration that a step has completed on given vibrator. */ public void vibratorComplete(int vibratorId) { - synchronized (this) { + synchronized (mLock) { if (mCurrentVibrateStep != null) { mCurrentVibrateStep.vibratorComplete(vibratorId); } @@ -168,7 +173,7 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi final int stepCount = steps.size(); for (int i = 0; i < stepCount; i++) { Step step = steps.get(i); - synchronized (this) { + synchronized (mLock) { if (step instanceof VibrateStep) { mCurrentVibrateStep = (VibrateStep) step; } else { @@ -295,21 +300,48 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi * Sleeps until given {@code wakeUpTime}. * * <p>This stops immediately when {@link #cancel()} is called. + * + * @return true if waited until wake-up time, false if it was cancelled. */ - private void waitUntil(long wakeUpTime) { - synchronized (this) { + private boolean waitUntil(long wakeUpTime) { + synchronized (mLock) { long durationRemaining = wakeUpTime - SystemClock.uptimeMillis(); while (durationRemaining > 0) { try { - VibrationThread.this.wait(durationRemaining); + mLock.wait(durationRemaining); } catch (InterruptedException e) { } if (mForceStop) { - break; + return false; + } + durationRemaining = wakeUpTime - SystemClock.uptimeMillis(); + } + } + return true; + } + + /** + * Sleeps until given {@link VibrateStep#isVibrationComplete()}, or until {@code wakeUpTime}. + * + * <p>This stops immediately when {@link #cancel()} is called. + * + * @return true if finished on vibration complete, false if it was cancelled or timed out. + */ + private boolean waitForVibrationComplete(VibrateStep step, long wakeUpTime) { + synchronized (mLock) { + long durationRemaining = wakeUpTime - SystemClock.uptimeMillis(); + while (!step.isVibrationComplete() && durationRemaining > 0) { + try { + mLock.wait(durationRemaining); + } catch (InterruptedException e) { + } + if (mForceStop) { + return false; } durationRemaining = wakeUpTime - SystemClock.uptimeMillis(); } } + return step.isVibrationComplete(); } private void noteVibratorOn(long duration) { @@ -341,6 +373,9 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi private interface VibrateStep extends Step { /** Callback to notify a vibrator has finished playing a effect. */ void vibratorComplete(int vibratorId); + + /** Returns true if the vibration played by this step is complete. */ + boolean isVibrationComplete(); } /** Represent a vibration on a single vibrator. */ @@ -348,11 +383,20 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi private final VibratorController mVibrator; private final VibrationEffect mEffect; + @GuardedBy("mLock") + private boolean mVibrationComplete; + SingleVibrateStep(VibratorController vibrator, VibrationEffect effect) { mVibrator = vibrator; mEffect = effect; } + @GuardedBy("mLock") + @Override + public boolean isVibrationComplete() { + return mVibrationComplete; + } + @Override public void vibratorComplete(int vibratorId) { if (mVibrator.getVibratorInfo().getId() != vibratorId) { @@ -364,8 +408,9 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi return; } mVibrator.off(); - synchronized (VibrationThread.this) { - VibrationThread.this.notify(); + synchronized (mLock) { + mVibrationComplete = true; + mLock.notify(); } } @@ -384,12 +429,13 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi noteVibratorOn(duration); // Vibration is playing with no need to control amplitudes, just wait for native // callback or timeout. - waitUntil(startTime + duration + CALLBACKS_EXTRA_TIMEOUT); - if (mForceStop) { - mVibrator.off(); - return Vibration.Status.CANCELLED; + if (waitForVibrationComplete(this, + startTime + duration + CALLBACKS_EXTRA_TIMEOUT)) { + return Vibration.Status.FINISHED; } - return Vibration.Status.FINISHED; + // Timed out or vibration cancelled. Stop vibrator anyway. + mVibrator.off(); + return mForceStop ? Vibration.Status.CANCELLED : Vibration.Status.FINISHED; } startTime = SystemClock.uptimeMillis(); @@ -407,8 +453,7 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi noteVibratorOn(duration); } while (amplitudeStep != null) { - waitUntil(amplitudeStep.startTime); - if (mForceStop) { + if (!waitUntil(amplitudeStep.startTime)) { mVibrator.off(); return Vibration.Status.CANCELLED; } @@ -482,7 +527,7 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi private final int mRequiredCapabilities; private final int[] mVibratorIds; - @GuardedBy("VibrationThread.this") + @GuardedBy("mLock") private int mActiveVibratorCounter; SyncedVibrateStep(SparseArray<VibrationEffect> effects) { @@ -496,6 +541,12 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi } } + @GuardedBy("mLock") + @Override + public boolean isVibrationComplete() { + return mActiveVibratorCounter <= 0; + } + @Override public void vibratorComplete(int vibratorId) { VibrationEffect effect = mEffects.get(vibratorId); @@ -508,10 +559,9 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi return; } mVibrators.get(vibratorId).off(); - synchronized (VibrationThread.this) { - if (--mActiveVibratorCounter <= 0) { - VibrationThread.this.notify(); - } + synchronized (mLock) { + --mActiveVibratorCounter; + mLock.notify(); } } @@ -532,8 +582,7 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi while (!nextSteps.isEmpty()) { AmplitudeStep step = nextSteps.poll(); - waitUntil(step.startTime); - if (mForceStop) { + if (!waitUntil(step.startTime)) { stopAllVibrators(); return Vibration.Status.CANCELLED; } @@ -541,7 +590,7 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi AmplitudeStep nextStep = step.nextStep(); if (nextStep == null) { // This vibrator has finished playing the effect for this step. - synchronized (VibrationThread.this) { + synchronized (mLock) { mActiveVibratorCounter--; } } else { @@ -549,19 +598,18 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi } } - // All OneShot and Waveform effects have finished. Just wait for the other effects - // to end via native callbacks before finishing this synced step. - synchronized (VibrationThread.this) { - if (mActiveVibratorCounter > 0) { - waitUntil(startTime + timeout + CALLBACKS_EXTRA_TIMEOUT); + synchronized (mLock) { + // All OneShot and Waveform effects have finished. Just wait for the other + // effects to end via native callbacks before finishing this synced step. + final long wakeUpTime = startTime + timeout + CALLBACKS_EXTRA_TIMEOUT; + if (mActiveVibratorCounter <= 0 || waitForVibrationComplete(this, wakeUpTime)) { + return Vibration.Status.FINISHED; } - } - if (mForceStop) { + + // Timed out or vibration cancelled. Stop all vibrators anyway. stopAllVibrators(); - return Vibration.Status.CANCELLED; + return mForceStop ? Vibration.Status.CANCELLED : Vibration.Status.FINISHED; } - - return Vibration.Status.FINISHED; } finally { if (timeout > 0) { noteVibratorOff(); @@ -774,8 +822,10 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi if (DEBUG) { Slog.d(TAG, "DelayStep of " + mDelay + "ms starting..."); } - waitUntil(SystemClock.uptimeMillis() + mDelay); - return mForceStop ? Vibration.Status.CANCELLED : Vibration.Status.FINISHED; + if (waitUntil(SystemClock.uptimeMillis() + mDelay)) { + return Vibration.Status.FINISHED; + } + return Vibration.Status.CANCELLED; } finally { if (DEBUG) { Slog.d(TAG, "DelayStep done."); diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index baa672c4805c..1692df6d82cd 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -16,7 +16,6 @@ package com.android.server.wm; -import static android.os.Build.IS_DEBUGGABLE; import static android.view.InsetsState.ITYPE_CLIMATE_BAR; import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_IME; @@ -553,11 +552,6 @@ class InsetsSourceProvider { // TODO: use 0 alpha and remove t.hide() once b/138459974 is fixed. t.setAlpha(animationLeash, 1 /* alpha */); t.hide(animationLeash); - - // TODO(b/175954493): Remove this after finding root cause. - if (IS_DEBUGGABLE) { - animationLeash.setDebugRelease(true); - } } ProtoLog.i(WM_DEBUG_IME, "ControlAdapter startAnimation mSource: %s controlTarget: %s", mSource, diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 12595af1db7f..fd8dfc32e5d3 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -326,13 +326,15 @@ public final class SystemServer implements Dumpable { private static final String TIME_ZONE_DETECTOR_SERVICE_CLASS = "com.android.server.timezonedetector.TimeZoneDetectorService$Lifecycle"; private static final String LOCATION_TIME_ZONE_MANAGER_SERVICE_CLASS = - "com.android.server.location.timezone.LocationTimeZoneManagerService$Lifecycle"; + "com.android.server.timezonedetector.location.LocationTimeZoneManagerService$Lifecycle"; private static final String GNSS_TIME_UPDATE_SERVICE_CLASS = "com.android.server.timedetector.GnssTimeUpdateService$Lifecycle"; private static final String ACCESSIBILITY_MANAGER_SERVICE_CLASS = "com.android.server.accessibility.AccessibilityManagerService$Lifecycle"; private static final String ADB_SERVICE_CLASS = "com.android.server.adb.AdbService$Lifecycle"; + private static final String SPEECH_RECOGNITION_MANAGER_SERVICE_CLASS = + "com.android.server.speech.SpeechRecognitionManagerService"; private static final String APP_PREDICTION_MANAGER_SERVICE_CLASS = "com.android.server.appprediction.AppPredictionManagerService"; private static final String CONTENT_SUGGESTIONS_SERVICE_CLASS = @@ -1629,12 +1631,21 @@ public final class SystemServer implements Dumpable { "MusicRecognitionManagerService not defined by OEM or disabled by flag"); } - startContentCaptureService(context, t); startAttentionService(context, t); startRotationResolverService(context, t); startSystemCaptionsManagerService(context, t); + // System Speech Recognition Service + if (deviceHasConfigString(context, + R.string.config_defaultOnDeviceSpeechRecognitionService)) { + t.traceBegin("StartSpeechRecognitionManagerService"); + mSystemServiceManager.startService(SPEECH_RECOGNITION_MANAGER_SERVICE_CLASS); + t.traceEnd(); + } else { + Slog.d(TAG, "System speech recognition is not defined by OEM"); + } + // App prediction manager service if (deviceHasConfigString(context, R.string.config_defaultAppPredictionService)) { t.traceBegin("StartAppPredictionService"); diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/OWNERS b/services/tests/servicestests/src/com/android/server/location/timezone/OWNERS deleted file mode 100644 index 28aff188dbd8..000000000000 --- a/services/tests/servicestests/src/com/android/server/location/timezone/OWNERS +++ /dev/null @@ -1,3 +0,0 @@ -# Bug component: 847766 -nfuller@google.com -include /core/java/android/app/timedetector/OWNERS diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java index 972b3bb8b459..4284240c72b4 100644 --- a/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java @@ -13,18 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.server.location.timezone; - -import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED; -import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN; -import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING; -import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN; -import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED; -import static com.android.server.location.timezone.TestSupport.USER1_CONFIG_GEO_DETECTION_DISABLED; -import static com.android.server.location.timezone.TestSupport.USER1_CONFIG_GEO_DETECTION_ENABLED; -import static com.android.server.location.timezone.TestSupport.USER2_CONFIG_GEO_DETECTION_ENABLED; -import static com.android.server.location.timezone.TimeZoneProviderEvent.createPermanentFailureEvent; -import static com.android.server.location.timezone.TimeZoneProviderEvent.createUncertainEvent; +package com.android.server.timezonedetector.location; + +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED; +import static com.android.server.timezonedetector.location.TestSupport.USER1_CONFIG_GEO_DETECTION_DISABLED; +import static com.android.server.timezonedetector.location.TestSupport.USER1_CONFIG_GEO_DETECTION_ENABLED; +import static com.android.server.timezonedetector.location.TestSupport.USER2_CONFIG_GEO_DETECTION_ENABLED; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -40,10 +38,10 @@ import android.platform.test.annotations.Presubmit; import android.service.timezone.TimeZoneProviderSuggestion; import android.util.IndentingPrintWriter; -import com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.ProviderStateEnum; import com.android.server.timezonedetector.ConfigurationInternal; import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion; import com.android.server.timezonedetector.TestState; +import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.ProviderStateEnum; import org.junit.Before; import org.junit.Test; @@ -66,9 +64,9 @@ public class ControllerImplTest { private static final TimeZoneProviderEvent USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2 = createSuggestionEvent(asList("Europe/Paris")); private static final TimeZoneProviderEvent USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT = - createUncertainEvent(); + TimeZoneProviderEvent.createUncertainEvent(); private static final TimeZoneProviderEvent USER1_PERM_FAILURE_LOCATION_TIME_ZONE_EVENT = - createPermanentFailureEvent("Test"); + TimeZoneProviderEvent.createPermanentFailureEvent("Test"); private TestThreadingDomain mTestThreadingDomain; private TestCallback mTestCallback; diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/HandlerThreadingDomainTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/HandlerThreadingDomainTest.java index 02de24de435e..e7dd97949bb0 100644 --- a/services/tests/servicestests/src/com/android/server/location/timezone/HandlerThreadingDomainTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/HandlerThreadingDomainTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.server.location.timezone; +package com.android.server.timezonedetector.location; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertSame; @@ -24,7 +24,7 @@ import android.os.Handler; import android.os.HandlerThread; import android.platform.test.annotations.Presubmit; -import com.android.server.location.timezone.ThreadingDomain.SingleRunnableQueue; +import com.android.server.timezonedetector.location.ThreadingDomain.SingleRunnableQueue; import org.junit.After; import org.junit.Before; diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/LocationTimeZoneProviderTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java index cb292db50115..095c868fc74c 100644 --- a/services/tests/servicestests/src/com/android/server/location/timezone/LocationTimeZoneProviderTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java @@ -13,17 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.server.location.timezone; +package com.android.server.timezonedetector.location; import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_ERROR_KEY; import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_SUCCESS_KEY; -import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED; -import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN; -import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING; -import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN; -import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED; -import static com.android.server.location.timezone.TestSupport.USER1_CONFIG_GEO_DETECTION_ENABLED; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED; +import static com.android.server.timezonedetector.location.TestSupport.USER1_CONFIG_GEO_DETECTION_ENABLED; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -40,10 +40,10 @@ import android.platform.test.annotations.Presubmit; import android.service.timezone.TimeZoneProviderSuggestion; import android.util.IndentingPrintWriter; -import com.android.server.location.timezone.LocationTimeZoneProvider.ProviderListener; -import com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState; import com.android.server.timezonedetector.ConfigurationInternal; import com.android.server.timezonedetector.TestState; +import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderListener; +import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState; import org.junit.Before; import org.junit.Test; diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/TestSupport.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java index 48105634c69d..d319488ba73b 100644 --- a/services/tests/servicestests/src/com/android/server/location/timezone/TestSupport.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.server.location.timezone; +package com.android.server.timezonedetector.location; import android.annotation.UserIdInt; diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/TestThreadingDomain.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestThreadingDomain.java index b1a5ff9b549c..e08fea083d81 100644 --- a/services/tests/servicestests/src/com/android/server/location/timezone/TestThreadingDomain.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestThreadingDomain.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.server.location.timezone; +package com.android.server.timezonedetector.location; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 88aab56bbafe..137543a8147c 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -64,6 +64,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY; import static android.net.NetworkCapabilities.NET_CAPABILITY_RCS; @@ -7326,36 +7327,66 @@ public class ConnectivityServiceTest { b2.expectBroadcast(); } + /** + * Test mutable and requestable network capabilities such as + * {@link NetworkCapabilities#NET_CAPABILITY_TRUSTED} and + * {@link NetworkCapabilities#NET_CAPABILITY_NOT_VCN_MANAGED}. Verify that the + * {@code ConnectivityService} re-assign the networks accordingly. + */ @Test - public final void testLoseTrusted() throws Exception { - final NetworkRequest trustedRequest = new NetworkRequest.Builder() - .addCapability(NET_CAPABILITY_TRUSTED) - .build(); - final TestNetworkCallback trustedCallback = new TestNetworkCallback(); - mCm.requestNetwork(trustedRequest, trustedCallback); - - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.connect(true); - trustedCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId)); - reset(mMockNetd); - - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(true); - trustedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); - verify(mMockNetd).networkSetDefault(eq(mWiFiNetworkAgent.getNetwork().netId)); - reset(mMockNetd); + public final void testLoseMutableAndRequestableCaps() throws Exception { + final int[] testCaps = new int [] { + NET_CAPABILITY_TRUSTED, + NET_CAPABILITY_NOT_VCN_MANAGED + }; + for (final int testCap : testCaps) { + // Create requests with and without the testing capability. + final TestNetworkCallback callbackWithCap = new TestNetworkCallback(); + final TestNetworkCallback callbackWithoutCap = new TestNetworkCallback(); + mCm.requestNetwork(new NetworkRequest.Builder().addCapability(testCap).build(), + callbackWithCap); + mCm.requestNetwork(new NetworkRequest.Builder().removeCapability(testCap).build(), + callbackWithoutCap); + + // Setup networks with testing capability and verify the default network changes. + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.addCapability(testCap); + mCellNetworkAgent.connect(true); + callbackWithCap.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + callbackWithoutCap.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId)); + reset(mMockNetd); - mWiFiNetworkAgent.removeCapability(NET_CAPABILITY_TRUSTED); - trustedCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); - verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId)); - reset(mMockNetd); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.addCapability(testCap); + mWiFiNetworkAgent.connect(true); + callbackWithCap.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); + callbackWithoutCap.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); + verify(mMockNetd).networkSetDefault(eq(mWiFiNetworkAgent.getNetwork().netId)); + reset(mMockNetd); + + // Remove the testing capability on wifi, verify the callback and default network + // changes back to cellular. + mWiFiNetworkAgent.removeCapability(testCap); + callbackWithCap.expectAvailableCallbacksValidated(mCellNetworkAgent); + callbackWithoutCap.expectCapabilitiesWithout(testCap, mWiFiNetworkAgent); + // TODO: Test default network changes for NOT_VCN_MANAGED once the default request has + // it. + if (testCap == NET_CAPABILITY_TRUSTED) { + verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId)); + reset(mMockNetd); + } - mCellNetworkAgent.removeCapability(NET_CAPABILITY_TRUSTED); - trustedCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - verify(mMockNetd).networkClearDefault(); + mCellNetworkAgent.removeCapability(testCap); + callbackWithCap.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + callbackWithoutCap.assertNoCallback(); + if (testCap == NET_CAPABILITY_TRUSTED) { + verify(mMockNetd).networkClearDefault(); + } - mCm.unregisterNetworkCallback(trustedCallback); + mCm.unregisterNetworkCallback(callbackWithCap); + mCm.unregisterNetworkCallback(callbackWithoutCap); + } } @Test |