diff options
207 files changed, 4316 insertions, 1880 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..0f1296ee01fe 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 @@ -12963,11 +12964,100 @@ package android.telephony.ims { field public static final String RCS_PROFILE_2_3 = "UP_2.3"; } + public final class RcsContactPresenceTuple implements android.os.Parcelable { + method public int describeContents(); + method @Nullable public android.net.Uri getContactUri(); + method @Nullable public android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities getServiceCapabilities(); + method @Nullable public String getServiceDescription(); + method @NonNull public String getServiceId(); + method @NonNull public String getServiceVersion(); + method @NonNull public String getStatus(); + method @Nullable public String getTimestamp(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsContactPresenceTuple> CREATOR; + field public static final String SERVICE_ID_CALL_COMPOSER = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.callcomposer"; + field public static final String SERVICE_ID_CHATBOT = "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.chatbot"; + field public static final String SERVICE_ID_CHATBOT_ROLE = "org.gsma.rcs.isbot"; + field public static final String SERVICE_ID_CHATBOT_STANDALONE = " org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.chatbot.sa"; + field public static final String SERVICE_ID_CHAT_V1 = "org.openmobilealliance:IM-session"; + field public static final String SERVICE_ID_CHAT_V2 = "org.openmobilealliance:ChatSession"; + field public static final String SERVICE_ID_FT = "org.openmobilealliance:File-Transfer-HTTP"; + field public static final String SERVICE_ID_FT_OVER_SMS = "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.ftsms"; + field public static final String SERVICE_ID_GEO_PUSH = "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.geopush"; + field public static final String SERVICE_ID_GEO_PUSH_VIA_SMS = "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.geosms"; + field public static final String SERVICE_ID_MMTEL = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.mmtel"; + field public static final String SERVICE_ID_POST_CALL = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.callunanswered"; + field public static final String SERVICE_ID_SHARED_MAP = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.sharedmap"; + field public static final String SERVICE_ID_SHARED_SKETCH = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.sharedsketch"; + field public static final String TUPLE_BASIC_STATUS_CLOSED = "closed"; + field public static final String TUPLE_BASIC_STATUS_OPEN = "open"; + } + + public static final class RcsContactPresenceTuple.Builder { + ctor public RcsContactPresenceTuple.Builder(@NonNull String, @NonNull String, @NonNull String); + method @NonNull public android.telephony.ims.RcsContactPresenceTuple build(); + method @NonNull public android.telephony.ims.RcsContactPresenceTuple.Builder setContactUri(@NonNull android.net.Uri); + method @NonNull public android.telephony.ims.RcsContactPresenceTuple.Builder setServiceCapabilities(@NonNull android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities); + method @NonNull public android.telephony.ims.RcsContactPresenceTuple.Builder setServiceDescription(@NonNull String); + method @NonNull public android.telephony.ims.RcsContactPresenceTuple.Builder setTimestamp(@NonNull String); + } + + public static final class RcsContactPresenceTuple.ServiceCapabilities implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public java.util.List<java.lang.String> getSupportedDuplexModes(); + method @NonNull public java.util.List<java.lang.String> getUnsupportedDuplexModes(); + method public boolean isAudioCapable(); + method public boolean isVideoCapable(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities> CREATOR; + field public static final String DUPLEX_MODE_FULL = "full"; + field public static final String DUPLEX_MODE_HALF = "half"; + field public static final String DUPLEX_MODE_RECEIVE_ONLY = "receive-only"; + field public static final String DUPLEX_MODE_SEND_ONLY = "send-only"; + } + + public static final class RcsContactPresenceTuple.ServiceCapabilities.Builder { + ctor public RcsContactPresenceTuple.ServiceCapabilities.Builder(boolean, boolean); + method @NonNull public android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities.Builder addSupportedDuplexMode(@NonNull String); + method @NonNull public android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities.Builder addUnsupportedDuplexMode(@NonNull String); + method @NonNull public android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities build(); + } + + public final class RcsContactUceCapability implements android.os.Parcelable { + method public int describeContents(); + method public int getCapabilityMechanism(); + method @Nullable public android.telephony.ims.RcsContactPresenceTuple getCapabilityTuple(@NonNull String); + method @NonNull public java.util.List<android.telephony.ims.RcsContactPresenceTuple> getCapabilityTuples(); + method @NonNull public android.net.Uri getContactUri(); + method public int getRequestResult(); + method public int getSourceType(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field public static final int CAPABILITY_MECHANISM_OPTIONS = 2; // 0x2 + field public static final int CAPABILITY_MECHANISM_PRESENCE = 1; // 0x1 + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsContactUceCapability> CREATOR; + field public static final int REQUEST_RESULT_FOUND = 3; // 0x3 + field public static final int REQUEST_RESULT_NOT_FOUND = 2; // 0x2 + field public static final int REQUEST_RESULT_NOT_ONLINE = 1; // 0x1 + field public static final int REQUEST_RESULT_UNKNOWN = 0; // 0x0 + field public static final int SOURCE_TYPE_CACHED = 1; // 0x1 + field public static final int SOURCE_TYPE_NETWORK = 0; // 0x0 + } + + public static final class RcsContactUceCapability.PresenceBuilder { + ctor public RcsContactUceCapability.PresenceBuilder(@NonNull android.net.Uri, int, int); + method @NonNull public android.telephony.ims.RcsContactUceCapability.PresenceBuilder addCapabilityTuple(@NonNull android.telephony.ims.RcsContactPresenceTuple); + method @NonNull public android.telephony.ims.RcsContactUceCapability.PresenceBuilder addCapabilityTuples(@NonNull java.util.List<android.telephony.ims.RcsContactPresenceTuple>); + method @NonNull public android.telephony.ims.RcsContactUceCapability build(); + } + public class RcsUceAdapter { method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void addOnPublishStateChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.OnPublishStateChangedListener) throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getUcePublishState() throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void removeOnPublishStateChangedListener(@NonNull android.telephony.ims.RcsUceAdapter.OnPublishStateChangedListener) throws android.telephony.ims.ImsException; + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void requestAvailability(@NonNull android.net.Uri, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException; + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void requestCapabilities(@NonNull java.util.List<android.net.Uri>, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException; + field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2 field public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 1; // 0x1 field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G = 7; // 0x7 field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G = 6; // 0x6 @@ -12980,6 +13070,18 @@ package android.telephony.ims { field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED = 11; // 0xb field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN = 8; // 0x8 field public static final int CAPABILITY_UPDATE_TRIGGER_UNKNOWN = 0; // 0x0 + field public static final int ERROR_FORBIDDEN = 6; // 0x6 + field public static final int ERROR_GENERIC_FAILURE = 1; // 0x1 + field public static final int ERROR_INSUFFICIENT_MEMORY = 10; // 0xa + field public static final int ERROR_LOST_NETWORK = 11; // 0xb + field public static final int ERROR_NOT_AUTHORIZED = 5; // 0x5 + field public static final int ERROR_NOT_AVAILABLE = 3; // 0x3 + field public static final int ERROR_NOT_ENABLED = 2; // 0x2 + field public static final int ERROR_NOT_FOUND = 7; // 0x7 + field public static final int ERROR_NOT_REGISTERED = 4; // 0x4 + field public static final int ERROR_REQUEST_TIMEOUT = 9; // 0x9 + field public static final int ERROR_REQUEST_TOO_LARGE = 8; // 0x8 + field public static final int ERROR_SERVER_UNAVAILABLE = 12; // 0xc field public static final int PUBLISH_STATE_NOT_PUBLISHED = 2; // 0x2 field public static final int PUBLISH_STATE_OK = 1; // 0x1 field public static final int PUBLISH_STATE_OTHER_ERROR = 6; // 0x6 @@ -12988,6 +13090,12 @@ package android.telephony.ims { field public static final int PUBLISH_STATE_VOICE_PROVISION_ERROR = 3; // 0x3 } + public static interface RcsUceAdapter.CapabilitiesCallback { + method public void onCapabilitiesReceived(@NonNull java.util.List<android.telephony.ims.RcsContactUceCapability>); + method public void onComplete(); + method public void onError(int, long); + } + public static interface RcsUceAdapter.OnPublishStateChangedListener { method public void onPublishStateChange(int); } @@ -13399,6 +13507,7 @@ package android.telephony.ims.stub { public class RcsCapabilityExchangeImplBase { ctor public RcsCapabilityExchangeImplBase(@NonNull java.util.concurrent.Executor); method public void publishCapabilities(@NonNull String, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.PublishResponseCallback); + method public void subscribeForCapabilities(@NonNull java.util.List<android.net.Uri>, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.SubscribeResponseCallback); field public static final int COMMAND_CODE_FETCH_ERROR = 3; // 0x3 field public static final int COMMAND_CODE_GENERIC_FAILURE = 1; // 0x1 field public static final int COMMAND_CODE_INSUFFICIENT_MEMORY = 5; // 0x5 @@ -13417,6 +13526,14 @@ package android.telephony.ims.stub { method public void onNetworkResponse(@IntRange(from=100, to=699) int, @NonNull String) throws android.telephony.ims.ImsException; } + public static interface RcsCapabilityExchangeImplBase.SubscribeResponseCallback { + method public void onCommandError(int) throws android.telephony.ims.ImsException; + method public void onNetworkResponse(@IntRange(from=100, to=699) int, @NonNull String) throws android.telephony.ims.ImsException; + method public void onNotifyCapabilitiesUpdate(@NonNull java.util.List<java.lang.String>) throws android.telephony.ims.ImsException; + method public void onResourceTerminated(@NonNull java.util.List<android.util.Pair<android.net.Uri,java.lang.String>>) throws android.telephony.ims.ImsException; + method public void onTerminated(@NonNull String, long) throws android.telephony.ims.ImsException; + } + public interface SipDelegate { method public void closeDialog(@NonNull String); method public void notifyMessageReceiveError(@NonNull String, int); diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 415105f88f25..bdd541a2f0ac 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -2669,6 +2669,10 @@ public class Activity extends ContextThemeWrapper dispatchActivityDestroyed(); notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_STOP); + + if (mUiTranslationController != null) { + mUiTranslationController.onActivityDestroyed(); + } } /** @@ -7877,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 @@ -7899,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) { @@ -8004,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); @@ -8055,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(); @@ -8073,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() { @@ -8081,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*/); @@ -8126,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(); @@ -8141,6 +8169,7 @@ public class Activity extends ContextThemeWrapper mVoiceInteractor.detachActivity(); } dispatchActivityPostDestroyed(); + Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); } final void dispatchMultiWindowModeChanged(boolean isInMultiWindowMode, @@ -8450,9 +8479,8 @@ public class Activity extends ContextThemeWrapper } /** @hide */ - @Override @Nullable - public final View autofillClientFindViewByAutofillIdTraversal(AutofillId autofillId) { + public View findViewByAutofillIdTraversal(@NonNull AutofillId autofillId) { final ArrayList<ViewRootImpl> roots = WindowManagerGlobal.getInstance().getRootViews(getActivityToken()); for (int rootNum = 0; rootNum < roots.size(); rootNum++) { @@ -8465,12 +8493,18 @@ public class Activity extends ContextThemeWrapper } } } - return null; } /** @hide */ @Override + @Nullable + public final View autofillClientFindViewByAutofillIdTraversal(AutofillId autofillId) { + return findViewByAutofillIdTraversal(autofillId); + } + + /** @hide */ + @Override public final @NonNull boolean[] autofillClientGetViewVisibility( @NonNull AutofillId[] autofillIds) { final int autofillIdCount = autofillIds.length; diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java index d465b220f854..401f8cc13bad 100644 --- a/core/java/android/app/ActivityClient.java +++ b/core/java/android/app/ActivityClient.java @@ -26,6 +26,8 @@ import android.os.RemoteException; import android.util.Singleton; import android.view.RemoteAnimationDefinition; +import com.android.internal.policy.IKeyguardDismissCallback; + /** * Provides the activity associated operations that communicate with system. * @@ -431,6 +433,37 @@ public class ActivityClient { } } + /** + * Restart the process and activity to adopt the latest configuration for size compat mode. + * This only takes effect for visible activity because invisible background activity can be + * restarted naturally when it becomes visible. + */ + public void restartActivityProcessIfVisible(IBinder token) { + try { + getActivityClientController().restartActivityProcessIfVisible(token); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** Removes the snapshot of home task. */ + public void invalidateHomeTaskSnapshot(IBinder homeToken) { + try { + getActivityClientController().invalidateHomeTaskSnapshot(homeToken); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + void dismissKeyguard(IBinder token, IKeyguardDismissCallback callback, + CharSequence message) { + try { + getActivityClientController().dismissKeyguard(token, callback, message); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + void registerRemoteAnimations(IBinder token, RemoteAnimationDefinition definition) { try { getActivityClientController().registerRemoteAnimations(token, definition); diff --git a/core/java/android/app/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl index ebf1027382c5..c2c62c11b9a6 100644 --- a/core/java/android/app/IActivityClientController.aidl +++ b/core/java/android/app/IActivityClientController.aidl @@ -25,6 +25,8 @@ import android.os.Bundle; import android.os.PersistableBundle; import android.view.RemoteAnimationDefinition; +import com.android.internal.policy.IKeyguardDismissCallback; + /** * Interface for the callback and request from an activity to system. * @@ -34,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); @@ -95,6 +103,27 @@ interface IActivityClientController { /** See {@link android.app.Activity#setDisablePreviewScreenshots}. */ oneway void setDisablePreviewScreenshots(in IBinder token, boolean disable); + /** + * Restarts the activity by killing its process if it is visible. If the activity is not + * visible, the activity will not be restarted immediately and just keep the activity record in + * the stack. It also resets the current override configuration so the activity will use the + * configuration according to the latest state. + * + * @param activityToken The token of the target activity to restart. + */ + void restartActivityProcessIfVisible(in IBinder activityToken); + + /** + * It should only be called from home activity to remove its outdated snapshot. The home + * snapshot is used to speed up entering home from screen off. If the content of home activity + * is significantly different from before taking the snapshot, then the home activity can use + * this method to avoid inconsistent transition. + */ + void invalidateHomeTaskSnapshot(IBinder homeToken); + + void dismissKeyguard(in IBinder token, in IKeyguardDismissCallback callback, + in CharSequence message); + /** Registers remote animations for a specific activity. */ void registerRemoteAnimations(in IBinder token, in RemoteAnimationDefinition definition); diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index b0853404a0aa..38a3e70b3742 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -72,7 +72,6 @@ import android.view.RemoteAnimationAdapter; import android.window.IWindowOrganizerController; import com.android.internal.app.IVoiceInteractor; import com.android.internal.os.IResultReceiver; -import com.android.internal.policy.IKeyguardDismissCallback; import java.util.List; @@ -85,8 +84,6 @@ import java.util.List; // TODO(b/174040395): Make this interface private to ActivityTaskManager.java and have external // caller go through that call instead. This would help us better separate and control the API // surface exposed. -// TODO(b/174041144): Move callback methods from Activity (Things that take param 'IBinder token') -// to a separate interface that is only available to the Activity. // TODO(b/174041603): Create a builder interface for things like startActivityXXX(...) to reduce // interface duplication. // TODO(b/174040691): Clean-up/remove all obsolete or unused interfaces like things that should be @@ -294,9 +291,6 @@ interface IActivityTaskManager { // Get device configuration ConfigurationInfo getDeviceConfigurationInfo(); - void dismissKeyguard(in IBinder token, in IKeyguardDismissCallback callback, - in CharSequence message); - /** Cancels the window transitions for the given task. */ void cancelTaskWindowTransition(int taskId); @@ -309,14 +303,6 @@ interface IActivityTaskManager { android.window.TaskSnapshot getTaskSnapshot(int taskId, boolean isLowResolution); /** - * It should only be called from home activity to remove its outdated snapshot. The home - * snapshot is used to speed up entering home from screen off. If the content of home activity - * is significantly different from before taking the snapshot, then the home activity can use - * this method to avoid inconsistent transition. - */ - void invalidateHomeTaskSnapshot(IBinder homeToken); - - /** * Return the user id of last resumed activity. */ int getLastResumedActivityUserId(); @@ -362,14 +348,4 @@ interface IActivityTaskManager { * Clears launch params for given packages. */ void clearLaunchParamsForPackages(in List<String> packageNames); - - /** - * Restarts the activity by killing its process if it is visible. If the activity is not - * visible, the activity will not be restarted immediately and just keep the activity record in - * the stack. It also resets the current override configuration so the activity will use the - * configuration according to the latest state. - * - * @param activityToken The token of the target activity to restart. - */ - void restartActivityProcessIfVisible(in IBinder activityToken); } diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java index 545c3f70d466..b6d25cfb26ce 100644 --- a/core/java/android/app/KeyguardManager.java +++ b/core/java/android/app/KeyguardManager.java @@ -598,33 +598,29 @@ public class KeyguardManager { @SystemApi public void requestDismissKeyguard(@NonNull Activity activity, @Nullable CharSequence message, @Nullable KeyguardDismissCallback callback) { - try { - ActivityTaskManager.getService().dismissKeyguard( - activity.getActivityToken(), new IKeyguardDismissCallback.Stub() { - @Override - public void onDismissError() throws RemoteException { - if (callback != null && !activity.isDestroyed()) { - activity.mHandler.post(callback::onDismissError); - } + ActivityClient.getInstance().dismissKeyguard( + activity.getActivityToken(), new IKeyguardDismissCallback.Stub() { + @Override + public void onDismissError() throws RemoteException { + if (callback != null && !activity.isDestroyed()) { + activity.mHandler.post(callback::onDismissError); } + } - @Override - public void onDismissSucceeded() throws RemoteException { - if (callback != null && !activity.isDestroyed()) { - activity.mHandler.post(callback::onDismissSucceeded); - } + @Override + public void onDismissSucceeded() throws RemoteException { + if (callback != null && !activity.isDestroyed()) { + activity.mHandler.post(callback::onDismissSucceeded); } + } - @Override - public void onDismissCancelled() throws RemoteException { - if (callback != null && !activity.isDestroyed()) { - activity.mHandler.post(callback::onDismissCancelled); - } + @Override + public void onDismissCancelled() throws RemoteException { + if (callback != null && !activity.isDestroyed()) { + activity.mHandler.post(callback::onDismissCancelled); } - }, message); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + } + }, message); } /** 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/integrity/OWNERS b/core/java/android/content/integrity/OWNERS new file mode 100644 index 000000000000..20c758aedd67 --- /dev/null +++ b/core/java/android/content/integrity/OWNERS @@ -0,0 +1,5 @@ +# Bug component: 722021 + +toddke@android.com +toddke@google.com +patb@google.com 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/Network.java b/core/java/android/net/Network.java index e8298218e456..46141e0d0c1e 100644 --- a/core/java/android/net/Network.java +++ b/core/java/android/net/Network.java @@ -22,6 +22,7 @@ import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Parcel; +import android.os.ParcelFileDescriptor; import android.os.Parcelable; import android.system.ErrnoException; import android.system.Os; @@ -381,7 +382,13 @@ public class Network implements Parcelable { // Query a property of the underlying socket to ensure that the socket's file descriptor // exists, is available to bind to a network and is not closed. socket.getReuseAddress(); - bindSocket(socket.getFileDescriptor$()); + final ParcelFileDescriptor pfd = ParcelFileDescriptor.fromDatagramSocket(socket); + bindSocket(pfd.getFileDescriptor()); + // ParcelFileDescriptor.fromSocket() creates a dup of the original fd. The original and the + // dup share the underlying socket in the kernel. The socket is never truly closed until the + // last fd pointing to the socket being closed. So close the dup one after binding the + // socket to control the lifetime of the dup fd. + pfd.close(); } /** @@ -393,7 +400,13 @@ public class Network implements Parcelable { // Query a property of the underlying socket to ensure that the socket's file descriptor // exists, is available to bind to a network and is not closed. socket.getReuseAddress(); - bindSocket(socket.getFileDescriptor$()); + final ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket); + bindSocket(pfd.getFileDescriptor()); + // ParcelFileDescriptor.fromSocket() creates a dup of the original fd. The original and the + // dup share the underlying socket in the kernel. The socket is never truly closed until the + // last fd pointing to the socket being closed. So close the dup one after binding the + // socket to control the lifetime of the dup fd. + pfd.close(); } /** 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/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java index 66b99b9ba319..c4d1b09a5c20 100644 --- a/core/java/android/net/NetworkRequest.java +++ b/core/java/android/net/NetworkRequest.java @@ -353,7 +353,9 @@ public class NetworkRequest implements Parcelable { * NetworkSpecifier. */ public Builder setNetworkSpecifier(NetworkSpecifier networkSpecifier) { - MatchAllNetworkSpecifier.checkNotMatchAllNetworkSpecifier(networkSpecifier); + if (networkSpecifier instanceof MatchAllNetworkSpecifier) { + throw new IllegalArgumentException("A MatchAllNetworkSpecifier is not permitted"); + } mNetworkCapabilities.setNetworkSpecifier(networkSpecifier); return this; } diff --git a/core/java/android/net/ProxyInfo.java b/core/java/android/net/ProxyInfo.java index 950d39369425..9c9fed102828 100644 --- a/core/java/android/net/ProxyInfo.java +++ b/core/java/android/net/ProxyInfo.java @@ -355,7 +355,7 @@ public class ProxyInfo implements Parcelable { port = in.readInt(); } String exclList = in.readString(); - String[] parsedExclList = in.readStringArray(); + String[] parsedExclList = in.createStringArray(); ProxyInfo proxyProperties = new ProxyInfo(host, port, exclList, parsedExclList); return proxyProperties; } 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/rotationresolver/OWNERS b/core/java/android/rotationresolver/OWNERS new file mode 100644 index 000000000000..81b6f05a1658 --- /dev/null +++ b/core/java/android/rotationresolver/OWNERS @@ -0,0 +1 @@ +include /core/java/android/rotationresolver/OWNERS 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/service/rotationresolver/OWNERS b/core/java/android/service/rotationresolver/OWNERS new file mode 100644 index 000000000000..e381d17b57eb --- /dev/null +++ b/core/java/android/service/rotationresolver/OWNERS @@ -0,0 +1,10 @@ +# Bug component: 814982 + +asalo@google.com +augale@google.com +bquezada@google.com +eejiang@google.com +payamp@google.com +siddikap@google.com +svetoslavganov@google.com +tgadh@google.com 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 fc42cd07950e..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); @@ -547,16 +556,17 @@ public final class DisplayInfo implements Parcelable { * Returns the id of the "default" mode with the given refresh rate, or {@code 0} if no suitable * mode could be found. */ - public int findDefaultModeByRefreshRate(float refreshRate) { + @Nullable + public Display.Mode findDefaultModeByRefreshRate(float refreshRate) { Display.Mode[] modes = supportedModes; Display.Mode defaultMode = getDefaultMode(); for (int i = 0; i < modes.length; i++) { if (modes[i].matches( defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), refreshRate)) { - return modes[i].getModeId(); + return modes[i]; } } - return 0; + return null; } /** @@ -661,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/InsetsState.java b/core/java/android/view/InsetsState.java index d68e9032c19d..bf377b0bcfd7 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -106,9 +106,7 @@ public class InsetsState implements Parcelable { public static final int ITYPE_NAVIGATION_BAR = 1; public static final int ITYPE_CAPTION_BAR = 2; - // The always visible types are visible to all windows regardless of the z-order. - public static final int FIRST_ALWAYS_VISIBLE_TYPE = 3; - public static final int ITYPE_TOP_GESTURES = FIRST_ALWAYS_VISIBLE_TYPE; + public static final int ITYPE_TOP_GESTURES = 3; public static final int ITYPE_BOTTOM_GESTURES = 4; public static final int ITYPE_LEFT_GESTURES = 5; public static final int ITYPE_RIGHT_GESTURES = 6; @@ -119,16 +117,15 @@ public class InsetsState implements Parcelable { public static final int ITYPE_LEFT_MANDATORY_GESTURES = 9; public static final int ITYPE_RIGHT_MANDATORY_GESTURES = 10; - public static final int ITYPE_LEFT_DISPLAY_CUTOUT = 11; - public static final int ITYPE_TOP_DISPLAY_CUTOUT = 12; - public static final int ITYPE_RIGHT_DISPLAY_CUTOUT = 13; - public static final int ITYPE_BOTTOM_DISPLAY_CUTOUT = 14; - public static final int LAST_ALWAYS_VISIBLE_TYPE = ITYPE_BOTTOM_DISPLAY_CUTOUT; + public static final int ITYPE_LEFT_TAPPABLE_ELEMENT = 11; + public static final int ITYPE_TOP_TAPPABLE_ELEMENT = 12; + public static final int ITYPE_RIGHT_TAPPABLE_ELEMENT = 13; + public static final int ITYPE_BOTTOM_TAPPABLE_ELEMENT = 14; - public static final int ITYPE_LEFT_TAPPABLE_ELEMENT = 15; - public static final int ITYPE_TOP_TAPPABLE_ELEMENT = 16; - public static final int ITYPE_RIGHT_TAPPABLE_ELEMENT = 17; - public static final int ITYPE_BOTTOM_TAPPABLE_ELEMENT = 18; + public static final int ITYPE_LEFT_DISPLAY_CUTOUT = 15; + public static final int ITYPE_TOP_DISPLAY_CUTOUT = 16; + public static final int ITYPE_RIGHT_DISPLAY_CUTOUT = 17; + public static final int ITYPE_BOTTOM_DISPLAY_CUTOUT = 18; /** Input method window. */ public static final int ITYPE_IME = 19; @@ -185,18 +182,6 @@ public class InsetsState implements Parcelable { } /** - * Mirror the always visible sources from the other state. They will share the same object for - * the always visible types. - * - * @param other the state to mirror the mirrored sources from. - */ - public void mirrorAlwaysVisibleInsetsSources(InsetsState other) { - for (int type = FIRST_ALWAYS_VISIBLE_TYPE; type <= LAST_ALWAYS_VISIBLE_TYPE; type++) { - mSources[type] = other.mSources[type]; - } - } - - /** * Calculates {@link WindowInsets} based on the current source configuration. * * @param frame The frame to calculate the insets relative to. diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java index a2777fe985d5..24bc30874318 100644 --- a/core/java/android/view/Surface.java +++ b/core/java/android/view/Surface.java @@ -218,6 +218,15 @@ public class Surface implements Parcelable { public static final int FRAME_RATE_COMPATIBILITY_FIXED_SOURCE = 1; /** + * This surface belongs to an app on the High Refresh Rate Deny list, and needs the display + * to operate at the exact frame rate. + * + * This is used internally by the platform and should not be used by apps. + * @hide + */ + public static final int FRAME_RATE_COMPATIBILITY_EXACT = 100; + + /** * Create an empty surface, which will later be filled in by readFromParcel(). * @hide */ 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/android/view/translation/Translator.java b/core/java/android/view/translation/Translator.java index 675f32b19d17..22c3e57ecc95 100644 --- a/core/java/android/view/translation/Translator.java +++ b/core/java/android/view/translation/Translator.java @@ -28,6 +28,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; +import android.service.translation.ITranslationCallback; import android.util.Log; import com.android.internal.annotations.GuardedBy; @@ -36,9 +37,11 @@ import com.android.internal.util.SyncResultReceiver; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.List; import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; /** * The {@link Translator} for translation, defined by a source and a dest {@link TranslationSpec}. @@ -295,4 +298,49 @@ public class Translator { } // TODO: add methods for UI-toolkit case. + /** @hide */ + public void requestUiTranslate(@NonNull List<TranslationRequest> requests, + @NonNull Consumer<TranslationResponse> responseCallback) { + if (mDirectServiceBinder == null) { + Log.wtf(TAG, "Translator created without proper initialization."); + return; + } + final android.service.translation.TranslationRequest request = + new android.service.translation.TranslationRequest + .Builder(getNextRequestId(), mSourceSpec, mDestSpec, requests) + .build(); + final ITranslationCallback callback = + new TranslationResponseCallbackImpl(responseCallback); + try { + mDirectServiceBinder.onTranslationRequest(request, mId, callback, null); + } catch (RemoteException e) { + Log.w(TAG, "RemoteException calling flushRequest"); + } + } + + private static class TranslationResponseCallbackImpl extends ITranslationCallback.Stub { + + private final WeakReference<Consumer<TranslationResponse>> mResponseCallback; + + TranslationResponseCallbackImpl(Consumer<TranslationResponse> responseCallback) { + mResponseCallback = new WeakReference<>(responseCallback); + } + + @Override + public void onTranslationComplete(TranslationResponse response) throws RemoteException { + provideTranslationResponse(response); + } + + @Override + public void onError() throws RemoteException { + provideTranslationResponse(null); + } + + private void provideTranslationResponse(TranslationResponse response) { + final Consumer<TranslationResponse> responseCallback = mResponseCallback.get(); + if (responseCallback != null) { + responseCallback.accept(response); + } + } + } } diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java index a810c2e6fb41..fa4614628102 100644 --- a/core/java/android/view/translation/UiTranslationController.java +++ b/core/java/android/view/translation/UiTranslationController.java @@ -16,35 +16,274 @@ package android.view.translation; +import static android.view.translation.UiTranslationManager.STATE_UI_TRANSLATION_FINISHED; +import static android.view.translation.UiTranslationManager.STATE_UI_TRANSLATION_PAUSED; +import static android.view.translation.UiTranslationManager.STATE_UI_TRANSLATION_RESUMED; +import static android.view.translation.UiTranslationManager.STATE_UI_TRANSLATION_STARTED; + +import android.annotation.NonNull; +import android.annotation.WorkerThread; import android.app.Activity; import android.content.Context; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Process; +import android.util.ArrayMap; +import android.util.Log; +import android.util.Pair; +import android.view.View; import android.view.autofill.AutofillId; +import android.view.translation.UiTranslationManager.UiTranslationState; + +import com.android.internal.util.function.pooled.PooledLambda; +import java.lang.ref.WeakReference; +import java.util.ArrayList; import java.util.List; +import java.util.function.Consumer; /** - * A controller to manage the ui translation requests. + * A controller to manage the ui translation requests for the {@link Activity}. * * @hide */ public class UiTranslationController { private static final String TAG = "UiTranslationController"; - + @NonNull private final Activity mActivity; - + @NonNull private final Context mContext; + @NonNull + private final Object mLock = new Object(); + + // Each Translator is distinguished by sourceSpec and desSepc. + @NonNull + private final ArrayMap<Pair<TranslationSpec, TranslationSpec>, Translator> mTranslators; + @NonNull + private final ArrayMap<AutofillId, WeakReference<View>> mViews; + @NonNull + private final HandlerThread mWorkerThread; + @NonNull + private final Handler mWorkerHandler; public UiTranslationController(Activity activity, Context context) { mActivity = activity; mContext = context; + mViews = new ArrayMap<>(); + mTranslators = new ArrayMap<>(); + + mWorkerThread = + new HandlerThread("UiTranslationController_" + mActivity.getComponentName(), + Process.THREAD_PRIORITY_FOREGROUND); + mWorkerThread.start(); + mWorkerHandler = mWorkerThread.getThreadHandler(); } /** * Update the Ui translation state. */ - public void updateUiTranslationState(int state, TranslationSpec sourceSpec, + public void updateUiTranslationState(@UiTranslationState int state, TranslationSpec sourceSpec, TranslationSpec destSpec, List<AutofillId> views) { - // Implement it. Deal with the each states + if (!mActivity.isResumed()) { + return; + } + switch (state) { + case STATE_UI_TRANSLATION_STARTED: + final Pair<TranslationSpec, TranslationSpec> specs = + new Pair<>(sourceSpec, destSpec); + if (!mTranslators.containsKey(specs)) { + mWorkerHandler.sendMessage(PooledLambda.obtainMessage( + UiTranslationController::createTranslatorAndStart, + UiTranslationController.this, sourceSpec, destSpec, views)); + } else { + onUiTranslationStarted(mTranslators.get(specs), views); + } + break; + case STATE_UI_TRANSLATION_PAUSED: + runForEachView((view) -> view.onPauseUiTranslation(), STATE_UI_TRANSLATION_PAUSED); + break; + case STATE_UI_TRANSLATION_RESUMED: + runForEachView((view) -> view.onRestoreUiTranslation(), + STATE_UI_TRANSLATION_PAUSED); + break; + case STATE_UI_TRANSLATION_FINISHED: + destroyTranslators(); + runForEachView((view) -> view.onFinishUiTranslation(), STATE_UI_TRANSLATION_PAUSED); + break; + default: + Log.w(TAG, "onAutoTranslationStateChange(): unknown state: " + state); + } + } + + /** + * Called when the Activity is destroyed. + */ + public void onActivityDestroyed() { + synchronized (mLock) { + mViews.clear(); + destroyTranslators(); + mWorkerThread.quitSafely(); + } + } + + /** + * The method is used by {@link Translator}, it will be called when the translation is done. The + * translation result can be get from here. + */ + public void onTranslationCompleted(TranslationResponse response) { + if (response == null || response.getTranslationStatus() + != TranslationResponse.TRANSLATION_STATUS_SUCCESS) { + Log.w(TAG, "Fail result from TranslationService, response: " + response); + return; + } + final List<TranslationRequest> translatedResult = response.getTranslations(); + onTranslationCompleted(translatedResult); + } + + private void onTranslationCompleted(List<TranslationRequest> translatedResult) { + if (!mActivity.isResumed()) { + return; + } + final int resultCount = translatedResult.size(); + synchronized (mLock) { + for (int i = 0; i < resultCount; i++) { + final TranslationRequest request = translatedResult.get(i); + final AutofillId autofillId = request.getAutofillId(); + if (autofillId == null) { + continue; + } + final View view = mViews.get(autofillId).get(); + if (view == null) { + Log.w(TAG, "onTranslationCompleted: the Veiew for autofill id " + autofillId + + " may be gone."); + continue; + } + mActivity.runOnUiThread(() -> view.onTranslationComplete(request)); + } + } + } + + /** + * Called when there is an ui translation request comes to request view translation. + */ + @WorkerThread + private void createTranslatorAndStart(TranslationSpec sourceSpec, TranslationSpec destSpec, + List<AutofillId> views) { + // Create Translator + final Translator translator = createTranslatorIfNeeded(sourceSpec, destSpec); + if (translator == null) { + Log.w(TAG, "Can not create Translator for sourceSpec:" + sourceSpec + " destSpec:" + + destSpec); + return; + } + onUiTranslationStarted(translator, views); + } + + @WorkerThread + private void sendTranslationRequest(Translator translator, + ArrayList<TranslationRequest> requests) { + translator.requestUiTranslate(requests, this::onTranslationCompleted); + } + + /** + * Called when there is an ui translation request comes to request view translation. + */ + private void onUiTranslationStarted(Translator translator, List<AutofillId> views) { + synchronized (mLock) { + if (views == null || views.size() == 0) { + throw new IllegalArgumentException("Invalid empty views: " + views); + } + // Find Views collect the translation data + // TODO(b/178084101): try to optimize, e.g. to this in a single traversal + final int viewCounts = views.size(); + final ArrayList<TranslationRequest> requests = new ArrayList<>(); + for (int i = 0; i < viewCounts; i++) { + final AutofillId viewAutofillId = views.get(i); + final View view = mActivity.findViewByAutofillIdTraversal(viewAutofillId); + if (view == null) { + Log.w(TAG, "Can not find the View for autofill id= " + viewAutofillId); + continue; + } + mViews.put(viewAutofillId, new WeakReference<>(view)); + mActivity.runOnUiThread(() -> { + final TranslationRequest translationRequest = view.onCreateTranslationRequest(); + if (translationRequest != null + && translationRequest.getTranslationText().length() > 0) { + requests.add(translationRequest); + } + if (requests.size() == viewCounts) { + Log.v(TAG, "onUiTranslationStarted: send " + requests.size() + " request."); + mWorkerHandler.sendMessage(PooledLambda.obtainMessage( + UiTranslationController::sendTranslationRequest, + UiTranslationController.this, translator, requests)); + } + }); + } + } + } + + private void runForEachView(Consumer<View> action, @UiTranslationState int state) { + synchronized (mLock) { + mActivity.runOnUiThread(() -> { + final int viewCounts = mViews.size(); + for (int i = 0; i < viewCounts; i++) { + final View view = mViews.valueAt(i).get(); + if (view == null) { + Log.w(TAG, "The View for autofill id " + mViews.keyAt(i) + + " may be gone for state " + stateToString(state)); + continue; + } + action.accept(view); + } + if (state == STATE_UI_TRANSLATION_FINISHED) { + mViews.clear(); + } + }); + } + } + + private Translator createTranslatorIfNeeded( + TranslationSpec sourceSpec, TranslationSpec destSpec) { + final TranslationManager tm = mContext.getSystemService(TranslationManager.class); + if (tm == null) { + Log.e(TAG, "Can not find TranslationManager when trying to create translator."); + return null; + } + final Translator translator = tm.createTranslator(sourceSpec, destSpec); + if (translator != null) { + final Pair<TranslationSpec, TranslationSpec> specs = new Pair<>(sourceSpec, destSpec); + mTranslators.put(specs, translator); + } + return translator; + } + + private void destroyTranslators() { + synchronized (mLock) { + final int count = mTranslators.size(); + for (int i = 0; i < count; i++) { + Translator translator = mTranslators.valueAt(i); + translator.destroy(); + } + mTranslators.clear(); + } + } + + /** + * Returns a string representation of the state. + */ + public static String stateToString(@UiTranslationState int state) { + switch (state) { + case STATE_UI_TRANSLATION_STARTED: + return "UI_TRANSLATION_STARTED"; + case STATE_UI_TRANSLATION_PAUSED: + return "UI_TRANSLATION_PAUSED"; + case STATE_UI_TRANSLATION_RESUMED: + return "UI_TRANSLATION_RESUMED"; + case STATE_UI_TRANSLATION_FINISHED: + return "UI_TRANSLATION_FINISHED"; + default: + return "Unknown state (" + state + ")"; + } } } diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl index 7a30ee2376ac..55f8c400022f 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -134,7 +134,7 @@ interface IBatteryStats { void noteWifiBatchedScanStartedFromSource(in WorkSource ws, int csph); void noteWifiBatchedScanStoppedFromSource(in WorkSource ws); void noteWifiRadioPowerState(int powerState, long timestampNs, int uid); - void noteNetworkInterfaceType(String iface, int type); + void noteNetworkInterfaceForTransports(String iface, in int[] transportTypes); void noteNetworkStatsEnabled(); void noteDeviceIdleMode(int mode, String activeReason, int activeUid); void setBatteryState(int status, int health, int plugType, int level, int temp, int volt, diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java index 4a24358ad5d5..fcf8bb4e748b 100644 --- a/core/java/com/android/internal/os/BatteryStatsHelper.java +++ b/core/java/com/android/internal/os/BatteryStatsHelper.java @@ -23,7 +23,6 @@ import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.res.Resources; import android.hardware.SensorManager; -import android.net.ConnectivityManager; import android.os.BatteryStats; import android.os.BatteryStats.Uid; import android.os.Build; @@ -37,11 +36,11 @@ import android.os.SELinux; import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; +import android.telephony.TelephonyManager; import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.Log; import android.util.SparseArray; -import android.util.SparseLongArray; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; @@ -120,12 +119,11 @@ public class BatteryStatsHelper { private double mMaxDrainedPower; public static boolean checkWifiOnly(Context context) { - ConnectivityManager cm = (ConnectivityManager) context.getSystemService( - Context.CONNECTIVITY_SERVICE); - if (cm == null) { + final TelephonyManager tm = context.getSystemService(TelephonyManager.class); + if (tm == null) { return false; } - return !cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE); + return !tm.isDataCapable(); } @UnsupportedAppUsage @@ -380,6 +378,7 @@ public class BatteryStatsHelper { mMaxDrainedPower = (mStats.getHighDischargeAmountSinceCharge() * mPowerProfile.getBatteryCapacity()) / 100; + // Create list of (almost all) sippers, calculate their usage, and put them in mUsageList. processAppUsage(asUsers); Collections.sort(mUsageList); @@ -557,8 +556,7 @@ public class BatteryStatsHelper { } /** - * Mark the {@link BatterySipper} that we should hide and smear the screen usage based on - * foreground activity time. + * Mark the {@link BatterySipper} that we should hide. * * @param sippers sipper list that need to check and remove * @return the total power of the hidden items of {@link BatterySipper} @@ -566,7 +564,6 @@ public class BatteryStatsHelper { */ public double removeHiddenBatterySippers(List<BatterySipper> sippers) { double proportionalSmearPowerMah = 0; - BatterySipper screenSipper = null; for (int i = sippers.size() - 1; i >= 0; i--) { final BatterySipper sipper = sippers.get(i); sipper.shouldHide = shouldHideSipper(sipper); @@ -582,45 +579,11 @@ public class BatteryStatsHelper { proportionalSmearPowerMah += sipper.totalPowerMah; } } - - if (sipper.drainType == BatterySipper.DrainType.SCREEN) { - screenSipper = sipper; - } } - - smearScreenBatterySipper(sippers, screenSipper); - return proportionalSmearPowerMah; } /** - * Smear the screen on power usage among {@code sippers}, based on ratio of foreground activity - * time. - */ - public void smearScreenBatterySipper(List<BatterySipper> sippers, BatterySipper screenSipper) { - long totalActivityTimeMs = 0; - final SparseLongArray activityTimeArray = new SparseLongArray(); - for (int i = 0, size = sippers.size(); i < size; i++) { - final BatteryStats.Uid uid = sippers.get(i).uidObj; - if (uid != null) { - final long timeMs = getProcessForegroundTimeMs(uid, - BatteryStats.STATS_SINCE_CHARGED); - activityTimeArray.put(uid.getUid(), timeMs); - totalActivityTimeMs += timeMs; - } - } - - if (screenSipper != null && totalActivityTimeMs >= 10 * DateUtils.MINUTE_IN_MILLIS) { - final double screenPowerMah = screenSipper.totalPowerMah; - for (int i = 0, size = sippers.size(); i < size; i++) { - final BatterySipper sipper = sippers.get(i); - sipper.screenPowerMah = screenPowerMah * activityTimeArray.get(sipper.getUid(), 0) - / totalActivityTimeMs; - } - } - } - - /** * Check whether we should hide the battery sipper. */ public boolean shouldHideSipper(BatterySipper sipper) { @@ -683,33 +646,6 @@ public class BatteryStatsHelper { } @VisibleForTesting - public long getForegroundActivityTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs) { - final BatteryStats.Timer timer = uid.getForegroundActivityTimer(); - if (timer != null) { - return timer.getTotalTimeLocked(rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED); - } - - return 0; - } - - @VisibleForTesting - public long getProcessForegroundTimeMs(BatteryStats.Uid uid, int which) { - final long rawRealTimeUs = convertMsToUs(SystemClock.elapsedRealtime()); - final int foregroundTypes[] = {BatteryStats.Uid.PROCESS_STATE_TOP}; - - long timeUs = 0; - for (int type : foregroundTypes) { - final long localTime = uid.getProcessStateTime(type, rawRealTimeUs, which); - timeUs += localTime; - } - - // Return the min value of STATE_TOP time and foreground activity time, since both of these - // time have some errors. - return convertUsToMs( - Math.min(timeUs, getForegroundActivityTotalTimeUs(uid, rawRealTimeUs))); - } - - @VisibleForTesting public void setPackageManager(PackageManager packageManager) { mPackageManager = packageManager; } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 37b621d781d1..2b034b0667d6 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -16,6 +16,8 @@ package com.android.internal.os; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; +import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.os.BatteryStatsManager.NUM_WIFI_STATES; import static android.os.BatteryStatsManager.NUM_WIFI_SUPPL_STATES; @@ -36,7 +38,6 @@ import android.content.IntentFilter; import android.database.ContentObserver; import android.hardware.usb.UsbManager; import android.location.GnssSignalQuality; -import android.net.ConnectivityManager; import android.net.INetworkStatsService; import android.net.NetworkStats; import android.net.Uri; @@ -111,6 +112,7 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.XmlUtils; +import com.android.net.module.util.NetworkCapabilitiesUtils; import libcore.util.EmptyArray; @@ -1097,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; @@ -6711,11 +6703,12 @@ public class BatteryStatsImpl extends BatteryStats { } /** @hide */ - public void noteNetworkInterfaceType(String iface, int networkType) { + public void noteNetworkInterfaceForTransports(String iface, int[] transportTypes) { if (TextUtils.isEmpty(iface)) return; + final int displayTransport = NetworkCapabilitiesUtils.getDisplayTransport(transportTypes); synchronized (mModemNetworkLock) { - if (ConnectivityManager.isNetworkTypeMobile(networkType)) { + if (displayTransport == TRANSPORT_CELLULAR) { mModemIfaces = includeInStringArray(mModemIfaces, iface); if (DEBUG) Slog.d(TAG, "Note mobile iface " + iface + ": " + mModemIfaces); } else { @@ -6725,7 +6718,7 @@ public class BatteryStatsImpl extends BatteryStats { } synchronized (mWifiNetworkLock) { - if (ConnectivityManager.isNetworkTypeWifi(networkType)) { + if (displayTransport == TRANSPORT_WIFI) { mWifiIfaces = includeInStringArray(mWifiIfaces, iface); if (DEBUG) Slog.d(TAG, "Note wifi iface " + iface + ": " + mWifiIfaces); } else { @@ -10853,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; } @@ -11505,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; @@ -12692,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++) { @@ -12723,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)); } } @@ -14019,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); } /** @@ -14503,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++) { @@ -16094,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); } @@ -16305,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/ScreenPowerCalculator.java b/core/java/com/android/internal/os/ScreenPowerCalculator.java index 25f6b4d16971..9c4a26724ba8 100644 --- a/core/java/com/android/internal/os/ScreenPowerCalculator.java +++ b/core/java/com/android/internal/os/ScreenPowerCalculator.java @@ -21,9 +21,14 @@ import android.os.BatteryStats; import android.os.BatteryUsageStats; import android.os.BatteryUsageStatsQuery; import android.os.SystemBatteryConsumer; +import android.os.SystemClock; import android.os.UserHandle; +import android.text.format.DateUtils; import android.util.Log; import android.util.SparseArray; +import android.util.SparseLongArray; + +import com.android.internal.annotations.VisibleForTesting; import java.util.List; @@ -57,6 +62,7 @@ public class ScreenPowerCalculator extends PowerCalculator { .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE, durationMs) .setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, powerMah); } + // TODO(b/178140704): Attribute screen usage similar to smearScreenBatterySipper. } /** @@ -73,6 +79,8 @@ public class ScreenPowerCalculator extends PowerCalculator { bs.usageTimeMs = durationMs; bs.sumPower(); sippers.add(bs); + + smearScreenBatterySipper(sippers, bs); } } @@ -96,4 +104,60 @@ public class ScreenPowerCalculator extends PowerCalculator { } return power; } + + /** + * Smear the screen on power usage among {@code sippers}, based on ratio of foreground activity + * time, and store this in the {@link BatterySipper#screenPowerMah} field. + */ + @VisibleForTesting + public void smearScreenBatterySipper(List<BatterySipper> sippers, BatterySipper screenSipper) { + + long totalActivityTimeMs = 0; + final SparseLongArray activityTimeArray = new SparseLongArray(); + for (int i = sippers.size() - 1; i >= 0; i--) { + final BatteryStats.Uid uid = sippers.get(i).uidObj; + if (uid != null) { + final long timeMs = getProcessForegroundTimeMs(uid); + activityTimeArray.put(uid.getUid(), timeMs); + totalActivityTimeMs += timeMs; + } + } + + if (screenSipper != null && totalActivityTimeMs >= 10 * DateUtils.MINUTE_IN_MILLIS) { + final double screenPowerMah = screenSipper.totalPowerMah; + for (int i = sippers.size() - 1; i >= 0; i--) { + final BatterySipper sipper = sippers.get(i); + sipper.screenPowerMah = screenPowerMah * activityTimeArray.get(sipper.getUid(), 0) + / totalActivityTimeMs; + } + } + } + + /** Get the minimum of the uid's ForegroundActivity time and its TOP time. */ + @VisibleForTesting + public long getProcessForegroundTimeMs(BatteryStats.Uid uid) { + final long rawRealTimeUs = SystemClock.elapsedRealtime() * 1000; + final int[] foregroundTypes = {BatteryStats.Uid.PROCESS_STATE_TOP}; + + long timeUs = 0; + for (int type : foregroundTypes) { + final long localTime = uid.getProcessStateTime(type, rawRealTimeUs, + BatteryStats.STATS_SINCE_CHARGED); + timeUs += localTime; + } + + // Return the min value of STATE_TOP time and foreground activity time, since both of these + // time have some errors. + return Math.min(timeUs, getForegroundActivityTotalTimeUs(uid, rawRealTimeUs)) / 1000; + } + + /** Get the ForegroundActivity time of the given uid. */ + @VisibleForTesting + public long getForegroundActivityTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs) { + final BatteryStats.Timer timer = uid.getForegroundActivityTimer(); + if (timer == null) { + return 0; + } + return timer.getTotalTimeLocked(rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED); + } } 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/android/content/OWNERS b/core/tests/coretests/src/android/content/OWNERS index 912db1e835dc..c61a4b538a44 100644 --- a/core/tests/coretests/src/android/content/OWNERS +++ b/core/tests/coretests/src/android/content/OWNERS @@ -1,3 +1,4 @@ +per-file AssetTest.java = file:/core/java/android/content/res/OWNERS per-file ContextTest.java = file:/services/core/java/com/android/server/wm/OWNERS -per-file *Shortcut* = file:/core/java/android/content/pm/SHORTCUT_OWNERS per-file *Launcher* = file:/core/java/android/content/pm/LAUNCHER_OWNERS +per-file *Shortcut* = file:/core/java/android/content/pm/SHORTCUT_OWNERS diff --git a/core/tests/coretests/src/android/content/integrity/OWNERS b/core/tests/coretests/src/android/content/integrity/OWNERS new file mode 100644 index 000000000000..4ffc7041a527 --- /dev/null +++ b/core/tests/coretests/src/android/content/integrity/OWNERS @@ -0,0 +1 @@ +include /core/java/android/content/integrity/OWNERS diff --git a/core/tests/coretests/src/android/content/pm/OWNERS b/core/tests/coretests/src/android/content/pm/OWNERS index 7b7670696bfa..867336515ce3 100644 --- a/core/tests/coretests/src/android/content/pm/OWNERS +++ b/core/tests/coretests/src/android/content/pm/OWNERS @@ -1,3 +1,5 @@ +include /core/java/android/content/pm/OWNERS + per-file AppSearchPersonTest.java = file:/core/java/android/content/pm/SHORTCUT_OWNERS -per-file SigningDetailsTest.java = mpgroover@google.com per-file SigningDetailsTest.java = cbrubaker@google.com +per-file SigningDetailsTest.java = mpgroover@google.com diff --git a/core/tests/coretests/src/android/content/res/OWNERS b/core/tests/coretests/src/android/content/res/OWNERS new file mode 100644 index 000000000000..3e79d8ff0bbe --- /dev/null +++ b/core/tests/coretests/src/android/content/res/OWNERS @@ -0,0 +1 @@ +include /core/java/android/content/res/OWNERS diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java index fbe16f2c39d1..d2107eaefefc 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java @@ -21,12 +21,10 @@ import static android.os.BatteryStats.Uid.PROCESS_STATE_TOP; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.RETURNS_DEEP_STUBS; -import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; @@ -194,7 +192,6 @@ public class BatteryStatsHelperTest extends TestCase { sippers.add(mBluetoothBatterySipper); sippers.add(mIdleBatterySipper); doReturn(true).when(mBatteryStatsHelper).isTypeSystem(mSystemBatterySipper); - doNothing().when(mBatteryStatsHelper).smearScreenBatterySipper(any(), any()); final double totalUsage = mBatteryStatsHelper.removeHiddenBatterySippers(sippers); @@ -208,19 +205,20 @@ public class BatteryStatsHelperTest extends TestCase { @Test public void testSmearScreenBatterySipper() { + final ScreenPowerCalculator spc = spy(ScreenPowerCalculator.class); final BatterySipper sipperNull = createTestSmearBatterySipper(TIME_FOREGROUND_ACTIVITY_ZERO, - BATTERY_APP_USAGE, 0 /* uid */, true /* isUidNull */); + BATTERY_APP_USAGE, 0 /* uid */, true /* isUidNull */, spc); final BatterySipper sipperBg = createTestSmearBatterySipper(TIME_FOREGROUND_ACTIVITY_ZERO, - BATTERY_APP_USAGE, 1 /* uid */, false /* isUidNull */); + BATTERY_APP_USAGE, 1 /* uid */, false /* isUidNull */, spc); final BatterySipper sipperFg = createTestSmearBatterySipper(TIME_FOREGROUND_ACTIVITY, - BATTERY_APP_USAGE, 2 /* uid */, false /* isUidNull */); + BATTERY_APP_USAGE, 2 /* uid */, false /* isUidNull */, spc); final List<BatterySipper> sippers = new ArrayList<>(); sippers.add(sipperNull); sippers.add(sipperBg); sippers.add(sipperFg); - mBatteryStatsHelper.smearScreenBatterySipper(sippers, mScreenBatterySipper); + spc.smearScreenBatterySipper(sippers, mScreenBatterySipper); assertThat(sipperNull.screenPowerMah).isWithin(PRECISION).of(0); assertThat(sipperBg.screenPowerMah).isWithin(PRECISION).of(0); @@ -249,13 +247,13 @@ public class BatteryStatsHelperTest extends TestCase { @Test public void testGetProcessForegroundTimeMs_largerActivityTime_returnMinTime() { - doReturn(TIME_STATE_FOREGROUND_US + 500).when(mBatteryStatsHelper) + final ScreenPowerCalculator spc = spy(ScreenPowerCalculator.class); + doReturn(TIME_STATE_FOREGROUND_US + 500).when(spc) .getForegroundActivityTotalTimeUs(eq(mUid), anyLong()); doReturn(TIME_STATE_FOREGROUND_US).when(mUid).getProcessStateTime(eq(PROCESS_STATE_TOP), anyLong(), anyInt()); - final long time = mBatteryStatsHelper.getProcessForegroundTimeMs(mUid, - BatteryStats.STATS_SINCE_CHARGED); + final long time = spc.getProcessForegroundTimeMs(mUid); assertThat(time).isEqualTo(TIME_STATE_FOREGROUND_MS); } @@ -291,15 +289,14 @@ public class BatteryStatsHelperTest extends TestCase { } private BatterySipper createTestSmearBatterySipper(long activityTime, double totalPowerMah, - int uidCode, boolean isUidNull) { + int uidCode, boolean isUidNull, ScreenPowerCalculator spc) { final BatterySipper sipper = mock(BatterySipper.class); sipper.drainType = BatterySipper.DrainType.APP; sipper.totalPowerMah = totalPowerMah; doReturn(uidCode).when(sipper).getUid(); if (!isUidNull) { final BatteryStats.Uid uid = mock(BatteryStats.Uid.class, RETURNS_DEEP_STUBS); - doReturn(activityTime).when(mBatteryStatsHelper).getProcessForegroundTimeMs(eq(uid), - anyInt()); + doReturn(activityTime).when(spc).getProcessForegroundTimeMs(eq(uid)); doReturn(uidCode).when(uid).getUid(); sipper.uidObj = uid; } 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/MobileRadioPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java index 4230066f32a1..66a8379177c2 100644 --- a/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java @@ -21,7 +21,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import android.net.ConnectivityManager; +import android.net.NetworkCapabilities; import android.net.NetworkStats; import android.os.BatteryConsumer; import android.os.Process; @@ -78,7 +78,8 @@ public class MobileRadioPowerCalculatorTest { 8_000_000_000L, APP_UID, 8000, 8000); // Note established network - stats.noteNetworkInterfaceType("cellular", ConnectivityManager.TYPE_MOBILE); + stats.noteNetworkInterfaceForTransports("cellular", + new int[] { NetworkCapabilities.TRANSPORT_CELLULAR }); // Note application network activity NetworkStats networkStats = new NetworkStats(10000, 1) 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/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 03f89185b5f4..f84d947b51ed 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -354,6 +354,7 @@ applications that come with the platform <permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> <permission name="android.permission.MOVE_PACKAGE"/> <!-- Needed for test only --> + <permission name="android.permission.RESTART_WIFI_SUBSYSTEM"/> <permission name="android.permission.NETWORK_AIRPLANE_MODE"/> <permission name="android.permission.OBSERVE_APP_USAGE"/> <permission name="android.permission.NETWORK_SCAN"/> 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/KeyStore2.java b/keystore/java/android/security/KeyStore2.java index 92d87aa0fed6..f7477bf92c81 100644 --- a/keystore/java/android/security/KeyStore2.java +++ b/keystore/java/android/security/KeyStore2.java @@ -23,6 +23,7 @@ import android.os.Build; import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceSpecificException; +import android.security.keymaster.KeymasterDefs; import android.system.keystore2.IKeystoreService; import android.system.keystore2.KeyDescriptor; import android.system.keystore2.KeyEntryResponse; @@ -107,7 +108,7 @@ public class KeyStore2 { return request.execute(service); } catch (ServiceSpecificException e) { Log.e(TAG, "KeyStore exception", e); - throw new KeyStoreException(e.errorCode, ""); + throw getKeyStoreException(e.errorCode); } catch (RemoteException e) { if (firstTry) { Log.w(TAG, "Looks like we may have lost connection to the Keystore " @@ -274,4 +275,40 @@ public class KeyStore2 { } } + static KeyStoreException getKeyStoreException(int errorCode) { + if (errorCode > 0) { + // KeyStore layer error + switch (errorCode) { + case ResponseCode.LOCKED: + return new KeyStoreException(errorCode, "User authentication required"); + case ResponseCode.UNINITIALIZED: + return new KeyStoreException(errorCode, "Keystore not initialized"); + case ResponseCode.SYSTEM_ERROR: + return new KeyStoreException(errorCode, "System error"); + case ResponseCode.PERMISSION_DENIED: + return new KeyStoreException(errorCode, "Permission denied"); + case ResponseCode.KEY_NOT_FOUND: + return new KeyStoreException(errorCode, "Key not found"); + case ResponseCode.VALUE_CORRUPTED: + return new KeyStoreException(errorCode, "Key blob corrupted"); + case ResponseCode.KEY_PERMANENTLY_INVALIDATED: + return new KeyStoreException(errorCode, "Key permanently invalidated"); + default: + return new KeyStoreException(errorCode, String.valueOf(errorCode)); + } + } else { + // Keymaster layer error + switch (errorCode) { + case KeymasterDefs.KM_ERROR_INVALID_AUTHORIZATION_TIMEOUT: + // The name of this parameter significantly differs between Keymaster and + // framework APIs. Use the framework wording to make life easier for developers. + return new KeyStoreException(errorCode, + "Invalid user authentication validity duration"); + default: + return new KeyStoreException(errorCode, + KeymasterDefs.getErrorMessage(errorCode)); + } + } + } + } diff --git a/keystore/java/android/security/KeyStoreOperation.java b/keystore/java/android/security/KeyStoreOperation.java index 7ea9e1438845..a6552dddc630 100644 --- a/keystore/java/android/security/KeyStoreOperation.java +++ b/keystore/java/android/security/KeyStoreOperation.java @@ -73,8 +73,7 @@ public class KeyStoreOperation { ); } default: - // TODO Human readable string. Use something like KeyStore.getKeyStoreException - throw new KeyStoreException(e.errorCode, ""); + throw KeyStore2.getKeyStoreException(e.errorCode); } } catch (RemoteException e) { // Log exception and report invalid operation handle. diff --git a/keystore/java/android/security/KeyStoreSecurityLevel.java b/keystore/java/android/security/KeyStoreSecurityLevel.java index 3ef4aa5b7ec3..bc669bae056b 100644 --- a/keystore/java/android/security/KeyStoreSecurityLevel.java +++ b/keystore/java/android/security/KeyStoreSecurityLevel.java @@ -52,7 +52,7 @@ public class KeyStoreSecurityLevel { try { return request.execute(); } catch (ServiceSpecificException e) { - throw new KeyStoreException(e.errorCode, ""); + throw KeyStore2.getKeyStoreException(e.errorCode); } catch (RemoteException e) { // Log exception and report invalid operation handle. // This should prompt the caller drop the reference to this operation and retry. @@ -114,7 +114,7 @@ public class KeyStoreSecurityLevel { break; } default: - throw new KeyStoreException(e.errorCode, ""); + throw KeyStore2.getKeyStoreException(e.errorCode); } } catch (RemoteException e) { Log.w(TAG, "Cannot connect to keystore", e); 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/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt index ed305a21f444..e79820f520dd 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt @@ -67,7 +67,8 @@ class NonResizableDismissInLegacySplitScreenTest( visibleLayersShownMoreThanOneConsecutiveEntry( listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName, nonResizeableApp.defaultWindowName, LETTER_BOX_NAME, - TOAST_NAME, LIVE_WALLPAPER_PACKAGE_NAME) + TOAST_NAME, LIVE_WALLPAPER_PACKAGE_NAME), + bugId = 178447631 ) } windowManagerTrace { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt index 88dab51f9b9f..280af5d708c9 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt @@ -68,7 +68,7 @@ class NonResizableLaunchInLegacySplitScreenTest( listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName, nonResizeableApp.defaultWindowName, LETTER_BOX_NAME, TOAST_NAME, LIVE_WALLPAPER_PACKAGE_NAME), - enabled = false + bugId = 178447631 ) } windowManagerTrace { 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/SettingsLib/SettingsTheme/res/layout/preference_category_settings.xml b/packages/SettingsLib/SettingsTheme/res/layout/preference_category_settings.xml index 4b1b255a3744..4a1b089970c7 100644 --- a/packages/SettingsLib/SettingsTheme/res/layout/preference_category_settings.xml +++ b/packages/SettingsLib/SettingsTheme/res/layout/preference_category_settings.xml @@ -19,14 +19,13 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingLeft="24dp" - android:paddingStart="24dp" android:paddingRight="?android:attr/listPreferredItemPaddingRight" android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" android:background="?android:attr/selectableItemBackground" android:baselineAligned="false" android:layout_marginTop="16dp" - android:gravity="center_vertical"> + android:gravity="center_vertical" + style="@style/PreferenceCategoryStartMargin"> <RelativeLayout android:layout_width="0dp" @@ -57,6 +56,5 @@ android:textColor="?android:attr/textColorSecondary" android:maxLines="10" style="@style/PreferenceSummaryTextStyle"/> - </RelativeLayout> </LinearLayout>
\ No newline at end of file diff --git a/packages/SettingsLib/SettingsTheme/res/values-sw360dp/styles.xml b/packages/SettingsLib/SettingsTheme/res/values-sw360dp/styles.xml new file mode 100644 index 000000000000..4f402565787c --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-sw360dp/styles.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<resources> + <style name="PreferenceCategoryStartMargin"> + <item name="android:paddingLeft">24dp</item> + <item name="android:paddingStart">24dp</item> + </style> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values/styles.xml b/packages/SettingsLib/SettingsTheme/res/values/styles.xml index 6b285d5b727e..a6623b01fede 100644 --- a/packages/SettingsLib/SettingsTheme/res/values/styles.xml +++ b/packages/SettingsLib/SettingsTheme/res/values/styles.xml @@ -22,4 +22,9 @@ <!-- 0.8 Spacing, 0.8/11 = 0.072727273 --> <item name="android:letterSpacing">0.072727273</item> </style> + + <style name="PreferenceCategoryStartMargin"> + <item name="android:paddingLeft">?android:attr/listPreferredItemPaddingLeft</item> + <item name="android:paddingStart">?android:attr/listPreferredItemPaddingStart</item> + </style> </resources> diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 5d8fc0b6bffc..7eab559cb9b9 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -300,6 +300,9 @@ <!-- Permission needed to test mainline permission module rollback --> <uses-permission android:name="android.permission.UPGRADE_RUNTIME_PERMISSIONS" /> + <!-- Permission needed to restart WiFi Subsystem --> + <uses-permission android:name="android.permission.RESTART_WIFI_SUBSYSTEM" /> + <!-- Permission needed to read wifi network credentials for CtsNetTestCases --> <uses-permission android:name="android.permission.NETWORK_AIRPLANE_MODE" /> 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/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java index e5c4bf32db3a..9164137feb41 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java @@ -24,6 +24,7 @@ import static android.app.ActivityTaskManager.getService; import android.annotation.NonNull; import android.app.Activity; +import android.app.ActivityClient; import android.app.ActivityManager; import android.app.ActivityManager.RecentTaskInfo; import android.app.ActivityManager.RunningTaskInfo; @@ -140,8 +141,9 @@ public class ActivityManagerWrapper { */ public void invalidateHomeTaskSnapshot(final Activity homeActivity) { try { - getService().invalidateHomeTaskSnapshot(homeActivity.getActivityToken()); - } catch (RemoteException e) { + ActivityClient.getInstance().invalidateHomeTaskSnapshot( + homeActivity.getActivityToken()); + } catch (Throwable e) { Log.w(TAG, "Failed to invalidate home snapshot", e); } } diff --git a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java index 02f34ac3dec0..5384ddfd18bf 100644 --- a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java +++ b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java @@ -16,7 +16,7 @@ package com.android.systemui; -import android.app.ActivityTaskManager; +import android.app.ActivityClient; import android.content.Context; import android.content.res.ColorStateList; import android.graphics.Color; @@ -27,7 +27,6 @@ import android.graphics.drawable.RippleDrawable; import android.hardware.display.DisplayManager; import android.inputmethodservice.InputMethodService; import android.os.IBinder; -import android.os.RemoteException; import android.util.Log; import android.util.SparseArray; import android.view.Display; @@ -237,12 +236,7 @@ public class SizeCompatModeActivityController extends SystemUI implements Comman @Override public void onClick(View v) { - try { - ActivityTaskManager.getService().restartActivityProcessIfVisible( - mLastActivityToken); - } catch (RemoteException e) { - Log.w(TAG, "Unable to restart activity", e); - } + ActivityClient.getInstance().restartActivityProcessIfVisible(mLastActivityToken); } @Override 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/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 74a6e07c27f2..d129b9c074a9 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -5658,7 +5658,9 @@ public class ConnectivityService extends IConnectivityManager.Stub if (ns == null) { return; } - MatchAllNetworkSpecifier.checkNotMatchAllNetworkSpecifier(ns); + if (ns instanceof MatchAllNetworkSpecifier) { + throw new IllegalArgumentException("A MatchAllNetworkSpecifier is not permitted"); + } } private void ensureValid(NetworkCapabilities nc) { @@ -6194,7 +6196,7 @@ public class ConnectivityService extends IConnectivityManager.Stub nai.networkAgentPortalData = lp.getCaptivePortalData(); } - private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties newLp, + private void updateLinkProperties(NetworkAgentInfo networkAgent, @NonNull LinkProperties newLp, @NonNull LinkProperties oldLp) { int netId = networkAgent.network.getNetId(); @@ -6203,8 +6205,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // the LinkProperties for the network are accurate. networkAgent.clatd.fixupLinkProperties(oldLp, newLp); - updateInterfaces(newLp, oldLp, netId, networkAgent.networkCapabilities, - networkAgent.networkInfo.getType()); + updateInterfaces(newLp, oldLp, netId, networkAgent.networkCapabilities); // update filtering rules, need to happen after the interface update so netd knows about the // new interface (the interface name -> index map becomes initialized) @@ -6343,7 +6344,7 @@ public class ConnectivityService extends IConnectivityManager.Stub private void updateInterfaces(final @Nullable LinkProperties newLp, final @Nullable LinkProperties oldLp, final int netId, - final @Nullable NetworkCapabilities caps, final int legacyType) { + final @NonNull NetworkCapabilities caps) { final CompareResult<String> interfaceDiff = new CompareResult<>( oldLp != null ? oldLp.getAllInterfaceNames() : null, newLp != null ? newLp.getAllInterfaceNames() : null); @@ -6354,7 +6355,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (DBG) log("Adding iface " + iface + " to network " + netId); mNetd.networkAddInterface(netId, iface); wakeupModifyInterface(iface, caps, true); - bs.noteNetworkInterfaceType(iface, legacyType); + bs.noteNetworkInterfaceForTransports(iface, caps.getTransportTypes()); } catch (Exception e) { loge("Exception adding interface: " + e); } @@ -6626,6 +6627,7 @@ public class ConnectivityService extends IConnectivityManager.Stub * maintained here that the NetworkAgent is not aware of (e.g., validated, captive portal, * and foreground status). */ + @NonNull private NetworkCapabilities mixInCapabilities(NetworkAgentInfo nai, NetworkCapabilities nc) { // Once a NetworkAgent is connected, complain if some immutable capabilities are removed. // Don't complain for VPNs since they're not driven by requests and there is no risk of @@ -6682,6 +6684,25 @@ public class ConnectivityService extends IConnectivityManager.Stub return newNc; } + private void updateNetworkInfoForRoamingAndSuspended(NetworkAgentInfo nai, + NetworkCapabilities prevNc, NetworkCapabilities newNc) { + final boolean prevSuspended = !prevNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED); + final boolean suspended = !newNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED); + final boolean prevRoaming = !prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING); + final boolean roaming = !newNc.hasCapability(NET_CAPABILITY_NOT_ROAMING); + if (prevSuspended != suspended) { + // TODO (b/73132094) : remove this call once the few users of onSuspended and + // onResumed have been removed. + notifyNetworkCallbacks(nai, suspended ? ConnectivityManager.CALLBACK_SUSPENDED + : ConnectivityManager.CALLBACK_RESUMED); + } + if (prevSuspended != suspended || prevRoaming != roaming) { + // updateNetworkInfo will mix in the suspended info from the capabilities and + // take appropriate action for the network having possibly changed state. + updateNetworkInfo(nai, nai.networkInfo); + } + } + /** * Update the NetworkCapabilities for {@code nai} to {@code nc}. Specifically: * @@ -6713,25 +6734,13 @@ public class ConnectivityService extends IConnectivityManager.Stub // on this network. We might have been called by rematchNetworkAndRequests when a // network changed foreground state. processListenRequests(nai); - final boolean prevSuspended = !prevNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED); - final boolean suspended = !newNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED); - final boolean prevRoaming = !prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING); - final boolean roaming = !newNc.hasCapability(NET_CAPABILITY_NOT_ROAMING); - if (prevSuspended != suspended || prevRoaming != roaming) { - // TODO (b/73132094) : remove this call once the few users of onSuspended and - // onResumed have been removed. - notifyNetworkCallbacks(nai, suspended ? ConnectivityManager.CALLBACK_SUSPENDED - : ConnectivityManager.CALLBACK_RESUMED); - // updateNetworkInfo will mix in the suspended info from the capabilities and - // take appropriate action for the network having possibly changed state. - updateNetworkInfo(nai, nai.networkInfo); - } } else { // If the requestable capabilities have changed or the score changed, we can't have been // called by rematchNetworkAndRequests, so it's safe to start a rematch. rematchAllNetworksAndRequests(); notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED); } + updateNetworkInfoForRoamingAndSuspended(nai, prevNc, newNc); final boolean oldMetered = prevNc.isMetered(); final boolean newMetered = newNc.isMetered(); 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 b1cbb4acdb13..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); } @@ -1674,11 +1676,11 @@ public final class BatteryStatsService extends IBatteryStats.Stub } @Override - public void noteNetworkInterfaceType(final String iface, final int networkType) { + public void noteNetworkInterfaceForTransports(final String iface, int[] transportTypes) { enforceCallingPermission(); synchronized (mLock) { mHandler.post(() -> { - mStats.noteNetworkInterfaceType(iface, networkType); + mStats.noteNetworkInterfaceForTransports(iface, transportTypes); }); } } diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java index b40df9560a70..27a238dd33ea 100644 --- a/services/core/java/com/android/server/am/CachedAppOptimizer.java +++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java @@ -234,6 +234,8 @@ public final class CachedAppOptimizer { @VisibleForTesting Handler mCompactionHandler; private Handler mFreezeHandler; + @GuardedBy("mAm") + private boolean mFreezerOverride = false; // Maps process ID to last compaction statistics for processes that we've fully compacted. Used // when evaluating throttles that we only consider for "full" compaction, so we don't store @@ -464,21 +466,35 @@ public final class CachedAppOptimizer { } } - try { - enableFreezerInternal(enable); - return true; - } catch (java.lang.RuntimeException e) { - if (enable) { - mFreezerDisableCount = 0; - } else { - mFreezerDisableCount = 1; - } + // Override is applied immediately, restore is delayed + synchronized (mAm) { + int processCount = mAm.mProcessList.mLruProcesses.size(); + + mFreezerOverride = !enable; + Slog.d(TAG_AM, "freezer override set to " + mFreezerOverride); - Slog.e(TAG_AM, "Exception handling freezer state (enable: " + enable + "): " - + e.toString()); + for (int i = 0; i < processCount; i++) { + ProcessRecord process = mAm.mProcessList.mLruProcesses.get(i); + + if (process == null) { + continue; + } + + if (enable && process.freezerOverride) { + freezeAppAsync(process); + process.freezerOverride = false; + } + + if (!enable && process.frozen) { + unfreezeAppLocked(process); + + // Set freezerOverride *after* calling unfreezeAppLocked (it resets the flag) + process.freezerOverride = true; + } + } } - return false; + return true; } /** @@ -525,7 +541,7 @@ public final class CachedAppOptimizer { FileReader fr = null; try { - fr = new FileReader("/sys/fs/cgroup/freezer/cgroup.freeze"); + fr = new FileReader("/sys/fs/cgroup/uid_0/cgroup.freeze"); char state = (char) fr.read(); if (state == '1' || state == '0') { @@ -747,6 +763,8 @@ public final class CachedAppOptimizer { void unfreezeAppLocked(ProcessRecord app) { mFreezeHandler.removeMessages(SET_FROZEN_PROCESS_MSG, app); + app.freezerOverride = false; + if (!app.frozen) { if (DEBUG_FREEZER) { Slog.d(TAG_AM, @@ -756,6 +774,8 @@ public final class CachedAppOptimizer { return; } + // Unfreeze the binder interface first, to avoid transactions triggered by timers fired + // right after unfreezing the process to fail boolean processKilled = false; try { @@ -1135,12 +1155,31 @@ public final class CachedAppOptimizer { return; } + if (mFreezerOverride) { + proc.freezerOverride = true; + Slog.d(TAG_AM, "Skipping freeze for process " + pid + + " " + name + " curAdj = " + proc.curAdj + + "(override)"); + return; + } + if (pid == 0 || proc.frozen) { // Already frozen or not a real process, either one being // launched or one being killed return; } + // Freeze binder interface before the process, to flush any + // transactions that might be pending. + try { + freezeBinder(pid, true); + } catch (RuntimeException e) { + Slog.e(TAG_AM, "Unable to freeze binder for " + pid + " " + name); + proc.kill("Unable to freeze binder interface", + ApplicationExitInfo.REASON_OTHER, + ApplicationExitInfo.SUBREASON_INVALID_STATE, true); + } + long unfreezeTime = proc.freezeUnfreezeTime; try { @@ -1167,15 +1206,6 @@ public final class CachedAppOptimizer { EventLog.writeEvent(EventLogTags.AM_FREEZE, pid, name); - try { - freezeBinder(pid, true); - } catch (RuntimeException e) { - Slog.e(TAG_AM, "Unable to freeze binder for " + pid + " " + name); - proc.kill("Unable to freeze binder interface", - ApplicationExitInfo.REASON_OTHER, - ApplicationExitInfo.SUBREASON_INVALID_STATE, true); - } - // See above for why we're not taking mPhenotypeFlagLock here if (mRandom.nextFloat() < mFreezerStatsdSampleRate) { FrameworkStatsLog.write(FrameworkStatsLog.APP_FREEZE_CHANGED, diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 520a28bfd889..63195d35b049 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -179,6 +179,7 @@ class ProcessRecord implements WindowProcessListener { int reqCompactAction; // The most recent compaction action requested for this app. int lastCompactAction; // The most recent compaction action performed for this app. boolean frozen; // True when the process is frozen. + boolean freezerOverride; // An override on the freeze state is in progress. long freezeUnfreezeTime; // Last time the app was (un)frozen, 0 for never boolean shouldNotFreeze; // True if a process has a WPRI binding from an unfrozen process private int mCurSchedGroup; // Currently desired scheduling class diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index ab0360b0395a..b2824846008c 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -329,7 +329,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { private final QosCallbackTracker mQosCallbackTracker; public NetworkAgentInfo(INetworkAgent na, Network net, NetworkInfo info, - LinkProperties lp, NetworkCapabilities nc, int score, Context context, + @NonNull LinkProperties lp, @NonNull NetworkCapabilities nc, int score, Context context, Handler handler, NetworkAgentConfig config, ConnectivityService connService, INetd netd, IDnsResolver dnsResolver, INetworkManagementService nms, int factorySerialNumber, int creatorUid, QosCallbackTracker qosCallbackTracker) { diff --git a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java index a7be657ae7a3..5e6b9f39b40a 100644 --- a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java +++ b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java @@ -686,7 +686,7 @@ public class NetworkDiagnostics { mHostname = hostname; mMeasurement.description = "DNS TLS dst{" + mTarget.getHostAddress() + "} hostname{" - + TextUtils.emptyIfNull(mHostname) + "}"; + + (mHostname == null ? "" : mHostname) + "}"; } private SSLSocket setupSSLSocket() throws IOException { 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/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 55103ca6cd1c..c3f8d8cf219a 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -1357,7 +1357,7 @@ public final class DisplayManagerService extends SystemService { // Scan supported modes returned by display.getInfo() to find a mode with the same // size as the default display mode but with the specified refresh rate instead. requestedModeId = display.getDisplayInfoLocked().findDefaultModeByRefreshRate( - requestedRefreshRate); + requestedRefreshRate).getModeId(); } mDisplayModeDirector.getAppRequestObserver().setAppRequestedMode( displayId, requestedModeId); @@ -1538,6 +1538,14 @@ public final class DisplayManagerService extends SystemService { } } + void setDisplayModeDirectorLoggingEnabled(boolean enabled) { + synchronized (mSyncRoot) { + if (mDisplayModeDirector != null) { + mDisplayModeDirector.setLoggingEnabled(enabled); + } + } + } + void setAmbientColorTemperatureOverride(float cct) { synchronized (mSyncRoot) { final DisplayPowerController displayPowerController = mDisplayPowerControllers.get( diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java index 111664a078df..aaea15a27e01 100644 --- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java +++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java @@ -54,6 +54,10 @@ class DisplayManagerShellCommand extends ShellCommand { return setDisplayWhiteBalanceLoggingEnabled(true); case "dwb-logging-disable": return setDisplayWhiteBalanceLoggingEnabled(false); + case "dmd-logging-enable": + return setDisplayModeDirectorLoggingEnabled(true); + case "dmd-logging-disable": + return setDisplayModeDirectorLoggingEnabled(false); case "dwb-set-cct": return setAmbientColorTemperatureOverride(); case "set-fold": @@ -82,6 +86,10 @@ class DisplayManagerShellCommand extends ShellCommand { pw.println(" Enable display white-balance logging."); pw.println(" dwb-logging-disable"); pw.println(" Disable display white-balance logging."); + pw.println(" dmd-logging-enable"); + pw.println(" Enable display mode director logging."); + pw.println(" dmd-logging-disable"); + pw.println(" Disable display mode director logging."); pw.println(" dwb-set-cct CCT"); pw.println(" Sets the ambient color temperature override to CCT (use -1 to disable)."); pw.println(" set-fold [fold|unfold|reset]"); @@ -136,6 +144,11 @@ class DisplayManagerShellCommand extends ShellCommand { return 0; } + private int setDisplayModeDirectorLoggingEnabled(boolean enabled) { + mService.setDisplayModeDirectorLoggingEnabled(enabled); + return 0; + } + private int setAmbientColorTemperatureOverride() { String cctText = getNextArg(); if (cctText == null) { diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java index 006f87571f82..dce6bd849953 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/DisplayModeDirector.java @@ -67,7 +67,7 @@ import java.util.Objects; */ public class DisplayModeDirector { private static final String TAG = "DisplayModeDirector"; - private static final boolean DEBUG = false; + private boolean mLoggingEnabled; private static final int MSG_REFRESH_RATE_RANGE_CHANGED = 1; private static final int MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED = 2; @@ -155,6 +155,14 @@ public class DisplayModeDirector { } } + public void setLoggingEnabled(boolean loggingEnabled) { + if (mLoggingEnabled == loggingEnabled) { + return; + } + mLoggingEnabled = loggingEnabled; + mBrightnessObserver.setLoggingEnabled(loggingEnabled); + } + @NonNull private SparseArray<Vote> getVotesLocked(int displayId) { SparseArray<Vote> displayVotes = mVotesByDisplay.get(displayId); @@ -269,7 +277,7 @@ public class DisplayModeDirector { availableModes = filterModes(modes, primarySummary); if (availableModes.length > 0) { - if (DEBUG) { + if (mLoggingEnabled) { Slog.w(TAG, "Found available modes=" + Arrays.toString(availableModes) + " with lowest priority considered " + Vote.priorityToString(lowestConsideredPriority) @@ -282,7 +290,7 @@ public class DisplayModeDirector { break; } - if (DEBUG) { + if (mLoggingEnabled) { Slog.w(TAG, "Couldn't find available modes with lowest priority set to " + Vote.priorityToString(lowestConsideredPriority) + " and with the following constraints: " @@ -307,7 +315,7 @@ public class DisplayModeDirector { Math.min(appRequestSummary.minRefreshRate, primarySummary.minRefreshRate); appRequestSummary.maxRefreshRate = Math.max(appRequestSummary.maxRefreshRate, primarySummary.maxRefreshRate); - if (DEBUG) { + if (mLoggingEnabled) { Slog.i(TAG, String.format("App request range: [%.0f %.0f]", appRequestSummary.minRefreshRate, @@ -357,7 +365,7 @@ public class DisplayModeDirector { for (Display.Mode mode : supportedModes) { if (mode.getPhysicalWidth() != summary.width || mode.getPhysicalHeight() != summary.height) { - if (DEBUG) { + if (mLoggingEnabled) { Slog.w(TAG, "Discarding mode " + mode.getModeId() + ", wrong size" + ": desiredWidth=" + summary.width + ": desiredHeight=" + summary.height @@ -372,7 +380,7 @@ public class DisplayModeDirector { // comparison. if (refreshRate < (summary.minRefreshRate - FLOAT_TOLERANCE) || refreshRate > (summary.maxRefreshRate + FLOAT_TOLERANCE)) { - if (DEBUG) { + if (mLoggingEnabled) { Slog.w(TAG, "Discarding mode " + mode.getModeId() + ", outside refresh rate bounds" + ": minRefreshRate=" + summary.minRefreshRate @@ -516,7 +524,7 @@ public class DisplayModeDirector { } private void updateVoteLocked(int displayId, int priority, Vote vote) { - if (DEBUG) { + if (mLoggingEnabled) { Slog.i(TAG, "updateVoteLocked(displayId=" + displayId + ", priority=" + Vote.priorityToString(priority) + ", vote=" + vote + ")"); @@ -537,7 +545,7 @@ public class DisplayModeDirector { } if (votes.size() == 0) { - if (DEBUG) { + if (mLoggingEnabled) { Slog.i(TAG, "No votes left for display " + displayId + ", removing."); } mVotesByDisplay.remove(displayId); @@ -1287,6 +1295,7 @@ public class DisplayModeDirector { private boolean mShouldObserveAmbientLowChange; private boolean mShouldObserveDisplayHighChange; private boolean mShouldObserveAmbientHighChange; + private boolean mLoggingEnabled; private SensorManager mSensorManager; private Sensor mLightSensor; @@ -1303,7 +1312,6 @@ public class DisplayModeDirector { // changeable and low power mode off. After initialization, these states will // be updated from the same handler thread. private int mDefaultDisplayState = Display.STATE_UNKNOWN; - private boolean mIsDeviceActive = false; private boolean mRefreshRateChangeable = false; private boolean mLowPowerModeEnabled = false; @@ -1415,6 +1423,14 @@ public class DisplayModeDirector { mDeviceConfigDisplaySettings.startListening(); } + public void setLoggingEnabled(boolean loggingEnabled) { + if (mLoggingEnabled == loggingEnabled) { + return; + } + mLoggingEnabled = loggingEnabled; + mLightSensorListener.setLoggingEnabled(loggingEnabled); + } + public void onRefreshRateSettingChangedLocked(float min, float max) { boolean changeable = (max - min > 1f && max > 60f); if (mRefreshRateChangeable != changeable) { @@ -1485,7 +1501,6 @@ public class DisplayModeDirector { pw.println(" mAmbientLux: " + mAmbientLux); pw.println(" mBrightness: " + mBrightness); pw.println(" mDefaultDisplayState: " + mDefaultDisplayState); - pw.println(" mIsDeviceActive: " + mIsDeviceActive); pw.println(" mLowPowerModeEnabled: " + mLowPowerModeEnabled); pw.println(" mRefreshRateChangeable: " + mRefreshRateChangeable); pw.println(" mShouldObserveDisplayLowChange: " + mShouldObserveDisplayLowChange); @@ -1691,7 +1706,7 @@ public class DisplayModeDirector { vote = Vote.forRefreshRates(mRefreshRateInHighZone, mRefreshRateInHighZone); } - if (DEBUG) { + if (mLoggingEnabled) { Slog.d(TAG, "Display brightness " + mBrightness + ", ambient lux " + mAmbientLux + ", Vote " + vote); } @@ -1720,6 +1735,11 @@ public class DisplayModeDirector { @VisibleForTesting public void setDefaultDisplayState(int state) { + if (mLoggingEnabled) { + Slog.d(TAG, "setDefaultDisplayState: mDefaultDisplayState = " + + mDefaultDisplayState + ", state = " + state); + } + if (mDefaultDisplayState != state) { mDefaultDisplayState = state; updateSensorStatus(); @@ -1731,36 +1751,58 @@ public class DisplayModeDirector { return; } + if (mLoggingEnabled) { + Slog.d(TAG, "updateSensorStatus: mShouldObserveAmbientLowChange = " + + mShouldObserveAmbientLowChange + ", mShouldObserveAmbientHighChange = " + + mShouldObserveAmbientHighChange); + Slog.d(TAG, "updateSensorStatus: mLowPowerModeEnabled = " + + mLowPowerModeEnabled + ", mRefreshRateChangeable = " + + mRefreshRateChangeable); + } + if ((mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange) && isDeviceActive() && !mLowPowerModeEnabled && mRefreshRateChangeable) { mSensorManager.registerListener(mLightSensorListener, mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler); + if (mLoggingEnabled) { + Slog.d(TAG, "updateSensorStatus: registerListener"); + } } else { mLightSensorListener.removeCallbacks(); mSensorManager.unregisterListener(mLightSensorListener); + if (mLoggingEnabled) { + Slog.d(TAG, "updateSensorStatus: unregisterListener"); + } } } private boolean isDeviceActive() { - mIsDeviceActive = mInjector.isDeviceInteractive(mContext); - return (mDefaultDisplayState == Display.STATE_ON) - && mIsDeviceActive; + return mDefaultDisplayState == Display.STATE_ON; } private final class LightSensorEventListener implements SensorEventListener { final private static int INJECT_EVENTS_INTERVAL_MS = LIGHT_SENSOR_RATE_MS; private float mLastSensorData; private long mTimestamp; + private boolean mLoggingEnabled; public void dumpLocked(PrintWriter pw) { pw.println(" mLastSensorData: " + mLastSensorData); pw.println(" mTimestamp: " + formatTimestamp(mTimestamp)); } + + public void setLoggingEnabled(boolean loggingEnabled) { + if (mLoggingEnabled == loggingEnabled) { + return; + } + mLoggingEnabled = loggingEnabled; + } + @Override public void onSensorChanged(SensorEvent event) { mLastSensorData = event.values[0]; - if (DEBUG) { + if (mLoggingEnabled) { Slog.d(TAG, "On sensor changed: " + mLastSensorData); } @@ -2009,8 +2051,6 @@ public class DisplayModeDirector { void registerPeakRefreshRateObserver(@NonNull ContentResolver cr, @NonNull ContentObserver observer); - - boolean isDeviceInteractive(@NonNull Context context); } @VisibleForTesting @@ -2041,11 +2081,6 @@ public class DisplayModeDirector { cr.registerContentObserver(PEAK_REFRESH_RATE_URI, false /*notifyDescendants*/, observer, UserHandle.USER_SYSTEM); } - - @Override - public boolean isDeviceInteractive(@NonNull Context ctx) { - return ctx.getSystemService(PowerManager.class).isInteractive(); - } } } 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/graphics/fonts/UpdatableFontDir.java b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java index 8ec72770d782..720105d29836 100644 --- a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java +++ b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java @@ -23,6 +23,8 @@ import android.annotation.Nullable; import android.graphics.fonts.FontManager; import android.graphics.fonts.SystemFonts; import android.os.FileUtils; +import android.system.ErrnoException; +import android.system.Os; import android.text.FontConfig; import android.util.Base64; import android.util.Slog; @@ -223,6 +225,14 @@ final class UpdatableFontDir { FontManager.ERROR_CODE_FAILED_TO_WRITE_FONT_FILE, "Failed to create font directory."); } + try { + // Make newDir executable so that apps can access font file inside newDir. + Os.chmod(newDir.getAbsolutePath(), 0711); + } catch (ErrnoException e) { + throw new SystemFontException( + FontManager.ERROR_CODE_FAILED_TO_WRITE_FONT_FILE, + "Failed to change mode to 711", e); + } boolean success = false; try { File tempNewFontFile = new File(newDir, "font.ttf"); @@ -262,6 +272,14 @@ final class UpdatableFontDir { FontManager.ERROR_CODE_FAILED_TO_WRITE_FONT_FILE, "Failed to move verified font file."); } + try { + // Make the font file readable by apps. + Os.chmod(newFontFile.getAbsolutePath(), 0644); + } catch (ErrnoException e) { + throw new SystemFontException( + FontManager.ERROR_CODE_FAILED_TO_WRITE_FONT_FILE, + "Failed to change mode to 711", e); + } FontFileInfo fontFileInfo = validateFontFile(newFontFile); // Write config file. 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/rotationresolver/OWNERS b/services/core/java/com/android/server/rotationresolver/OWNERS new file mode 100644 index 000000000000..81b6f05a1658 --- /dev/null +++ b/services/core/java/com/android/server/rotationresolver/OWNERS @@ -0,0 +1 @@ +include /core/java/android/rotationresolver/OWNERS 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/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java index 8805fa2f4dbb..703bfab6d868 100644 --- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java +++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java @@ -638,6 +638,22 @@ public class VcnGatewayConnection extends StateMachine { protected abstract void processStateMsg(Message msg) throws Exception; + @Override + public void exit() { + try { + exitState(); + } catch (Exception e) { + Slog.wtf(TAG, "Uncaught exception", e); + sendMessage( + EVENT_DISCONNECT_REQUESTED, + TOKEN_ALL, + new EventDisconnectRequestedInfo( + DISCONNECT_REASON_INTERNAL_ERROR + e.toString())); + } + } + + protected void exitState() throws Exception {} + protected void logUnhandledMessage(Message msg) { // Log as unexpected all known messages, and log all else as unknown. switch (msg.what) { @@ -664,18 +680,11 @@ public class VcnGatewayConnection extends StateMachine { } } - protected void teardownIke() { - if (mIkeSession != null) { - mIkeSession.close(); - } - } - protected void handleDisconnectRequested(String msg) { Slog.v(TAG, "Tearing down. Cause: " + msg); mIsRunning = false; teardownNetwork(); - teardownIke(); if (mIkeSession == null) { // Already disconnected, go straight to DisconnectedState @@ -768,6 +777,20 @@ public class VcnGatewayConnection extends StateMachine { * does not complete teardown in a timely fashion, it will be killed (forcibly closed). */ private class DisconnectingState extends ActiveBaseState { + /** + * Whether to skip the RetryTimeoutState and go straight to the ConnectingState. + * + * <p>This is used when an underlying network change triggered a restart on a new network. + * + * <p>Reset (to false) upon exit of the DisconnectingState. + */ + private boolean mSkipRetryTimeout = false; + + // TODO(b/178441390): Remove this in favor of resetting retry timers on UND_NET change. + public void setSkipRetryTimeout(boolean shouldSkip) { + mSkipRetryTimeout = shouldSkip; + } + @Override protected void enterState() throws Exception { if (mIkeSession == null) { @@ -783,6 +806,7 @@ public class VcnGatewayConnection extends StateMachine { return; } + mIkeSession.close(); sendMessageDelayed( EVENT_TEARDOWN_TIMEOUT_EXPIRED, mCurrentToken, @@ -822,7 +846,7 @@ public class VcnGatewayConnection extends StateMachine { mIkeSession = null; if (mIsRunning && mUnderlying != null) { - transitionTo(mRetryTimeoutState); + transitionTo(mSkipRetryTimeout ? mConnectingState : mRetryTimeoutState); } else { teardownNetwork(); transitionTo(mDisconnectedState); @@ -833,6 +857,11 @@ public class VcnGatewayConnection extends StateMachine { break; } } + + @Override + protected void exitState() throws Exception { + mSkipRetryTimeout = false; + } } /** @@ -843,7 +872,69 @@ public class VcnGatewayConnection extends StateMachine { */ private class ConnectingState extends ActiveBaseState { @Override - protected void processStateMsg(Message msg) {} + protected void enterState() { + if (mIkeSession != null) { + Slog.wtf(TAG, "ConnectingState entered with active session"); + + // Attempt to recover. + mIkeSession.kill(); + mIkeSession = null; + } + + mIkeSession = buildIkeSession(); + } + + @Override + protected void processStateMsg(Message msg) { + switch (msg.what) { + case EVENT_UNDERLYING_NETWORK_CHANGED: + final UnderlyingNetworkRecord oldUnderlying = mUnderlying; + mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying; + + if (oldUnderlying == null) { + // This should never happen, but if it does, there's likely a nasty bug. + Slog.wtf(TAG, "Old underlying network was null in connected state. Bug?"); + } + + // If new underlying is null, all underlying networks have been lost; disconnect + if (mUnderlying == null) { + transitionTo(mDisconnectingState); + break; + } + + if (oldUnderlying != null + && mUnderlying.network.equals(oldUnderlying.network)) { + break; // Only network properties have changed; continue connecting. + } + // Else, retry on the new network. + + // Immediately come back to the ConnectingState (skip RetryTimeout, since this + // isn't a failure) + mDisconnectingState.setSkipRetryTimeout(true); + + // fallthrough - disconnect, and retry on new network. + case EVENT_SESSION_LOST: + transitionTo(mDisconnectingState); + break; + case EVENT_SESSION_CLOSED: + deferMessage(msg); + + transitionTo(mDisconnectingState); + break; + case EVENT_SETUP_COMPLETED: // fallthrough + case EVENT_TRANSFORM_CREATED: + // Child setup complete; move to ConnectedState for NetworkAgent registration + deferMessage(msg); + transitionTo(mConnectedState); + break; + case EVENT_DISCONNECT_REQUESTED: + handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason); + break; + default: + logUnhandledMessage(msg); + break; + } + } } private abstract class ConnectedStateBase extends ActiveBaseState {} @@ -1015,12 +1106,12 @@ public class VcnGatewayConnection extends StateMachine { } private IkeSessionParams buildIkeParams() { - // TODO: Implement this with ConnectingState + // TODO: Implement this once IkeSessionParams is persisted return null; } private ChildSessionParams buildChildParams() { - // TODO: Implement this with ConnectingState + // TODO: Implement this once IkeSessionParams is persisted return null; } 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/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java index c25f1b419e66..5fe853a38dd7 100644 --- a/services/core/java/com/android/server/wm/ActivityClientController.java +++ b/services/core/java/com/android/server/wm/ActivityClientController.java @@ -63,6 +63,7 @@ import android.util.Slog; import android.view.RemoteAnimationDefinition; import com.android.internal.app.AssistUtils; +import com.android.internal.policy.IKeyguardDismissCallback; import com.android.internal.protolog.common.ProtoLog; import com.android.server.LocalServices; import com.android.server.Watchdog; @@ -1019,6 +1020,50 @@ class ActivityClientController extends IActivityClientController.Stub { } @Override + public void restartActivityProcessIfVisible(IBinder token) { + ActivityTaskManagerService.enforceTaskPermission("restartActivityProcess"); + final long callingId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); + if (r != null) { + r.restartProcessIfVisible(); + } + } + } finally { + Binder.restoreCallingIdentity(callingId); + } + } + + @Override + public void invalidateHomeTaskSnapshot(IBinder token) { + synchronized (mGlobalLock) { + final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); + if (r != null && r.isActivityTypeHome()) { + mService.mWindowManager.mTaskSnapshotController.removeSnapshotCache( + r.getTask().mTaskId); + } + } + } + + @Override + public void dismissKeyguard(IBinder token, IKeyguardDismissCallback callback, + CharSequence message) { + if (message != null) { + mService.mAmInternal.enforceCallingPermission( + android.Manifest.permission.SHOW_KEYGUARD_MESSAGE, "dismissKeyguard"); + } + final long callingId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + mService.mKeyguardController.dismissKeyguard(token, callback, message); + } + } finally { + Binder.restoreCallingIdentity(callingId); + } + } + + @Override public void registerRemoteAnimations(IBinder token, RemoteAnimationDefinition definition) { mService.mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS, "registerRemoteAnimations"); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 56105737bb82..f0db3f9855df 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -231,7 +231,6 @@ import com.android.internal.app.ProcessMap; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.os.TransferPipe; -import com.android.internal.policy.IKeyguardDismissCallback; import com.android.internal.policy.KeyguardDismissCallback; import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.ArrayUtils; @@ -1796,23 +1795,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public void restartActivityProcessIfVisible(IBinder activityToken) { - enforceTaskPermission("restartActivityProcess()"); - final long callingId = Binder.clearCallingIdentity(); - try { - synchronized (mGlobalLock) { - final ActivityRecord r = ActivityRecord.isInRootTaskLocked(activityToken); - if (r == null) { - return; - } - r.restartProcessIfVisible(); - } - } finally { - Binder.restoreCallingIdentity(callingId); - } - } - - @Override public boolean removeTask(int taskId) { enforceCallerIsRecentsOrHasPermission(REMOVE_TASKS, "removeTask()"); synchronized (mGlobalLock) { @@ -3263,7 +3245,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // If the keyguard is showing or occluded, then try and dismiss it before // entering picture-in-picture (this will prompt the user to authenticate if the // device is currently locked). - dismissKeyguard(r.appToken, new KeyguardDismissCallback() { + mActivityClientController.dismissKeyguard(r.appToken, new KeyguardDismissCallback() { @Override public void onDismissSucceeded() { mH.post(enterPipRunnable); @@ -3388,23 +3370,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public void dismissKeyguard(IBinder token, IKeyguardDismissCallback callback, - CharSequence message) { - if (message != null) { - mAmInternal.enforceCallingPermission( - Manifest.permission.SHOW_KEYGUARD_MESSAGE, "dismissKeyguard()"); - } - final long callingId = Binder.clearCallingIdentity(); - try { - synchronized (mGlobalLock) { - mKeyguardController.dismissKeyguard(token, callback, message); - } - } finally { - Binder.restoreCallingIdentity(callingId); - } - } - - @Override public void cancelTaskWindowTransition(int taskId) { enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS, "cancelTaskWindowTransition()"); @@ -3450,17 +3415,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return task.getSnapshot(isLowResolution, restoreFromDisk); } - @Override - public void invalidateHomeTaskSnapshot(IBinder token) { - synchronized (mGlobalLock) { - final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); - if (r == null || !r.isActivityTypeHome()) { - return; - } - mWindowManager.mTaskSnapshotController.removeSnapshotCache(r.getTask().mTaskId); - } - } - /** Return the user id of the last resumed activity. */ @Override public @UserIdInt diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 8841a9b87498..2a40500258c9 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -669,9 +669,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // Used in updating override configurations private final Configuration mTempConfig = new Configuration(); - // Used in performing layout, to record the insets provided by other windows above the current - // window. - private InsetsState mTmpAboveInsetsState = new InsetsState(); + // Used in performing layout + private boolean mTmpWindowsBehindIme; /** * Used to prevent recursions when calling @@ -770,11 +769,17 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp + " parentHidden=" + w.isParentWindowHidden()); } - // Sets mAboveInsets for each window. Windows behind the window providing the insets can - // receive the insets. - if (!w.mAboveInsetsState.equals(mTmpAboveInsetsState)) { - w.mAboveInsetsState.set(mTmpAboveInsetsState); - mWinInsetsChanged.add(w); + // Sets mBehindIme for each window. Windows behind IME can get IME insets. + if (w.mBehindIme != mTmpWindowsBehindIme) { + w.mBehindIme = mTmpWindowsBehindIme; + if (getInsetsStateController().getRawInsetsState().getSourceOrDefaultVisibility( + ITYPE_IME)) { + // If IME is invisible, behind IME or not doesn't make the insets different. + mWinInsetsChanged.add(w); + } + } + if (w == mInputMethodWindow) { + mTmpWindowsBehindIme = true; } // If this view is GONE, then skip it -- keep the current frame, and let the caller know @@ -810,16 +815,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp + " mContainingFrame=" + w.getContainingFrame() + " mDisplayFrame=" + w.getDisplayFrame()); } - provideInsetsByWindow(w); }; - private void provideInsetsByWindow(WindowState w) { - for (int i = 0; i < w.mProvidedInsetsSources.size(); i++) { - final InsetsSource providedSource = w.mProvidedInsetsSources.valueAt(i); - mTmpAboveInsetsState.addSource(providedSource); - } - } - private final Consumer<WindowState> mPerformLayoutAttached = w -> { if (w.mLayoutAttached) { if (DEBUG_LAYOUT) Slog.v(TAG, "2ND PASS " + w + " mHaveFrame=" + w.mHaveFrame @@ -4272,20 +4269,14 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp + " dh=" + mDisplayInfo.logicalHeight); } - // Used to indicate that we have processed the insets windows. This needs to be after - // beginLayoutLw to ensure the raw insets state display related info is initialized. - final InsetsState rawInsetsState = getInsetsStateController().getRawInsetsState(); - mTmpAboveInsetsState = new InsetsState(); - mTmpAboveInsetsState.setDisplayFrame(rawInsetsState.getDisplayFrame()); - mTmpAboveInsetsState.setDisplayCutout(rawInsetsState.getDisplayCutout()); - mTmpAboveInsetsState.mirrorAlwaysVisibleInsetsSources(rawInsetsState); - int seq = mLayoutSeq + 1; if (seq < 0) seq = 0; mLayoutSeq = seq; mTmpInitial = initial; + // Used to indicate that we have processed the IME window. + mTmpWindowsBehindIme = false; // First perform layout of any root windows (not attached to another window). forAllWindows(mPerformLayout, true /* traverseTopToBottom */); diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index 7d0854d59604..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; @@ -152,7 +151,6 @@ class InsetsSourceProvider { // animate-out as new one animates-in. mWin.cancelAnimation(); mWin.mPendingPositionChanged = null; - mWin.mProvidedInsetsSources.remove(mSource.getType()); } ProtoLog.d(WM_DEBUG_IME, "InsetsSource setWin %s", win); mWin = win; @@ -162,14 +160,11 @@ class InsetsSourceProvider { setServerVisible(false); mSource.setFrame(new Rect()); mSource.setVisibleFrame(null); - } else { - mWin.mProvidedInsetsSources.put(mSource.getType(), mSource); - if (mControllable) { - mWin.setControllableInsetProvider(this); - if (mPendingControlTarget != null) { - updateControlForTarget(mPendingControlTarget, true /* force */); - mPendingControlTarget = null; - } + } else if (mControllable) { + mWin.setControllableInsetProvider(this); + if (mPendingControlTarget != null) { + updateControlForTarget(mPendingControlTarget, true /* force */); + mPendingControlTarget = null; } } } @@ -557,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/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java index 7b709ea5d641..398049fb97c3 100644 --- a/services/core/java/com/android/server/wm/InsetsStateController.java +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -104,8 +104,6 @@ class InsetsStateController { * visible to the target. e.g., the source which represents the target window itself, and the * IME source when the target is above IME. We also need to exclude certain types of insets * source for client within specific windowing modes. - * This is to get the insets for a window layout on the screen. If the window is not there, use - * the {@link #getInsetsForWindowMetrics} to get insets instead. * * @param target The window associate with the perspective. * @return The state stripped of the necessary information. @@ -119,7 +117,7 @@ class InsetsStateController { final @InternalInsetsType int type = provider != null ? provider.getSource().getType() : ITYPE_INVALID; return getInsetsForTarget(type, target.getWindowingMode(), target.isAlwaysOnTop(), - target.mAboveInsetsState); + isAboveIme(target)); } InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) { @@ -134,7 +132,19 @@ class InsetsStateController { final @WindowingMode int windowingMode = token != null ? token.getWindowingMode() : WINDOWING_MODE_UNDEFINED; final boolean alwaysOnTop = token != null && token.isAlwaysOnTop(); - return getInsetsForTarget(type, windowingMode, alwaysOnTop, mState); + return getInsetsForTarget(type, windowingMode, alwaysOnTop, isAboveIme(token)); + } + + private boolean isAboveIme(WindowContainer target) { + final WindowState imeWindow = mDisplayContent.mInputMethodWindow; + if (target == null || imeWindow == null) { + return false; + } + if (target instanceof WindowState) { + final WindowState win = (WindowState) target; + return win.needsRelativeLayeringToIme() || !win.mBehindIme; + } + return false; } private static @InternalInsetsType @@ -170,12 +180,11 @@ class InsetsStateController { * @see #getInsetsForWindowMetrics */ private InsetsState getInsetsForTarget(@InternalInsetsType int type, - @WindowingMode int windowingMode, boolean isAlwaysOnTop, InsetsState state) { - boolean stateCopied = false; + @WindowingMode int windowingMode, boolean isAlwaysOnTop, boolean aboveIme) { + InsetsState state = mState; if (type != ITYPE_INVALID) { state = new InsetsState(state); - stateCopied = true; state.removeSource(type); // Navigation bar doesn't get influenced by anything else @@ -210,15 +219,23 @@ class InsetsStateController { if (WindowConfiguration.isFloating(windowingMode) || (windowingMode == WINDOWING_MODE_MULTI_WINDOW && isAlwaysOnTop)) { - if (!stateCopied) { - state = new InsetsState(state); - stateCopied = true; - } + state = new InsetsState(state); state.removeSource(ITYPE_STATUS_BAR); state.removeSource(ITYPE_NAVIGATION_BAR); state.removeSource(ITYPE_EXTRA_NAVIGATION_BAR); } + if (aboveIme) { + InsetsSource imeSource = state.peekSource(ITYPE_IME); + if (imeSource != null && imeSource.isVisible()) { + imeSource = new InsetsSource(imeSource); + imeSource.setVisible(false); + imeSource.setFrame(0, 0, 0, 0); + state = new InsetsState(state); + state.addSource(imeSource); + } + } + return state; } diff --git a/services/core/java/com/android/server/wm/RefreshRatePolicy.java b/services/core/java/com/android/server/wm/RefreshRatePolicy.java index 91014aa69831..26871d130fbf 100644 --- a/services/core/java/com/android/server/wm/RefreshRatePolicy.java +++ b/services/core/java/com/android/server/wm/RefreshRatePolicy.java @@ -28,7 +28,7 @@ import android.view.DisplayInfo; */ class RefreshRatePolicy { - private final int mLowRefreshRateId; + private final Mode mLowRefreshRateMode; private final ArraySet<String> mNonHighRefreshRatePackages = new ArraySet<>(); private final HighRefreshRateDenylist mHighRefreshRateDenylist; private final WindowManagerService mWmService; @@ -56,7 +56,7 @@ class RefreshRatePolicy { RefreshRatePolicy(WindowManagerService wmService, DisplayInfo displayInfo, HighRefreshRateDenylist denylist) { - mLowRefreshRateId = findLowRefreshRateModeId(displayInfo); + mLowRefreshRateMode = findLowRefreshRateMode(displayInfo); mHighRefreshRateDenylist = denylist; mWmService = wmService; } @@ -65,7 +65,7 @@ class RefreshRatePolicy { * Finds the mode id with the lowest refresh rate which is >= 60hz and same resolution as the * default mode. */ - private int findLowRefreshRateModeId(DisplayInfo displayInfo) { + private Mode findLowRefreshRateMode(DisplayInfo displayInfo) { Mode mode = displayInfo.getDefaultMode(); float[] refreshRates = displayInfo.getDefaultRefreshRates(); float bestRefreshRate = mode.getRefreshRate(); @@ -104,13 +104,9 @@ class RefreshRatePolicy { // If app is using Camera, force it to default (lower) refresh rate. if (mNonHighRefreshRatePackages.contains(packageName)) { - return mLowRefreshRateId; + return mLowRefreshRateMode.getModeId(); } - // If app is denylisted using higher refresh rate, return default (lower) refresh rate - if (mHighRefreshRateDenylist.isDenylisted(packageName)) { - return mLowRefreshRateId; - } return 0; } @@ -137,4 +133,18 @@ class RefreshRatePolicy { } return LAYER_PRIORITY_UNSET; } + + float getPreferredRefreshRate(WindowState w) { + // If app is animating, it's not able to control refresh rate because we want the animation + // to run in default refresh rate. + if (w.isAnimating(TRANSITION | PARENTS)) { + return 0; + } + + final String packageName = w.getOwningPackage(); + if (mHighRefreshRateDenylist.isDenylisted(packageName)) { + return mLowRefreshRateMode.getRefreshRate(); + } + return 0; + } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 3be4e78a122b..093106f123e5 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -212,7 +212,6 @@ import android.os.Trace; import android.os.WorkSource; import android.provider.Settings; import android.text.TextUtils; -import android.util.ArrayMap; import android.util.ArraySet; import android.util.DisplayMetrics; import android.util.MergedConfiguration; @@ -233,6 +232,7 @@ import android.view.InputWindowHandle; import android.view.InsetsSource; import android.view.InsetsState; import android.view.InsetsState.InternalInsetsType; +import android.view.Surface; import android.view.Surface.Rotation; import android.view.SurfaceControl; import android.view.SurfaceSession; @@ -647,14 +647,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP boolean mSeamlesslyRotated = false; /** - * The insets state of sources provided by windows above the current window. + * Indicates if this window is behind IME. Only windows behind IME can get insets from IME. */ - InsetsState mAboveInsetsState = new InsetsState(); - - /** - * The insets sources provided by this window. - */ - ArrayMap<Integer, InsetsSource> mProvidedInsetsSources = new ArrayMap<>(); + boolean mBehindIme = false; /** * Surface insets from the previous call to relayout(), used to track @@ -730,6 +725,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP */ int mFrameRateSelectionPriority = RefreshRatePolicy.LAYER_PRIORITY_UNSET; + /** + * This is the frame rate which is passed to SurfaceFlinger if the window is part of the + * high refresh rate deny list. The variable is cached, so we do not send too many updates to + * SF. + */ + float mDenyListFrameRate = 0f; + static final int BLAST_TIMEOUT_DURATION = 5000; /* milliseconds */ private final WindowProcessController mWpcForDisplayAreaConfigChanges; @@ -5233,7 +5235,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return (mAttrs.flags & FLAG_BLUR_BEHIND) != 0 && mOwnerCanUseBackgroundBlur; } - /** * Notifies SF about the priority of the window, if it changed. SF then uses this information * to decide which window's desired rendering rate should have a priority when deciding about @@ -5242,13 +5243,21 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP */ @VisibleForTesting void updateFrameRateSelectionPriorityIfNeeded() { - final int priority = getDisplayContent().getDisplayPolicy().getRefreshRatePolicy() - .calculatePriority(this); + RefreshRatePolicy refreshRatePolicy = + getDisplayContent().getDisplayPolicy().getRefreshRatePolicy(); + final int priority = refreshRatePolicy.calculatePriority(this); if (mFrameRateSelectionPriority != priority) { mFrameRateSelectionPriority = priority; getPendingTransaction().setFrameRateSelectionPriority(mSurfaceControl, mFrameRateSelectionPriority); } + + final float refreshRate = refreshRatePolicy.getPreferredRefreshRate(this); + if (mDenyListFrameRate != refreshRate) { + mDenyListFrameRate = refreshRate; + getPendingTransaction().setFrameRate( + mSurfaceControl, mDenyListFrameRate, Surface.FRAME_RATE_COMPATIBILITY_EXACT); + } } private void updateGlobalScaleIfNeeded() { 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/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java index 26c304f763d6..2e3178b13892 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java @@ -818,10 +818,5 @@ public class DisplayModeDirectorTest { PEAK_REFRESH_RATE_URI); } } - - @Override - public boolean isDeviceInteractive(@NonNull Context context) { - return true; - } } } diff --git a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java index 0e872d96bd85..f437d1f214ea 100644 --- a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java +++ b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java @@ -25,6 +25,7 @@ import android.content.Context; import android.graphics.fonts.FontManager; import android.os.FileUtils; import android.platform.test.annotations.Presubmit; +import android.system.Os; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; @@ -281,6 +282,10 @@ public final class UpdatableFontDirTest { installFontFile(dir, "test,1", GOOD_SIGNATURE); assertThat(dir.getFontFileMap()).containsKey("test.ttf"); assertThat(parser.getRevision(dir.getFontFileMap().get("test.ttf"))).isEqualTo(1); + File fontFile = dir.getFontFileMap().get("test.ttf"); + assertThat(Os.stat(fontFile.getAbsolutePath()).st_mode & 0777).isEqualTo(0644); + File fontDir = fontFile.getParentFile(); + assertThat(Os.stat(fontDir.getAbsolutePath()).st_mode & 0777).isEqualTo(0711); } @Test 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/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java index 37fb0e930acc..82ffa765cc27 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java @@ -123,15 +123,6 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { updateDisplayFrames(); } - void addWindowWithRawInsetsState(WindowState win) { - addWindow(win); - // Without mPerformLayout in display content, the window cannot see any insets. Override the - // insets state with the global one. - final InsetsState insetsState = - win.getDisplayContent().getInsetsStateController().getRawInsetsState(); - win.mAboveInsetsState = insetsState; - } - public void setRotation(int rotation, boolean includingWindows) { mRotation = rotation; updateDisplayFrames(); @@ -281,7 +272,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { @Test public void layoutWindowLw_fitStatusBars() { mWindow.mAttrs.setFitInsetsTypes(Type.statusBars()); - addWindowWithRawInsetsState(mWindow); + addWindow(mWindow); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -292,7 +283,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { @Test public void layoutWindowLw_fitNavigationBars() { mWindow.mAttrs.setFitInsetsTypes(Type.navigationBars()); - addWindowWithRawInsetsState(mWindow); + addWindow(mWindow); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -303,7 +294,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { @Test public void layoutWindowLw_fitAllSides() { mWindow.mAttrs.setFitInsetsSides(Side.all()); - addWindowWithRawInsetsState(mWindow); + addWindow(mWindow); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -314,7 +305,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { @Test public void layoutWindowLw_fitTopOnly() { mWindow.mAttrs.setFitInsetsSides(Side.TOP); - addWindowWithRawInsetsState(mWindow); + addWindow(mWindow); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -324,12 +315,11 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { @Test public void layoutWindowLw_fitInsetsIgnoringVisibility() { - final InsetsState state = - mDisplayContent.getInsetsStateController().getRawInsetsState(); + final InsetsState state = mWindow.getInsetsState(); state.getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false); state.getSource(InsetsState.ITYPE_NAVIGATION_BAR).setVisible(false); mWindow.mAttrs.setFitInsetsIgnoringVisibility(true); - addWindowWithRawInsetsState(mWindow); + addWindow(mWindow); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -339,12 +329,11 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { @Test public void layoutWindowLw_fitInsetsNotIgnoringVisibility() { - final InsetsState state = - mDisplayContent.getInsetsStateController().getRawInsetsState(); + final InsetsState state = mWindow.getInsetsState(); state.getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false); state.getSource(InsetsState.ITYPE_NAVIGATION_BAR).setVisible(false); mWindow.mAttrs.setFitInsetsIgnoringVisibility(false); - addWindowWithRawInsetsState(mWindow); + addWindow(mWindow); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -360,7 +349,8 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { state.getSource(InsetsState.ITYPE_IME).setFrame( 0, DISPLAY_HEIGHT - IME_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT); mWindow.mAttrs.privateFlags |= PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME; - addWindowWithRawInsetsState(mWindow); + mWindow.mBehindIme = true; + addWindow(mWindow); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -374,7 +364,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mWindow.mAttrs.setFitInsetsTypes(Type.displayCutout()); mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; - addWindowWithRawInsetsState(mWindow); + addWindow(mWindow); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -389,7 +379,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mWindow.mAttrs.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; mWindow.mAttrs.setFitInsetsTypes(0 /* types */); - addWindowWithRawInsetsState(mWindow); + addWindow(mWindow); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -405,7 +395,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; mWindow.mAttrs.setFitInsetsTypes(0 /* types */); mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER; - addWindowWithRawInsetsState(mWindow); + addWindow(mWindow); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -421,7 +411,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; mWindow.mAttrs.setFitInsetsTypes(0 /* types */); mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; - addWindowWithRawInsetsState(mWindow); + addWindow(mWindow); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -437,7 +427,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; mWindow.mAttrs.setFitInsetsTypes(0 /* types */); mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; - addWindowWithRawInsetsState(mWindow); + addWindow(mWindow); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -452,7 +442,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mWindow.mAttrs.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; mWindow.mAttrs.setFitInsetsTypes(0 /* types */); - addWindowWithRawInsetsState(mWindow); + addWindow(mWindow); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -467,12 +457,11 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mWindow.mAttrs.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; mWindow.mAttrs.setFitInsetsTypes(0 /* types */); - mDisplayContent.getInsetsStateController().getRawInsetsState() - .getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false); + mWindow.getInsetsState().getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false); final InsetsState requestedState = new InsetsState(); requestedState.getSource(ITYPE_STATUS_BAR).setVisible(false); mWindow.updateRequestedVisibility(requestedState); - addWindowWithRawInsetsState(mWindow); + addWindow(mWindow); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -487,13 +476,12 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mWindow.mAttrs.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; mWindow.mAttrs.setFitInsetsTypes(0 /* types */); - mDisplayContent.getInsetsStateController().getRawInsetsState() - .getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false); + mWindow.getInsetsState().getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false); final InsetsState requestedState = new InsetsState(); requestedState.getSource(ITYPE_STATUS_BAR).setVisible(false); mWindow.updateRequestedVisibility(requestedState); mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; - addWindowWithRawInsetsState(mWindow); + addWindow(mWindow); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -509,7 +497,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mWindow.mAttrs.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; mWindow.mAttrs.setFitInsetsTypes(0 /* types */); - addWindowWithRawInsetsState(mWindow); + addWindow(mWindow); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -525,7 +513,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mWindow.mAttrs.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; mWindow.mAttrs.setFitInsetsTypes(0 /* types */); - addWindowWithRawInsetsState(mWindow); + addWindow(mWindow); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -541,7 +529,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mWindow.mAttrs.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; mWindow.mAttrs.setFitInsetsTypes(0 /* types */); - addWindowWithRawInsetsState(mWindow); + addWindow(mWindow); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -557,7 +545,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mWindow.mAttrs.type = TYPE_APPLICATION_OVERLAY; mWindow.mAttrs.width = DISPLAY_WIDTH; mWindow.mAttrs.height = DISPLAY_HEIGHT; - addWindowWithRawInsetsState(mWindow); + addWindow(mWindow); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -574,7 +562,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; mWindow.mAttrs.setFitInsetsTypes(0 /* types */); mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; - addWindowWithRawInsetsState(mWindow); + addWindow(mWindow); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -588,7 +576,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mWindow.mAttrs.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; mWindow.mAttrs.setFitInsetsTypes(0 /* types */); - addWindowWithRawInsetsState(mWindow); + addWindow(mWindow); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -604,7 +592,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; mWindow.mAttrs.setFitInsetsTypes(0 /* types */); mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER; - addWindowWithRawInsetsState(mWindow); + addWindow(mWindow); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -620,7 +608,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; mWindow.mAttrs.setFitInsetsTypes(0 /* types */); mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; - addWindowWithRawInsetsState(mWindow); + addWindow(mWindow); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -636,7 +624,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; mWindow.mAttrs.setFitInsetsTypes(0 /* types */); mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; - addWindowWithRawInsetsState(mWindow); + addWindow(mWindow); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); @@ -650,7 +638,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; mWindow.mAttrs.setFitInsetsTypes(0 /* types */); mWindow.mAttrs.softInputMode = SOFT_INPUT_ADJUST_NOTHING; - addWindowWithRawInsetsState(mWindow); + addWindow(mWindow); final int forwardedInsetBottom = 50; mDisplayPolicy.setForwardedInsets(Insets.of(0, 0, 0, forwardedInsetBottom)); @@ -788,13 +776,9 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { public void testFixedRotationInsetsSourceFrame() { doReturn((mDisplayContent.getRotation() + 1) % 4).when(mDisplayContent) .rotationForActivityInDifferentOrientation(eq(mWindow.mActivityRecord)); - mWindow.mAboveInsetsState.addSource(mDisplayContent.getInsetsStateController() - .getRawInsetsState().peekSource(ITYPE_STATUS_BAR)); - final Rect frame = mDisplayPolicy.getInsetsPolicy().getInsetsForWindow(mWindow) - .getSource(ITYPE_STATUS_BAR).getFrame(); + final Rect frame = mWindow.getInsetsState().getSource(ITYPE_STATUS_BAR).getFrame(); mDisplayContent.rotateInDifferentOrientationIfNeeded(mWindow.mActivityRecord); - final Rect rotatedFrame = mDisplayPolicy.getInsetsPolicy().getInsetsForWindow(mWindow) - .getSource(ITYPE_STATUS_BAR).getFrame(); + final Rect rotatedFrame = mWindow.getInsetsState().getSource(ITYPE_STATUS_BAR).getFrame(); assertEquals(DISPLAY_WIDTH, frame.width()); assertEquals(DISPLAY_HEIGHT, rotatedFrame.width()); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java index 499507e969cf..77537a9de6be 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java @@ -300,7 +300,6 @@ public class DisplayPolicyTests extends WindowTestsBase { displayPolicy.addWindowLw(mNavBarWindow, mNavBarWindow.mAttrs); mNavBarWindow.getControllableInsetProvider().setServerVisible(true); final InsetsState state = mDisplayContent.getInsetsStateController().getRawInsetsState(); - mImeWindow.mAboveInsetsState = state; mDisplayContent.mDisplayFrames = new DisplayFrames(mDisplayContent.getDisplayId(), state, displayInfo, null /* displayCutout */); diff --git a/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java b/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java index 032edde61bec..325bca418d13 100644 --- a/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java @@ -18,15 +18,24 @@ package com.android.server.wm; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import android.platform.test.annotations.Presubmit; +import android.view.Display.Mode; +import android.view.DisplayInfo; +import android.view.Surface; +import android.view.SurfaceControl; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -40,16 +49,40 @@ import org.junit.runner.RunWith; @Presubmit @RunWith(WindowTestRunner.class) public class FrameRateSelectionPriorityTests extends WindowTestsBase { + private static final float FLOAT_TOLERANCE = 0.01f; + private static final int LOW_MODE_ID = 3; + + private DisplayPolicy mDisplayPolicy = mock(DisplayPolicy.class); + private RefreshRatePolicy mRefreshRatePolicy; + private HighRefreshRateDenylist mDenylist = mock(HighRefreshRateDenylist.class); + + @Before + public void setUp() { + DisplayInfo di = new DisplayInfo(mDisplayInfo); + Mode defaultMode = di.getDefaultMode(); + di.supportedModes = new Mode[] { + new Mode(1, defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), 90), + new Mode(2, defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), 70), + new Mode(LOW_MODE_ID, + defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), 60), + }; + di.defaultModeId = 1; + mRefreshRatePolicy = new RefreshRatePolicy(mWm, di, mDenylist); + when(mDisplayPolicy.getRefreshRatePolicy()).thenReturn(mRefreshRatePolicy); + } @Test public void basicTest() { final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow"); assertNotNull("Window state is created", appWindow); + assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET); + assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE); appWindow.updateFrameRateSelectionPriorityIfNeeded(); // Priority doesn't change. assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET); + assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE); // Call the function a few times. appWindow.updateFrameRateSelectionPriorityIfNeeded(); @@ -57,7 +90,9 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase { // Since nothing changed in the priority state, the transaction should not be updating. verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionPriority( - appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET); + any(SurfaceControl.class), anyInt()); + verify(appWindow.getPendingTransaction(), never()).setFrameRate( + any(SurfaceControl.class), anyInt(), anyInt()); } @Test @@ -66,10 +101,16 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase { assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET); assertEquals(appWindow.getDisplayContent().getDisplayPolicy().getRefreshRatePolicy() .getPreferredModeId(appWindow), 0); + assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE); + assertEquals(appWindow.getDisplayContent().getDisplayPolicy().getRefreshRatePolicy() + .getPreferredRefreshRate(appWindow), 0, FLOAT_TOLERANCE); + + assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE); appWindow.updateFrameRateSelectionPriorityIfNeeded(); // Priority stays MAX_VALUE. assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET); + assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE); verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionPriority( appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET); @@ -78,31 +119,38 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase { appWindow.updateFrameRateSelectionPriorityIfNeeded(); // Priority changes to 1. assertEquals(appWindow.mFrameRateSelectionPriority, 1); + assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE); verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority( appWindow.getSurfaceControl(), 1); + verify(appWindow.getPendingTransaction(), never()).setFrameRate( + any(SurfaceControl.class), anyInt(), anyInt()); } @Test public void testApplicationInFocusWithModeId() { final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow"); assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET); + assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE); // Application is in focus. appWindow.mToken.mDisplayContent.mCurrentFocus = appWindow; appWindow.updateFrameRateSelectionPriorityIfNeeded(); // Priority changes. assertEquals(appWindow.mFrameRateSelectionPriority, 1); + assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE); // Update the mode ID to a requested number. appWindow.mAttrs.preferredDisplayModeId = 1; appWindow.updateFrameRateSelectionPriorityIfNeeded(); // Priority changes. assertEquals(appWindow.mFrameRateSelectionPriority, 0); + assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE); // Remove the mode ID request. appWindow.mAttrs.preferredDisplayModeId = 0; appWindow.updateFrameRateSelectionPriorityIfNeeded(); // Priority changes. assertEquals(appWindow.mFrameRateSelectionPriority, 1); + assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE); // Verify we called actions on Transactions correctly. verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionPriority( @@ -111,12 +159,15 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase { appWindow.getSurfaceControl(), 0); verify(appWindow.getPendingTransaction(), times(2)).setFrameRateSelectionPriority( appWindow.getSurfaceControl(), 1); + verify(appWindow.getPendingTransaction(), never()).setFrameRate( + any(SurfaceControl.class), anyInt(), anyInt()); } @Test public void testApplicationNotInFocusWithModeId() { final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow"); assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET); + assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE); final WindowState inFocusWindow = createWindow(null, TYPE_APPLICATION, "inFocus"); appWindow.mToken.mDisplayContent.mCurrentFocus = inFocusWindow; @@ -124,23 +175,28 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase { appWindow.updateFrameRateSelectionPriorityIfNeeded(); // The window is not in focus. assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET); + assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE); // Update the mode ID to a requested number. appWindow.mAttrs.preferredDisplayModeId = 1; appWindow.updateFrameRateSelectionPriorityIfNeeded(); // Priority changes. assertEquals(appWindow.mFrameRateSelectionPriority, 2); + assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE); verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority( appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET); verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority( appWindow.getSurfaceControl(), 2); + verify(appWindow.getPendingTransaction(), never()).setFrameRate( + any(SurfaceControl.class), anyInt(), anyInt()); } @Test public void testApplicationNotInFocusWithoutModeId() { final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow"); assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET); + assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE); final WindowState inFocusWindow = createWindow(null, TYPE_APPLICATION, "inFocus"); appWindow.mToken.mDisplayContent.mCurrentFocus = inFocusWindow; @@ -148,14 +204,45 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase { appWindow.updateFrameRateSelectionPriorityIfNeeded(); // The window is not in focus. assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET); + assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE); // Make sure that the mode ID is not set. appWindow.mAttrs.preferredDisplayModeId = 0; appWindow.updateFrameRateSelectionPriorityIfNeeded(); // Priority doesn't change. assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET); + assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE); verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority( appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET); + verify(appWindow.getPendingTransaction(), never()).setFrameRate( + any(SurfaceControl.class), anyInt(), anyInt()); + } + + @Test + public void testPreferredRefreshRate() { + final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow"); + assertNotNull("Window state is created", appWindow); + when(appWindow.getDisplayContent().getDisplayPolicy()).thenReturn(mDisplayPolicy); + + appWindow.mAttrs.packageName = "com.android.test"; + when(mDenylist.isDenylisted("com.android.test")).thenReturn(true); + + assertEquals(0, mRefreshRatePolicy.getPreferredModeId(appWindow)); + assertEquals(60, mRefreshRatePolicy.getPreferredRefreshRate(appWindow), FLOAT_TOLERANCE); + + appWindow.updateFrameRateSelectionPriorityIfNeeded(); + assertEquals(RefreshRatePolicy.LAYER_PRIORITY_UNSET, appWindow.mFrameRateSelectionPriority); + assertEquals(60, appWindow.mDenyListFrameRate, FLOAT_TOLERANCE); + + // Call the function a few times. + appWindow.updateFrameRateSelectionPriorityIfNeeded(); + appWindow.updateFrameRateSelectionPriorityIfNeeded(); + + // Since nothing changed in the priority state, the transaction should not be updating. + verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionPriority( + any(SurfaceControl.class), anyInt()); + verify(appWindow.getPendingTransaction(), times(1)).setFrameRate( + appWindow.getSurfaceControl(), 60, Surface.FRAME_RATE_COMPATIBILITY_EXACT); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java index bf3ed692dc8e..e0fd3796f2aa 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java @@ -45,7 +45,6 @@ import static org.mockito.Mockito.verify; import android.app.StatusBarManager; import android.platform.test.annotations.Presubmit; -import android.view.InsetsSource; import android.view.InsetsSourceControl; import android.view.InsetsState; @@ -273,6 +272,7 @@ public class InsetsPolicyTest extends WindowTestsBase { final WindowState navBar = addNonFocusableWindow(TYPE_NAVIGATION_BAR, "navBar"); navBar.setHasSurface(true); navBar.getControllableInsetProvider().setServerVisible(true); + final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy()); doNothing().when(policy).startAnimation(anyBoolean(), any()); @@ -337,14 +337,11 @@ public class InsetsPolicyTest extends WindowTestsBase { @UseTestDisplay(addWindows = W_ACTIVITY) @Test public void testAbortTransientBars_bothCanBeAborted_appGetsBothRealControls() { - final InsetsSource statusBarSource = addNonFocusableWindow(TYPE_STATUS_BAR, "statusBar") - .getControllableInsetProvider().getSource(); - final InsetsSource navBarSource = addNonFocusableWindow(TYPE_NAVIGATION_BAR, "navBar") - .getControllableInsetProvider().getSource(); - statusBarSource.setVisible(false); - navBarSource.setVisible(false); - mAppWindow.mAboveInsetsState.addSource(navBarSource); - mAppWindow.mAboveInsetsState.addSource(statusBarSource); + addNonFocusableWindow(TYPE_STATUS_BAR, "statusBar") + .getControllableInsetProvider().getSource().setVisible(false); + addNonFocusableWindow(TYPE_NAVIGATION_BAR, "navBar") + .getControllableInsetProvider().getSource().setVisible(false); + final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy()); doNothing().when(policy).startAnimation(anyBoolean(), any()); policy.updateBarControlTarget(mAppWindow); diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java index 2107ab1eeeea..276643847712 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java @@ -59,6 +59,25 @@ import org.junit.runner.RunWith; public class InsetsStateControllerTest extends WindowTestsBase { @Test + public void testStripForDispatch_notOwn() { + final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar"); + final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null); + statusBar.setControllableInsetProvider(getController().getSourceProvider(ITYPE_STATUS_BAR)); + assertNotNull(getController().getInsetsForWindow(app).peekSource(ITYPE_STATUS_BAR)); + } + + @Test + public void testStripForDispatch_own() { + final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar"); + mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_STATUS_BAR) + .setWindow(statusBar, null, null); + statusBar.setControllableInsetProvider(getController().getSourceProvider(ITYPE_STATUS_BAR)); + final InsetsState state = getController().getInsetsForWindow(statusBar); + assertNull(state.peekSource(ITYPE_STATUS_BAR)); + } + + @Test public void testStripForDispatch_navBar() { final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar"); final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar"); @@ -123,15 +142,14 @@ public class InsetsStateControllerTest extends WindowTestsBase { getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null); final WindowState app1 = createWindow(null, TYPE_APPLICATION, "app1"); - final WindowState app2 = createWindow(null, TYPE_APPLICATION, "app2"); + app1.mBehindIme = true; - app1.mAboveInsetsState.addSource(getController().getRawInsetsState().getSource(ITYPE_IME)); + final WindowState app2 = createWindow(null, TYPE_APPLICATION, "app2"); + app2.mBehindIme = false; getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true); - assertFalse(getController().getInsetsForWindow(app2).getSource(ITYPE_IME) - .isVisible()); - assertTrue(getController().getInsetsForWindow(app1).getSource(ITYPE_IME) - .isVisible()); + assertFalse(getController().getInsetsForWindow(app2).getSource(ITYPE_IME).isVisible()); + assertTrue(getController().getInsetsForWindow(app1).getSource(ITYPE_IME).isVisible()); } @UseTestDisplay(addWindows = W_INPUT_METHOD) @@ -140,8 +158,7 @@ public class InsetsStateControllerTest extends WindowTestsBase { getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null); final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); - app.mAboveInsetsState.getSource(ITYPE_IME).setVisible(true); - app.mAboveInsetsState.getSource(ITYPE_IME).setFrame(mImeWindow.getFrame()); + app.mBehindIme = true; getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true); assertTrue(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible()); @@ -153,10 +170,10 @@ public class InsetsStateControllerTest extends WindowTestsBase { getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null); final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + app.mBehindIme = false; getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true); - assertFalse(getController().getInsetsForWindow(app).getSource(ITYPE_IME) - .isVisible()); + assertFalse(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible()); } @UseTestDisplay(addWindows = W_INPUT_METHOD) @@ -193,8 +210,7 @@ public class InsetsStateControllerTest extends WindowTestsBase { // app won't get visible IME insets while above IME even when IME is visible. assertTrue(getController().getRawInsetsState().getSourceOrDefaultVisibility(ITYPE_IME)); - assertFalse(getController().getInsetsForWindow(app).getSource(ITYPE_IME) - .isVisible()); + assertFalse(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible()); // Reset invocation counter. clearInvocations(app); @@ -203,8 +219,6 @@ public class InsetsStateControllerTest extends WindowTestsBase { app.mAttrs.flags &= ~FLAG_NOT_FOCUSABLE; mDisplayContent.computeImeTarget(true); mDisplayContent.applySurfaceChangesTransaction(); - app.mAboveInsetsState.getSource(ITYPE_IME).setVisible(true); - app.mAboveInsetsState.getSource(ITYPE_IME).setFrame(mImeWindow.getFrame()); // Make sure app got notified. verify(app, atLeast(1)).notifyInsetsChanged(); @@ -220,8 +234,6 @@ public class InsetsStateControllerTest extends WindowTestsBase { final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); final WindowState child = createWindow(app, TYPE_APPLICATION, "child"); - app.mAboveInsetsState.set(getController().getRawInsetsState()); - child.mAboveInsetsState.set(getController().getRawInsetsState()); child.mAttrs.flags |= FLAG_ALT_FOCUSABLE_IM; mDisplayContent.computeImeTarget(true); @@ -230,8 +242,7 @@ public class InsetsStateControllerTest extends WindowTestsBase { getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true); assertTrue(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible()); - assertFalse(getController().getInsetsForWindow(child).getSource(ITYPE_IME) - .isVisible()); + assertFalse(getController().getInsetsForWindow(child).getSource(ITYPE_IME).isVisible()); } @UseTestDisplay(addWindows = W_INPUT_METHOD) @@ -241,7 +252,6 @@ public class InsetsStateControllerTest extends WindowTestsBase { final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); final WindowState child = createWindow(app, TYPE_APPLICATION, "child"); - app.mAboveInsetsState.addSource(getController().getRawInsetsState().peekSource(ITYPE_IME)); child.mAttrs.flags |= FLAG_NOT_FOCUSABLE; child.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); @@ -251,8 +261,7 @@ public class InsetsStateControllerTest extends WindowTestsBase { getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true); assertTrue(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible()); - assertFalse(getController().getInsetsForWindow(child).getSource(ITYPE_IME) - .isVisible()); + assertFalse(getController().getInsetsForWindow(child).getSource(ITYPE_IME).isVisible()); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java index 77a4b0507a42..ef3c7ae91fed 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java @@ -44,7 +44,7 @@ import org.junit.runner.RunWith; @RunWith(WindowTestRunner.class) @FlakyTest public class RefreshRatePolicyTest extends WindowTestsBase { - + private static final float FLOAT_TOLERANCE = 0.01f; private static final int LOW_MODE_ID = 3; private RefreshRatePolicy mPolicy; @@ -70,28 +70,34 @@ public class RefreshRatePolicyTest extends WindowTestsBase { "cameraUsingWindow"); cameraUsingWindow.mAttrs.packageName = "com.android.test"; assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow)); + assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); mPolicy.addNonHighRefreshRatePackage("com.android.test"); assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(cameraUsingWindow)); + assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); mPolicy.removeNonHighRefreshRatePackage("com.android.test"); assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow)); + assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); } @Test - public void testBlacklist() { - final WindowState blacklistedWindow = createWindow(null, TYPE_BASE_APPLICATION, - "blacklistedWindow"); - blacklistedWindow.mAttrs.packageName = "com.android.test"; + public void testDenyList() { + final WindowState denylistedWindow = createWindow(null, TYPE_BASE_APPLICATION, + "denylistedWindow"); + denylistedWindow.mAttrs.packageName = "com.android.test"; when(mDenylist.isDenylisted("com.android.test")).thenReturn(true); - assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(blacklistedWindow)); + assertEquals(0, mPolicy.getPreferredModeId(denylistedWindow)); + assertEquals(60, mPolicy.getPreferredRefreshRate(denylistedWindow), FLOAT_TOLERANCE); } @Test public void testAppOverride_blacklist() { final WindowState overrideWindow = createWindow(null, TYPE_BASE_APPLICATION, "overrideWindow"); + overrideWindow.mAttrs.packageName = "com.android.test"; overrideWindow.mAttrs.preferredDisplayModeId = LOW_MODE_ID; when(mDenylist.isDenylisted("com.android.test")).thenReturn(true); assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(overrideWindow)); + assertEquals(60, mPolicy.getPreferredRefreshRate(overrideWindow), FLOAT_TOLERANCE); } @Test @@ -102,6 +108,7 @@ public class RefreshRatePolicyTest extends WindowTestsBase { overrideWindow.mAttrs.preferredDisplayModeId = LOW_MODE_ID; mPolicy.addNonHighRefreshRatePackage("com.android.test"); assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(overrideWindow)); + assertEquals(0, mPolicy.getPreferredRefreshRate(overrideWindow), FLOAT_TOLERANCE); } @Test @@ -115,6 +122,7 @@ public class RefreshRatePolicyTest extends WindowTestsBase { false /* hidden */, ANIMATION_TYPE_APP_TRANSITION); mPolicy.addNonHighRefreshRatePackage("com.android.test"); assertEquals(0, mPolicy.getPreferredModeId(overrideWindow)); + assertEquals(0, mPolicy.getPreferredRefreshRate(overrideWindow), FLOAT_TOLERANCE); } @Test @@ -125,10 +133,12 @@ public class RefreshRatePolicyTest extends WindowTestsBase { mPolicy.addNonHighRefreshRatePackage("com.android.test"); assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(cameraUsingWindow)); + assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); cameraUsingWindow.mActivityRecord.mSurfaceAnimator.startAnimation( cameraUsingWindow.getPendingTransaction(), mock(AnimationAdapter.class), false /* hidden */, ANIMATION_TYPE_APP_TRANSITION); assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow)); + assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index db773241f063..371e6802ced7 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -109,7 +109,7 @@ public class SizeCompatTests extends WindowTestsBase { final Rect originalOverrideBounds = new Rect(mActivity.getBounds()); resizeDisplay(mTask.mDisplayContent, 600, 1200); // The visible activity should recompute configuration according to the last parent bounds. - mAtm.restartActivityProcessIfVisible(mActivity.appToken); + mAtm.mActivityClientController.restartActivityProcessIfVisible(mActivity.appToken); assertEquals(Task.ActivityState.RESTARTING_PROCESS, mActivity.getState()); assertNotEquals(originalOverrideBounds, mActivity.getBounds()); diff --git a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java index c308fdbf8491..b8d44f605bca 100644 --- a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java +++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java @@ -245,6 +245,12 @@ public class StubTransaction extends SurfaceControl.Transaction { } @Override + public SurfaceControl.Transaction setFrameRate(SurfaceControl sc, float frameRate, + int compatibility) { + return this; + } + + @Override public SurfaceControl.Transaction unsetColor(SurfaceControl sc) { return this; } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java index a1f89ec75784..1607f013ee81 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java @@ -275,7 +275,7 @@ public class WindowFrameTests extends WindowTestsBase { imeSource.setFrame(imeFrame); imeSource.setVisible(true); w.updateRequestedVisibility(state); - w.mAboveInsetsState.addSource(imeSource); + w.mBehindIme = true; // With no insets or system decor all the frames incoming from PhoneWindowManager // are identical. diff --git a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java index 519d0164b0d6..5eb75e762fc9 100644 --- a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java +++ b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java @@ -19,6 +19,7 @@ package android.telephony.ims; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringDef; +import android.annotation.SystemApi; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; @@ -34,14 +35,135 @@ import java.util.List; * network during a SUBSCRIBE request. See RFC3863 for more information. * @hide */ +@SystemApi public final class RcsContactPresenceTuple implements Parcelable { - /** The service id of the MMTEL */ + /** + * The service ID used to indicate that MMTEL service is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ public static final String SERVICE_ID_MMTEL = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.mmtel"; - /** The service id of the Call Composer */ + /** + * The service ID used to indicate that the chat(v1.0) is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_CHAT_V1 = "org.openmobilealliance:IM-session"; + + /** + * The service ID used to indicate that the chat(v2.0) is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_CHAT_V2 = "org.openmobilealliance:ChatSession"; + + /** + * The service ID used to indicate that the File Transfer is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_FT = "org.openmobilealliance:File-Transfer-HTTP"; + + /** + * The service ID used to indicate that the File Transfer over SMS is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_FT_OVER_SMS = + "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.ftsms"; + + /** + * The service ID used to indicate that the Geolocation Push is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_GEO_PUSH = + "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.geopush"; + + /** + * The service ID used to indicate that the Geolocation Push via SMS is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_GEO_PUSH_VIA_SMS = + "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.geosms"; + + /** + * The service ID used to indicate that the Call Composer is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ public static final String SERVICE_ID_CALL_COMPOSER = - "org.3gpp.urn:urn-7:3gppservice.ims.icsi.gsma.callcomposer"; + "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.callcomposer"; + + /** + * The service ID used to indicate that the Post Call is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_POST_CALL = + "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.callunanswered"; + + /** + * The service ID used to indicate that the Shared Map is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_SHARED_MAP = + "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.sharedmap"; + + /** + * The service ID used to indicate that the Shared Sketch is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_SHARED_SKETCH = + "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.sharedsketch"; + + /** + * The service ID used to indicate that the Chatbot using Session is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_CHATBOT = + "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.chatbot"; + + /** + * The service ID used to indicate that the Standalone Messaging is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_CHATBOT_STANDALONE = + " org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.chatbot.sa"; + + /** + * The service ID used to indicate that the Chatbot Role is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_CHATBOT_ROLE = "org.gsma.rcs.isbot"; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @StringDef(prefix = "SERVICE_ID_", value = { + SERVICE_ID_MMTEL, + SERVICE_ID_CHAT_V1, + SERVICE_ID_CHAT_V2, + SERVICE_ID_FT, + SERVICE_ID_FT_OVER_SMS, + SERVICE_ID_GEO_PUSH, + SERVICE_ID_GEO_PUSH_VIA_SMS, + SERVICE_ID_CALL_COMPOSER, + SERVICE_ID_POST_CALL, + SERVICE_ID_SHARED_MAP, + SERVICE_ID_SHARED_SKETCH, + SERVICE_ID_CHATBOT, + SERVICE_ID_CHATBOT_STANDALONE, + SERVICE_ID_CHATBOT_ROLE + }) + public @interface ServiceId {} /** The service capabilities is available. */ public static final String TUPLE_BASIC_STATUS_OPEN = "open"; @@ -149,6 +271,7 @@ public final class RcsContactPresenceTuple implements Parcelable { in.readStringList(mSupportedDuplexModeList); in.readStringList(mUnsupportedDuplexModeList); } + @Override public void writeToParcel(@NonNull Parcel out, int flags) { out.writeBoolean(mIsAudioCapable); @@ -217,12 +340,14 @@ public final class RcsContactPresenceTuple implements Parcelable { /** * Builds a RcsContactPresenceTuple instance. + * @param status The status associated with the service capability. See RFC3865 for more + * information. * @param serviceId The OMA Presence service-id associated with this capability. See the * OMA Presence SIMPLE specification v1.1, section 10.5.1. * @param serviceVersion The OMA Presence version associated with the service capability. * See the OMA Presence SIMPLE specification v1.1, section 10.5.1. */ - public Builder(@NonNull @BasicStatus String status, @NonNull String serviceId, + public Builder(@NonNull @BasicStatus String status, @NonNull @ServiceId String serviceId, @NonNull String serviceVersion) { mPresenceTuple = new RcsContactPresenceTuple(status, serviceId, serviceVersion); } @@ -230,16 +355,17 @@ public final class RcsContactPresenceTuple implements Parcelable { /** * The optional SIP Contact URI associated with the PIDF tuple element. */ - public @NonNull Builder addContactUri(@NonNull Uri contactUri) { + public @NonNull Builder setContactUri(@NonNull Uri contactUri) { mPresenceTuple.mContactUri = contactUri; return this; } /** * The optional timestamp indicating the data and time of the status change of this tuple. - * See RFC3863, section 4.1.7 for more information on the expected format. + * Per RFC3863 section 4.1.7, the timestamp is formatted as an IMPP datetime format + * string per RFC3339. */ - public @NonNull Builder addTimeStamp(@NonNull String timestamp) { + public @NonNull Builder setTimestamp(@NonNull String timestamp) { mPresenceTuple.mTimestamp = timestamp; return this; } @@ -248,7 +374,7 @@ public final class RcsContactPresenceTuple implements Parcelable { * An optional parameter containing the description element of the service-description. See * OMA Presence SIMPLE specification v1.1 */ - public @NonNull Builder addDescription(@NonNull String description) { + public @NonNull Builder setServiceDescription(@NonNull String description) { mPresenceTuple.mServiceDescription = description; return this; } @@ -257,7 +383,7 @@ public final class RcsContactPresenceTuple implements Parcelable { * An optional parameter containing the service capabilities of the presence tuple if they * are present in the servcaps element. */ - public @NonNull Builder addServiceCapabilities(@NonNull ServiceCapabilities caps) { + public @NonNull Builder setServiceCapabilities(@NonNull ServiceCapabilities caps) { mPresenceTuple.mServiceCapabilities = caps; return this; } diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java index d4715bfeeb3e..fe855023f5d0 100644 --- a/telephony/java/android/telephony/ims/RcsContactUceCapability.java +++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java @@ -19,6 +19,7 @@ package android.telephony.ims; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; @@ -33,6 +34,7 @@ import java.util.List; * Contains the User Capability Exchange capabilities corresponding to a contact's URI. * @hide */ +@SystemApi public final class RcsContactUceCapability implements Parcelable { /** Contains presence information associated with the contact */ @@ -70,52 +72,46 @@ public final class RcsContactUceCapability implements Parcelable { public @interface SourceType {} /** + * Capability information for the requested contact has expired and can not be refreshed due to + * a temporary network error. This is a temporary error and the capabilities of the contact + * should be queried again at a later time. + */ + public static final int REQUEST_RESULT_UNKNOWN = 0; + + /** * The requested contact was found to be offline when queried. This is only applicable to * contact capabilities that were queried via OPTIONS requests and the network returned a * 408/480 response. */ - public static final int REQUEST_RESULT_NOT_ONLINE = 0; + public static final int REQUEST_RESULT_NOT_ONLINE = 1; /** * Capability information for the requested contact was not found. The contact should not be * considered an RCS user. */ - public static final int REQUEST_RESULT_NOT_FOUND = 1; + public static final int REQUEST_RESULT_NOT_FOUND = 2; /** * Capability information for the requested contact was found successfully. */ - public static final int REQUEST_RESULT_FOUND = 2; - - /** - * Capability information for the requested contact has expired and can not be refreshed due to - * a temporary network error. This is a temporary error and the capabilities of the contact - * should be queried again at a later time. - */ - public static final int REQUEST_RESULT_UNKNOWN = 3; + public static final int REQUEST_RESULT_FOUND = 3; /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = "REQUEST_RESULT_", value = { + REQUEST_RESULT_UNKNOWN, REQUEST_RESULT_NOT_ONLINE, REQUEST_RESULT_NOT_FOUND, - REQUEST_RESULT_FOUND, - REQUEST_RESULT_UNKNOWN + REQUEST_RESULT_FOUND }) public @interface RequestResult {} /** - * The base class of {@link OptionsBuilder} and {@link PresenceBuilder} - */ - public static abstract class RcsUcsCapabilityBuilder { - public abstract @NonNull RcsContactUceCapability build(); - } - - /** * Builder to help construct {@link RcsContactUceCapability} instances when capabilities were * queried through SIP OPTIONS. + * @hide */ - public static class OptionsBuilder extends RcsUcsCapabilityBuilder { + public static final class OptionsBuilder { private final RcsContactUceCapability mCapabilities; @@ -162,7 +158,6 @@ public final class RcsContactUceCapability implements Parcelable { /** * @return the constructed instance. */ - @Override public @NonNull RcsContactUceCapability build() { return mCapabilities; } @@ -172,7 +167,7 @@ public final class RcsContactUceCapability implements Parcelable { * Builder to help construct {@link RcsContactUceCapability} instances when capabilities were * queried through a presence server. */ - public static class PresenceBuilder extends RcsUcsCapabilityBuilder { + public static final class PresenceBuilder { private final RcsContactUceCapability mCapabilities; @@ -214,7 +209,6 @@ public final class RcsContactUceCapability implements Parcelable { /** * @return the RcsContactUceCapability instance. */ - @Override public @NonNull RcsContactUceCapability build() { return mCapabilities; } @@ -284,6 +278,7 @@ public final class RcsContactUceCapability implements Parcelable { * <p> * Note: this is only populated if {@link #getCapabilityMechanism} is * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_OPTIONS} + * @hide */ public @NonNull List<String> getOptionsFeatureTags() { if (mCapabilityMechanism != CAPABILITY_MECHANISM_OPTIONS) { @@ -299,7 +294,7 @@ public final class RcsContactUceCapability implements Parcelable { * Note: this is only populated if {@link #getCapabilityMechanism} is * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_PRESENCE} */ - public @NonNull List<RcsContactPresenceTuple> getPresenceTuples() { + public @NonNull List<RcsContactPresenceTuple> getCapabilityTuples() { if (mCapabilityMechanism != CAPABILITY_MECHANISM_PRESENCE) { return Collections.emptyList(); } @@ -309,13 +304,14 @@ public final class RcsContactUceCapability implements Parcelable { /** * Get the RcsContactPresenceTuple associated with the given service id. * @param serviceId The service id to get the presence tuple. - * @return The RcsContactPresenceTuple which has the given service id. + * @return The RcsContactPresenceTuple which has the given service id or {@code null} if the + * service id does not exist in the list of presence tuples returned from the network. * * <p> * Note: this is only populated if {@link #getCapabilityMechanism} is * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_PRESENCE} */ - public @Nullable RcsContactPresenceTuple getPresenceTuple(@NonNull String serviceId) { + public @Nullable RcsContactPresenceTuple getCapabilityTuple(@NonNull String serviceId) { if (mCapabilityMechanism != CAPABILITY_MECHANISM_PRESENCE) { return null; } diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java index 6c31466c2a89..070fd799d6cc 100644 --- a/telephony/java/android/telephony/ims/RcsUceAdapter.java +++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java @@ -63,6 +63,7 @@ public class RcsUceAdapter { * RcsFeature should not publish capabilities or service capability requests. * @hide */ + @SystemApi public static final int CAPABILITY_TYPE_PRESENCE_UCE = 1 << 1; /**@hide*/ @@ -77,12 +78,14 @@ public class RcsUceAdapter { * An unknown error has caused the request to fail. * @hide */ + @SystemApi public static final int ERROR_GENERIC_FAILURE = 1; /** * The carrier network does not have UCE support enabled for this subscriber. * @hide */ + @SystemApi public static final int ERROR_NOT_ENABLED = 2; /** @@ -90,12 +93,14 @@ public class RcsUceAdapter { * 1x only currently). * @hide */ + @SystemApi public static final int ERROR_NOT_AVAILABLE = 3; /** * The network has responded with SIP 403 error and a reason "User not registered." * @hide */ + @SystemApi public static final int ERROR_NOT_REGISTERED = 4; /** @@ -103,12 +108,14 @@ public class RcsUceAdapter { * presence" for this subscriber. * @hide */ + @SystemApi public static final int ERROR_NOT_AUTHORIZED = 5; /** * The network has responded to this request with a SIP 403 error and no reason. * @hide */ + @SystemApi public static final int ERROR_FORBIDDEN = 6; /** @@ -116,6 +123,7 @@ public class RcsUceAdapter { * subscriber to the carrier network. * @hide */ + @SystemApi public static final int ERROR_NOT_FOUND = 7; /** @@ -123,6 +131,7 @@ public class RcsUceAdapter { * with a lower number of contact numbers. The number varies per carrier. * @hide */ + @SystemApi // TODO: Try to integrate this into the API so that the service will split based on carrier. public static final int ERROR_REQUEST_TOO_LARGE = 8; @@ -130,18 +139,21 @@ public class RcsUceAdapter { * The network did not respond to the capabilities request before the request timed out. * @hide */ + @SystemApi public static final int ERROR_REQUEST_TIMEOUT = 9; /** * The request failed due to the service having insufficient memory. * @hide */ + @SystemApi public static final int ERROR_INSUFFICIENT_MEMORY = 10; /** * The network was lost while trying to complete the request. * @hide */ + @SystemApi public static final int ERROR_LOST_NETWORK = 11; /** @@ -149,6 +161,7 @@ public class RcsUceAdapter { * time returned in {@link CapabilitiesCallback#onError} has elapsed. * @hide */ + @SystemApi public static final int ERROR_SERVER_UNAVAILABLE = 12; /**@hide*/ @@ -405,6 +418,7 @@ public class RcsUceAdapter { * @see #requestCapabilities(Executor, List, CapabilitiesCallback) * @hide */ + @SystemApi public interface CapabilitiesCallback { /** @@ -424,10 +438,10 @@ public class RcsUceAdapter { * The pending request has resulted in an error and may need to be retried, depending on the * error code. * @param errorCode The reason for the framework being unable to process the request. - * @param retryAfterMilliseconds The time in milliseconds the requesting application should + * @param retryIntervalMillis The time in milliseconds the requesting application should * wait before retrying, if non-zero. */ - void onError(@ErrorCode int errorCode, long retryAfterMilliseconds); + void onError(@ErrorCode int errorCode, long retryIntervalMillis); } private final Context mContext; @@ -458,9 +472,9 @@ public class RcsUceAdapter { * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} is enabled or else * this operation will fail with {@link #ERROR_NOT_AVAILABLE} or {@link #ERROR_NOT_ENABLED}. * + * @param contactNumbers A list of numbers that the capabilities are being requested for. * @param executor The executor that will be used when the request is completed and the * {@link CapabilitiesCallback} is called. - * @param contactNumbers A list of numbers that the capabilities are being requested for. * @param c A one-time callback for when the request for capabilities completes or there is an * error processing the request. * @throws ImsException if the subscription associated with this instance of @@ -469,9 +483,10 @@ public class RcsUceAdapter { * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes. * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public void requestCapabilities(@NonNull @CallbackExecutor Executor executor, - @NonNull List<Uri> contactNumbers, + public void requestCapabilities(@NonNull List<Uri> contactNumbers, + @NonNull @CallbackExecutor Executor executor, @NonNull CapabilitiesCallback c) throws ImsException { if (c == null) { throw new IllegalArgumentException("Must include a non-null CapabilitiesCallback."); @@ -495,8 +510,7 @@ public class RcsUceAdapter { public void onCapabilitiesReceived(List<RcsContactUceCapability> contactCapabilities) { final long callingIdentity = Binder.clearCallingIdentity(); try { - executor.execute(() -> - c.onCapabilitiesReceived(contactCapabilities)); + executor.execute(() -> c.onCapabilitiesReceived(contactCapabilities)); } finally { restoreCallingIdentity(callingIdentity); } @@ -550,13 +564,17 @@ public class RcsUceAdapter { * {@link #ERROR_NOT_AVAILABLE} or {@link #ERROR_NOT_ENABLED}. * * @param contactNumber The contact of the capabilities is being requested for. + * @param executor The executor that will be used when the request is completed and the + * {@link CapabilitiesCallback} is called. * @param c A one-time callback for when the request for capabilities completes or there is * an error processing the request. * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) - public void requestNetworkAvailability(@NonNull @CallbackExecutor Executor executor, - @NonNull Uri contactNumber, @NonNull CapabilitiesCallback c) throws ImsException { + public void requestAvailability(@NonNull Uri contactNumber, + @NonNull @CallbackExecutor Executor executor, + @NonNull CapabilitiesCallback c) throws ImsException { if (executor == null) { throw new IllegalArgumentException("Must include a non-null Executor."); } @@ -569,7 +587,7 @@ public class RcsUceAdapter { IImsRcsController imsRcsController = getIImsRcsController(); if (imsRcsController == null) { - Log.e(TAG, "requestNetworkAvailability: IImsRcsController is null"); + Log.e(TAG, "requestAvailability: IImsRcsController is null"); throw new ImsException("Cannot find remote IMS service", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } @@ -579,8 +597,7 @@ public class RcsUceAdapter { public void onCapabilitiesReceived(List<RcsContactUceCapability> contactCapabilities) { final long callingIdentity = Binder.clearCallingIdentity(); try { - executor.execute(() -> - c.onCapabilitiesReceived(contactCapabilities)); + executor.execute(() -> c.onCapabilitiesReceived(contactCapabilities)); } finally { restoreCallingIdentity(callingIdentity); } @@ -606,12 +623,12 @@ public class RcsUceAdapter { }; try { - imsRcsController.requestNetworkAvailability(mSubId, mContext.getOpPackageName(), + imsRcsController.requestAvailability(mSubId, mContext.getOpPackageName(), mContext.getAttributionTag(), contactNumber, internalCallback); } catch (ServiceSpecificException e) { throw new ImsException(e.toString(), e.errorCode); } catch (RemoteException e) { - Log.e(TAG, "Error calling IImsRcsController#requestNetworkAvailability", e); + Log.e(TAG, "Error calling IImsRcsController#requestAvailability", e); throw new ImsException("Remote IMS Service is not available", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } @@ -683,7 +700,7 @@ public class RcsUceAdapter { if (imsRcsController == null) { Log.e(TAG, "addOnPublishStateChangedListener : IImsRcsController is null"); throw new ImsException("Cannot find remote IMS service", - ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } PublishStateCallbackAdapter stateCallback = addPublishStateCallback(executor, listener); @@ -694,7 +711,7 @@ public class RcsUceAdapter { } catch (RemoteException e) { Log.e(TAG, "Error calling IImsRcsController#registerUcePublishStateCallback", e); throw new ImsException("Remote IMS Service is not available", - ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } } diff --git a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl index 36349895c35b..7a6c28bddd09 100644 --- a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl +++ b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl @@ -52,7 +52,7 @@ interface IImsRcsController { // ImsUceAdapter specific void requestCapabilities(int subId, String callingPackage, String callingFeatureId, in List<Uri> contactNumbers, IRcsUceControllerCallback c); - void requestNetworkAvailability(int subId, String callingPackage, + void requestAvailability(int subId, String callingPackage, String callingFeatureId, in Uri contactNumber, IRcsUceControllerCallback c); int getUcePublishState(int subId); diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java index c84e23c38e97..7eba709a11da 100644 --- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java +++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java @@ -24,6 +24,7 @@ import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.net.Uri; import android.telephony.ims.ImsException; +import android.telephony.ims.RcsUceAdapter; import android.telephony.ims.feature.ImsFeature; import android.telephony.ims.feature.RcsFeature; import android.util.Log; @@ -139,18 +140,19 @@ public class RcsCapabilityExchangeImplBase { * Provide the framework with a subsequent network response update to * {@link #publishCapabilities(String, PublishResponseCallback)}. * - * @param code The SIP response code sent from the network for the operation + * @param sipCode The SIP response code sent from the network for the operation * token specified. * @param reason The optional reason response from the network. If there is a reason header * included in the response, that should take precedence over the reason provided in the - * status line. If the network provided no reason with the code, the string should be empty. + * status line. If the network provided no reason with the sip code, the string should be + * empty. * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is * not currently connected to the framework. This can happen if the {@link RcsFeature} * is not {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases * when the Telephony stack has crashed. */ - void onNetworkResponse(@IntRange(from = 100, to = 699) int code, + void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode, @NonNull String reason) throws ImsException; } @@ -173,7 +175,7 @@ public class RcsCapabilityExchangeImplBase { /** * Send the response of a SIP OPTIONS capability exchange to the framework. - * @param code The SIP response code that was sent by the network in response + * @param sipCode The SIP response code that was sent by the network in response * to the request sent by {@link #sendOptionsCapabilityRequest}. * @param reason The optional SIP response reason sent by the network. * If none was sent, this should be an empty string. @@ -186,17 +188,20 @@ public class RcsCapabilityExchangeImplBase { * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare * cases when the Telephony stack has crashed. */ - void onNetworkResponse(int code, @NonNull String reason, + void onNetworkResponse(int sipCode, @NonNull String reason, @Nullable List<String> theirCaps) throws ImsException; } /** * Interface used by the framework to receive the response of the subscribe request. - * @hide */ public interface SubscribeResponseCallback { /** * Notify the framework that the command associated with this callback has failed. + * <p> + * Must only be called when there was an error generating a SUBSCRIBE request due to an + * IMS stack error. This is a terminating event, so no other callback event will be + * expected after this callback. * * @param code The reason why the associated command has failed. * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is @@ -211,27 +216,38 @@ public class RcsCapabilityExchangeImplBase { /** * Notify the framework of the response to the SUBSCRIBE request from * {@link #subscribeForCapabilities(List<Uri>, SubscribeResponseCallback)}. + * <p> + * If the carrier network responds to the SUBSCRIBE request with a 2XX response, then the + * framework will expect the IMS stack to call {@link #onNotifyCapabilitiesUpdate}, + * {@link #onResourceTerminated}, and {@link #onTerminated} as required for the + * subsequent NOTIFY responses to the subscription. * - * @param code The SIP response code sent from the network for the operation + * @param sipCode The SIP response code sent from the network for the operation * token specified. * @param reason The optional reason response from the network. If the network - * provided no reason with the code, the string should be empty. + * provided no reason with the sip code, the string should be empty. * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is * not currently connected to the framework. This can happen if the * {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the * {@link RcsFeature} has not received the {@link ImsFeature#onFeatureReady()} callback. * This may also happen in rare cases when the Telephony stack has crashed. */ - void onNetworkResponse(@IntRange(from = 100, to = 699) int code, + void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode, @NonNull String reason) throws ImsException; /** - * Provides the framework with latest XML PIDF documents included in the - * network response for the requested contacts' capabilities requested by the - * Framework using {@link #requestCapabilities(List, int)}. This should be - * called every time a new NOTIFY event is received with new capability - * information. + * Notify the framework of the latest XML PIDF documents included in the network response + * for the requested contacts' capabilities requested by the Framework using + * {@link RcsUceAdapter#requestCapabilities(Executor, List<Uri>, CapabilitiesCallback)}. + * <p> + * The expected format for the PIDF XML is defined in RFC3861. Each XML document must be a + * "application/pidf+xml" object and start with a root <presence> element. For NOTIFY + * responses that contain RLMI information and potentially multiple PIDF XMLs, each + * PIDF XML should be separated and added as a separate item in the List. This should be + * called every time a new NOTIFY event is received with new capability information. * + * @param pidfXmls The list of the PIDF XML data for the contact URIs that it subscribed + * for. * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is * not currently connected to the framework. * This can happen if the {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the @@ -242,21 +258,42 @@ public class RcsCapabilityExchangeImplBase { void onNotifyCapabilitiesUpdate(@NonNull List<String> pidfXmls) throws ImsException; /** - * A resource in the resource list for the presence subscribe event has been terminated. + * Notify the framework that a resource in the RLMI XML contained in the NOTIFY response + * for the ongoing SUBSCRIBE dialog has been terminated. * <p> - * This allows the framework to know that there will not be any capability information for - * a specific contact URI that they subscribed for. + * This will be used to notify the framework that a contact URI that the IMS stack has + * subscribed to on the Resource List Server has been terminated as well as the reason why. + * Usually this means that there will not be any capability information for the contact URI + * that they subscribed for. See RFC 4662 for more information. + * + * @param uriTerminatedReason The contact URIs which have been terminated. Each pair in the + * list is the contact URI and its terminated reason. + * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is + * not currently connected to the framework. + * This can happen if the {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the + * {@link RcsFeature} {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not + * received the {@link ImsFeature#onFeatureReady()} callback. This may also happen in + * rare cases when the Telephony stack has crashed. */ void onResourceTerminated( @NonNull List<Pair<Uri, String>> uriTerminatedReason) throws ImsException; /** - * The subscription associated with a previous #requestCapabilities operation - * has been terminated. This will mostly be due to the subscription expiring, - * but may also happen due to an error. - * <p> - * This allows the framework to know that there will no longer be any - * capability updates for the requested operationToken. + * The subscription associated with a previous + * {@link RcsUceAdapter#requestCapabilities(Executor, List<Uri>, CapabilitiesCallback)} + * operation has been terminated. This will mostly be due to the network sending a final + * NOTIFY response due to the subscription expiring, but this may also happen due to a + * network error. + * + * @param reason The reason for the request being unable to process. + * @param retryAfterMilliseconds The time in milliseconds the requesting application should + * wait before retrying, if non-zero. + * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is + * not currently connected to the framework. + * This can happen if the {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the + * {@link RcsFeature} {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not + * received the {@link ImsFeature#onFeatureReady()} callback. This may also happen in + * rare cases when the Telephony stack has crashed. */ void onTerminated(@NonNull String reason, long retryAfterMilliseconds) throws ImsException; } @@ -278,18 +315,23 @@ public class RcsCapabilityExchangeImplBase { /** * The user capabilities of one or multiple contacts have been requested by the framework. * <p> + * The implementer must follow up this call with an + * {@link SubscribeResponseCallback#onCommandError} call to indicate this operation has failed. * The response from the network to the SUBSCRIBE request must be sent back to the framework - * using {@link #onSubscribeNetworkResponse(int, String, int)}. As NOTIFY requests come in from - * the network, the requested contact’s capabilities should be sent back to the framework using - * {@link #onSubscribeNotifyRequest} and {@link onSubscribeResourceTerminated} + * using {@link SubscribeResponseCallback#onNetworkResponse(int, String)}. + * As NOTIFY requests come in from the network, the requested contact’s capabilities should be + * sent back to the framework using + * {@link SubscribeResponseCallback#onNotifyCapabilitiesUpdate(List<String>}) and + * {@link SubscribeResponseCallback#onResourceTerminated(List<Pair<Uri, String>>)} * should be called with the presence information for the contacts specified. * <p> - * Once the subscription is terminated, {@link #onSubscriptionTerminated} must be called for - * the framework to finish listening for NOTIFY responses. + * Once the subscription is terminated, + * {@link SubscribeResponseCallback#onTerminated(String, long)} must be called for the + * framework to finish listening for NOTIFY responses. + * * @param uris A {@link List} of the {@link Uri}s that the framework is requesting the UCE * capabilities for. * @param cb The callback of the subscribe request. - * @hide */ // executor used is defined in the constructor. @SuppressLint("ExecutorRegistration") diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 2d3c8f29ec9c..ee6c36ca6b76 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -2350,6 +2350,11 @@ interface ITelephony { */ String getMobileProvisioningUrl(); + /* + * Remove the EAB contacts from the EAB database. + */ + int removeContactFromEab(int subId, String contacts); + /** * Set a SignalStrengthUpdateRequest to receive notification when Signal Strength breach the * specified thresholds. diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index b3092b92989d..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; @@ -5963,23 +5964,18 @@ public class ConnectivityServiceTest { callback.expectCapabilitiesThat(mMockVpn, nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED) && nc.hasTransport(TRANSPORT_WIFI)); - - // BUG: the VPN is no longer suspended, so a RESUMED callback should have been sent. - // callback.expectCallback(CallbackEntry.RESUMED, mMockVpn); + callback.expectCallback(CallbackEntry.RESUMED, mMockVpn); callback.assertNoCallback(); assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) .hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED); assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); - assertNetworkInfo(TYPE_VPN, DetailedState.SUSPENDED); // BUG: VPN caps have NOT_SUSPENDED. + assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED); assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); - // BUG: the device has connectivity, so this should return true. - assertGetNetworkInfoOfGetActiveNetworkIsConnected(false); + assertGetNetworkInfoOfGetActiveNetworkIsConnected(true); - // Unsuspend cellular and then switch back to it. - // The same bug happens in the opposite direction: the VPN's capabilities correctly have - // NOT_SUSPENDED, but the VPN's NetworkInfo is in state SUSPENDED. + // Unsuspend cellular and then switch back to it. The VPN remains not suspended. mCellNetworkAgent.resume(); callback.assertNoCallback(); mWiFiNetworkAgent.disconnect(); @@ -5996,12 +5992,11 @@ public class ConnectivityServiceTest { .hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED); - assertNetworkInfo(TYPE_VPN, DetailedState.SUSPENDED); // BUG: VPN caps have NOT_SUSPENDED. + assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED); assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); - // BUG: the device has connectivity, so this should return true. - assertGetNetworkInfoOfGetActiveNetworkIsConnected(false); + assertGetNetworkInfoOfGetActiveNetworkIsConnected(true); - // Re-suspending the current network fixes the problem. + // Suspend cellular and expect no connectivity. mCellNetworkAgent.suspend(); callback.expectCapabilitiesThat(mMockVpn, nc -> !nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED) @@ -6017,6 +6012,7 @@ public class ConnectivityServiceTest { assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.SUSPENDED); assertGetNetworkInfoOfGetActiveNetworkIsConnected(false); + // Resume cellular and expect that connectivity comes back. mCellNetworkAgent.resume(); callback.expectCapabilitiesThat(mMockVpn, nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED) @@ -6407,10 +6403,7 @@ public class ConnectivityServiceTest { && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasCapability(NET_CAPABILITY_NOT_METERED) && !caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); - // While the SUSPENDED callback should in theory be sent here, it is not. This is - // a bug in ConnectivityService, but as the SUSPENDED and RESUMED callbacks have never - // been public and are deprecated and slated for removal, there is no sense in spending - // resources fixing this bug now. + vpnNetworkCallback.expectCallback(CallbackEntry.SUSPENDED, mMockVpn); assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent); // Use both again. @@ -6422,8 +6415,7 @@ public class ConnectivityServiceTest { && caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI) && !caps.hasCapability(NET_CAPABILITY_NOT_METERED) && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); - // As above, the RESUMED callback not being sent here is a bug, but not a bug that's - // worth anybody's time to fix. + vpnNetworkCallback.expectCallback(CallbackEntry.RESUMED, mMockVpn); assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent); // Disconnect cell. Receive update without even removing the dead network from the @@ -7335,39 +7327,68 @@ 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); + } } - @Ignore // 40%+ flakiness : figure out why and re-enable. @Test public final void testBatteryStatsNetworkType() throws Exception { final LinkProperties cellLp = new LinkProperties(); @@ -7375,8 +7396,8 @@ public class ConnectivityServiceTest { mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp); mCellNetworkAgent.connect(true); waitForIdle(); - verify(mBatteryStatsService).noteNetworkInterfaceType(cellLp.getInterfaceName(), - TYPE_MOBILE); + verify(mBatteryStatsService).noteNetworkInterfaceForTransports(cellLp.getInterfaceName(), + new int[] { TRANSPORT_CELLULAR }); reset(mBatteryStatsService); final LinkProperties wifiLp = new LinkProperties(); @@ -7384,18 +7405,20 @@ public class ConnectivityServiceTest { mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp); mWiFiNetworkAgent.connect(true); waitForIdle(); - verify(mBatteryStatsService).noteNetworkInterfaceType(wifiLp.getInterfaceName(), - TYPE_WIFI); + verify(mBatteryStatsService).noteNetworkInterfaceForTransports(wifiLp.getInterfaceName(), + new int[] { TRANSPORT_WIFI }); reset(mBatteryStatsService); mCellNetworkAgent.disconnect(); + mWiFiNetworkAgent.disconnect(); cellLp.setInterfaceName("wifi0"); mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp); mCellNetworkAgent.connect(true); waitForIdle(); - verify(mBatteryStatsService).noteNetworkInterfaceType(cellLp.getInterfaceName(), - TYPE_MOBILE); + verify(mBatteryStatsService).noteNetworkInterfaceForTransports(cellLp.getInterfaceName(), + new int[] { TRANSPORT_CELLULAR }); + mCellNetworkAgent.disconnect(); } /** @@ -7468,8 +7491,8 @@ public class ConnectivityServiceTest { assertRoutesAdded(cellNetId, ipv6Subnet, defaultRoute); verify(mMockDnsResolver, times(1)).createNetworkCache(eq(cellNetId)); verify(mMockNetd, times(1)).networkAddInterface(cellNetId, MOBILE_IFNAME); - verify(mBatteryStatsService).noteNetworkInterfaceType(cellLp.getInterfaceName(), - TYPE_MOBILE); + verify(mBatteryStatsService).noteNetworkInterfaceForTransports(cellLp.getInterfaceName(), + new int[] { TRANSPORT_CELLULAR }); networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); verify(mMockDnsResolver, times(1)).startPrefix64Discovery(cellNetId); @@ -7489,7 +7512,8 @@ public class ConnectivityServiceTest { // Make sure BatteryStats was not told about any v4- interfaces, as none should have // come online yet. waitForIdle(); - verify(mBatteryStatsService, never()).noteNetworkInterfaceType(startsWith("v4-"), anyInt()); + verify(mBatteryStatsService, never()).noteNetworkInterfaceForTransports(startsWith("v4-"), + any()); verifyNoMoreInteractions(mMockNetd); verifyNoMoreInteractions(mMockDnsResolver); @@ -7542,8 +7566,8 @@ public class ConnectivityServiceTest { assertTrue(ArrayUtils.contains(resolvrParams.servers, "8.8.8.8")); for (final LinkProperties stackedLp : stackedLpsAfterChange) { - verify(mBatteryStatsService).noteNetworkInterfaceType(stackedLp.getInterfaceName(), - TYPE_MOBILE); + verify(mBatteryStatsService).noteNetworkInterfaceForTransports( + stackedLp.getInterfaceName(), new int[] { TRANSPORT_CELLULAR }); } reset(mMockNetd); when(mMockNetd.interfaceGetCfg(CLAT_PREFIX + MOBILE_IFNAME)) diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java new file mode 100644 index 000000000000..d936183e5a0b --- /dev/null +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.vcn; + +import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** Tests for VcnGatewayConnection.ConnectingState */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class VcnGatewayConnectionConnectingStateTest extends VcnGatewayConnectionTestBase { + private VcnIkeSession mIkeSession; + + @Before + public void setUp() throws Exception { + super.setUp(); + + mGatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_1); + mGatewayConnection.transitionTo(mGatewayConnection.mConnectingState); + mTestLooper.dispatchAll(); + + mIkeSession = mGatewayConnection.getIkeSession(); + } + + @Test + public void testEnterStateCreatesNewIkeSession() throws Exception { + verify(mDeps).newIkeSession(any(), any(), any(), any(), any()); + } + + @Test + public void testNullNetworkTriggersDisconnect() throws Exception { + mGatewayConnection + .getUnderlyingNetworkTrackerCallback() + .onSelectedUnderlyingNetworkChanged(null); + mTestLooper.dispatchAll(); + + assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState()); + verify(mIkeSession).kill(); + } + + @Test + public void testNewNetworkTriggersReconnect() throws Exception { + mGatewayConnection + .getUnderlyingNetworkTrackerCallback() + .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_2); + mTestLooper.dispatchAll(); + + assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState()); + verify(mIkeSession).close(); + verify(mIkeSession, never()).kill(); + } + + @Test + public void testSameNetworkDoesNotTriggerReconnect() throws Exception { + mGatewayConnection + .getUnderlyingNetworkTrackerCallback() + .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1); + mTestLooper.dispatchAll(); + + assertEquals(mGatewayConnection.mConnectingState, mGatewayConnection.getCurrentState()); + } + + @Test + public void testChildSessionClosedTriggersDisconnect() throws Exception { + getChildSessionCallback().onClosed(); + mTestLooper.dispatchAll(); + + assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState()); + verify(mIkeSession).close(); + } + + @Test + public void testIkeSessionClosedTriggersDisconnect() throws Exception { + getIkeSessionCallback().onClosed(); + mTestLooper.dispatchAll(); + + assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState()); + verify(mIkeSession).close(); + } +} diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java index 346785907fcf..b4d39bf74a4b 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java @@ -32,6 +32,7 @@ import android.net.IpSecTunnelInterfaceResponse; import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; +import android.net.ipsec.ike.ChildSessionCallback; import android.net.ipsec.ike.IkeSessionCallback; import android.net.vcn.VcnGatewayConnectionConfig; import android.net.vcn.VcnGatewayConnectionConfigTest; @@ -117,4 +118,11 @@ public class VcnGatewayConnectionTestBase { verify(mDeps).newIkeSession(any(), any(), any(), captor.capture(), any()); return captor.getValue(); } + + protected ChildSessionCallback getChildSessionCallback() { + ArgumentCaptor<ChildSessionCallback> captor = + ArgumentCaptor.forClass(ChildSessionCallback.class); + verify(mDeps).newIkeSession(any(), any(), any(), any(), captor.capture()); + return captor.getValue(); + } } |