Merge "Activity canceled due to settings launch should trigger a different code."
diff --git a/core/api/current.txt b/core/api/current.txt
index 63ac7f7..c811a09 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -7412,6 +7412,7 @@
method public void dump(android.util.Printer, String);
method public android.content.pm.ActivityInfo getActivityInfo();
method @NonNull public android.content.ComponentName getComponent();
+ method public int getHeadlessDeviceOwnerMode();
method public String getPackageName();
method public String getReceiverName();
method public String getTagForPolicy(int);
@@ -7423,6 +7424,8 @@
method public boolean usesPolicy(int);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.DeviceAdminInfo> CREATOR;
+ field public static final int HEADLESS_DEVICE_OWNER_MODE_AFFILIATED = 1; // 0x1
+ field public static final int HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED = 0; // 0x0
field public static final int USES_ENCRYPTED_STORAGE = 7; // 0x7
field public static final int USES_POLICY_DISABLE_CAMERA = 8; // 0x8
field public static final int USES_POLICY_DISABLE_KEYGUARD_FEATURES = 9; // 0x9
@@ -28411,15 +28414,24 @@
public final class NfcAdapter {
method public void disableForegroundDispatch(android.app.Activity);
+ method @Deprecated public void disableForegroundNdefPush(android.app.Activity);
method public void disableReaderMode(android.app.Activity);
method public void enableForegroundDispatch(android.app.Activity, android.app.PendingIntent, android.content.IntentFilter[], String[][]);
+ method @Deprecated public void enableForegroundNdefPush(android.app.Activity, android.nfc.NdefMessage);
method public void enableReaderMode(android.app.Activity, android.nfc.NfcAdapter.ReaderCallback, int, android.os.Bundle);
method public static android.nfc.NfcAdapter getDefaultAdapter(android.content.Context);
method @Nullable public android.nfc.NfcAntennaInfo getNfcAntennaInfo();
method public boolean ignore(android.nfc.Tag, int, android.nfc.NfcAdapter.OnTagRemovedListener, android.os.Handler);
+ method @Deprecated public boolean invokeBeam(android.app.Activity);
method public boolean isEnabled();
+ method @Deprecated public boolean isNdefPushEnabled();
method public boolean isSecureNfcEnabled();
method public boolean isSecureNfcSupported();
+ method @Deprecated public void setBeamPushUris(android.net.Uri[], android.app.Activity);
+ method @Deprecated public void setBeamPushUrisCallback(android.nfc.NfcAdapter.CreateBeamUrisCallback, android.app.Activity);
+ method @Deprecated public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, android.app.Activity...);
+ method @Deprecated public void setNdefPushMessageCallback(android.nfc.NfcAdapter.CreateNdefMessageCallback, android.app.Activity, android.app.Activity...);
+ method @Deprecated public void setOnNdefPushCompleteCallback(android.nfc.NfcAdapter.OnNdefPushCompleteCallback, android.app.Activity, android.app.Activity...);
field public static final String ACTION_ADAPTER_STATE_CHANGED = "android.nfc.action.ADAPTER_STATE_CHANGED";
field public static final String ACTION_NDEF_DISCOVERED = "android.nfc.action.NDEF_DISCOVERED";
field @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public static final String ACTION_PREFERRED_PAYMENT_CHANGED = "android.nfc.action.PREFERRED_PAYMENT_CHANGED";
@@ -28451,6 +28463,18 @@
field public static final int STATE_TURNING_ON = 2; // 0x2
}
+ @Deprecated public static interface NfcAdapter.CreateBeamUrisCallback {
+ method @Deprecated public android.net.Uri[] createBeamUris(android.nfc.NfcEvent);
+ }
+
+ @Deprecated public static interface NfcAdapter.CreateNdefMessageCallback {
+ method @Deprecated public android.nfc.NdefMessage createNdefMessage(android.nfc.NfcEvent);
+ }
+
+ @Deprecated public static interface NfcAdapter.OnNdefPushCompleteCallback {
+ method @Deprecated public void onNdefPushComplete(android.nfc.NfcEvent);
+ }
+
public static interface NfcAdapter.OnTagRemovedListener {
method public void onTagRemoved();
}
@@ -44882,6 +44906,7 @@
method public int getCardIdForDefaultEuicc();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @WorkerThread public android.os.PersistableBundle getCarrierConfig();
method public int getCarrierIdFromSimMccMnc();
+ method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void getCarrierRestrictionStatus(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public android.telephony.CellLocation getCellLocation();
method public int getDataActivity();
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_BASIC_PHONE_STATE}) public int getDataNetworkType();
@@ -45041,6 +45066,10 @@
field public static final int CALL_STATE_OFFHOOK = 2; // 0x2
field public static final int CALL_STATE_RINGING = 1; // 0x1
field public static final String CAPABILITY_SLICING_CONFIG_SUPPORTED = "CAPABILITY_SLICING_CONFIG_SUPPORTED";
+ field public static final int CARRIER_RESTRICTION_STATUS_NOT_RESTRICTED = 1; // 0x1
+ field public static final int CARRIER_RESTRICTION_STATUS_RESTRICTED = 2; // 0x2
+ field public static final int CARRIER_RESTRICTION_STATUS_RESTRICTED_TO_CALLER = 3; // 0x3
+ field public static final int CARRIER_RESTRICTION_STATUS_UNKNOWN = 0; // 0x0
field public static final int CDMA_ROAMING_MODE_AFFILIATED = 1; // 0x1
field public static final int CDMA_ROAMING_MODE_ANY = 2; // 0x2
field public static final int CDMA_ROAMING_MODE_HOME = 0; // 0x0
@@ -51032,6 +51061,7 @@
method public void relayout(int, int);
method public void release();
method public void setView(@NonNull android.view.View, int, int);
+ method public boolean transferTouchGestureToHost();
}
public static final class SurfaceControlViewHost.SurfacePackage implements android.os.Parcelable {
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 55ef6de..af35d96 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -83,7 +83,7 @@
public abstract class Context {
method @NonNull public android.content.Context createContextForSdkInSandbox(@NonNull android.content.pm.ApplicationInfo, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method @NonNull public android.os.IBinder getIApplicationThreadBinder();
+ method @NonNull public android.os.IBinder getProcessToken();
method @NonNull public android.os.UserHandle getUser();
field public static final String PAC_PROXY_SERVICE = "pac_proxy";
field public static final String TEST_NETWORK_SERVICE = "test_network";
diff --git a/core/api/removed.txt b/core/api/removed.txt
index 5c4fd10..1fa1e89 100644
--- a/core/api/removed.txt
+++ b/core/api/removed.txt
@@ -252,34 +252,6 @@
}
-package android.nfc {
-
- public final class NfcAdapter {
- method @Deprecated public void disableForegroundNdefPush(android.app.Activity);
- method @Deprecated public void enableForegroundNdefPush(android.app.Activity, android.nfc.NdefMessage);
- method @Deprecated public boolean invokeBeam(android.app.Activity);
- method @Deprecated public boolean isNdefPushEnabled();
- method @Deprecated public void setBeamPushUris(android.net.Uri[], android.app.Activity);
- method @Deprecated public void setBeamPushUrisCallback(android.nfc.NfcAdapter.CreateBeamUrisCallback, android.app.Activity);
- method @Deprecated public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, android.app.Activity...);
- method @Deprecated public void setNdefPushMessageCallback(android.nfc.NfcAdapter.CreateNdefMessageCallback, android.app.Activity, android.app.Activity...);
- method @Deprecated public void setOnNdefPushCompleteCallback(android.nfc.NfcAdapter.OnNdefPushCompleteCallback, android.app.Activity, android.app.Activity...);
- }
-
- @Deprecated public static interface NfcAdapter.CreateBeamUrisCallback {
- method public android.net.Uri[] createBeamUris(android.nfc.NfcEvent);
- }
-
- @Deprecated public static interface NfcAdapter.CreateNdefMessageCallback {
- method public android.nfc.NdefMessage createNdefMessage(android.nfc.NfcEvent);
- }
-
- @Deprecated public static interface NfcAdapter.OnNdefPushCompleteCallback {
- method public void onNdefPushComplete(android.nfc.NfcEvent);
- }
-
-}
-
package android.os {
public class BatteryManager {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 5183a82..d3775ad 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1278,6 +1278,7 @@
field public static final int STATUS_DEVICE_ADMIN_NOT_SUPPORTED = 13; // 0xd
field public static final int STATUS_HAS_DEVICE_OWNER = 1; // 0x1
field public static final int STATUS_HAS_PAIRED = 8; // 0x8
+ field public static final int STATUS_HEADLESS_SYSTEM_USER_MODE_NOT_SUPPORTED = 16; // 0x10
field public static final int STATUS_MANAGED_USERS_NOT_SUPPORTED = 9; // 0x9
field public static final int STATUS_NONSYSTEM_USER_EXISTS = 5; // 0x5
field public static final int STATUS_NOT_SYSTEM_USER = 7; // 0x7
@@ -3269,6 +3270,7 @@
field public static final String SYSTEM_CONFIG_SERVICE = "system_config";
field public static final String SYSTEM_UPDATE_SERVICE = "system_update";
field public static final String TETHERING_SERVICE = "tethering";
+ field public static final String TIME_MANAGER_SERVICE = "time_manager";
field public static final String TRANSLATION_MANAGER_SERVICE = "translation";
field public static final String UI_TRANSLATION_SERVICE = "ui_translation";
field public static final String UWB_SERVICE = "uwb";
@@ -9692,7 +9694,9 @@
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean addNfcUnlockHandler(android.nfc.NfcAdapter.NfcUnlockHandler, String[]);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean disable();
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean disable(boolean);
+ method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean disableNdefPush();
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enable();
+ method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableNdefPush();
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableSecureNfc(boolean);
method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public java.util.Map<java.lang.String,java.lang.Boolean> getTagIntentAppPreferenceForUser(int);
method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOn();
@@ -9701,8 +9705,10 @@
method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void registerControllerAlwaysOnListener(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcAdapter.ControllerAlwaysOnListener);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean removeNfcUnlockHandler(android.nfc.NfcAdapter.NfcUnlockHandler);
method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean setControllerAlwaysOn(boolean);
+ method public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, int);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int setTagIntentAppPreferenceForUser(int, @NonNull String, boolean);
method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void unregisterControllerAlwaysOnListener(@NonNull android.nfc.NfcAdapter.ControllerAlwaysOnListener);
+ field public static final int FLAG_NDEF_PUSH_NO_CONFIRM = 1; // 0x1
field public static final int TAG_INTENT_APP_PREF_RESULT_PACKAGE_NOT_FOUND = -1; // 0xffffffff
field public static final int TAG_INTENT_APP_PREF_RESULT_SUCCESS = 0; // 0x0
field public static final int TAG_INTENT_APP_PREF_RESULT_UNAVAILABLE = -2; // 0xfffffffe
diff --git a/core/api/system-removed.txt b/core/api/system-removed.txt
index 1c10356..2c5acf1 100644
--- a/core/api/system-removed.txt
+++ b/core/api/system-removed.txt
@@ -140,17 +140,6 @@
}
-package android.nfc {
-
- public final class NfcAdapter {
- method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean disableNdefPush();
- method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableNdefPush();
- method public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, int);
- field public static final int FLAG_NDEF_PUSH_NO_CONFIRM = 1; // 0x1
- }
-
-}
-
package android.os {
public class Build {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 94075e5..44f3121 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1414,6 +1414,8 @@
package android.hardware.location {
public final class ContextHubManager {
+ method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public boolean disableTestMode();
+ method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public boolean enableTestMode();
method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public long[] getPreloadedNanoAppIds(@NonNull android.hardware.location.ContextHubInfo);
}
@@ -1627,20 +1629,27 @@
public class AudioManager {
method @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public int abandonAudioFocusForTest(@NonNull android.media.AudioFocusRequest, @NonNull String);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) public void forceComputeCsdOnAllDevices(boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) public void forceUseFrameworkMel(boolean);
method @NonNull @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION) public android.media.AudioRecord getCallDownlinkExtractionAudioRecord(@NonNull android.media.AudioFormat);
method @NonNull @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION) public android.media.AudioTrack getCallUplinkInjectionAudioTrack(@NonNull android.media.AudioFormat);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) public float getCsd();
method @Nullable public static android.media.AudioDeviceInfo getDeviceInfoFromType(int);
method @IntRange(from=0) @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public long getFadeOutDurationOnFocusLossMillis(@NonNull android.media.AudioAttributes);
method @Nullable public static android.media.AudioHalVersionInfo getHalVersion();
method public static final int[] getPublicStreamTypes();
method @NonNull public java.util.List<java.lang.Integer> getReportedSurroundFormats();
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) public float getRs2Value();
method public int getStreamMinVolumeInt(int);
method @NonNull public java.util.Map<java.lang.Integer,java.lang.Boolean> getSurroundFormats();
method public boolean hasRegisteredDynamicPolicy();
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) public boolean isCsdEnabled();
method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.QUERY_AUDIO_STATE}) public boolean isFullVolumeDevice();
method @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION) public boolean isPstnCallAudioInterceptable();
method @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public int requestAudioFocusForTest(@NonNull android.media.AudioFocusRequest, @NonNull String, int, int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) public void setCsd(float);
method public void setRampingRingerEnabled(boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) public void setRs2Value(float);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setTestDeviceConnectionState(@NonNull android.media.AudioDeviceAttributes, boolean);
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index b91fa35..89740af 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2028,7 +2028,7 @@
/** @hide */
@NonNull
@Override
- public IBinder getIApplicationThreadBinder() {
+ public IBinder getProcessToken() {
return getIApplicationThread().asBinder();
}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 5b3b2a6..1f5182a 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -1335,7 +1335,7 @@
return new TimeZoneDetectorImpl();
}});
- registerService(Context.TIME_MANAGER, TimeManager.class,
+ registerService(Context.TIME_MANAGER_SERVICE, TimeManager.class,
new CachedServiceFetcher<TimeManager>() {
@Override
public TimeManager createService(ContextImpl ctx)
diff --git a/core/java/android/app/admin/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java
index 67408a4..e4ee959 100644
--- a/core/java/android/app/admin/DeviceAdminInfo.java
+++ b/core/java/android/app/admin/DeviceAdminInfo.java
@@ -16,6 +16,7 @@
package android.app.admin;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
@@ -158,6 +159,24 @@
*/
public static final int USES_POLICY_DISABLE_KEYGUARD_FEATURES = 9;
+
+ /**
+ * Value for {@link #getHeadlessDeviceOwnerMode} which indicates that this DPC should not
+ * be provisioned into Device Owner mode on a Headless System User Mode device.
+ */
+ public static final int HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED = 0;
+
+ /**
+ * Value for {@link #getHeadlessDeviceOwnerMode} which indicates that this DPC should be
+ * provisioned into "affiliated" mode when on a Headless System User Mode device.
+ *
+ * <p>This mode adds a Profile Owner to all users other than the user the Device Owner is on.
+ */
+ public static final int HEADLESS_DEVICE_OWNER_MODE_AFFILIATED = 1;
+
+ @IntDef({HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED, HEADLESS_DEVICE_OWNER_MODE_AFFILIATED})
+ private @interface HeadlessDeviceOwnerMode {}
+
/** @hide */
public static class PolicyInfo {
public final int ident;
@@ -255,6 +274,8 @@
*/
boolean mSupportsTransferOwnership;
+ @HeadlessDeviceOwnerMode int mHeadlessDeviceOwnerMode = HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED;
+
/**
* Constructor.
*
@@ -341,6 +362,17 @@
"support-transfer-ownership tag must be empty.");
}
mSupportsTransferOwnership = true;
+ } else if (tagName.equals("headless-system-user")) {
+ String deviceOwnerModeStringValue =
+ parser.getAttributeValue(null, "device-owner-mode");
+
+ if (deviceOwnerModeStringValue.equalsIgnoreCase("unsupported")) {
+ mHeadlessDeviceOwnerMode = HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED;
+ } else if (deviceOwnerModeStringValue.equalsIgnoreCase("affiliated")) {
+ mHeadlessDeviceOwnerMode = HEADLESS_DEVICE_OWNER_MODE_AFFILIATED;
+ } else {
+ throw new XmlPullParserException("headless-system-user mode must be valid");
+ }
}
}
} catch (NameNotFoundException e) {
@@ -355,6 +387,7 @@
mActivityInfo = ActivityInfo.CREATOR.createFromParcel(source);
mUsesPolicies = source.readInt();
mSupportsTransferOwnership = source.readBoolean();
+ mHeadlessDeviceOwnerMode = source.readInt();
}
/**
@@ -460,6 +493,14 @@
return mSupportsTransferOwnership;
}
+ /**
+ * Returns the mode this DeviceAdmin wishes to use if provisioned as a Device Owner on a
+ * headless system user mode device.
+ */
+ public @HeadlessDeviceOwnerMode int getHeadlessDeviceOwnerMode() {
+ return mHeadlessDeviceOwnerMode;
+ }
+
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public ArrayList<PolicyInfo> getUsedPolicies() {
@@ -505,6 +546,7 @@
mActivityInfo.writeToParcel(dest, flags);
dest.writeInt(mUsesPolicies);
dest.writeBoolean(mSupportsTransferOwnership);
+ dest.writeInt(mHeadlessDeviceOwnerMode);
}
/**
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 11584cc..7e5523a 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -16,10 +16,10 @@
package android.app.admin;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.Manifest.permission.QUERY_ADMIN_POLICY;
import static android.Manifest.permission.SET_TIME;
import static android.Manifest.permission.SET_TIME_ZONE;
-import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.content.Intent.LOCAL_FLAG_FROM_SYSTEM;
import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
@@ -2812,6 +2812,17 @@
public static final int STATUS_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS = 15;
/**
+ * Result code for {@link #checkProvisioningPrecondition}.
+ *
+ * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} when provisioning a DPC which does
+ * not support headless system user mode on a headless system user mode device.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int STATUS_HEADLESS_SYSTEM_USER_MODE_NOT_SUPPORTED = 16;
+
+ /**
* Result codes for {@link #checkProvisioningPrecondition} indicating all the provisioning pre
* conditions.
*
@@ -2824,7 +2835,8 @@
STATUS_HAS_PAIRED, STATUS_MANAGED_USERS_NOT_SUPPORTED, STATUS_SYSTEM_USER,
STATUS_CANNOT_ADD_MANAGED_PROFILE, STATUS_DEVICE_ADMIN_NOT_SUPPORTED,
STATUS_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER,
- STATUS_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS
+ STATUS_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS,
+ STATUS_HEADLESS_SYSTEM_USER_MODE_NOT_SUPPORTED
})
public @interface ProvisioningPrecondition {}
diff --git a/core/java/android/app/time/TimeManager.java b/core/java/android/app/time/TimeManager.java
index e35e359..7cb6c34 100644
--- a/core/java/android/app/time/TimeManager.java
+++ b/core/java/android/app/time/TimeManager.java
@@ -41,7 +41,7 @@
* @hide
*/
@SystemApi
-@SystemService(Context.TIME_MANAGER)
+@SystemService(Context.TIME_MANAGER_SERVICE)
public final class TimeManager {
private static final String TAG = "time.TimeManager";
private static final boolean DEBUG = false;
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index a58cac3..7310138 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -5854,7 +5854,7 @@
public static final String SECURE_ELEMENT_SERVICE = "secure_element";
/**
- * Use with {@link #getSystemService(String)} to retrieve an
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.app.timedetector.TimeDetector}.
* @hide
*
@@ -5863,7 +5863,7 @@
public static final String TIME_DETECTOR_SERVICE = "time_detector";
/**
- * Use with {@link #getSystemService(String)} to retrieve an
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.app.timezonedetector.TimeZoneDetector}.
* @hide
*
@@ -5872,12 +5872,14 @@
public static final String TIME_ZONE_DETECTOR_SERVICE = "time_zone_detector";
/**
- * Use with {@link #getSystemService(String)} to retrieve an {@link TimeManager}.
+ * Use with {@link #getSystemService(String)} to retrieve a {@link TimeManager}.
* @hide
*
* @see #getSystemService(String)
*/
- public static final String TIME_MANAGER = "time_manager";
+ @SystemApi
+ @SuppressLint("ServiceName")
+ public static final String TIME_MANAGER_SERVICE = "time_manager";
/**
* Binder service name for {@link AppBindingService}.
@@ -7527,14 +7529,12 @@
}
/**
- * Get the binder object associated with the IApplicationThread of this Context.
- *
- * This can be used by a mainline module to uniquely identify a specific app process.
+ * Used by a mainline module to uniquely identify a specific app process.
* @hide
*/
@NonNull
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
- public IBinder getIApplicationThreadBinder() {
+ public IBinder getProcessToken() {
throw new RuntimeException("Not implemented. Must override in a subclass.");
}
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index e65e91c..9027e2e 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -1286,8 +1286,8 @@
* @hide
*/
@Override
- public IBinder getIApplicationThreadBinder() {
- return mBase.getIApplicationThreadBinder();
+ public IBinder getProcessToken() {
+ return mBase.getProcessToken();
}
/**
diff --git a/core/java/android/content/res/FontScaleConverterFactory.java b/core/java/android/content/res/FontScaleConverterFactory.java
index c77a372..ffdc7b38 100644
--- a/core/java/android/content/res/FontScaleConverterFactory.java
+++ b/core/java/android/content/res/FontScaleConverterFactory.java
@@ -42,7 +42,7 @@
/* fromSp= */
new float[] { 8f, 10f, 12f, 14f, 18f, 20f, 24f, 30f, 100},
/* toDp= */
- new float[] { 9.2f, 11.5f, 13.8f, 16.1f, 20.7f, 23f, 27.6f, 34.5f, 115})
+ new float[] { 9.2f, 11.5f, 13.8f, 16.4f, 19.8f, 21.8f, 25.2f, 30f, 100})
);
put(
@@ -51,7 +51,7 @@
/* fromSp= */
new float[] { 8f, 10f, 12f, 14f, 18f, 20f, 24f, 30f, 100},
/* toDp= */
- new float[] {10.4f, 13f, 15.6f, 18.2f, 23.4f, 26f, 31.2f, 39f, 130})
+ new float[] {10.4f, 13f, 15.6f, 18.8f, 21.6f, 23.6f, 26.4f, 30f, 100})
);
put(
@@ -60,7 +60,7 @@
/* fromSp= */
new float[] { 8f, 10f, 12f, 14f, 18f, 20f, 24f, 30f, 100},
/* toDp= */
- new float[] { 12f, 15f, 18f, 21f, 27f, 30f, 36f, 45f, 150})
+ new float[] { 12f, 15f, 18f, 22f, 24f, 26f, 28f, 30f, 100})
);
put(
@@ -69,7 +69,7 @@
/* fromSp= */
new float[] { 8f, 10f, 12f, 14f, 18f, 20f, 24f, 30f, 100},
/* toDp= */
- new float[] {14.4f, 18f, 21.6f, 25.2f, 32.4f, 36f, 43.2f, 54f, 180})
+ new float[] {14.4f, 18f, 21.6f, 24.4f, 27.6f, 30.8f, 32.8f, 34.8f, 100})
);
put(
@@ -78,7 +78,7 @@
/* fromSp= */
new float[] { 8f, 10f, 12f, 14f, 18f, 20f, 24f, 30f, 100},
/* toDp= */
- new float[] { 16f, 20f, 24f, 28f, 36f, 40f, 48f, 60f, 200})
+ new float[] { 16f, 20f, 24f, 26f, 30f, 34f, 36f, 38f, 100})
);
}
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index ac23af4..01ce7b9 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -995,6 +995,51 @@
}
/**
+ * Puts the Context Hub in test mode.
+ *
+ * The purpose of this API is to make testing CHRE/Context Hub more
+ * predictable and robust. This temporarily unloads all
+ * nanoapps.
+ *
+ * Note that this API must not cause CHRE/Context Hub to behave differently
+ * in test compared to production.
+ *
+ * @return true if the enable test mode operation succeeded.
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
+ @NonNull public boolean enableTestMode() {
+ try {
+ return mService.setTestMode(true);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Puts the Context Hub out of test mode.
+ *
+ * This API will undo any previously made enableTestMode() calls.
+ * After this API is called, it should restore the state of the system
+ * to the normal/production mode before any enableTestMode() call was
+ * made. If the enableTestMode() call unloaded any nanoapps
+ * to enter test mode, it should reload those nanoapps in this API call.
+ *
+ * @return true if the disable operation succeeded.
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
+ @NonNull public boolean disableTestMode() {
+ try {
+ return mService.setTestMode(false);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Unregister a callback for receive messages from the context hub.
*
* @see Callback
diff --git a/core/java/android/hardware/location/IContextHubService.aidl b/core/java/android/hardware/location/IContextHubService.aidl
index 490267f..11f30461 100644
--- a/core/java/android/hardware/location/IContextHubService.aidl
+++ b/core/java/android/hardware/location/IContextHubService.aidl
@@ -113,4 +113,8 @@
// Queries for a list of preloaded nanoapps
@EnforcePermission("ACCESS_CONTEXT_HUB")
long[] getPreloadedNanoAppIds(in ContextHubInfo hubInfo);
+
+ // Enables or disables test mode
+ @EnforcePermission("ACCESS_CONTEXT_HUB")
+ boolean setTestMode(in boolean enable);
}
diff --git a/core/java/android/nfc/BeamShareData.aidl b/core/java/android/nfc/BeamShareData.aidl
new file mode 100644
index 0000000..a47e240
--- /dev/null
+++ b/core/java/android/nfc/BeamShareData.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2013 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.nfc;
+
+parcelable BeamShareData;
diff --git a/core/java/android/nfc/BeamShareData.java b/core/java/android/nfc/BeamShareData.java
new file mode 100644
index 0000000..6a40f98
--- /dev/null
+++ b/core/java/android/nfc/BeamShareData.java
@@ -0,0 +1,67 @@
+package android.nfc;
+
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.UserHandle;
+
+/**
+ * Class to IPC data to be shared over Android Beam.
+ * Allows bundling NdefMessage, Uris and flags in a single
+ * IPC call. This is important as we want to reduce the
+ * amount of IPC calls at "touch time".
+ * @hide
+ */
+public final class BeamShareData implements Parcelable {
+ public final NdefMessage ndefMessage;
+ public final Uri[] uris;
+ public final UserHandle userHandle;
+ public final int flags;
+
+ public BeamShareData(NdefMessage msg, Uri[] uris, UserHandle userHandle, int flags) {
+ this.ndefMessage = msg;
+ this.uris = uris;
+ this.userHandle = userHandle;
+ this.flags = flags;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ int urisLength = (uris != null) ? uris.length : 0;
+ dest.writeParcelable(ndefMessage, 0);
+ dest.writeInt(urisLength);
+ if (urisLength > 0) {
+ dest.writeTypedArray(uris, 0);
+ }
+ dest.writeParcelable(userHandle, 0);
+ dest.writeInt(this.flags);
+ }
+
+ public static final @android.annotation.NonNull Parcelable.Creator<BeamShareData> CREATOR =
+ new Parcelable.Creator<BeamShareData>() {
+ @Override
+ public BeamShareData createFromParcel(Parcel source) {
+ Uri[] uris = null;
+ NdefMessage msg = source.readParcelable(NdefMessage.class.getClassLoader(), android.nfc.NdefMessage.class);
+ int numUris = source.readInt();
+ if (numUris > 0) {
+ uris = new Uri[numUris];
+ source.readTypedArray(uris, Uri.CREATOR);
+ }
+ UserHandle userHandle = source.readParcelable(UserHandle.class.getClassLoader(), android.os.UserHandle.class);
+ int flags = source.readInt();
+
+ return new BeamShareData(msg, uris, userHandle, flags);
+ }
+
+ @Override
+ public BeamShareData[] newArray(int size) {
+ return new BeamShareData[size];
+ }
+ };
+}
diff --git a/core/java/android/nfc/IAppCallback.aidl b/core/java/android/nfc/IAppCallback.aidl
index b06bf06..133146d 100644
--- a/core/java/android/nfc/IAppCallback.aidl
+++ b/core/java/android/nfc/IAppCallback.aidl
@@ -16,6 +16,7 @@
package android.nfc;
+import android.nfc.BeamShareData;
import android.nfc.Tag;
/**
@@ -23,5 +24,7 @@
*/
interface IAppCallback
{
+ BeamShareData createBeamShareData(byte peerLlcpVersion);
+ oneway void onNdefPushComplete(byte peerLlcpVersion);
oneway void onTagDiscovered(in Tag tag);
}
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index a6d8caf..8a30ef4 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -18,6 +18,7 @@
import android.app.PendingIntent;
import android.content.IntentFilter;
+import android.nfc.BeamShareData;
import android.nfc.NdefMessage;
import android.nfc.Tag;
import android.nfc.TechListParcel;
@@ -46,18 +47,24 @@
int getState();
boolean disable(boolean saveState);
boolean enable();
+ boolean enableNdefPush();
+ boolean disableNdefPush();
+ boolean isNdefPushEnabled();
void pausePolling(int timeoutInMs);
void resumePolling();
void setForegroundDispatch(in PendingIntent intent,
in IntentFilter[] filters, in TechListParcel techLists);
void setAppCallback(in IAppCallback callback);
+ oneway void invokeBeam();
+ oneway void invokeBeamInternal(in BeamShareData shareData);
boolean ignore(int nativeHandle, int debounceMs, ITagRemovedCallback callback);
void dispatch(in Tag tag);
void setReaderMode (IBinder b, IAppCallback callback, int flags, in Bundle extras);
+ void setP2pModes(int initatorModes, int targetModes);
void addNfcUnlockHandler(INfcUnlockHandler unlockHandler, in int[] techList);
void removeNfcUnlockHandler(INfcUnlockHandler unlockHandler);
diff --git a/core/java/android/nfc/NfcActivityManager.java b/core/java/android/nfc/NfcActivityManager.java
index 8d75cac..911aaf3 100644
--- a/core/java/android/nfc/NfcActivityManager.java
+++ b/core/java/android/nfc/NfcActivityManager.java
@@ -19,6 +19,9 @@
import android.app.Activity;
import android.app.Application;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ContentProvider;
+import android.content.Intent;
+import android.net.Uri;
import android.nfc.NfcAdapter.ReaderCallback;
import android.os.Binder;
import android.os.Bundle;
@@ -107,8 +110,14 @@
class NfcActivityState {
boolean resumed = false;
Activity activity;
- NfcAdapter.ReaderCallback readerCallback = null;
+ NdefMessage ndefMessage = null; // static NDEF message
+ NfcAdapter.CreateNdefMessageCallback ndefMessageCallback = null;
+ NfcAdapter.OnNdefPushCompleteCallback onNdefPushCompleteCallback = null;
+ NfcAdapter.CreateBeamUrisCallback uriCallback = null;
+ Uri[] uris = null;
+ int flags = 0;
int readerModeFlags = 0;
+ NfcAdapter.ReaderCallback readerCallback = null;
Bundle readerModeExtras = null;
Binder token;
@@ -128,16 +137,24 @@
unregisterApplication(activity.getApplication());
resumed = false;
activity = null;
- readerCallback = null;
+ ndefMessage = null;
+ ndefMessageCallback = null;
+ onNdefPushCompleteCallback = null;
+ uriCallback = null;
+ uris = null;
readerModeFlags = 0;
- readerModeExtras = null;
token = null;
}
@Override
public String toString() {
- StringBuilder s = new StringBuilder("[");
- s.append(readerCallback);
- s.append("]");
+ StringBuilder s = new StringBuilder("[").append(" ");
+ s.append(ndefMessage).append(" ").append(ndefMessageCallback).append(" ");
+ s.append(uriCallback).append(" ");
+ if (uris != null) {
+ for (Uri uri : uris) {
+ s.append(onNdefPushCompleteCallback).append(" ").append(uri).append("]");
+ }
+ }
return s.toString();
}
}
@@ -228,6 +245,92 @@
}
}
+ public void setNdefPushContentUri(Activity activity, Uri[] uris) {
+ boolean isResumed;
+ synchronized (NfcActivityManager.this) {
+ NfcActivityState state = getActivityState(activity);
+ state.uris = uris;
+ isResumed = state.resumed;
+ }
+ if (isResumed) {
+ // requestNfcServiceCallback() verifies permission also
+ requestNfcServiceCallback();
+ } else {
+ // Crash API calls early in case NFC permission is missing
+ verifyNfcPermission();
+ }
+ }
+
+
+ public void setNdefPushContentUriCallback(Activity activity,
+ NfcAdapter.CreateBeamUrisCallback callback) {
+ boolean isResumed;
+ synchronized (NfcActivityManager.this) {
+ NfcActivityState state = getActivityState(activity);
+ state.uriCallback = callback;
+ isResumed = state.resumed;
+ }
+ if (isResumed) {
+ // requestNfcServiceCallback() verifies permission also
+ requestNfcServiceCallback();
+ } else {
+ // Crash API calls early in case NFC permission is missing
+ verifyNfcPermission();
+ }
+ }
+
+ public void setNdefPushMessage(Activity activity, NdefMessage message, int flags) {
+ boolean isResumed;
+ synchronized (NfcActivityManager.this) {
+ NfcActivityState state = getActivityState(activity);
+ state.ndefMessage = message;
+ state.flags = flags;
+ isResumed = state.resumed;
+ }
+ if (isResumed) {
+ // requestNfcServiceCallback() verifies permission also
+ requestNfcServiceCallback();
+ } else {
+ // Crash API calls early in case NFC permission is missing
+ verifyNfcPermission();
+ }
+ }
+
+ public void setNdefPushMessageCallback(Activity activity,
+ NfcAdapter.CreateNdefMessageCallback callback, int flags) {
+ boolean isResumed;
+ synchronized (NfcActivityManager.this) {
+ NfcActivityState state = getActivityState(activity);
+ state.ndefMessageCallback = callback;
+ state.flags = flags;
+ isResumed = state.resumed;
+ }
+ if (isResumed) {
+ // requestNfcServiceCallback() verifies permission also
+ requestNfcServiceCallback();
+ } else {
+ // Crash API calls early in case NFC permission is missing
+ verifyNfcPermission();
+ }
+ }
+
+ public void setOnNdefPushCompleteCallback(Activity activity,
+ NfcAdapter.OnNdefPushCompleteCallback callback) {
+ boolean isResumed;
+ synchronized (NfcActivityManager.this) {
+ NfcActivityState state = getActivityState(activity);
+ state.onNdefPushCompleteCallback = callback;
+ isResumed = state.resumed;
+ }
+ if (isResumed) {
+ // requestNfcServiceCallback() verifies permission also
+ requestNfcServiceCallback();
+ } else {
+ // Crash API calls early in case NFC permission is missing
+ verifyNfcPermission();
+ }
+ }
+
/**
* Request or unrequest NFC service callbacks.
* Makes IPC call - do not hold lock.
@@ -248,6 +351,86 @@
}
}
+ /** Callback from NFC service, usually on binder thread */
+ @Override
+ public BeamShareData createBeamShareData(byte peerLlcpVersion) {
+ NfcAdapter.CreateNdefMessageCallback ndefCallback;
+ NfcAdapter.CreateBeamUrisCallback urisCallback;
+ NdefMessage message;
+ Activity activity;
+ Uri[] uris;
+ int flags;
+ NfcEvent event = new NfcEvent(mAdapter, peerLlcpVersion);
+ synchronized (NfcActivityManager.this) {
+ NfcActivityState state = findResumedActivityState();
+ if (state == null) return null;
+
+ ndefCallback = state.ndefMessageCallback;
+ urisCallback = state.uriCallback;
+ message = state.ndefMessage;
+ uris = state.uris;
+ flags = state.flags;
+ activity = state.activity;
+ }
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ // Make callbacks without lock
+ if (ndefCallback != null) {
+ message = ndefCallback.createNdefMessage(event);
+ }
+ if (urisCallback != null) {
+ uris = urisCallback.createBeamUris(event);
+ if (uris != null) {
+ ArrayList<Uri> validUris = new ArrayList<Uri>();
+ for (Uri uri : uris) {
+ if (uri == null) {
+ Log.e(TAG, "Uri not allowed to be null.");
+ continue;
+ }
+ String scheme = uri.getScheme();
+ if (scheme == null || (!scheme.equalsIgnoreCase("file") &&
+ !scheme.equalsIgnoreCase("content"))) {
+ Log.e(TAG, "Uri needs to have " +
+ "either scheme file or scheme content");
+ continue;
+ }
+ uri = ContentProvider.maybeAddUserId(uri, activity.getUserId());
+ validUris.add(uri);
+ }
+
+ uris = validUris.toArray(new Uri[validUris.size()]);
+ }
+ }
+ if (uris != null && uris.length > 0) {
+ for (Uri uri : uris) {
+ // Grant the NFC process permission to read these URIs
+ activity.grantUriPermission("com.android.nfc", uri,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ return new BeamShareData(message, uris, activity.getUser(), flags);
+ }
+
+ /** Callback from NFC service, usually on binder thread */
+ @Override
+ public void onNdefPushComplete(byte peerLlcpVersion) {
+ NfcAdapter.OnNdefPushCompleteCallback callback;
+ synchronized (NfcActivityManager.this) {
+ NfcActivityState state = findResumedActivityState();
+ if (state == null) return;
+
+ callback = state.onNdefPushCompleteCallback;
+ }
+ NfcEvent event = new NfcEvent(mAdapter, peerLlcpVersion);
+ // Make callback without lock
+ if (callback != null) {
+ callback.onNdefPushComplete(event);
+ }
+ }
+
@Override
public void onTagDiscovered(Tag tag) throws RemoteException {
NfcAdapter.ReaderCallback callback;
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index d947b48..a980158 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -345,10 +345,7 @@
*/
public static final String EXTRA_READER_PRESENCE_CHECK_DELAY = "presence";
- /**
- * @hide
- * @removed
- */
+ /** @hide */
@SystemApi
public static final int FLAG_NDEF_PUSH_NO_CONFIRM = 0x1;
@@ -423,6 +420,7 @@
// Guarded by NfcAdapter.class
static boolean sIsInitialized = false;
static boolean sHasNfcFeature;
+ static boolean sHasBeamFeature;
// Final after first constructor, except for
// attemptDeadServiceRecovery() when NFC crashes - we accept a best effort
@@ -486,7 +484,7 @@
* A callback to be invoked when the system successfully delivers your {@link NdefMessage}
* to another device.
* @see #setOnNdefPushCompleteCallback
- * @removed this feature is removed. File sharing can work using other technology like
+ * @deprecated this feature is deprecated. File sharing can work using other technology like
* Bluetooth.
*/
@java.lang.Deprecated
@@ -512,7 +510,7 @@
* content currently visible to the user. Alternatively, you can call {@link
* #setNdefPushMessage setNdefPushMessage()} if the {@link NdefMessage} always contains the
* same data.
- * @removed this feature is removed. File sharing can work using other technology like
+ * @deprecated this feature is deprecated. File sharing can work using other technology like
* Bluetooth.
*/
@java.lang.Deprecated
@@ -542,7 +540,7 @@
/**
- * @removed this feature is removed. File sharing can work using other technology like
+ * @deprecated this feature is deprecated. File sharing can work using other technology like
* Bluetooth.
*/
@java.lang.Deprecated
@@ -574,6 +572,26 @@
}
/**
+ * Helper to check if this device has FEATURE_NFC_BEAM, but without using
+ * a context.
+ * Equivalent to
+ * context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC_BEAM)
+ */
+ private static boolean hasBeamFeature() {
+ IPackageManager pm = ActivityThread.getPackageManager();
+ if (pm == null) {
+ Log.e(TAG, "Cannot get package manager, assuming no Android Beam feature");
+ return false;
+ }
+ try {
+ return pm.hasSystemFeature(PackageManager.FEATURE_NFC_BEAM, 0);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Package manager query failed, assuming no Android Beam feature", e);
+ return false;
+ }
+ }
+
+ /**
* Helper to check if this device has FEATURE_NFC, but without using
* a context.
* Equivalent to
@@ -652,6 +670,7 @@
public static synchronized NfcAdapter getNfcAdapter(Context context) {
if (!sIsInitialized) {
sHasNfcFeature = hasNfcFeature();
+ sHasBeamFeature = hasBeamFeature();
boolean hasHceFeature = hasNfcHceFeature();
/* is this device meant to have NFC */
if (!sHasNfcFeature && !hasHceFeature) {
@@ -1144,7 +1163,7 @@
* @param uris an array of Uri(s) to push over Android Beam
* @param activity activity for which the Uri(s) will be pushed
* @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
- * @removed this feature is removed. File sharing can work using other technology like
+ * @deprecated this feature is deprecated. File sharing can work using other technology like
* Bluetooth.
*/
@java.lang.Deprecated
@@ -1153,7 +1172,26 @@
if (!sHasNfcFeature) {
throw new UnsupportedOperationException();
}
+ if (!sHasBeamFeature) {
+ return;
+ }
}
+ if (activity == null) {
+ throw new NullPointerException("activity cannot be null");
+ }
+ if (uris != null) {
+ for (Uri uri : uris) {
+ if (uri == null) throw new NullPointerException("Uri not " +
+ "allowed to be null");
+ String scheme = uri.getScheme();
+ if (scheme == null || (!scheme.equalsIgnoreCase("file") &&
+ !scheme.equalsIgnoreCase("content"))) {
+ throw new IllegalArgumentException("URI needs to have " +
+ "either scheme file or scheme content");
+ }
+ }
+ }
+ mNfcActivityManager.setNdefPushContentUri(activity, uris);
}
/**
@@ -1213,7 +1251,7 @@
* @param callback callback, or null to disable
* @param activity activity for which the Uri(s) will be pushed
* @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
- * @removed this feature is removed. File sharing can work using other technology like
+ * @deprecated this feature is deprecated. File sharing can work using other technology like
* Bluetooth.
*/
@java.lang.Deprecated
@@ -1222,7 +1260,14 @@
if (!sHasNfcFeature) {
throw new UnsupportedOperationException();
}
+ if (!sHasBeamFeature) {
+ return;
+ }
}
+ if (activity == null) {
+ throw new NullPointerException("activity cannot be null");
+ }
+ mNfcActivityManager.setNdefPushContentUriCallback(activity, callback);
}
/**
@@ -1296,7 +1341,7 @@
* to only register one at a time, and to do so in that activity's
* {@link Activity#onCreate}
* @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
- * @removed this feature is removed. File sharing can work using other technology like
+ * @deprecated this feature is deprecated. File sharing can work using other technology like
* Bluetooth.
*/
@java.lang.Deprecated
@@ -1306,12 +1351,36 @@
if (!sHasNfcFeature) {
throw new UnsupportedOperationException();
}
+ if (!sHasBeamFeature) {
+ return;
+ }
+ }
+ int targetSdkVersion = getSdkVersion();
+ try {
+ if (activity == null) {
+ throw new NullPointerException("activity cannot be null");
+ }
+ mNfcActivityManager.setNdefPushMessage(activity, message, 0);
+ for (Activity a : activities) {
+ if (a == null) {
+ throw new NullPointerException("activities cannot contain null");
+ }
+ mNfcActivityManager.setNdefPushMessage(a, message, 0);
+ }
+ } catch (IllegalStateException e) {
+ if (targetSdkVersion < android.os.Build.VERSION_CODES.JELLY_BEAN) {
+ // Less strict on old applications - just log the error
+ Log.e(TAG, "Cannot call API with Activity that has already " +
+ "been destroyed", e);
+ } else {
+ // Prevent new applications from making this mistake, re-throw
+ throw(e);
+ }
}
}
/**
* @hide
- * @removed
*/
@SystemApi
public void setNdefPushMessage(NdefMessage message, Activity activity, int flags) {
@@ -1320,6 +1389,10 @@
throw new UnsupportedOperationException();
}
}
+ if (activity == null) {
+ throw new NullPointerException("activity cannot be null");
+ }
+ mNfcActivityManager.setNdefPushMessage(activity, message, flags);
}
/**
@@ -1387,7 +1460,7 @@
* to only register one at a time, and to do so in that activity's
* {@link Activity#onCreate}
* @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
- * @removed this feature is removed. File sharing can work using other technology like
+ * @deprecated this feature is deprecated. File sharing can work using other technology like
* Bluetooth.
*/
@java.lang.Deprecated
@@ -1397,7 +1470,44 @@
if (!sHasNfcFeature) {
throw new UnsupportedOperationException();
}
+ if (!sHasBeamFeature) {
+ return;
+ }
}
+ int targetSdkVersion = getSdkVersion();
+ try {
+ if (activity == null) {
+ throw new NullPointerException("activity cannot be null");
+ }
+ mNfcActivityManager.setNdefPushMessageCallback(activity, callback, 0);
+ for (Activity a : activities) {
+ if (a == null) {
+ throw new NullPointerException("activities cannot contain null");
+ }
+ mNfcActivityManager.setNdefPushMessageCallback(a, callback, 0);
+ }
+ } catch (IllegalStateException e) {
+ if (targetSdkVersion < android.os.Build.VERSION_CODES.JELLY_BEAN) {
+ // Less strict on old applications - just log the error
+ Log.e(TAG, "Cannot call API with Activity that has already " +
+ "been destroyed", e);
+ } else {
+ // Prevent new applications from making this mistake, re-throw
+ throw(e);
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @UnsupportedAppUsage
+ public void setNdefPushMessageCallback(CreateNdefMessageCallback callback, Activity activity,
+ int flags) {
+ if (activity == null) {
+ throw new NullPointerException("activity cannot be null");
+ }
+ mNfcActivityManager.setNdefPushMessageCallback(activity, callback, flags);
}
/**
@@ -1437,7 +1547,7 @@
* to only register one at a time, and to do so in that activity's
* {@link Activity#onCreate}
* @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
- * @removed this feature is removed. File sharing can work using other technology like
+ * @deprecated this feature is deprecated. File sharing can work using other technology like
* Bluetooth.
*/
@java.lang.Deprecated
@@ -1447,6 +1557,31 @@
if (!sHasNfcFeature) {
throw new UnsupportedOperationException();
}
+ if (!sHasBeamFeature) {
+ return;
+ }
+ }
+ int targetSdkVersion = getSdkVersion();
+ try {
+ if (activity == null) {
+ throw new NullPointerException("activity cannot be null");
+ }
+ mNfcActivityManager.setOnNdefPushCompleteCallback(activity, callback);
+ for (Activity a : activities) {
+ if (a == null) {
+ throw new NullPointerException("activities cannot contain null");
+ }
+ mNfcActivityManager.setOnNdefPushCompleteCallback(a, callback);
+ }
+ } catch (IllegalStateException e) {
+ if (targetSdkVersion < android.os.Build.VERSION_CODES.JELLY_BEAN) {
+ // Less strict on old applications - just log the error
+ Log.e(TAG, "Cannot call API with Activity that has already " +
+ "been destroyed", e);
+ } else {
+ // Prevent new applications from making this mistake, re-throw
+ throw(e);
+ }
}
}
@@ -1629,7 +1764,7 @@
* @param activity the current foreground Activity that has registered data to share
* @return whether the Beam animation was successfully invoked
* @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
- * @removed this feature is removed. File sharing can work using other technology like
+ * @deprecated this feature is deprecated. File sharing can work using other technology like
* Bluetooth.
*/
@java.lang.Deprecated
@@ -1638,8 +1773,37 @@
if (!sHasNfcFeature) {
throw new UnsupportedOperationException();
}
+ if (!sHasBeamFeature) {
+ return false;
+ }
}
- return false;
+ if (activity == null) {
+ throw new NullPointerException("activity may not be null.");
+ }
+ enforceResumed(activity);
+ try {
+ sService.invokeBeam();
+ return true;
+ } catch (RemoteException e) {
+ Log.e(TAG, "invokeBeam: NFC process has died.");
+ attemptDeadServiceRecovery(e);
+ return false;
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public boolean invokeBeam(BeamShareData shareData) {
+ try {
+ Log.e(TAG, "invokeBeamInternal()");
+ sService.invokeBeamInternal(shareData);
+ return true;
+ } catch (RemoteException e) {
+ Log.e(TAG, "invokeBeam: NFC process has died.");
+ attemptDeadServiceRecovery(e);
+ return false;
+ }
}
/**
@@ -1665,9 +1829,9 @@
*
* @param activity foreground activity
* @param message a NDEF Message to push over NFC
- * @throws UnsupportedOperationException if FEATURE_NFC is unavailable
- * @removed this feature is removed. File sharing can work using other technology like
- * Bluetooth.
+ * @throws IllegalStateException if the activity is not currently in the foreground
+ * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+ * @deprecated use {@link #setNdefPushMessage} instead
*/
@Deprecated
public void enableForegroundNdefPush(Activity activity, NdefMessage message) {
@@ -1675,7 +1839,15 @@
if (!sHasNfcFeature) {
throw new UnsupportedOperationException();
}
+ if (!sHasBeamFeature) {
+ return;
+ }
}
+ if (activity == null || message == null) {
+ throw new NullPointerException();
+ }
+ enforceResumed(activity);
+ mNfcActivityManager.setNdefPushMessage(activity, message, 0);
}
/**
@@ -1694,9 +1866,9 @@
* <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
*
* @param activity the Foreground activity
- * @throws UnsupportedOperationException if FEATURE_NFC is unavailable
- * @removed this feature is removed. File sharing can work using other technology like
- * Bluetooth.
+ * @throws IllegalStateException if the Activity has already been paused
+ * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+ * @deprecated use {@link #setNdefPushMessage} instead
*/
@Deprecated
public void disableForegroundNdefPush(Activity activity) {
@@ -1704,7 +1876,17 @@
if (!sHasNfcFeature) {
throw new UnsupportedOperationException();
}
+ if (!sHasBeamFeature) {
+ return;
+ }
}
+ if (activity == null) {
+ throw new NullPointerException();
+ }
+ enforceResumed(activity);
+ mNfcActivityManager.setNdefPushMessage(activity, null, 0);
+ mNfcActivityManager.setNdefPushMessageCallback(activity, null, 0);
+ mNfcActivityManager.setOnNdefPushCompleteCallback(activity, null);
}
/**
@@ -1829,24 +2011,40 @@
* Enable NDEF Push feature.
* <p>This API is for the Settings application.
* @hide
- * @removed
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
public boolean enableNdefPush() {
- return false;
+ if (!sHasNfcFeature) {
+ throw new UnsupportedOperationException();
+ }
+ try {
+ return sService.enableNdefPush();
+ } catch (RemoteException e) {
+ attemptDeadServiceRecovery(e);
+ return false;
+ }
}
/**
* Disable NDEF Push feature.
* <p>This API is for the Settings application.
* @hide
- * @removed
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
public boolean disableNdefPush() {
- return false;
+ synchronized (NfcAdapter.class) {
+ if (!sHasNfcFeature) {
+ throw new UnsupportedOperationException();
+ }
+ }
+ try {
+ return sService.disableNdefPush();
+ } catch (RemoteException e) {
+ attemptDeadServiceRecovery(e);
+ return false;
+ }
}
/**
@@ -1872,17 +2070,26 @@
* @see android.provider.Settings#ACTION_NFCSHARING_SETTINGS
* @return true if NDEF Push feature is enabled
* @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
- * @removed this feature is removed. File sharing can work using other technology like
+ * @deprecated this feature is deprecated. File sharing can work using other technology like
* Bluetooth.
*/
@java.lang.Deprecated
+
public boolean isNdefPushEnabled() {
synchronized (NfcAdapter.class) {
if (!sHasNfcFeature) {
throw new UnsupportedOperationException();
}
+ if (!sHasBeamFeature) {
+ return false;
+ }
}
- return false;
+ try {
+ return sService.isNdefPushEnabled();
+ } catch (RemoteException e) {
+ attemptDeadServiceRecovery(e);
+ return false;
+ }
}
/**
@@ -1973,6 +2180,17 @@
}
/**
+ * @hide
+ */
+ public void setP2pModes(int initiatorModes, int targetModes) {
+ try {
+ sService.setP2pModes(initiatorModes, targetModes);
+ } catch (RemoteException e) {
+ attemptDeadServiceRecovery(e);
+ }
+ }
+
+ /**
* Registers a new NFC unlock handler with the NFC service.
*
* <p />NFC unlock handlers are intended to unlock the keyguard in the presence of a trusted
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 32773a0..249f486 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -754,9 +754,12 @@
* PackageManager.setComponentEnabledSetting} will now throw an
* IllegalArgumentException if the given component class name does not
* exist in the application's manifest.
- * <li> {@code NfcAdapter.setNdefPushMessage},
- * {@code NfcAdapter.setNdefPushMessageCallback} and
- * {@code NfcAdapter.setOnNdefPushCompleteCallback} will throw
+ * <li> {@link android.nfc.NfcAdapter#setNdefPushMessage
+ * NfcAdapter.setNdefPushMessage},
+ * {@link android.nfc.NfcAdapter#setNdefPushMessageCallback
+ * NfcAdapter.setNdefPushMessageCallback} and
+ * {@link android.nfc.NfcAdapter#setOnNdefPushCompleteCallback
+ * NfcAdapter.setOnNdefPushCompleteCallback} will throw
* IllegalStateException if called after the Activity has been destroyed.
* <li> Accessibility services must require the new
* {@link android.Manifest.permission#BIND_ACCESSIBILITY_SERVICE} permission or
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 62b99ba..5b05f21 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1711,6 +1711,7 @@
* Input: Nothing.
* <p>
* Output: Nothing
+ * @see android.nfc.NfcAdapter#isNdefPushEnabled()
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_NFCSHARING_SETTINGS =
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index fc47e69..5810642 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -153,6 +153,12 @@
boolean performHapticFeedback(int effectId, boolean always);
/**
+ * Called by attached views to perform predefined haptic feedback without requiring VIBRATE
+ * permission.
+ */
+ oneway void performHapticFeedbackAsync(int effectId, boolean always);
+
+ /**
* Initiate the drag operation itself
*
* @param window Window which initiates drag operation.
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index 3efbb75..f6348d7 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -485,7 +485,6 @@
* SurfaceControlViewHost was created with an associated hostInputToken.
*
* @return Whether the touch stream was transferred.
- * @hide
*/
public boolean transferTouchGestureToHost() {
if (mViewRoot == null) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 2df4b5b..5dccd06 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -280,6 +280,14 @@
private static final boolean ENABLE_INPUT_LATENCY_TRACKING = true;
/**
+ * Controls whether to use the new oneway performHapticFeedback call. This returns
+ * true in a few more conditions, but doesn't affect which haptics happen. Notably, it
+ * makes the call to performHapticFeedback non-blocking, which reduces potential UI jank.
+ * This is intended as a temporary flag, ultimately becoming permanently 'true'.
+ */
+ private static final boolean USE_ASYNC_PERFORM_HAPTIC_FEEDBACK = true;
+
+ /**
* Whether the caption is drawn by the shell.
* @hide
*/
@@ -8583,7 +8591,13 @@
}
try {
- return mWindowSession.performHapticFeedback(effectId, always);
+ if (USE_ASYNC_PERFORM_HAPTIC_FEEDBACK) {
+ mWindowSession.performHapticFeedbackAsync(effectId, always);
+ return true;
+ } else {
+ // Original blocking binder call path.
+ return mWindowSession.performHapticFeedback(effectId, always);
+ }
} catch (RemoteException e) {
return false;
}
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 4ddd485..7d37c50 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -466,6 +466,11 @@
}
@Override
+ public void performHapticFeedbackAsync(int effectId, boolean always) {
+ performHapticFeedback(effectId, always);
+ }
+
+ @Override
public android.os.IBinder performDrag(android.view.IWindow window, int flags,
android.view.SurfaceControl surface, int touchSource, float touchX, float touchY,
float thumbCenterX, float thumbCenterY, android.content.ClipData data) {
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 95a42aa..f2f4557 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -73,10 +73,12 @@
import android.text.SpannedString;
import android.text.StaticLayout;
import android.text.TextUtils;
+import android.text.method.InsertModeTransformationMethod;
import android.text.method.KeyListener;
import android.text.method.MetaKeyKeyListener;
import android.text.method.MovementMethod;
import android.text.method.OffsetMapping;
+import android.text.method.TransformationMethod;
import android.text.method.WordIterator;
import android.text.style.EasyEditSpan;
import android.text.style.SuggestionRangeSpan;
@@ -136,6 +138,7 @@
import android.window.OnBackInvokedDispatcher;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.graphics.ColorUtils;
import com.android.internal.inputmethod.EditableInputConnection;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -461,6 +464,7 @@
private int mLineChangeSlopMin;
private final AccessibilitySmartActions mA11ySmartActions;
+ private InsertModeController mInsertModeController;
Editor(TextView textView) {
mTextView = textView;
@@ -2109,6 +2113,10 @@
}
}
+ if (mInsertModeController != null) {
+ mInsertModeController.onDraw(canvas);
+ }
+
if (mTextView.canHaveDisplayList() && canvas.isHardwareAccelerated()) {
drawHardwareAccelerated(canvas, layout, highlightPaths, highlightPaints,
selectionHighlight, selectionHighlightPaint, cursorOffsetVertical);
@@ -8054,6 +8062,110 @@
}
}
+ private static final class InsertModeController {
+ private final TextView mTextView;
+ private boolean mIsInsertModeActive;
+ private InsertModeTransformationMethod mInsertModeTransformationMethod;
+ private final Paint mHighlightPaint;
+
+ InsertModeController(@NonNull TextView textView) {
+ mTextView = Objects.requireNonNull(textView);
+ mIsInsertModeActive = false;
+ mInsertModeTransformationMethod = null;
+ mHighlightPaint = new Paint();
+
+ // The highlight color is supposed to be 12% of the color primary40. We can't
+ // directly access Material 3 theme. But because Material 3 sets the colorPrimary to
+ // be primary40, here we hardcoded it to be 12% of colorPrimary.
+ final TypedValue typedValue = new TypedValue();
+ mTextView.getContext().getTheme()
+ .resolveAttribute(R.attr.colorPrimary, typedValue, true);
+ final int colorPrimary = typedValue.data;
+ final int highlightColor = ColorUtils.setAlphaComponent(colorPrimary,
+ (int) (0.12f * Color.alpha(colorPrimary)));
+ mHighlightPaint.setColor(highlightColor);
+ }
+
+ /**
+ * Enter insert mode.
+ * @param offset the index to set the cursor.
+ * @return true if the call is successful. false if a) it's already in the insert mode,
+ * b) it failed to enter the insert mode.
+ */
+ boolean enterInsertMode(int offset) {
+ if (mIsInsertModeActive) return false;
+
+ TransformationMethod oldTransformationMethod =
+ mTextView.getTransformationMethod();
+ if (oldTransformationMethod instanceof OffsetMapping) {
+ // We can't support the case where the oldTransformationMethod is an OffsetMapping.
+ return false;
+ }
+
+ final boolean isSingleLine = mTextView.isSingleLine();
+ mInsertModeTransformationMethod = new InsertModeTransformationMethod(offset,
+ isSingleLine, oldTransformationMethod);
+ mTextView.setTransformationMethod(mInsertModeTransformationMethod);
+ Selection.setSelection((Spannable) mTextView.getText(), offset);
+
+ mIsInsertModeActive = true;
+ return true;
+ }
+
+ void exitInsertMode() {
+ if (!mIsInsertModeActive) return;
+ if (mInsertModeTransformationMethod == null
+ || mInsertModeTransformationMethod != mTextView.getTransformationMethod()) {
+ // If mInsertionModeTransformationMethod doesn't match the one on TextView,
+ // something else have changed the TextView's TransformationMethod while the
+ // insertion mode is active. We don't need to restore the oldTransformationMethod.
+ // TODO(265871733): support the case where setTransformationMethod is called in
+ // the insert mode.
+ mIsInsertModeActive = false;
+ return;
+ }
+ // Changing TransformationMethod will reset selection range to [0, 0), we need to
+ // manually restore the old selection range.
+ final int selectionStart = mTextView.getSelectionStart();
+ final int selectionEnd = mTextView.getSelectionEnd();
+ final TransformationMethod oldTransformationMethod =
+ mInsertModeTransformationMethod.getOldTransformationMethod();
+ mTextView.setTransformationMethod(oldTransformationMethod);
+ Selection.setSelection((Spannable) mTextView.getText(), selectionStart, selectionEnd);
+ mIsInsertModeActive = false;
+ }
+
+ void onDraw(Canvas canvas) {
+ if (!mIsInsertModeActive) return;
+ final CharSequence transformedText = mTextView.getTransformed();
+ if (transformedText instanceof InsertModeTransformationMethod.TransformedText) {
+ final Layout layout = mTextView.getLayout();
+ if (layout == null) return;
+ final InsertModeTransformationMethod.TransformedText insertModeTransformedText =
+ ((InsertModeTransformationMethod.TransformedText) transformedText);
+ final int highlightStart = insertModeTransformedText.getHighlightStart();
+ final int highlightEnd = insertModeTransformedText.getHighlightEnd();
+ final Layout.SelectionRectangleConsumer consumer =
+ (left, top, right, bottom, textSelectionLayout) ->
+ canvas.drawRect(left, top, right, bottom, mHighlightPaint);
+ layout.getSelection(highlightStart, highlightEnd, consumer);
+ }
+ }
+ }
+
+ boolean enterInsertMode(int offset) {
+ if (mInsertModeController == null) {
+ if (mTextView == null) return false;
+ mInsertModeController = new InsertModeController(mTextView);
+ }
+ return mInsertModeController.enterInsertMode(offset);
+ }
+
+ void exitInsertMode() {
+ if (mInsertModeController == null) return;
+ mInsertModeController.exitInsertMode();
+ }
+
/**
* Initializes the nodeInfo with smart actions.
*/
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 6ff808e..6e36a03 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -202,6 +202,7 @@
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InsertGesture;
+import android.view.inputmethod.InsertModeGesture;
import android.view.inputmethod.JoinOrSplitGesture;
import android.view.inputmethod.PreviewableHandwritingGesture;
import android.view.inputmethod.RemoveSpaceGesture;
@@ -2534,7 +2535,7 @@
/**
* @hide
*/
- @VisibleForTesting
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public CharSequence getTransformed() {
return mTransformed;
}
@@ -9620,6 +9621,7 @@
gestures.add(InsertGesture.class);
gestures.add(RemoveSpaceGesture.class);
gestures.add(JoinOrSplitGesture.class);
+ gestures.add(InsertModeGesture.class);
outAttrs.setSupportedHandwritingGestures(gestures);
Set<Class<? extends PreviewableHandwritingGesture>> previews = new ArraySet<>();
@@ -10169,6 +10171,27 @@
return InputConnection.HANDWRITING_GESTURE_RESULT_SUCCESS;
}
+ /** @hide */
+ public int performHandwritingInsertModeGesture(@NonNull InsertModeGesture gesture) {
+ final PointF insertPoint =
+ convertFromScreenToContentCoordinates(gesture.getInsertionPoint());
+ final int line = getLineForHandwritingGesture(insertPoint);
+ final CancellationSignal cancellationSignal = gesture.getCancellationSignal();
+
+ // If no cancellationSignal is provided, don't enter the insert mode.
+ if (line == -1 || cancellationSignal == null) {
+ return handleGestureFailure(gesture);
+ }
+
+ final int offset = mLayout.getOffsetForHorizontal(line, insertPoint.x);
+
+ if (!mEditor.enterInsertMode(offset)) {
+ return InputConnection.HANDWRITING_GESTURE_RESULT_FAILED;
+ }
+ cancellationSignal.setOnCancelListener(() -> mEditor.exitInsertMode());
+ return InputConnection.HANDWRITING_GESTURE_RESULT_SUCCESS;
+ }
+
private int handleGestureFailure(HandwritingGesture gesture) {
return handleGestureFailure(gesture, /* isPreview= */ false);
}
diff --git a/core/java/com/android/internal/app/procstats/ProcessState.java b/core/java/com/android/internal/app/procstats/ProcessState.java
index 72b9cd2..818a503 100644
--- a/core/java/com/android/internal/app/procstats/ProcessState.java
+++ b/core/java/com/android/internal/app/procstats/ProcessState.java
@@ -73,6 +73,7 @@
import java.io.PrintWriter;
import java.util.Comparator;
+import java.util.concurrent.TimeUnit;
public final class ProcessState {
private static final String TAG = "ProcessStats";
@@ -1542,6 +1543,75 @@
proto.write(fieldId, procName);
}
+ /** Dumps the duration of each state to statsEventOutput. */
+ public void dumpStateDurationToStatsd(
+ int atomTag, ProcessStats processStats, StatsEventOutput statsEventOutput) {
+ long topMs = 0;
+ long fgsMs = 0;
+ long boundTopMs = 0;
+ long boundFgsMs = 0;
+ long importantForegroundMs = 0;
+ long cachedMs = 0;
+ long frozenMs = 0;
+ long otherMs = 0;
+ for (int i = 0, size = mDurations.getKeyCount(); i < size; i++) {
+ final int key = mDurations.getKeyAt(i);
+ final int type = SparseMappingTable.getIdFromKey(key);
+ int procStateIndex = type % STATE_COUNT;
+ long duration = mDurations.getValue(key);
+ switch (procStateIndex) {
+ case STATE_TOP:
+ topMs += duration;
+ break;
+ case STATE_BOUND_TOP_OR_FGS:
+ boundTopMs += duration;
+ break;
+ case STATE_FGS:
+ fgsMs += duration;
+ break;
+ case STATE_IMPORTANT_FOREGROUND:
+ case STATE_IMPORTANT_BACKGROUND:
+ importantForegroundMs += duration;
+ break;
+ case STATE_BACKUP:
+ case STATE_SERVICE:
+ case STATE_SERVICE_RESTARTING:
+ case STATE_RECEIVER:
+ case STATE_HEAVY_WEIGHT:
+ case STATE_HOME:
+ case STATE_LAST_ACTIVITY:
+ case STATE_PERSISTENT:
+ otherMs += duration;
+ break;
+ case STATE_CACHED_ACTIVITY:
+ case STATE_CACHED_ACTIVITY_CLIENT:
+ case STATE_CACHED_EMPTY:
+ cachedMs += duration;
+ break;
+ // TODO (b/261910877) Add support for tracking boundFgsMs and
+ // frozenMs.
+ }
+ }
+ statsEventOutput.write(
+ atomTag,
+ getUid(),
+ getName(),
+ (int) TimeUnit.MILLISECONDS.toSeconds(processStats.mTimePeriodStartUptime),
+ (int) TimeUnit.MILLISECONDS.toSeconds(processStats.mTimePeriodEndUptime),
+ (int)
+ TimeUnit.MILLISECONDS.toSeconds(
+ processStats.mTimePeriodEndUptime
+ - processStats.mTimePeriodStartUptime),
+ (int) TimeUnit.MILLISECONDS.toSeconds(topMs),
+ (int) TimeUnit.MILLISECONDS.toSeconds(fgsMs),
+ (int) TimeUnit.MILLISECONDS.toSeconds(boundTopMs),
+ (int) TimeUnit.MILLISECONDS.toSeconds(boundFgsMs),
+ (int) TimeUnit.MILLISECONDS.toSeconds(importantForegroundMs),
+ (int) TimeUnit.MILLISECONDS.toSeconds(cachedMs),
+ (int) TimeUnit.MILLISECONDS.toSeconds(frozenMs),
+ (int) TimeUnit.MILLISECONDS.toSeconds(otherMs));
+ }
+
/** Similar to {@code #dumpDebug}, but with a reduced/aggregated subset of states. */
public void dumpAggregatedProtoForStatsd(ProtoOutputStream proto, long fieldId,
String procName, int uid, long now,
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index d2b2f0a..f3ed09a 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -43,6 +43,7 @@
import com.android.internal.app.ProcessMap;
import com.android.internal.app.procstats.AssociationState.SourceKey;
import com.android.internal.app.procstats.AssociationState.SourceState;
+import com.android.internal.util.function.QuintConsumer;
import dalvik.system.VMRuntime;
@@ -56,6 +57,8 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -2389,6 +2392,79 @@
}
}
+ void forEachProcess(Consumer<ProcessState> consumer) {
+ final ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
+ for (int ip = 0, size = procMap.size(); ip < size; ip++) {
+ final SparseArray<ProcessState> uids = procMap.valueAt(ip);
+ for (int iu = 0, uidsSize = uids.size(); iu < uidsSize; iu++) {
+ final ProcessState processState = uids.valueAt(iu);
+ consumer.accept(processState);
+ }
+ }
+ }
+
+ void forEachAssociation(
+ QuintConsumer<AssociationState, Integer, String, SourceKey, SourceState> consumer) {
+ final ArrayMap<String, SparseArray<LongSparseArray<PackageState>>> pkgMap =
+ mPackages.getMap();
+ for (int ip = 0, size = pkgMap.size(); ip < size; ip++) {
+ final SparseArray<LongSparseArray<PackageState>> uids = pkgMap.valueAt(ip);
+ for (int iu = 0, uidsSize = uids.size(); iu < uidsSize; iu++) {
+ final int uid = uids.keyAt(iu);
+ final LongSparseArray<PackageState> versions = uids.valueAt(iu);
+ for (int iv = 0, versionsSize = versions.size(); iv < versionsSize; iv++) {
+ final PackageState state = versions.valueAt(iv);
+ for (int iasc = 0, ascSize = state.mAssociations.size();
+ iasc < ascSize;
+ iasc++) {
+ final String serviceName = state.mAssociations.keyAt(iasc);
+ final AssociationState asc = state.mAssociations.valueAt(iasc);
+ for (int is = 0, sourcesSize = asc.mSources.size();
+ is < sourcesSize;
+ is++) {
+ final SourceState src = asc.mSources.valueAt(is);
+ final SourceKey key = asc.mSources.keyAt(is);
+ consumer.accept(asc, uid, serviceName, key, src);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /** Dumps the stats of all processes to statsEventOutput. */
+ public void dumpProcessState(int atomTag, StatsEventOutput statsEventOutput) {
+ forEachProcess(
+ (processState) -> {
+ if (processState.isMultiPackage()
+ && processState.getCommonProcess() != processState) {
+ return;
+ }
+ processState.dumpStateDurationToStatsd(atomTag, this, statsEventOutput);
+ });
+ }
+
+ /** Dumps all process association data to statsEventOutput. */
+ public void dumpProcessAssociation(int atomTag, StatsEventOutput statsEventOutput) {
+ forEachAssociation(
+ (asc, serviceUid, serviceName, key, src) -> {
+ statsEventOutput.write(
+ atomTag,
+ key.mUid,
+ key.mProcess,
+ serviceUid,
+ serviceName,
+ (int) TimeUnit.MILLISECONDS.toSeconds(mTimePeriodStartUptime),
+ (int) TimeUnit.MILLISECONDS.toSeconds(mTimePeriodEndUptime),
+ (int)
+ TimeUnit.MILLISECONDS.toSeconds(
+ mTimePeriodEndUptime - mTimePeriodStartUptime),
+ (int) TimeUnit.MILLISECONDS.toSeconds(src.mDuration),
+ src.mActiveCount,
+ asc.getProcessName());
+ });
+ }
+
private void dumpProtoPreamble(ProtoOutputStream proto) {
proto.write(ProcessStatsSectionProto.START_REALTIME_MS, mTimePeriodStartRealtime);
proto.write(ProcessStatsSectionProto.END_REALTIME_MS,
diff --git a/core/java/com/android/internal/app/procstats/StatsEventOutput.java b/core/java/com/android/internal/app/procstats/StatsEventOutput.java
new file mode 100644
index 0000000..b2e4054
--- /dev/null
+++ b/core/java/com/android/internal/app/procstats/StatsEventOutput.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app.procstats;
+
+import android.util.StatsEvent;
+
+import com.android.internal.util.FrameworkStatsLog;
+
+import java.util.List;
+
+/**
+ * A simple wrapper of FrameworkStatsLog.buildStatsEvent. This allows unit tests to mock out the
+ * dependency.
+ */
+public class StatsEventOutput {
+
+ List<StatsEvent> mOutput;
+
+ public StatsEventOutput(List<StatsEvent> output) {
+ mOutput = output;
+ }
+
+ /** Writes the data to the output. */
+ public void write(
+ int atomTag,
+ int uid,
+ String processName,
+ int measurementStartUptimeSecs,
+ int measurementEndUptimeSecs,
+ int measurementDurationUptimeSecs,
+ int topSeconds,
+ int fgsSeconds,
+ int boundTopSeconds,
+ int boundFgsSeconds,
+ int importantForegroundSeconds,
+ int cachedSeconds,
+ int frozenSeconds,
+ int otherSeconds) {
+ mOutput.add(
+ FrameworkStatsLog.buildStatsEvent(
+ atomTag,
+ uid,
+ processName,
+ measurementStartUptimeSecs,
+ measurementEndUptimeSecs,
+ measurementDurationUptimeSecs,
+ topSeconds,
+ fgsSeconds,
+ boundTopSeconds,
+ boundFgsSeconds,
+ importantForegroundSeconds,
+ cachedSeconds,
+ frozenSeconds,
+ otherSeconds));
+ }
+
+ /** Writes the data to the output. */
+ public void write(
+ int atomTag,
+ int clientUid,
+ String processName,
+ int serviceUid,
+ String serviceName,
+ int measurementStartUptimeSecs,
+ int measurementEndUptimeSecs,
+ int measurementDurationUptimeSecs,
+ int activeDurationUptimeSecs,
+ int activeCount,
+ String serviceProcessName) {
+ mOutput.add(
+ FrameworkStatsLog.buildStatsEvent(
+ atomTag,
+ clientUid,
+ processName,
+ serviceUid,
+ serviceName,
+ measurementStartUptimeSecs,
+ measurementEndUptimeSecs,
+ measurementDurationUptimeSecs,
+ activeDurationUptimeSecs,
+ activeCount,
+ serviceProcessName));
+ }
+}
diff --git a/core/java/com/android/internal/inputmethod/EditableInputConnection.java b/core/java/com/android/internal/inputmethod/EditableInputConnection.java
index 8690e8d..3020d77 100644
--- a/core/java/com/android/internal/inputmethod/EditableInputConnection.java
+++ b/core/java/com/android/internal/inputmethod/EditableInputConnection.java
@@ -44,6 +44,7 @@
import android.view.inputmethod.HandwritingGesture;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InsertGesture;
+import android.view.inputmethod.InsertModeGesture;
import android.view.inputmethod.JoinOrSplitGesture;
import android.view.inputmethod.PreviewableHandwritingGesture;
import android.view.inputmethod.RemoveSpaceGesture;
@@ -314,6 +315,8 @@
result = mTextView.performHandwritingRemoveSpaceGesture((RemoveSpaceGesture) gesture);
} else if (gesture instanceof JoinOrSplitGesture) {
result = mTextView.performHandwritingJoinOrSplitGesture((JoinOrSplitGesture) gesture);
+ } else if (gesture instanceof InsertModeGesture) {
+ result = mTextView.performHandwritingInsertModeGesture((InsertModeGesture) gesture);
} else {
result = HANDWRITING_GESTURE_RESULT_UNSUPPORTED;
}
diff --git a/core/proto/android/nfc/nfc_service.proto b/core/proto/android/nfc/nfc_service.proto
index 1dcd5cc..2df1d5d 100644
--- a/core/proto/android/nfc/nfc_service.proto
+++ b/core/proto/android/nfc/nfc_service.proto
@@ -60,7 +60,7 @@
optional bool secure_nfc_capable = 13;
optional bool vr_mode_enabled = 14;
optional DiscoveryParamsProto discovery_params = 15;
- reserved 16;
+ optional P2pLinkManagerProto p2p_link_manager = 16;
optional com.android.nfc.cardemulation.CardEmulationManagerProto card_emulation_manager = 17;
optional NfcDispatcherProto nfc_dispatcher = 18;
optional string native_crash_logs = 19 [(.android.privacy).dest = DEST_EXPLICIT];
@@ -77,6 +77,38 @@
optional bool enable_p2p = 5;
}
+// Debugging information for com.android.nfc.P2pLinkManager
+message P2pLinkManagerProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ enum LinkState {
+ LINK_STATE_UNKNOWN = 0;
+ LINK_STATE_DOWN = 1;
+ LINK_STATE_DEBOUNCE = 2;
+ LINK_STATE_UP = 3;
+ }
+
+ enum SendState {
+ SEND_STATE_UNKNOWN = 0;
+ SEND_STATE_NOTHING_TO_SEND = 1;
+ SEND_STATE_NEED_CONFIRMATION = 2;
+ SEND_STATE_SENDING = 3;
+ SEND_STATE_COMPLETE = 4;
+ SEND_STATE_CANCELED = 5;
+ }
+
+ optional int32 default_miu = 1;
+ optional int32 default_rw_size = 2;
+ optional LinkState link_state = 3;
+ optional SendState send_state = 4;
+ optional int32 send_flags = 5;
+ optional bool send_enabled = 6;
+ optional bool receive_enabled = 7;
+ optional string callback_ndef = 8 [(.android.privacy).dest = DEST_EXPLICIT];
+ optional .android.nfc.NdefMessageProto message_to_send = 9;
+ repeated string uris_to_send = 10 [(.android.privacy).dest = DEST_EXPLICIT];
+}
+
// Debugging information for com.android.nfc.NfcDispatcher
message NfcDispatcherProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 4b0db3b..be22095 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2003,6 +2003,9 @@
STREAM_MUSIC as if it's on TV platform. -->
<bool name="config_single_volume">false</bool>
+ <!-- Volume policy -->
+ <bool name="config_volume_down_to_enter_silent">false</bool>
+
<!-- The number of volume steps for the notification stream -->
<integer name="config_audio_notif_vol_steps">7</integer>
@@ -5414,6 +5417,9 @@
<!-- Whether using split screen aspect ratio as a default aspect ratio for unresizable apps. -->
<bool name="config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled">false</bool>
+ <!-- Whether using display aspect ratio as a default aspect ratio for all letterboxed apps. -->
+ <bool name="config_letterboxIsDisplayAspectRatioForFixedOrientationLetterboxEnabled">false</bool>
+
<!-- Whether the specific behaviour for translucent activities letterboxing is enabled.
TODO(b/255532890) Enable when ignoreOrientationRequest is set -->
<bool name="config_letterboxIsEnabledForTranslucentActivities">false</bool>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 399d145..2715c94 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -328,6 +328,7 @@
<java-symbol type="bool" name="config_use_strict_phone_number_comparation_for_kazakhstan" />
<java-symbol type="integer" name="config_phonenumber_compare_min_match" />
<java-symbol type="bool" name="config_single_volume" />
+ <java-symbol type="bool" name="config_volume_down_to_enter_silent" />
<java-symbol type="bool" name="config_voice_capable" />
<java-symbol type="bool" name="config_requireCallCapableAccountForHandle" />
<java-symbol type="bool" name="config_user_notification_of_restrictied_mobile_access" />
@@ -4442,6 +4443,7 @@
<java-symbol type="bool" name="config_letterboxIsEducationEnabled" />
<java-symbol type="dimen" name="config_letterboxDefaultMinAspectRatioForUnresizableApps" />
<java-symbol type="bool" name="config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled" />
+ <java-symbol type="bool" name="config_letterboxIsDisplayAspectRatioForFixedOrientationLetterboxEnabled" />
<java-symbol type="bool" name="config_isCompatFakeFocusEnabled" />
<java-symbol type="bool" name="config_isWindowManagerCameraCompatTreatmentEnabled" />
<java-symbol type="bool" name="config_isCameraCompatControlForStretchedIssuesEnabled" />
diff --git a/core/tests/coretests/src/com/android/internal/app/procstats/ProcessStatsTest.java b/core/tests/coretests/src/com/android/internal/app/procstats/ProcessStatsTest.java
new file mode 100644
index 0000000..9b9a84b
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/app/procstats/ProcessStatsTest.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app.procstats;
+
+import static com.android.internal.app.procstats.ProcessStats.STATE_TOP;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.util.FrameworkStatsLog;
+
+import junit.framework.TestCase;
+
+import org.junit.Before;
+import org.mockito.Mock;
+
+import java.util.concurrent.TimeUnit;
+
+/** Provides test cases for ProcessStats. */
+public class ProcessStatsTest extends TestCase {
+
+ private static final String APP_1_PACKAGE_NAME = "com.android.testapp";
+ private static final int APP_1_UID = 5001;
+ private static final long APP_1_VERSION = 10;
+ private static final String APP_1_PROCESS_NAME = "com.android.testapp.p";
+ private static final String APP_1_SERVICE_NAME = "com.android.testapp.service";
+
+ private static final String APP_2_PACKAGE_NAME = "com.android.testapp2";
+ private static final int APP_2_UID = 5002;
+ private static final long APP_2_VERSION = 30;
+ private static final String APP_2_PROCESS_NAME = "com.android.testapp2.p";
+
+ private static final long NOW_MS = 123000;
+ private static final int DURATION_SECS = 6;
+
+ @Mock StatsEventOutput mStatsEventOutput;
+
+ @Before
+ public void setUp() {
+ initMocks(this);
+ }
+
+ @SmallTest
+ public void testDumpProcessState() throws Exception {
+ ProcessStats processStats = new ProcessStats();
+ processStats.getProcessStateLocked(
+ APP_1_PACKAGE_NAME, APP_1_UID, APP_1_VERSION, APP_1_PROCESS_NAME);
+ processStats.getProcessStateLocked(
+ APP_2_PACKAGE_NAME, APP_2_UID, APP_2_VERSION, APP_2_PROCESS_NAME);
+ processStats.dumpProcessState(FrameworkStatsLog.PROCESS_STATE, mStatsEventOutput);
+ verify(mStatsEventOutput)
+ .write(
+ eq(FrameworkStatsLog.PROCESS_STATE),
+ eq(APP_1_UID),
+ eq(APP_1_PROCESS_NAME),
+ anyInt(),
+ anyInt(),
+ eq(0),
+ eq(0),
+ eq(0),
+ eq(0),
+ eq(0),
+ eq(0),
+ eq(0),
+ eq(0),
+ eq(0));
+ verify(mStatsEventOutput)
+ .write(
+ eq(FrameworkStatsLog.PROCESS_STATE),
+ eq(APP_2_UID),
+ eq(APP_2_PROCESS_NAME),
+ anyInt(),
+ anyInt(),
+ eq(0),
+ eq(0),
+ eq(0),
+ eq(0),
+ eq(0),
+ eq(0),
+ eq(0),
+ eq(0),
+ eq(0));
+ }
+
+ @SmallTest
+ public void testNonZeroProcessStateDuration() throws Exception {
+ ProcessStats processStats = new ProcessStats();
+ ProcessState processState =
+ processStats.getProcessStateLocked(
+ APP_1_PACKAGE_NAME, APP_1_UID, APP_1_VERSION, APP_1_PROCESS_NAME);
+ processState.setCombinedState(STATE_TOP, NOW_MS);
+ processState.commitStateTime(NOW_MS + TimeUnit.SECONDS.toMillis(DURATION_SECS));
+ processStats.dumpProcessState(FrameworkStatsLog.PROCESS_STATE, mStatsEventOutput);
+ verify(mStatsEventOutput)
+ .write(
+ eq(FrameworkStatsLog.PROCESS_STATE),
+ eq(APP_1_UID),
+ eq(APP_1_PROCESS_NAME),
+ anyInt(),
+ anyInt(),
+ eq(0),
+ eq(DURATION_SECS),
+ eq(0),
+ eq(0),
+ eq(0),
+ eq(0),
+ eq(0),
+ eq(0),
+ eq(0));
+ }
+
+ @SmallTest
+ public void testDumpProcessAssociation() throws Exception {
+ ProcessStats processStats = new ProcessStats();
+ AssociationState associationState =
+ processStats.getAssociationStateLocked(
+ APP_1_PACKAGE_NAME,
+ APP_1_UID,
+ APP_1_VERSION,
+ APP_1_PROCESS_NAME,
+ APP_1_SERVICE_NAME);
+ AssociationState.SourceState sourceState =
+ associationState.startSource(APP_2_UID, APP_2_PROCESS_NAME, APP_2_PACKAGE_NAME);
+ sourceState.stop();
+ processStats.dumpProcessAssociation(
+ FrameworkStatsLog.PROCESS_ASSOCIATION, mStatsEventOutput);
+ verify(mStatsEventOutput)
+ .write(
+ eq(FrameworkStatsLog.PROCESS_ASSOCIATION),
+ eq(APP_2_UID),
+ eq(APP_2_PROCESS_NAME),
+ eq(APP_1_UID),
+ eq(APP_1_SERVICE_NAME),
+ anyInt(),
+ anyInt(),
+ eq(0),
+ eq(0),
+ eq(0),
+ eq(APP_1_PROCESS_NAME));
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 618c446..36ce0a6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -445,7 +445,6 @@
backgroundColorForTransition = getTransitionBackgroundColorIfSet(info, change, a,
backgroundColorForTransition);
- boolean delayedEdgeExtension = false;
if (!isTask && a.hasExtension()) {
if (!Transitions.isOpeningType(change.getMode())) {
// Can screenshot now (before startTransaction is applied)
@@ -455,7 +454,6 @@
// may not be visible or ready yet.
postStartTransactionCallbacks
.add(t -> edgeExtendWindow(change, a, t, finishTransaction));
- delayedEdgeExtension = true;
}
}
@@ -464,19 +462,9 @@
: new Rect(change.getEndAbsBounds());
clipRect.offsetTo(0, 0);
- if (delayedEdgeExtension) {
- // If the edge extension needs to happen after the startTransition has been
- // applied, then we want to only start the animation after the edge extension
- // postStartTransaction callback has been run
- postStartTransactionCallbacks.add(t ->
- startSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish,
- mTransactionPool, mMainExecutor, mAnimExecutor,
- change.getEndRelOffset(), cornerRadius, clipRect));
- } else {
- startSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish,
- mTransactionPool, mMainExecutor, mAnimExecutor,
- change.getEndRelOffset(), cornerRadius, clipRect);
- }
+ buildSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish,
+ mTransactionPool, mMainExecutor, change.getEndRelOffset(), cornerRadius,
+ clipRect);
if (info.getAnimationOptions() != null) {
attachThumbnail(animations, onAnimFinish, change, info.getAnimationOptions(),
@@ -490,19 +478,25 @@
startTransaction, finishTransaction);
}
- // postStartTransactionCallbacks require that the start transaction is already
- // applied to run otherwise they may result in flickers and UI inconsistencies.
- boolean waitForStartTransactionApply = postStartTransactionCallbacks.size() > 0;
- startTransaction.apply(waitForStartTransactionApply);
-
- // Run tasks that require startTransaction to already be applied
- for (Consumer<SurfaceControl.Transaction> postStartTransactionCallback :
- postStartTransactionCallbacks) {
- final SurfaceControl.Transaction t = mTransactionPool.acquire();
- postStartTransactionCallback.accept(t);
- t.apply();
- mTransactionPool.release(t);
+ if (postStartTransactionCallbacks.size() > 0) {
+ // postStartTransactionCallbacks require that the start transaction is already
+ // applied to run otherwise they may result in flickers and UI inconsistencies.
+ startTransaction.apply(true /* sync */);
+ // startTransaction is empty now, so fill it with the edge-extension setup
+ for (Consumer<SurfaceControl.Transaction> postStartTransactionCallback :
+ postStartTransactionCallbacks) {
+ postStartTransactionCallback.accept(startTransaction);
+ }
}
+ startTransaction.apply();
+
+ // now start animations. they are started on another thread, so we have to post them
+ // *after* applying the startTransaction
+ mAnimExecutor.execute(() -> {
+ for (int i = 0; i < animations.size(); ++i) {
+ animations.get(i).start();
+ }
+ });
mRotator.cleanUp(finishTransaction);
TransitionMetrics.getInstance().reportAnimationStart(transition);
@@ -539,8 +533,8 @@
animations.removeAll(animGroupStore);
onAnimFinish.run();
};
- anim.startAnimation(animGroup, finishCallback, mTransitionAnimationScaleSetting,
- mMainExecutor, mAnimExecutor);
+ anim.buildAnimation(animGroup, finishCallback, mTransitionAnimationScaleSetting,
+ mMainExecutor);
for (int i = animGroup.size() - 1; i >= 0; i--) {
final Animator animator = animGroup.get(i);
animGroupStore.add(animator);
@@ -633,11 +627,12 @@
return a;
}
- static void startSurfaceAnimation(@NonNull ArrayList<Animator> animations,
+ /** Builds an animator for the surface and adds it to the `animations` list. */
+ static void buildSurfaceAnimation(@NonNull ArrayList<Animator> animations,
@NonNull Animation anim, @NonNull SurfaceControl leash,
@NonNull Runnable finishCallback, @NonNull TransactionPool pool,
- @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor,
- @Nullable Point position, float cornerRadius, @Nullable Rect clipRect) {
+ @NonNull ShellExecutor mainExecutor, @Nullable Point position, float cornerRadius,
+ @Nullable Rect clipRect) {
final SurfaceControl.Transaction transaction = pool.acquire();
final ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
final Transformation transformation = new Transformation();
@@ -691,7 +686,6 @@
}
});
animations.add(va);
- animExecutor.execute(va::start);
}
private void attachThumbnail(@NonNull ArrayList<Animator> animations,
@@ -745,9 +739,8 @@
};
a.restrictDuration(MAX_ANIMATION_DURATION);
a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
- startSurfaceAnimation(animations, a, wt.getSurface(), finisher, mTransactionPool,
- mMainExecutor, mAnimExecutor, change.getEndRelOffset(),
- cornerRadius, change.getEndAbsBounds());
+ buildSurfaceAnimation(animations, a, wt.getSurface(), finisher, mTransactionPool,
+ mMainExecutor, change.getEndRelOffset(), cornerRadius, change.getEndAbsBounds());
}
private void attachThumbnailAnimation(@NonNull ArrayList<Animator> animations,
@@ -770,9 +763,8 @@
};
a.restrictDuration(MAX_ANIMATION_DURATION);
a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
- startSurfaceAnimation(animations, a, wt.getSurface(), finisher, mTransactionPool,
- mMainExecutor, mAnimExecutor, change.getEndRelOffset(),
- cornerRadius, change.getEndAbsBounds());
+ buildSurfaceAnimation(animations, a, wt.getSurface(), finisher, mTransactionPool,
+ mMainExecutor, change.getEndRelOffset(), cornerRadius, change.getEndAbsBounds());
}
private static int getWallpaperTransitType(TransitionInfo info) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
index 665267f..e643170 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
@@ -21,7 +21,7 @@
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT;
import static android.view.WindowManagerPolicyConstants.SCREEN_FREEZE_LAYER_BASE;
-import static com.android.wm.shell.transition.DefaultTransitionHandler.startSurfaceAnimation;
+import static com.android.wm.shell.transition.DefaultTransitionHandler.buildSurfaceAnimation;
import static com.android.wm.shell.transition.Transitions.TAG;
import android.animation.Animator;
@@ -244,11 +244,11 @@
}
/**
- * Returns true if animating.
+ * Returns true if any animations were added to `animations`.
*/
- public boolean startAnimation(@NonNull ArrayList<Animator> animations,
+ boolean buildAnimation(@NonNull ArrayList<Animator> animations,
@NonNull Runnable finishCallback, float animationScale,
- @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) {
+ @NonNull ShellExecutor mainExecutor) {
if (mScreenshotLayer == null) {
// Can't do animation.
return false;
@@ -311,13 +311,11 @@
mRotateAlphaAnimation.restrictDuration(MAX_ANIMATION_DURATION);
mRotateAlphaAnimation.scaleCurrentDuration(animationScale);
- startScreenshotAlphaAnimation(animations, finishCallback, mainExecutor,
- animExecutor);
- startDisplayRotation(animations, finishCallback, mainExecutor, animExecutor);
+ buildScreenshotAlphaAnimation(animations, finishCallback, mainExecutor);
+ startDisplayRotation(animations, finishCallback, mainExecutor);
} else {
- startDisplayRotation(animations, finishCallback, mainExecutor, animExecutor);
- startScreenshotRotationAnimation(animations, finishCallback, mainExecutor,
- animExecutor);
+ startDisplayRotation(animations, finishCallback, mainExecutor);
+ startScreenshotRotationAnimation(animations, finishCallback, mainExecutor);
//startColorAnimation(mTransaction, animationScale);
}
@@ -325,27 +323,24 @@
}
private void startDisplayRotation(@NonNull ArrayList<Animator> animations,
- @NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor,
- @NonNull ShellExecutor animExecutor) {
- startSurfaceAnimation(animations, mRotateEnterAnimation, mSurfaceControl, finishCallback,
- mTransactionPool, mainExecutor, animExecutor, null /* position */,
- 0 /* cornerRadius */, null /* clipRect */);
+ @NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor) {
+ buildSurfaceAnimation(animations, mRotateEnterAnimation, mSurfaceControl, finishCallback,
+ mTransactionPool, mainExecutor, null /* position */, 0 /* cornerRadius */,
+ null /* clipRect */);
}
private void startScreenshotRotationAnimation(@NonNull ArrayList<Animator> animations,
- @NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor,
- @NonNull ShellExecutor animExecutor) {
- startSurfaceAnimation(animations, mRotateExitAnimation, mAnimLeash, finishCallback,
- mTransactionPool, mainExecutor, animExecutor, null /* position */,
- 0 /* cornerRadius */, null /* clipRect */);
+ @NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor) {
+ buildSurfaceAnimation(animations, mRotateExitAnimation, mAnimLeash, finishCallback,
+ mTransactionPool, mainExecutor, null /* position */, 0 /* cornerRadius */,
+ null /* clipRect */);
}
- private void startScreenshotAlphaAnimation(@NonNull ArrayList<Animator> animations,
- @NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor,
- @NonNull ShellExecutor animExecutor) {
- startSurfaceAnimation(animations, mRotateAlphaAnimation, mAnimLeash, finishCallback,
- mTransactionPool, mainExecutor, animExecutor, null /* position */,
- 0 /* cornerRadius */, null /* clipRect */);
+ private void buildScreenshotAlphaAnimation(@NonNull ArrayList<Animator> animations,
+ @NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor) {
+ buildSurfaceAnimation(animations, mRotateAlphaAnimation, mAnimLeash, finishCallback,
+ mTransactionPool, mainExecutor, null /* position */, 0 /* cornerRadius */,
+ null /* clipRect */);
}
private void startColorAnimation(float animationScale, @NonNull ShellExecutor animExecutor) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
index ff1d2990..d5bb901 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
@@ -28,9 +28,11 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -82,13 +84,14 @@
@Mock
SyncTransactionQueue mSyncQueue;
@Mock
- TaskViewTransitions mTaskViewTransitions;
+ Transitions mTransitions;
SurfaceSession mSession;
SurfaceControl mLeash;
Context mContext;
TaskView mTaskView;
+ TaskViewTransitions mTaskViewTransitions;
@Before
public void setUp() {
@@ -118,6 +121,10 @@
return null;
}).when(mSyncQueue).runInSync(any());
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ doReturn(true).when(mTransitions).isRegistered();
+ }
+ mTaskViewTransitions = spy(new TaskViewTransitions(mTransitions));
mTaskView = new TaskView(mContext, mOrganizer, mTaskViewTransitions, mSyncQueue);
mTaskView.setListener(mExecutor, mViewListener);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index 65e1ea8..0bb809d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -29,11 +29,13 @@
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RETURN_HOME;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -45,6 +47,8 @@
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.window.WindowContainerToken;
@@ -57,6 +61,7 @@
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
+import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
@@ -65,6 +70,8 @@
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.split.SplitLayout;
import com.android.wm.shell.splitscreen.SplitScreen.SplitScreenListener;
+import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
import org.junit.Before;
@@ -98,11 +105,7 @@
@Mock
private DisplayInsetsController mDisplayInsetsController;
@Mock
- private Transitions mTransitions;
- @Mock
private TransactionPool mTransactionPool;
- @Mock
- private ShellExecutor mMainExecutor;
private final Rect mBounds1 = new Rect(10, 20, 30, 40);
private final Rect mBounds2 = new Rect(5, 10, 15, 20);
@@ -112,11 +115,16 @@
private SurfaceControl mRootLeash;
private ActivityManager.RunningTaskInfo mRootTask;
private StageCoordinator mStageCoordinator;
+ private Transitions mTransitions;
+ private final TestShellExecutor mMainExecutor = new TestShellExecutor();
+ private final ShellExecutor mAnimExecutor = new TestShellExecutor();
+ private final Handler mMainHandler = new Handler(Looper.getMainLooper());
@Before
@UiThreadTest
public void setup() {
MockitoAnnotations.initMocks(this);
+ mTransitions = createTestTransitions();
mStageCoordinator = spy(new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
mTaskOrganizer, mMainStage, mSideStage, mDisplayController, mDisplayImeController,
mDisplayInsetsController, mSplitLayout, mTransitions, mTransactionPool,
@@ -329,7 +337,20 @@
mStageCoordinator.onFoldedStateChanged(true);
- verify(mStageCoordinator).onSplitScreenExit();
- verify(mMainStage).deactivate(any(WindowContainerTransaction.class), eq(false));
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ verify(mTaskOrganizer).startNewTransition(eq(TRANSIT_SPLIT_DISMISS), notNull());
+ } else {
+ verify(mStageCoordinator).onSplitScreenExit();
+ verify(mMainStage).deactivate(any(WindowContainerTransaction.class), eq(false));
+ }
+ }
+
+ private Transitions createTestTransitions() {
+ ShellInit shellInit = new ShellInit(mMainExecutor);
+ final Transitions t = new Transitions(mContext, shellInit, mock(ShellController.class),
+ mTaskOrganizer, mTransactionPool, mock(DisplayController.class), mMainExecutor,
+ mMainHandler, mAnimExecutor);
+ shellInit.init();
+ return t;
}
}
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index cb385d4..c5580b3 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -21,6 +21,7 @@
#include <hwui/Paint.h>
#include <experimental/type_traits>
+#include <log/log.h>
#include <utility>
#include "SkAndroidFrameworkUtils.h"
@@ -65,16 +66,24 @@
template <typename S, typename... Rest>
static void copy_v(void* dst, const S* src, int n, Rest&&... rest) {
- SkASSERTF(((uintptr_t)dst & (alignof(S) - 1)) == 0,
- "Expected %p to be aligned for at least %zu bytes.", dst, alignof(S));
- sk_careful_memcpy(dst, src, n * sizeof(S));
- copy_v(SkTAddOffset<void>(dst, n * sizeof(S)), std::forward<Rest>(rest)...);
+ LOG_FATAL_IF(((uintptr_t)dst & (alignof(S) - 1)) != 0,
+ "Expected %p to be aligned for at least %zu bytes.",
+ dst, alignof(S));
+ // If n is 0, there is nothing to copy into dst from src.
+ if (n > 0) {
+ memcpy(dst, src, n * sizeof(S));
+ dst = reinterpret_cast<void*>(
+ reinterpret_cast<uint8_t*>(dst) + n * sizeof(S));
+ }
+ // Repeat for the next items, if any
+ copy_v(dst, std::forward<Rest>(rest)...);
}
// Helper for getting back at arrays which have been copy_v'd together after an Op.
template <typename D, typename T>
static const D* pod(const T* op, size_t offset = 0) {
- return SkTAddOffset<const D>(op + 1, offset);
+ return reinterpret_cast<const D*>(
+ reinterpret_cast<const uint8_t*>(op + 1) + offset);
}
namespace {
@@ -612,7 +621,7 @@
template <typename T, typename... Args>
void* DisplayListData::push(size_t pod, Args&&... args) {
size_t skip = SkAlignPtr(sizeof(T) + pod);
- SkASSERT(skip < (1 << 24));
+ LOG_FATAL_IF(skip >= (1 << 24));
if (fUsed + skip > fReserved) {
static_assert(is_power_of_two(SKLITEDL_PAGE),
"This math needs updating for non-pow2.");
@@ -621,7 +630,7 @@
fBytes.realloc(fReserved);
LOG_ALWAYS_FATAL_IF(fBytes.get() == nullptr, "realloc(%zd) failed", fReserved);
}
- SkASSERT(fUsed + skip <= fReserved);
+ LOG_FATAL_IF((fUsed + skip) > fReserved);
auto op = (T*)(fBytes.get() + fUsed);
fUsed += skip;
new (op) T{std::forward<Args>(args)...};
@@ -751,7 +760,7 @@
int fs = lattice.fRectTypes ? (xs + 1) * (ys + 1) : 0;
size_t bytes = (xs + ys) * sizeof(int) + fs * sizeof(SkCanvas::Lattice::RectType) +
fs * sizeof(SkColor);
- SkASSERT(lattice.fBounds);
+ LOG_FATAL_IF(!lattice.fBounds);
void* pod = this->push<DrawImageLattice>(bytes, std::move(image), xs, ys, fs, *lattice.fBounds,
dst, filter, paint, palette);
copy_v(pod, lattice.fXDivs, xs, lattice.fYDivs, ys, lattice.fColors, fs, lattice.fRectTypes,
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index af8bd26..1e0c359 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -19,6 +19,8 @@
#include <SkAndroidFrameworkUtils.h>
#include <SkAnimatedImage.h>
#include <SkBitmap.h>
+#include <SkBlendMode.h>
+#include <SkCanvas.h>
#include <SkCanvasPriv.h>
#include <SkCanvasStateUtils.h>
#include <SkColorFilter.h>
@@ -35,7 +37,6 @@
#include <SkRect.h>
#include <SkRefCnt.h>
#include <SkShader.h>
-#include <SkTemplates.h>
#include <SkTextBlob.h>
#include <SkVertices.h>
@@ -45,13 +46,14 @@
#include "CanvasProperty.h"
#include "NinePatchUtils.h"
-#include "SkBlendMode.h"
#include "VectorDrawable.h"
#include "hwui/Bitmap.h"
#include "hwui/MinikinUtils.h"
#include "hwui/PaintFilter.h"
+#include <log/log.h>
#include "pipeline/skia/AnimatedDrawables.h"
#include "pipeline/skia/HolePunch.h"
+#include <ui/FatVector.h>
namespace android {
@@ -248,7 +250,7 @@
? static_cast<const SaveRec*>(&mSaveStack->back())
: nullptr;
int currentSaveCount = mCanvas->getSaveCount();
- SkASSERT(!rec || currentSaveCount >= rec->saveCount);
+ LOG_FATAL_IF(!(!rec || currentSaveCount >= rec->saveCount));
return (rec && rec->saveCount == currentSaveCount) ? rec : nullptr;
}
@@ -298,7 +300,7 @@
// Applies and optionally removes all clips >= index.
void SkiaCanvas::applyPersistentClips(size_t clipStartIndex) {
- SkASSERT(clipStartIndex <= mClipStack.size());
+ LOG_FATAL_IF(clipStartIndex > mClipStack.size());
const auto begin = mClipStack.cbegin() + clipStartIndex;
const auto end = mClipStack.cend();
@@ -646,7 +648,7 @@
texsPtr += 1;
y += dy;
}
- SkASSERT(texsPtr - texs == ptCount);
+ LOG_FATAL_IF((texsPtr - texs) != ptCount);
}
// cons up indices
@@ -669,14 +671,14 @@
// bump to the next row
index += 1;
}
- SkASSERT(indexPtr - indices == indexCount);
+ LOG_FATAL_IF((indexPtr - indices) != indexCount);
}
// double-check that we have legal indices
-#ifdef SK_DEBUG
+#if !defined(NDEBUG)
{
for (int i = 0; i < indexCount; i++) {
- SkASSERT((unsigned)indices[i] < (unsigned)ptCount);
+ LOG_FATAL_IF((unsigned)indices[i] >= (unsigned)ptCount);
}
}
#endif
@@ -718,10 +720,12 @@
numFlags = (lattice.fXCount + 1) * (lattice.fYCount + 1);
}
- SkAutoSTMalloc<25, SkCanvas::Lattice::RectType> flags(numFlags);
- SkAutoSTMalloc<25, SkColor> colors(numFlags);
+ // Most times, we do not have very many flags/colors, so the stack allocated part of
+ // FatVector will save us a heap allocation.
+ FatVector<SkCanvas::Lattice::RectType, 25> flags(numFlags);
+ FatVector<SkColor, 25> colors(numFlags);
if (numFlags > 0) {
- NinePatchUtils::SetLatticeFlags(&lattice, flags.get(), numFlags, chunk, colors.get());
+ NinePatchUtils::SetLatticeFlags(&lattice, flags.data(), numFlags, chunk, colors.data());
}
lattice.fBounds = nullptr;
diff --git a/libs/hwui/jni/Graphics.cpp b/libs/hwui/jni/Graphics.cpp
index ae2e974..f5cd793 100644
--- a/libs/hwui/jni/Graphics.cpp
+++ b/libs/hwui/jni/Graphics.cpp
@@ -8,7 +8,6 @@
#include <nativehelper/JNIHelp.h>
#include "GraphicsJNI.h"
-#include "include/private/SkTemplates.h" // SkTAddOffset
#include "SkBitmap.h"
#include "SkCanvas.h"
#include "SkColorSpace.h"
@@ -21,6 +20,7 @@
#include "SkTypes.h"
#include <cutils/ashmem.h>
#include <hwui/Canvas.h>
+#include <log/log.h>
using namespace android;
@@ -490,7 +490,7 @@
void GraphicsJNI::set_metrics(JNIEnv* env, jobject metrics, const SkFontMetrics& skmetrics) {
if (metrics == nullptr) return;
- SkASSERT(env->IsInstanceOf(metrics, gFontMetrics_class));
+ LOG_FATAL_IF(!env->IsInstanceOf(metrics, gFontMetrics_class));
env->SetFloatField(metrics, gFontMetrics_top, SkScalarToFloat(skmetrics.fTop));
env->SetFloatField(metrics, gFontMetrics_ascent, SkScalarToFloat(skmetrics.fAscent));
env->SetFloatField(metrics, gFontMetrics_descent, SkScalarToFloat(skmetrics.fDescent));
@@ -504,7 +504,7 @@
int leading = SkScalarRoundToInt(skmetrics.fLeading);
if (metrics) {
- SkASSERT(env->IsInstanceOf(metrics, gFontMetricsInt_class));
+ LOG_FATAL_IF(!env->IsInstanceOf(metrics, gFontMetricsInt_class));
env->SetIntField(metrics, gFontMetricsInt_top, SkScalarFloorToInt(skmetrics.fTop));
env->SetIntField(metrics, gFontMetricsInt_ascent, ascent);
env->SetIntField(metrics, gFontMetricsInt_descent, descent);
@@ -713,7 +713,9 @@
mSkiaBitmap->info().height());
for (int y = 0; y < rowsToCopy; y++) {
memcpy(dst, mSkiaBitmap->getAddr(0, y), bytesToCopy);
- dst = SkTAddOffset<void>(dst, dstRowBytes);
+ // Cast to bytes in order to apply the dstRowBytes offset correctly.
+ dst = reinterpret_cast<void*>(
+ reinterpret_cast<uint8_t*>(dst) + dstRowBytes);
}
recycledPixels->notifyPixelsChanged();
recycledPixels->unref();
diff --git a/libs/hwui/jni/Mesh.h b/libs/hwui/jni/Mesh.h
index 7a73f2d..61c2260 100644
--- a/libs/hwui/jni/Mesh.h
+++ b/libs/hwui/jni/Mesh.h
@@ -20,6 +20,7 @@
#include <SkMesh.h>
#include <jni.h>
+#include <log/log.h>
#include <utility>
#include "graphics_jni_helpers.h"
@@ -186,23 +187,24 @@
std::enable_if_t<std::is_trivially_copyable<T>::value, MeshUniform> operator=(
const T& val) {
if (!fVar) {
- SkDEBUGFAIL("Assigning to missing variable");
+ LOG_FATAL("Assigning to missing variable");
} else if (sizeof(val) != fVar->sizeInBytes()) {
- SkDEBUGFAIL("Incorrect value size");
+ LOG_FATAL("Incorrect value size");
} else {
- memcpy(SkTAddOffset<void>(fOwner->writableUniformData(), fVar->offset), &val,
- szeof(val));
+ void* dst = reinterpret_cast<void*>(
+ reinterpret_cast<uint8_t*>(fOwner->writableUniformData()) + fVar->offset);
+ memcpy(dst, &val, sizeof(val));
}
}
MeshUniform& operator=(const SkMatrix& val) {
if (!fVar) {
- SkDEBUGFAIL("Assigning to missing variable");
+ LOG_FATAL("Assigning to missing variable");
} else if (fVar->sizeInBytes() != 9 * sizeof(float)) {
- SkDEBUGFAIL("Incorrect value size");
+ LOG_FATAL("Incorrect value size");
} else {
- float* data =
- SkTAddOffset<float>(fOwner->writableUniformData(), (ptrdiff_t)fVar->offset);
+ float* data = reinterpret_cast<float*>(
+ reinterpret_cast<uint8_t*>(fOwner->writableUniformData()) + fVar->offset);
data[0] = val.get(0);
data[1] = val.get(3);
data[2] = val.get(6);
@@ -220,14 +222,15 @@
bool set(const T val[], const int count) {
static_assert(std::is_trivially_copyable<T>::value, "Value must be trivial copyable");
if (!fVar) {
- SkDEBUGFAIL("Assigning to missing variable");
+ LOG_FATAL("Assigning to missing variable");
return false;
} else if (sizeof(T) * count != fVar->sizeInBytes()) {
- SkDEBUGFAIL("Incorrect value size");
+ LOG_FATAL("Incorrect value size");
return false;
} else {
- memcpy(SkTAddOffset<void>(fOwner->writableUniformData(), fVar->offset), val,
- sizeof(T) * count);
+ void* dst = reinterpret_cast<void*>(
+ reinterpret_cast<uint8_t*>(fOwner->writableUniformData()) + fVar->offset);
+ memcpy(dst, val, sizeof(T) * count);
}
return true;
}
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index db449d6..08f0291 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -16,7 +16,6 @@
#include "SkiaRecordingCanvas.h"
#include "hwui/Paint.h"
-#include <include/private/SkTemplates.h> // SkAutoSTMalloc
#include <SkBlendMode.h>
#include <SkData.h>
#include <SkDrawable.h>
@@ -43,6 +42,8 @@
#include "pipeline/skia/VkFunctorDrawable.h"
#include "pipeline/skia/VkInteropFunctorDrawable.h"
#endif
+#include <log/log.h>
+#include <ui/FatVector.h>
namespace android {
namespace uirenderer {
@@ -55,7 +56,7 @@
void SkiaRecordingCanvas::initDisplayList(uirenderer::RenderNode* renderNode, int width,
int height) {
mCurrentBarrier = nullptr;
- SkASSERT(mDisplayList.get() == nullptr);
+ LOG_FATAL_IF(mDisplayList.get() != nullptr);
if (renderNode) {
mDisplayList = renderNode->detachAvailableList();
@@ -285,10 +286,12 @@
numFlags = (lattice.fXCount + 1) * (lattice.fYCount + 1);
}
- SkAutoSTMalloc<25, SkCanvas::Lattice::RectType> flags(numFlags);
- SkAutoSTMalloc<25, SkColor> colors(numFlags);
+ // Most times, we do not have very many flags/colors, so the stack allocated part of
+ // FatVector will save us a heap allocation.
+ FatVector<SkCanvas::Lattice::RectType, 25> flags(numFlags);
+ FatVector<SkColor, 25> colors(numFlags);
if (numFlags > 0) {
- NinePatchUtils::SetLatticeFlags(&lattice, flags.get(), numFlags, chunk, colors.get());
+ NinePatchUtils::SetLatticeFlags(&lattice, flags.data(), numFlags, chunk, colors.data());
}
lattice.fBounds = nullptr;
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 3fbada7..813929e 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -6442,6 +6442,20 @@
}
/**
+ * Returns the registered volume controller interface.
+ *
+ * @hide
+ */
+ @Nullable
+ public IVolumeController getVolumeController() {
+ try {
+ return getService().getVolumeController();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Notify audio manager about volume controller visibility changes.
* Currently limited to SystemUI.
*
@@ -6505,6 +6519,106 @@
/**
* @hide
+ * @return the RS2 value used for momentary exposure warnings
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
+ public float getRs2Value() {
+ try {
+ return getService().getRs2Value();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ * Sets the RS2 value used for momentary exposure warnings
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
+ public void setRs2Value(float rs2Value) {
+ try {
+ getService().setRs2Value(rs2Value);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ * @return the current computed sound dose value
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
+ public float getCsd() {
+ try {
+ return getService().getCsd();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ * Sets the computed sound dose value to {@code csd}
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
+ public void setCsd(float csd) {
+ try {
+ getService().setCsd(csd);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ * Forces the computation of MEL values (used for CSD) on framework level. This will have the
+ * result of ignoring the MEL values computed on HAL level. Should only be used in testing
+ * since this can affect the certification of a device with EN50332-3 regulation.
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
+ public void forceUseFrameworkMel(boolean useFrameworkMel) {
+ try {
+ getService().forceUseFrameworkMel(useFrameworkMel);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ * Forces the computation of CSD on all output devices.
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
+ public void forceComputeCsdOnAllDevices(boolean computeCsdOnAllDevices) {
+ try {
+ getService().forceComputeCsdOnAllDevices(computeCsdOnAllDevices);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ * Returns whether CSD is enabled on this device.
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
+ public boolean isCsdEnabled() {
+ try {
+ return getService().isCsdEnabled();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
* Sound dose warning at every 100% of dose during integration window
*/
public static final int CSD_WARNING_DOSE_REACHED_1X = 1;
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index c06352c..5ba7891 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -260,6 +260,8 @@
void setVolumeController(in IVolumeController controller);
+ @nullable IVolumeController getVolumeController();
+
void notifyVolumeControllerVisible(in IVolumeController controller, boolean visible);
boolean isStreamAffectedByRingerMode(int streamType);
@@ -270,6 +272,27 @@
void lowerVolumeToRs1(String callingPackage);
+ @EnforcePermission("MODIFY_AUDIO_SYSTEM_SETTINGS")
+ float getRs2Value();
+
+ @EnforcePermission("MODIFY_AUDIO_SYSTEM_SETTINGS")
+ oneway void setRs2Value(float rs2Value);
+
+ @EnforcePermission("MODIFY_AUDIO_SYSTEM_SETTINGS")
+ float getCsd();
+
+ @EnforcePermission("MODIFY_AUDIO_SYSTEM_SETTINGS")
+ oneway void setCsd(float csd);
+
+ @EnforcePermission("MODIFY_AUDIO_SYSTEM_SETTINGS")
+ oneway void forceUseFrameworkMel(boolean useFrameworkMel);
+
+ @EnforcePermission("MODIFY_AUDIO_SYSTEM_SETTINGS")
+ oneway void forceComputeCsdOnAllDevices(boolean computeCsdOnAllDevices);
+
+ @EnforcePermission("MODIFY_AUDIO_SYSTEM_SETTINGS")
+ boolean isCsdEnabled();
+
int setHdmiSystemAudioSupported(boolean on);
boolean isHdmiSystemAudioSupported();
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
index 03d7de5..1e1c833 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
@@ -477,7 +477,8 @@
Icons.Outlined.NewReleases,
contentDescription = null,
modifier = Modifier.align(alignment = Alignment.CenterHorizontally)
- .padding(all = 24.dp)
+ .padding(all = 24.dp),
+ tint = LocalAndroidColorScheme.current.colorAccentPrimaryVariant,
)
TextOnSurface(
text = stringResource(
@@ -642,7 +643,7 @@
Icon(
painter = painterResource(R.drawable.ic_other_devices),
contentDescription = null,
- tint = Color.Unspecified,
+ tint = LocalAndroidColorScheme.current.colorAccentPrimaryVariant,
modifier = Modifier.align(alignment = Alignment.CenterHorizontally)
.padding(all = 24.dp).size(32.dp)
)
@@ -972,7 +973,8 @@
Icon(
Icons.Filled.Add,
contentDescription = null,
- modifier = Modifier.padding(start = 16.dp)
+ modifier = Modifier.padding(start = 16.dp),
+ tint = LocalAndroidColorScheme.current.colorAccentPrimaryVariant,
)
},
label = {
@@ -1006,8 +1008,8 @@
Icon(
painter = painterResource(R.drawable.ic_other_devices),
contentDescription = null,
- tint = Color.Unspecified,
- modifier = Modifier.padding(start = 18.dp)
+ tint = LocalAndroidColorScheme.current.colorAccentPrimaryVariant,
+ modifier = Modifier.padding(start = 10.dp)
)
},
label = {
@@ -1015,7 +1017,7 @@
TextOnSurfaceVariant(
text = stringResource(R.string.another_device),
style = MaterialTheme.typography.titleLarge,
- modifier = Modifier.padding(start = 16.dp, top = 18.dp, bottom = 18.dp)
+ modifier = Modifier.padding(start = 10.dp, top = 18.dp, bottom = 18.dp)
.align(alignment = Alignment.CenterHorizontally),
)
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
index 1cc1ac5..420b4d5 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
@@ -278,7 +278,7 @@
ContainerCard() {
Column() {
TopAppBar(
- colors = TopAppBarDefaults.smallTopAppBarColors(
+ colors = TopAppBarDefaults.topAppBarColors(
containerColor = Color.Transparent,
),
title = {
@@ -415,8 +415,8 @@
Icon(
painter = painterResource(R.drawable.ic_other_devices),
contentDescription = null,
- tint = Color.Unspecified,
- modifier = Modifier.padding(start = 16.dp)
+ tint = LocalAndroidColorScheme.current.colorAccentPrimaryVariant,
+ modifier = Modifier.padding(start = 10.dp)
)
},
label = {
@@ -424,7 +424,7 @@
text = stringResource(
R.string.get_dialog_option_headline_use_a_different_device),
style = MaterialTheme.typography.titleLarge,
- modifier = Modifier.padding(start = 16.dp, top = 18.dp, bottom = 18.dp)
+ modifier = Modifier.padding(start = 10.dp, top = 18.dp, bottom = 18.dp)
.align(alignment = Alignment.CenterHorizontally)
)
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavControllerWrapper.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavControllerWrapper.kt
index eb2bffe..1aa2079 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavControllerWrapper.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavControllerWrapper.kt
@@ -25,7 +25,7 @@
import androidx.navigation.NavHostController
interface NavControllerWrapper {
- fun navigate(route: String)
+ fun navigate(route: String, popUpCurrent: Boolean = false)
fun navigateBack()
val highlightEntryId: String?
@@ -48,17 +48,17 @@
val LocalNavController = compositionLocalOf<NavControllerWrapper> {
object : NavControllerWrapper {
- override fun navigate(route: String) {}
+ override fun navigate(route: String, popUpCurrent: Boolean) {}
override fun navigateBack() {}
}
}
@Composable
-fun navigator(route: String?): () -> Unit {
+fun navigator(route: String?, popUpCurrent: Boolean = false): () -> Unit {
if (route == null) return {}
val navController = LocalNavController.current
- return { navController.navigate(route) }
+ return { navController.navigate(route, popUpCurrent) }
}
internal class NavControllerWrapperImpl(
@@ -68,8 +68,16 @@
var highlightId: String? = null
var sessionName: String? = null
- override fun navigate(route: String) {
- navController.navigate(route)
+ override fun navigate(route: String, popUpCurrent: Boolean) {
+ navController.navigate(route) {
+ if (popUpCurrent) {
+ navController.currentDestination?.let { currentDestination ->
+ popUpTo(currentDestination.id) {
+ inclusive = true
+ }
+ }
+ }
+ }
}
override fun navigateBack() {
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/compose/NavControllerWrapperTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/compose/NavControllerWrapperTest.kt
new file mode 100644
index 0000000..118585e
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/compose/NavControllerWrapperTest.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.framework.compose
+
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.hasText
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.navigation.compose.NavHost
+import androidx.navigation.compose.composable
+import androidx.navigation.compose.rememberNavController
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.testutils.waitUntilExists
+import kotlinx.coroutines.delay
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class NavControllerWrapperTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ @Test
+ fun navigate_canNavigate() {
+ composeTestRule.setContent {
+ TestNavHost {
+ LocalNavController.current.navigate(ROUTE_B)
+ }
+ }
+
+ composeTestRule.onNodeWithText(ROUTE_B).assertIsDisplayed()
+ }
+
+ @Test
+ fun navigate_canNavigateBack() {
+ composeTestRule.setContent {
+ TestNavHost {
+ val navController = LocalNavController.current
+ LaunchedEffect(Unit) {
+ navController.navigate(ROUTE_B)
+ delay(100)
+ navController.navigateBack()
+ }
+ }
+ }
+
+ composeTestRule.waitUntilExists(hasText(ROUTE_A))
+ }
+
+ @Test
+ fun navigate_canNavigateAndPopUpCurrent() {
+ composeTestRule.setContent {
+ TestNavHost {
+ val navController = LocalNavController.current
+ LaunchedEffect(Unit) {
+ navController.navigate(ROUTE_B)
+ delay(100)
+ navController.navigate(ROUTE_C, popUpCurrent = true)
+ delay(100)
+ navController.navigateBack()
+ }
+ }
+ }
+
+ composeTestRule.waitUntilExists(hasText(ROUTE_A))
+ }
+
+ private companion object {
+ @Composable
+ fun TestNavHost(content: @Composable () -> Unit) {
+ val navController = rememberNavController()
+ CompositionLocalProvider(navController.localNavController()) {
+ NavHost(navController, ROUTE_A) {
+ composable(route = ROUTE_A) { Text(ROUTE_A) }
+ composable(route = ROUTE_B) { Text(ROUTE_B) }
+ composable(route = ROUTE_C) { Text(ROUTE_C) }
+ }
+ content()
+ }
+ }
+
+ const val ROUTE_A = "RouteA"
+ const val ROUTE_B = "RouteB"
+ const val ROUTE_C = "RouteC"
+ }
+}
diff --git a/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/FakeNavControllerWrapper.kt b/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/FakeNavControllerWrapper.kt
index 5a3044d..f289d0d 100644
--- a/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/FakeNavControllerWrapper.kt
+++ b/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/FakeNavControllerWrapper.kt
@@ -25,7 +25,7 @@
var navigateCalledWith: String? = null
var navigateBackIsCalled = false
- override fun navigate(route: String) {
+ override fun navigate(route: String, popUpCurrent: Boolean) {
navigateCalledWith = route
}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index c641a85..b4598bf 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -646,6 +646,9 @@
<uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" />
+ <!-- Permission required for CTS test - SoundDoseHelperTest -->
+ <uses-permission android:name="android.permission.MODIFY_AUDIO_SYSTEM_SETTINGS" />
+
<!-- Permission required for CTS test - CtsAmbientContextDetectionServiceDeviceTest -->
<uses-permission android:name="android.permission.ACCESS_AMBIENT_CONTEXT_EVENT" />
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 26a68bc..628fb38 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -937,7 +937,7 @@
android:excludeFromRecents="true"
android:theme="@android:style/Theme.NoDisplay"
android:label="@string/note_task_button_label"
- android:icon="@drawable/ic_note_task_button">
+ android:icon="@drawable/ic_note_task_shortcut_widget">
<intent-filter>
<action android:name="android.intent.action.CREATE_SHORTCUT" />
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index f92625b..333aeba 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -16,6 +16,9 @@
},
{
"exclude-annotation": "android.platform.test.annotations.Postsubmit"
+ },
+ {
+ "exclude-annotation": "android.platform.test.annotations.FlakyTest"
}
]
}
@@ -73,16 +76,16 @@
// Curious where your @Scenario tests will run?
//
// @Ignore: nowhere
- // @Staging or @FlakyTest: in staged-postsubmit, but not postsubmit or
- // presubmit
+ // @FlakyTest: in staged-postsubmit, but not blocking postsubmit or
+ // presubmit
// @Postsubmit: in postsubmit and staged-postsubmit, but not presubmit
// none of the above: in presubmit, postsubmit, and staged-postsubmit
//
- // Therefore, please annotate new tests with @Staging, then with @Postsubmit
- // once they're ready for postsubmit, then with neither once they're ready
- // for presubmit.
+ // Ideally, please annotate new tests with @FlakyTest, then with @Postsubmit
+ // once they're ready for postsubmit as they will immediately block go/android-platinum,
+ // then with neither once they're ready for presubmit.
//
- // If you don't use @Staging or @Postsubmit, your new test will immediately
+ // If you don't use @Postsubmit, your new test will immediately
// block presubmit, which is probably not what you want!
"sysui-platinum-postsubmit": [
{
@@ -99,6 +102,9 @@
},
{
"exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "android.platform.test.annotations.FlakyTest"
}
]
}
@@ -131,6 +137,9 @@
},
{
"include-filter": "android.platform.test.scenario.sysui"
+ },
+ {
+ "exclude-annotation": "android.platform.test.annotations.FlakyTest"
}
]
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
index dfac02d..26aa0e8 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
@@ -26,6 +26,7 @@
import android.graphics.drawable.GradientDrawable
import android.graphics.drawable.InsetDrawable
import android.graphics.drawable.LayerDrawable
+import android.graphics.drawable.StateListDrawable
import android.util.Log
import android.view.GhostView
import android.view.View
@@ -35,6 +36,7 @@
import com.android.internal.jank.InteractionJankMonitor
import java.util.LinkedList
import kotlin.math.min
+import kotlin.math.roundToInt
private const val TAG = "GhostedViewLaunchAnimatorController"
@@ -49,7 +51,9 @@
* Note: Avoid instantiating this directly and call [ActivityLaunchAnimator.Controller.fromView]
* whenever possible instead.
*/
-open class GhostedViewLaunchAnimatorController @JvmOverloads constructor(
+open class GhostedViewLaunchAnimatorController
+@JvmOverloads
+constructor(
/** The view that will be ghosted and from which the background will be extracted. */
private val ghostedView: View,
@@ -145,7 +149,8 @@
val gradient = findGradientDrawable(drawable) ?: return 0f
// TODO(b/184121838): Support more than symmetric top & bottom radius.
- return gradient.cornerRadii?.get(CORNER_RADIUS_TOP_INDEX) ?: gradient.cornerRadius
+ val radius = gradient.cornerRadii?.get(CORNER_RADIUS_TOP_INDEX) ?: gradient.cornerRadius
+ return radius * ghostedView.scaleX
}
/** Return the current bottom corner radius of the background. */
@@ -154,7 +159,8 @@
val gradient = findGradientDrawable(drawable) ?: return 0f
// TODO(b/184121838): Support more than symmetric top & bottom radius.
- return gradient.cornerRadii?.get(CORNER_RADIUS_BOTTOM_INDEX) ?: gradient.cornerRadius
+ val radius = gradient.cornerRadii?.get(CORNER_RADIUS_BOTTOM_INDEX) ?: gradient.cornerRadius
+ return radius * ghostedView.scaleX
}
override fun createAnimatorState(): LaunchAnimator.State {
@@ -173,9 +179,13 @@
ghostedView.getLocationOnScreen(ghostedViewLocation)
val insets = backgroundInsets
state.top = ghostedViewLocation[1] + insets.top
- state.bottom = ghostedViewLocation[1] + ghostedView.height - insets.bottom
+ state.bottom =
+ ghostedViewLocation[1] + (ghostedView.height * ghostedView.scaleY).roundToInt() -
+ insets.bottom
state.left = ghostedViewLocation[0] + insets.left
- state.right = ghostedViewLocation[0] + ghostedView.width - insets.right
+ state.right =
+ ghostedViewLocation[0] + (ghostedView.width * ghostedView.scaleX).roundToInt() -
+ insets.right
}
override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
@@ -341,6 +351,10 @@
}
}
+ if (drawable is StateListDrawable) {
+ return findGradientDrawable(drawable.current)
+ }
+
return null
}
}
diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt
index 2148cb0..d6b48f9 100644
--- a/packages/SystemUI/ktfmt_includes.txt
+++ b/packages/SystemUI/ktfmt_includes.txt
@@ -1,6 +1,5 @@
+packages/SystemUI
-packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
--packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
-packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
-packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
-packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogcatEchoTrackerDebug.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogcatEchoTrackerDebug.kt
index d3fabac..faf1b78 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogcatEchoTrackerDebug.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogcatEchoTrackerDebug.kt
@@ -21,6 +21,7 @@
import android.net.Uri
import android.os.Handler
import android.os.Looper
+import android.os.Trace
import android.provider.Settings
/**
@@ -51,14 +52,21 @@
}
}
+ private fun clearCache() {
+ Trace.beginSection("LogcatEchoTrackerDebug#clearCache")
+ cachedBufferLevels.clear()
+ Trace.endSection()
+ }
+
private fun attach(mainLooper: Looper) {
+ Trace.beginSection("LogcatEchoTrackerDebug#attach")
contentResolver.registerContentObserver(
Settings.Global.getUriFor(BUFFER_PATH),
true,
object : ContentObserver(Handler(mainLooper)) {
override fun onChange(selfChange: Boolean, uri: Uri?) {
super.onChange(selfChange, uri)
- cachedBufferLevels.clear()
+ clearCache()
}
}
)
@@ -69,10 +77,11 @@
object : ContentObserver(Handler(mainLooper)) {
override fun onChange(selfChange: Boolean, uri: Uri?) {
super.onChange(selfChange, uri)
- cachedTagLevels.clear()
+ clearCache()
}
}
)
+ Trace.endSection()
}
/** Whether [bufferName] should echo messages of [level] or higher to logcat. */
@@ -97,9 +106,12 @@
private fun readSetting(path: String): LogLevel {
return try {
+ Trace.beginSection("LogcatEchoTrackerDebug#readSetting")
parseProp(Settings.Global.getString(contentResolver, path))
} catch (_: Settings.SettingNotFoundException) {
DEFAULT_LEVEL
+ } finally {
+ Trace.endSection()
}
}
diff --git a/packages/SystemUI/res/drawable/ic_note_task_button.xml b/packages/SystemUI/res/drawable/ic_note_task_shortcut_keyguard.xml
similarity index 92%
copy from packages/SystemUI/res/drawable/ic_note_task_button.xml
copy to packages/SystemUI/res/drawable/ic_note_task_shortcut_keyguard.xml
index bb5e224..ee8d4883 100644
--- a/packages/SystemUI/res/drawable/ic_note_task_button.xml
+++ b/packages/SystemUI/res/drawable/ic_note_task_shortcut_keyguard.xml
@@ -19,10 +19,13 @@
android:viewportHeight="24"
android:viewportWidth="24">
<path
- android:fillColor="#636C6F"
+ android:fillAlpha="1"
+ android:fillColor="@android:color/white"
+ android:fillType="nonZero"
android:pathData="M17.6258,4.96L19.0358,6.37L7.4058,18.01L5.9958,16.6L17.6258,4.96ZM16.1358,3.62L4.1258,15.63L3.0158,19.83C2.9058,20.45 3.3858,21 3.9958,21C4.0558,21 4.1058,21 4.1658,20.99L8.3658,19.88L20.3758,7.86C20.7758,7.46 20.9958,6.93 20.9958,6.37C20.9958,5.81 20.7758,5.28 20.3758,4.88L19.1058,3.61C18.7158,3.22 18.1858,3 17.6258,3C17.0658,3 16.5358,3.22 16.1358,3.62Z" />
<path
- android:fillColor="#636C6F"
- android:fillType="evenOdd"
+ android:fillAlpha="1"
+ android:fillColor="@android:color/white"
+ android:fillType="nonZero"
android:pathData="M20.1936,15.3369C20.3748,16.3837 19.9151,17.5414 18.8846,18.7597C19.1546,18.872 19.4576,18.9452 19.7724,18.9867C20.0839,19.0278 20.3683,19.0325 20.5749,19.0266C20.6772,19.0236 20.7578,19.0181 20.8101,19.0138C20.8362,19.0116 20.855,19.0097 20.8657,19.0085L20.8754,19.0074L20.875,19.0075C21.4217,18.9385 21.9214,19.325 21.9918,19.8718C22.0624,20.4195 21.6756,20.9208 21.1279,20.9914L21,19.9996C21.1279,20.9914 21.1265,20.9916 21.1265,20.9916L21.1249,20.9918L21.1211,20.9923L21.1107,20.9935L21.0795,20.997C21.0542,20.9998 21.0199,21.0032 20.9775,21.0067C20.8929,21.0138 20.7753,21.0216 20.6323,21.0257C20.3481,21.0339 19.9533,21.0279 19.5109,20.9695C18.873,20.8854 18.0393,20.6793 17.3106,20.1662C16.9605,20.3559 16.5876,20.4952 16.2299,20.6003C15.5742,20.7927 14.8754,20.8968 14.2534,20.9534C13.6801,21.0055 13.4553,21.0037 13.1015,21.0008C13.0689,21.0005 13.0352,21.0002 13,21H12.8594C12.8214,21.0002 12.785,21.0006 12.7504,21.0009C12.6524,21.0019 12.5683,21.0027 12.5,21H12.0562C12.0277,21.0003 12.0054,21.0006 11.9926,21.001L11.9751,21H9L11,19H11.9795C11.9929,18.9997 12.0064,18.9997 12.0199,19H12.4117C12.4534,18.9996 12.4864,18.9995 12.5,19H12.9675C12.977,18.9999 12.9878,18.9999 13,19C13.0446,19.0003 13.0859,19.0007 13.1249,19.0011C13.4259,19.0038 13.591,19.0054 14.0723,18.9616C14.6201,18.9118 15.1795,18.8242 15.6665,18.6813C15.753,18.6559 15.8346,18.6295 15.9114,18.6022C15.0315,17.2981 14.7125,16.1044 15.015,15.0829C15.4095,13.7511 16.6784,13.2418 17.7026,13.2864C18.7262,13.3309 19.954,13.9529 20.1936,15.3369ZM16.9327,15.6508C16.873,15.8523 16.8651,16.3878 17.4697,17.334C18.2007,16.4284 18.2585,15.8839 18.2229,15.6781C18.1939,15.5108 18.0297,15.3025 17.6157,15.2845C17.2025,15.2665 16.9885,15.4626 16.9327,15.6508Z" />
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_note_task_button.xml b/packages/SystemUI/res/drawable/ic_note_task_shortcut_widget.xml
similarity index 95%
rename from packages/SystemUI/res/drawable/ic_note_task_button.xml
rename to packages/SystemUI/res/drawable/ic_note_task_shortcut_widget.xml
index bb5e224..7590182 100644
--- a/packages/SystemUI/res/drawable/ic_note_task_button.xml
+++ b/packages/SystemUI/res/drawable/ic_note_task_shortcut_widget.xml
@@ -19,10 +19,13 @@
android:viewportHeight="24"
android:viewportWidth="24">
<path
+ android:fillAlpha="1"
android:fillColor="#636C6F"
+ android:fillType="nonZero"
android:pathData="M17.6258,4.96L19.0358,6.37L7.4058,18.01L5.9958,16.6L17.6258,4.96ZM16.1358,3.62L4.1258,15.63L3.0158,19.83C2.9058,20.45 3.3858,21 3.9958,21C4.0558,21 4.1058,21 4.1658,20.99L8.3658,19.88L20.3758,7.86C20.7758,7.46 20.9958,6.93 20.9958,6.37C20.9958,5.81 20.7758,5.28 20.3758,4.88L19.1058,3.61C18.7158,3.22 18.1858,3 17.6258,3C17.0658,3 16.5358,3.22 16.1358,3.62Z" />
<path
+ android:fillAlpha="1"
android:fillColor="#636C6F"
- android:fillType="evenOdd"
+ android:fillType="nonZero"
android:pathData="M20.1936,15.3369C20.3748,16.3837 19.9151,17.5414 18.8846,18.7597C19.1546,18.872 19.4576,18.9452 19.7724,18.9867C20.0839,19.0278 20.3683,19.0325 20.5749,19.0266C20.6772,19.0236 20.7578,19.0181 20.8101,19.0138C20.8362,19.0116 20.855,19.0097 20.8657,19.0085L20.8754,19.0074L20.875,19.0075C21.4217,18.9385 21.9214,19.325 21.9918,19.8718C22.0624,20.4195 21.6756,20.9208 21.1279,20.9914L21,19.9996C21.1279,20.9914 21.1265,20.9916 21.1265,20.9916L21.1249,20.9918L21.1211,20.9923L21.1107,20.9935L21.0795,20.997C21.0542,20.9998 21.0199,21.0032 20.9775,21.0067C20.8929,21.0138 20.7753,21.0216 20.6323,21.0257C20.3481,21.0339 19.9533,21.0279 19.5109,20.9695C18.873,20.8854 18.0393,20.6793 17.3106,20.1662C16.9605,20.3559 16.5876,20.4952 16.2299,20.6003C15.5742,20.7927 14.8754,20.8968 14.2534,20.9534C13.6801,21.0055 13.4553,21.0037 13.1015,21.0008C13.0689,21.0005 13.0352,21.0002 13,21H12.8594C12.8214,21.0002 12.785,21.0006 12.7504,21.0009C12.6524,21.0019 12.5683,21.0027 12.5,21H12.0562C12.0277,21.0003 12.0054,21.0006 11.9926,21.001L11.9751,21H9L11,19H11.9795C11.9929,18.9997 12.0064,18.9997 12.0199,19H12.4117C12.4534,18.9996 12.4864,18.9995 12.5,19H12.9675C12.977,18.9999 12.9878,18.9999 13,19C13.0446,19.0003 13.0859,19.0007 13.1249,19.0011C13.4259,19.0038 13.591,19.0054 14.0723,18.9616C14.6201,18.9118 15.1795,18.8242 15.6665,18.6813C15.753,18.6559 15.8346,18.6295 15.9114,18.6022C15.0315,17.2981 14.7125,16.1044 15.015,15.0829C15.4095,13.7511 16.6784,13.2418 17.7026,13.2864C18.7262,13.3309 19.954,13.9529 20.1936,15.3369ZM16.9327,15.6508C16.873,15.8523 16.8651,16.3878 17.4697,17.334C18.2007,16.4284 18.2585,15.8839 18.2229,15.6781C18.1939,15.5108 18.0297,15.3025 17.6157,15.2845C17.2025,15.2665 16.9885,15.4626 16.9327,15.6508Z" />
</vector>
diff --git a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml
index 18fcebb..dfb73a9 100644
--- a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml
+++ b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml
@@ -27,22 +27,26 @@
android:top="3dp"
android:right="3dp"
android:bottom="3dp">
- <shape android:shape="oval">
+ <!-- We make the shapes a rounded rectangle instead of an oval so that it can animate -->
+ <!-- properly into an app/dialog. -->
+ <shape android:shape="rectangle">
<solid android:color="?androidprv:attr/colorSurface"/>
<size
- android:width="@dimen/keyguard_affordance_width"
- android:height="@dimen/keyguard_affordance_height"/>
+ android:width="@dimen/keyguard_affordance_fixed_width"
+ android:height="@dimen/keyguard_affordance_fixed_height"/>
+ <corners android:radius="@dimen/keyguard_affordance_fixed_radius"/>
</shape>
</item>
<item>
- <shape android:shape="oval">
+ <shape android:shape="rectangle">
<stroke
android:color="@color/control_primary_text"
android:width="2dp"/>
<size
- android:width="@dimen/keyguard_affordance_width"
- android:height="@dimen/keyguard_affordance_height"/>
+ android:width="@dimen/keyguard_affordance_fixed_width"
+ android:height="@dimen/keyguard_affordance_fixed_height"/>
+ <corners android:radius="@dimen/keyguard_affordance_fixed_radius"/>
</shape>
</item>
</layer-list>
@@ -55,11 +59,12 @@
android:top="3dp"
android:right="3dp"
android:bottom="3dp">
- <shape android:shape="oval">
+ <shape android:shape="rectangle">
<solid android:color="?androidprv:attr/colorSurface"/>
<size
- android:width="@dimen/keyguard_affordance_width"
- android:height="@dimen/keyguard_affordance_height"/>
+ android:width="@dimen/keyguard_affordance_fixed_width"
+ android:height="@dimen/keyguard_affordance_fixed_height"/>
+ <corners android:radius="@dimen/keyguard_affordance_fixed_radius"/>
</shape>
</item>
</layer-list>
diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml
index f4434e8..ea3c012 100644
--- a/packages/SystemUI/res/values-sw600dp/config.xml
+++ b/packages/SystemUI/res/values-sw600dp/config.xml
@@ -36,4 +36,15 @@
<integer name="qs_security_footer_maxLines">1</integer>
<bool name="config_use_large_screen_shade_header">true</bool>
+
+ <!-- A collection of defaults for the quick affordances on the lock screen. Each item must be a
+ string with two parts: the ID of the slot and the comma-delimited list of affordance IDs,
+ separated by a colon ':' character. For example: <item>bottom_end:home,wallet</item>. The
+ default is displayed by System UI as long as the user hasn't made a different choice for that
+ slot. If the user did make a choice, even if the choice is the "None" option, the default is
+ ignored. -->
+ <string-array name="config_keyguardQuickAffordanceDefaults" translatable="false">
+ <item>bottom_start:home</item>
+ <item>bottom_end:create_note</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 9bc0dde..db7fb48 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -30,10 +30,6 @@
<!-- Margin on the left side of the carrier text on Keyguard -->
<dimen name="keyguard_carrier_text_margin">24dp</dimen>
- <!-- The width/height of the phone/camera/unlock icon on keyguard. -->
- <dimen name="keyguard_affordance_height">80dp</dimen>
- <dimen name="keyguard_affordance_width">120dp</dimen>
-
<!-- Screen pinning request width -->
<dimen name="screen_pinning_request_width">400dp</dimen>
<!-- Screen pinning request bottom button circle widths -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index fc67015..6202939 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -762,12 +762,10 @@
<dimen name="go_to_full_shade_appearing_translation">200dp</dimen>
<!-- The width/height of the keyguard bottom area icon view on keyguard. -->
- <dimen name="keyguard_affordance_height">48dp</dimen>
- <dimen name="keyguard_affordance_width">48dp</dimen>
-
<dimen name="keyguard_affordance_fixed_height">48dp</dimen>
<dimen name="keyguard_affordance_fixed_width">48dp</dimen>
<dimen name="keyguard_affordance_fixed_radius">24dp</dimen>
+
<!-- Amount the button should shake when it's not long-pressed for long enough. -->
<dimen name="keyguard_affordance_shake_amplitude">8dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 2539828..428a0a8 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -534,6 +534,13 @@
val OUTPUT_SWITCHER_SHOW_API_ENABLED =
unreleasedFlag(2503, "output_switcher_show_api_enabled", teamfood = true)
+ // 2700 - unfold transitions
+ // TODO(b/265764985): Tracking Bug
+ @Keep
+ @JvmField
+ val ENABLE_DARK_VIGNETTE_WHEN_FOLDING =
+ unreleasedFlag(2700, "enable_dark_vignette_when_folding")
+
// TODO(b259590361): Tracking bug
val EXPERIMENTAL_FLAG = unreleasedFlag(2, "exp_flag_release")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt
index cd4dac0..76c2430 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt
@@ -25,6 +25,7 @@
object BuiltInKeyguardQuickAffordanceKeys {
// Please keep alphabetical order of const names to simplify future maintenance.
const val CAMERA = "camera"
+ const val CREATE_NOTE = "create_note"
const val DO_NOT_DISTURB = "do_not_disturb"
const val FLASHLIGHT = "flashlight"
const val HOME_CONTROLS = "home"
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskIntentResolver.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskIntentResolver.kt
index 26e3f49..4b10d69 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskIntentResolver.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskIntentResolver.kt
@@ -72,6 +72,9 @@
companion object {
// TODO(b/254606432): Use Intent.ACTION_CREATE_NOTE instead.
const val ACTION_CREATE_NOTE = "android.intent.action.CREATE_NOTE"
+
+ // TODO(b/265912743): Use RoleManager.NOTES_ROLE instead.
+ const val NOTE_ROLE = "android.app.role.NOTES"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
index 8bdf319..22ce121 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
@@ -18,11 +18,13 @@
import android.app.Activity
import android.app.KeyguardManager
+import android.app.role.RoleManager
import android.content.Context
import android.os.UserManager
import androidx.core.content.getSystemService
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.notetask.quickaffordance.NoteTaskQuickAffordanceModule
import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity
import com.android.systemui.notetask.shortcut.LaunchNoteTaskActivity
import dagger.Binds
@@ -33,20 +35,25 @@
import java.util.Optional
/** Compose all dependencies required by Note Task feature. */
-@Module
+@Module(includes = [NoteTaskQuickAffordanceModule::class])
internal interface NoteTaskModule {
@[Binds IntoMap ClassKey(LaunchNoteTaskActivity::class)]
- fun bindNoteTaskLauncherActivity(activity: LaunchNoteTaskActivity): Activity?
+ fun LaunchNoteTaskActivity.bindNoteTaskLauncherActivity(): Activity
@[Binds IntoMap ClassKey(CreateNoteTaskShortcutActivity::class)]
- fun bindNoteTaskShortcutActivity(activity: CreateNoteTaskShortcutActivity): Activity?
+ fun CreateNoteTaskShortcutActivity.bindNoteTaskShortcutActivity(): Activity
companion object {
@[Provides NoteTaskEnabledKey]
- fun provideIsNoteTaskEnabled(featureFlags: FeatureFlags): Boolean {
- return featureFlags.isEnabled(Flags.NOTE_TASKS)
+ fun provideIsNoteTaskEnabled(
+ featureFlags: FeatureFlags,
+ roleManager: RoleManager,
+ ): Boolean {
+ val isRoleAvailable = roleManager.isRoleAvailable(NoteTaskIntentResolver.NOTE_ROLE)
+ val isFeatureEnabled = featureFlags.isEnabled(Flags.NOTE_TASKS)
+ return isRoleAvailable && isFeatureEnabled
}
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
new file mode 100644
index 0000000..cfbaa48
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.notetask.quickaffordance
+
+import android.content.Context
+import com.android.systemui.R
+import com.android.systemui.animation.Expandable
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.LockScreenState
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.OnTriggeredResult
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.PickerScreenState
+import com.android.systemui.notetask.NoteTaskController
+import com.android.systemui.notetask.NoteTaskEnabledKey
+import javax.inject.Inject
+import kotlinx.coroutines.flow.flowOf
+
+internal class NoteTaskQuickAffordanceConfig
+@Inject
+constructor(
+ context: Context,
+ private val noteTaskController: NoteTaskController,
+ @NoteTaskEnabledKey private val isEnabled: Boolean,
+) : KeyguardQuickAffordanceConfig {
+
+ override val key = BuiltInKeyguardQuickAffordanceKeys.CREATE_NOTE
+
+ override val pickerName: String = context.getString(R.string.note_task_button_label)
+
+ override val pickerIconResourceId = R.drawable.ic_note_task_shortcut_keyguard
+
+ override val lockScreenState = flowOf(getLockScreenState())
+
+ // TODO(b/265949213)
+ private fun getLockScreenState() =
+ if (isEnabled) {
+ val icon = Icon.Resource(pickerIconResourceId, ContentDescription.Loaded(pickerName))
+ LockScreenState.Visible(icon)
+ } else {
+ LockScreenState.Hidden
+ }
+
+ override suspend fun getPickerScreenState() =
+ if (isEnabled) {
+ PickerScreenState.Default()
+ } else {
+ PickerScreenState.UnavailableOnDevice
+ }
+
+ override fun onTriggered(expandable: Expandable?): OnTriggeredResult {
+ noteTaskController.showNoteTask()
+ return OnTriggeredResult.Handled
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceModule.kt b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceModule.kt
new file mode 100644
index 0000000..7cb932a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceModule.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.notetask.quickaffordance
+
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoSet
+
+@Module
+internal interface NoteTaskQuickAffordanceModule {
+
+ @[Binds IntoSet]
+ fun NoteTaskQuickAffordanceConfig.bindNoteTaskQuickAffordance(): KeyguardQuickAffordanceConfig
+}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/CreateNoteTaskShortcutActivity.kt b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/CreateNoteTaskShortcutActivity.kt
index f6a623e..6ab0da6 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/CreateNoteTaskShortcutActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/CreateNoteTaskShortcutActivity.kt
@@ -46,7 +46,7 @@
id = SHORTCUT_ID,
shortLabel = getString(R.string.note_task_button_label),
intent = LaunchNoteTaskActivity.newIntent(context = this),
- iconResource = R.drawable.ic_note_task_button,
+ iconResource = R.drawable.ic_note_task_shortcut_widget,
)
setResult(Activity.RESULT_OK, intent)
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
index 1558ac5..287e810 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
@@ -62,12 +62,24 @@
fun removeCallback(callback: Callback)
/**
- * Ćallback for notifying of changes.
+ * Callback for notifying of changes.
*/
interface Callback {
/**
+ * Notifies that the current user is being changed.
+ * Override this method to run things while the screen is frozen for the user switch.
+ * Please use {@link #onUserChanged} if the task doesn't need to push the unfreezing of the
+ * screen further. Please be aware that code executed in this callback will lengthen the
+ * user switch duration.
+ */
+ @JvmDefault
+ fun onUserChanging(newUser: Int, userContext: Context) {}
+
+ /**
* Notifies that the current user has changed.
+ * Override this method to run things after the screen is unfrozen for the user switch.
+ * Please see {@link #onUserChanging} if you need to hide jank.
*/
@JvmDefault
fun onUserChanged(newUser: Int, userContext: Context) {}
@@ -78,4 +90,4 @@
@JvmDefault
fun onProfilesChanged(profiles: List<@JvmSuppressWildcards UserInfo>) {}
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
index 4dbe099..3a5d0a7 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
@@ -16,6 +16,8 @@
package com.android.systemui.settings
+import android.app.IActivityManager
+import android.app.UserSwitchObserver
import android.content.BroadcastReceiver
import android.content.ContentResolver
import android.content.Context
@@ -23,6 +25,7 @@
import android.content.IntentFilter
import android.content.pm.UserInfo
import android.os.Handler
+import android.os.IRemoteCallback
import android.os.UserHandle
import android.os.UserManager
import android.util.Log
@@ -33,6 +36,7 @@
import com.android.systemui.util.Assert
import java.io.PrintWriter
import java.lang.ref.WeakReference
+import java.util.concurrent.CountDownLatch
import java.util.concurrent.Executor
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
@@ -55,6 +59,7 @@
open class UserTrackerImpl internal constructor(
private val context: Context,
private val userManager: UserManager,
+ private val iActivityManager: IActivityManager,
private val dumpManager: DumpManager,
private val backgroundHandler: Handler
) : UserTracker, Dumpable, BroadcastReceiver() {
@@ -106,7 +111,6 @@
setUserIdInternal(startingUser)
val filter = IntentFilter().apply {
- addAction(Intent.ACTION_USER_SWITCHED)
addAction(Intent.ACTION_USER_INFO_CHANGED)
// These get called when a managed profile goes in or out of quiet mode.
addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
@@ -117,14 +121,13 @@
}
context.registerReceiverForAllUsers(this, filter, null /* permission */, backgroundHandler)
+ registerUserSwitchObserver()
+
dumpManager.registerDumpable(TAG, this)
}
override fun onReceive(context: Context, intent: Intent) {
when (intent.action) {
- Intent.ACTION_USER_SWITCHED -> {
- handleSwitchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL))
- }
Intent.ACTION_USER_INFO_CHANGED,
Intent.ACTION_MANAGED_PROFILE_AVAILABLE,
Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
@@ -156,22 +159,43 @@
return ctx to profiles
}
+ private fun registerUserSwitchObserver() {
+ iActivityManager.registerUserSwitchObserver(object : UserSwitchObserver() {
+ override fun onUserSwitching(newUserId: Int, reply: IRemoteCallback?) {
+ backgroundHandler.run {
+ handleUserSwitching(newUserId)
+ reply?.sendResult(null)
+ }
+ }
+
+ override fun onUserSwitchComplete(newUserId: Int) {
+ backgroundHandler.run {
+ handleUserSwitchComplete(newUserId)
+ }
+ }
+ }, TAG)
+ }
+
@WorkerThread
- protected open fun handleSwitchUser(newUser: Int) {
+ protected open fun handleUserSwitching(newUserId: Int) {
Assert.isNotMainThread()
- if (newUser == UserHandle.USER_NULL) {
- Log.w(TAG, "handleSwitchUser - Couldn't get new id from intent")
- return
- }
+ Log.i(TAG, "Switching to user $newUserId")
- if (newUser == userId) return
- Log.i(TAG, "Switching to user $newUser")
-
- val (ctx, profiles) = setUserIdInternal(newUser)
-
+ setUserIdInternal(newUserId)
notifySubscribers {
- onUserChanged(newUser, ctx)
- onProfilesChanged(profiles)
+ onUserChanging(newUserId, userContext)
+ }.await()
+ }
+
+ @WorkerThread
+ protected open fun handleUserSwitchComplete(newUserId: Int) {
+ Assert.isNotMainThread()
+ Log.i(TAG, "Switched to user $newUserId")
+
+ setUserIdInternal(newUserId)
+ notifySubscribers {
+ onUserChanged(newUserId, userContext)
+ onProfilesChanged(userProfiles)
}
}
@@ -200,17 +224,25 @@
}
}
- private inline fun notifySubscribers(crossinline action: UserTracker.Callback.() -> Unit) {
+ private inline fun notifySubscribers(
+ crossinline action: UserTracker.Callback.() -> Unit
+ ): CountDownLatch {
val list = synchronized(callbacks) {
callbacks.toList()
}
+ val latch = CountDownLatch(list.size)
+
list.forEach {
if (it.callback.get() != null) {
it.executor.execute {
it.callback.get()?.action()
+ latch.countDown()
}
+ } else {
+ latch.countDown()
}
}
+ return latch
}
override fun dump(pw: PrintWriter, args: Array<out String>) {
@@ -257,4 +289,4 @@
fun sameOrEmpty(other: UserTracker.Callback): Boolean {
return callback.get()?.equals(other) ?: true
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java b/packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java
index 2f62e44..809fa29 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java
@@ -17,6 +17,7 @@
package com.android.systemui.settings.dagger;
import android.app.ActivityManager;
+import android.app.IActivityManager;
import android.content.Context;
import android.os.Handler;
import android.os.UserManager;
@@ -57,11 +58,13 @@
static UserTracker provideUserTracker(
Context context,
UserManager userManager,
+ IActivityManager iActivityManager,
DumpManager dumpManager,
@Background Handler handler
) {
int startingUser = ActivityManager.getCurrentUser();
- UserTrackerImpl tracker = new UserTrackerImpl(context, userManager, dumpManager, handler);
+ UserTrackerImpl tracker = new UserTrackerImpl(context, userManager, iActivityManager,
+ dumpManager, handler);
tracker.initialize(startingUser);
return tracker;
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index bf4a768..b532c13 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -4186,9 +4186,7 @@
}
private void updateStatusBarIcons() {
- boolean showIconsWhenExpanded =
- (isPanelVisibleBecauseOfHeadsUp() || mIsFullWidth)
- && getExpandedHeight() < getOpeningHeight();
+ boolean showIconsWhenExpanded = getExpandedHeight() < getOpeningHeight();
if (showIconsWhenExpanded && isOnKeyguard()) {
showIconsWhenExpanded = false;
}
@@ -4255,7 +4253,7 @@
&& mHeadsUpAppearanceController.shouldBeVisible()) {
return false;
}
- return !mIsFullWidth || !mShowIconsWhenExpanded;
+ return !mShowIconsWhenExpanded;
}
private void onQsPanelScrollChanged(int scrollY) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryDumper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryDumper.kt
index ffd931c..197ae1a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryDumper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryDumper.kt
@@ -18,9 +18,12 @@
package com.android.systemui.statusbar.notification.logging
import android.stats.sysui.NotificationEnums
+import android.util.Log
import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
+import com.android.systemui.dump.DumpsysTableLogger
+import com.android.systemui.dump.Row
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import java.io.PrintWriter
import javax.inject.Inject
@@ -33,6 +36,7 @@
fun init() {
dumpManager.registerNormalDumpable(javaClass.simpleName, this)
+ Log.i("NotificationMemory", "Registered dumpable.")
}
override fun dump(pw: PrintWriter, args: Array<out String>) {
@@ -45,27 +49,36 @@
/** Renders a table of notification object usage into passed [PrintWriter]. */
private fun dumpNotificationObjects(pw: PrintWriter, memoryUse: List<NotificationMemoryUsage>) {
- pw.println("Notification Object Usage")
- pw.println("-----------")
- pw.println(
- "Package".padEnd(35) +
- "\t\tSmall\tLarge\t${"Style".padEnd(15)}\t\tStyle\tBig\tExtend.\tExtras\tCustom"
- )
- pw.println("".padEnd(35) + "\t\tIcon\tIcon\t${"".padEnd(15)}\t\tIcon\tPicture\t \t \tView")
- pw.println()
-
- memoryUse.forEach { use ->
- pw.println(
- use.packageName.padEnd(35) +
- "\t\t" +
- "${use.objectUsage.smallIcon}\t${use.objectUsage.largeIcon}\t" +
- (styleEnumToString(use.objectUsage.style).take(15) ?: "").padEnd(15) +
- "\t\t${use.objectUsage.styleIcon}\t" +
- "${use.objectUsage.bigPicture}\t${use.objectUsage.extender}\t" +
- "${use.objectUsage.extras}\t${use.objectUsage.hasCustomView}\t" +
- use.notificationKey
+ val columns =
+ listOf(
+ "Package",
+ "Small Icon",
+ "Large Icon",
+ "Style",
+ "Style Icon",
+ "Big Picture",
+ "Extender",
+ "Extras",
+ "Custom View",
+ "Key"
)
- }
+ val rows: List<Row> =
+ memoryUse.map {
+ listOf(
+ it.packageName,
+ toKb(it.objectUsage.smallIcon),
+ toKb(it.objectUsage.largeIcon),
+ styleEnumToString(it.objectUsage.style),
+ toKb(it.objectUsage.styleIcon),
+ toKb(it.objectUsage.bigPicture),
+ toKb(it.objectUsage.extender),
+ toKb(it.objectUsage.extras),
+ it.objectUsage.hasCustomView.toString(),
+ // | is a field delimiter in the output format so we need to replace
+ // it to avoid breakage.
+ it.notificationKey.replace('|', '│')
+ )
+ }
// Calculate totals for easily glanceable summary.
data class Totals(
@@ -88,18 +101,23 @@
t
}
- pw.println()
- pw.println("TOTALS")
- pw.println(
- "".padEnd(35) +
- "\t\t" +
- "${toKb(totals.smallIcon)}\t${toKb(totals.largeIcon)}\t" +
- "".padEnd(15) +
- "\t\t${toKb(totals.styleIcon)}\t" +
- "${toKb(totals.bigPicture)}\t${toKb(totals.extender)}\t" +
- toKb(totals.extras)
- )
- pw.println()
+ val totalsRow: List<Row> =
+ listOf(
+ listOf(
+ "TOTALS",
+ toKb(totals.smallIcon),
+ toKb(totals.largeIcon),
+ "",
+ toKb(totals.styleIcon),
+ toKb(totals.bigPicture),
+ toKb(totals.extender),
+ toKb(totals.extras),
+ "",
+ ""
+ )
+ )
+ val tableLogger = DumpsysTableLogger("Notification Object Usage", columns, rows + totalsRow)
+ tableLogger.printTableData(pw)
}
/** Renders a table of notification view usage into passed [PrintWriter] */
@@ -116,40 +134,65 @@
var softwareBitmapsPenalty: Int = 0,
)
- val totals = Totals()
- pw.println("Notification View Usage")
- pw.println("-----------")
- pw.println("View Type".padEnd(24) + "\tSmall\tLarge\tStyle\tCustom\tSoftware")
- pw.println("".padEnd(24) + "\tIcon\tIcon\tUse\tView\tBitmaps")
- pw.println()
- memoryUse
- .filter { it.viewUsage.isNotEmpty() }
- .forEach { use ->
- pw.println(use.packageName + " " + use.notificationKey)
- use.viewUsage.forEach { view ->
- pw.println(
- " ${view.viewType.toString().padEnd(24)}\t${view.smallIcon}" +
- "\t${view.largeIcon}\t${view.style}" +
- "\t${view.customViews}\t${view.softwareBitmapsPenalty}"
- )
-
- if (view.viewType == ViewType.TOTAL) {
- totals.smallIcon += view.smallIcon
- totals.largeIcon += view.largeIcon
- totals.style += view.style
- totals.customViews += view.customViews
- totals.softwareBitmapsPenalty += view.softwareBitmapsPenalty
+ val columns =
+ listOf(
+ "Package",
+ "View Type",
+ "Small Icon",
+ "Large Icon",
+ "Style Use",
+ "Custom View",
+ "Software Bitmaps",
+ "Key"
+ )
+ val rows =
+ memoryUse
+ .filter { it.viewUsage.isNotEmpty() }
+ .flatMap { use ->
+ use.viewUsage.map { view ->
+ listOf(
+ use.packageName,
+ view.viewType.toString(),
+ toKb(view.smallIcon),
+ toKb(view.largeIcon),
+ toKb(view.style),
+ toKb(view.customViews),
+ toKb(view.softwareBitmapsPenalty),
+ // | is a field delimiter in the output format so we need to replace
+ // it to avoid breakage.
+ use.notificationKey.replace('|', '│')
+ )
}
}
+
+ val totals = Totals()
+ memoryUse
+ .filter { it.viewUsage.isNotEmpty() }
+ .map { it.viewUsage.firstOrNull { view -> view.viewType == ViewType.TOTAL } }
+ .filterNotNull()
+ .forEach { view ->
+ totals.smallIcon += view.smallIcon
+ totals.largeIcon += view.largeIcon
+ totals.style += view.style
+ totals.customViews += view.customViews
+ totals.softwareBitmapsPenalty += view.softwareBitmapsPenalty
}
- pw.println()
- pw.println("TOTALS")
- pw.println(
- " ${"".padEnd(24)}\t${toKb(totals.smallIcon)}" +
- "\t${toKb(totals.largeIcon)}\t${toKb(totals.style)}" +
- "\t${toKb(totals.customViews)}\t${toKb(totals.softwareBitmapsPenalty)}"
- )
- pw.println()
+
+ val totalsRow: List<Row> =
+ listOf(
+ listOf(
+ "TOTALS",
+ "",
+ toKb(totals.smallIcon),
+ toKb(totals.largeIcon),
+ toKb(totals.style),
+ toKb(totals.customViews),
+ toKb(totals.softwareBitmapsPenalty),
+ ""
+ )
+ )
+ val tableLogger = DumpsysTableLogger("Notification View Usage", columns, rows + totalsRow)
+ tableLogger.printTableData(pw)
}
private fun styleEnumToString(styleEnum: Int): String =
@@ -168,6 +211,10 @@
}
private fun toKb(bytes: Int): String {
- return (bytes / 1024).toString() + " KB"
+ if (bytes == 0) {
+ return "--"
+ }
+
+ return "%.2f KB".format(bytes / 1024f)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index ec08bd4..e630271 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -867,6 +867,11 @@
wiredChargingRippleController.registerCallbacks();
mLightRevealScrimViewModelLazy = lightRevealScrimViewModelLazy;
+
+ // Based on teamfood flag, turn predictive back dispatch on at runtime.
+ if (mFeatureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_SYSUI)) {
+ mContext.getApplicationInfo().setEnableOnBackInvokedCallback(true);
+ }
}
@Override
@@ -981,11 +986,6 @@
// Lastly, call to the icon policy to install/update all the icons.
mIconPolicy.init();
- // Based on teamfood flag, turn predictive back dispatch on at runtime.
- if (mFeatureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_SYSUI)) {
- mContext.getApplicationInfo().setEnableOnBackInvokedCallback(true);
- }
-
mKeyguardStateController.addCallback(new KeyguardStateController.Callback() {
@Override
public void onUnlockedChanged() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index f784723..80093a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -791,20 +791,28 @@
if (!mScreenOffAnimationController.shouldExpandNotifications()
&& !mAnimatingPanelExpansionOnUnlock
&& !occluding) {
- float behindFraction = getInterpolatedFraction();
- behindFraction = (float) Math.pow(behindFraction, 0.8f);
if (mClipsQsScrim) {
+ float behindFraction = getInterpolatedFraction();
+ behindFraction = (float) Math.pow(behindFraction, 0.8f);
mBehindAlpha = mTransparentScrimBackground ? 0 : 1;
mNotificationsAlpha =
mTransparentScrimBackground ? 0 : behindFraction * mDefaultScrimAlpha;
} else {
- mBehindAlpha =
- mTransparentScrimBackground ? 0 : behindFraction * mDefaultScrimAlpha;
- // Delay fade-in of notification scrim a bit further, to coincide with the
- // view fade in. Otherwise the empty panel can be quite jarring.
- mNotificationsAlpha = mTransparentScrimBackground
- ? 0 : MathUtils.constrainedMap(0f, 1f, 0.3f, 0.75f,
- mPanelExpansionFraction);
+ if (mTransparentScrimBackground) {
+ mBehindAlpha = 0;
+ mNotificationsAlpha = 0;
+ } else {
+ // Behind scrim will finish fading in at 30% expansion.
+ float behindFraction = MathUtils
+ .constrainedMap(0f, 1f, 0f, 0.3f, mPanelExpansionFraction);
+ mBehindAlpha = behindFraction * mDefaultScrimAlpha;
+ // Delay fade-in of notification scrim a bit further, to coincide with the
+ // behind scrim finishing fading in.
+ // Also to coincide with the view starting to fade in, otherwise the empty
+ // panel can be quite jarring.
+ mNotificationsAlpha = MathUtils
+ .constrainedMap(0f, 1f, 0.3f, 0.75f, mPanelExpansionFraction);
+ }
}
mBehindTint = mState.getBehindTint();
mInFrontAlpha = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
index 523cf68..0069bb5 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
@@ -36,6 +36,8 @@
import android.view.WindowManager
import android.view.WindowlessWindowManager
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.statusbar.LightRevealEffect
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.LinearLightRevealEffect
@@ -57,6 +59,7 @@
@Inject
constructor(
private val context: Context,
+ private val featureFlags: FeatureFlags,
private val deviceStateManager: DeviceStateManager,
private val contentResolver: ContentResolver,
private val displayManager: DisplayManager,
@@ -81,6 +84,7 @@
private var scrimView: LightRevealScrim? = null
private var isFolded: Boolean = false
private var isUnfoldHandled: Boolean = true
+ private var overlayAddReason: AddOverlayReason? = null
private var currentRotation: Int = context.display!!.rotation
@@ -158,6 +162,8 @@
ensureInBackground()
ensureOverlayRemoved()
+ overlayAddReason = reason
+
val newRoot = SurfaceControlViewHost(context, context.display!!, wwm)
val params = getLayoutParams()
val newView =
@@ -170,11 +176,7 @@
.apply {
revealEffect = createLightRevealEffect()
isScrimOpaqueChangedListener = Consumer {}
- revealAmount =
- when (reason) {
- FOLD -> TRANSPARENT
- UNFOLD -> BLACK
- }
+ revealAmount = calculateRevealAmount()
}
newRoot.setView(newView, params)
@@ -207,6 +209,31 @@
root = newRoot
}
+ private fun calculateRevealAmount(animationProgress: Float? = null): Float {
+ val overlayAddReason = overlayAddReason ?: UNFOLD
+
+ if (animationProgress == null) {
+ // Animation progress is unknown, calculate the initial value based on the overlay
+ // add reason
+ return when (overlayAddReason) {
+ FOLD -> TRANSPARENT
+ UNFOLD -> BLACK
+ }
+ }
+
+ val showVignetteWhenFolding =
+ featureFlags.isEnabled(Flags.ENABLE_DARK_VIGNETTE_WHEN_FOLDING)
+
+ return if (!showVignetteWhenFolding && overlayAddReason == FOLD) {
+ // Do not darken the content when SHOW_VIGNETTE_WHEN_FOLDING flag is off
+ // and we are folding the device. We still add the overlay to block touches
+ // while the animation is running but the overlay is transparent.
+ TRANSPARENT
+ } else {
+ animationProgress
+ }
+ }
+
private fun getLayoutParams(): WindowManager.LayoutParams {
val params: WindowManager.LayoutParams = WindowManager.LayoutParams()
@@ -259,7 +286,7 @@
private inner class TransitionListener : TransitionProgressListener {
override fun onTransitionProgress(progress: Float) {
- executeInBackground { scrimView?.revealAmount = progress }
+ executeInBackground { scrimView?.revealAmount = calculateRevealAmount(progress) }
}
override fun onTransitionFinished() {
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
index c0f0390..8cb4deb 100644
--- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
@@ -174,7 +174,7 @@
val callback =
object : UserTracker.Callback {
- override fun onUserChanged(newUser: Int, userContext: Context) {
+ override fun onUserChanging(newUser: Int, userContext: Context) {
send()
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
index b61b2e6..47d505e 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
@@ -16,14 +16,21 @@
package com.android.systemui.util.kotlin
+import com.android.systemui.util.time.SystemClock
+import com.android.systemui.util.time.SystemClockImpl
+import kotlinx.coroutines.CoroutineStart
import java.util.concurrent.atomic.AtomicReference
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.launch
+import kotlin.math.max
/**
* Returns a new [Flow] that combines the two most recent emissions from [this] using [transform].
@@ -167,3 +174,89 @@
* Note that the returned Flow will not emit anything until [other] has emitted at least one value.
*/
fun <A> Flow<*>.sample(other: Flow<A>): Flow<A> = sample(other) { _, a -> a }
+
+/**
+ * Returns a flow that mirrors the original flow, but delays values following emitted values for the
+ * given [periodMs]. If the original flow emits more than one value during this period, only the
+ * latest value is emitted.
+ *
+ * Example:
+ *
+ * ```kotlin
+ * flow {
+ * emit(1) // t=0ms
+ * delay(90)
+ * emit(2) // t=90ms
+ * delay(90)
+ * emit(3) // t=180ms
+ * delay(1010)
+ * emit(4) // t=1190ms
+ * delay(1010)
+ * emit(5) // t=2200ms
+ * }.throttle(1000)
+ * ```
+ *
+ * produces the following emissions at the following times
+ *
+ * ```text
+ * 1 (t=0ms), 3 (t=1000ms), 4 (t=2000ms), 5 (t=3000ms)
+ * ```
+ */
+fun <T> Flow<T>.throttle(periodMs: Long): Flow<T> = this.throttle(periodMs, SystemClockImpl())
+
+/**
+ * Returns a flow that mirrors the original flow, but delays values following emitted values for the
+ * given [periodMs] as reported by the given [clock]. If the original flow emits more than one value
+ * during this period, only The latest value is emitted.
+ *
+ * Example:
+ *
+ * ```kotlin
+ * flow {
+ * emit(1) // t=0ms
+ * delay(90)
+ * emit(2) // t=90ms
+ * delay(90)
+ * emit(3) // t=180ms
+ * delay(1010)
+ * emit(4) // t=1190ms
+ * delay(1010)
+ * emit(5) // t=2200ms
+ * }.throttle(1000)
+ * ```
+ *
+ * produces the following emissions at the following times
+ *
+ * ```text
+ * 1 (t=0ms), 3 (t=1000ms), 4 (t=2000ms), 5 (t=3000ms)
+ * ```
+ */
+fun <T> Flow<T>.throttle(periodMs: Long, clock: SystemClock): Flow<T> = channelFlow {
+ coroutineScope {
+ var previousEmitTimeMs = 0L
+ var delayJob: Job? = null
+ var sendJob: Job? = null
+ val outerScope = this
+
+ collect {
+ delayJob?.cancel()
+ sendJob?.join()
+ val currentTimeMs = clock.elapsedRealtime()
+ val timeSinceLastEmit = currentTimeMs - previousEmitTimeMs
+ val timeUntilNextEmit = max(0L, periodMs - timeSinceLastEmit)
+ if (timeUntilNextEmit > 0L) {
+ // We create delayJob to allow cancellation during the delay period
+ delayJob = launch {
+ delay(timeUntilNextEmit)
+ sendJob = outerScope.launch(start = CoroutineStart.UNDISPATCHED) {
+ send(it)
+ previousEmitTimeMs = clock.elapsedRealtime()
+ }
+ }
+ } else {
+ send(it)
+ previousEmitTimeMs = currentTimeMs
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
index f71d988..a453726 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
@@ -25,6 +25,7 @@
import android.provider.Settings;
import android.view.WindowManager.LayoutParams;
+import com.android.internal.R;
import com.android.settingslib.applications.InterestingConfigChanges;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.demomode.DemoMode;
@@ -55,7 +56,7 @@
public static final String VOLUME_UP_SILENT = "sysui_volume_up_silent";
public static final String VOLUME_SILENT_DO_NOT_DISTURB = "sysui_do_not_disturb";
- public static final boolean DEFAULT_VOLUME_DOWN_TO_ENTER_SILENT = false;
+ private final boolean mDefaultVolumeDownToEnterSilent;
public static final boolean DEFAULT_VOLUME_UP_TO_EXIT_SILENT = false;
public static final boolean DEFAULT_DO_NOT_DISTURB_WHEN_SILENT = false;
@@ -72,12 +73,7 @@
private final KeyguardViewMediator mKeyguardViewMediator;
private final ActivityStarter mActivityStarter;
private VolumeDialog mDialog;
- private VolumePolicy mVolumePolicy = new VolumePolicy(
- DEFAULT_VOLUME_DOWN_TO_ENTER_SILENT, // volumeDownToEnterSilent
- DEFAULT_VOLUME_UP_TO_EXIT_SILENT, // volumeUpToExitSilent
- DEFAULT_DO_NOT_DISTURB_WHEN_SILENT, // doNotDisturbWhenSilent
- 400 // vibrateToSilentDebounce
- );
+ private VolumePolicy mVolumePolicy;
@Inject
public VolumeDialogComponent(
@@ -107,7 +103,20 @@
mDialog = dialog;
mDialog.init(LayoutParams.TYPE_VOLUME_OVERLAY, mVolumeDialogCallback);
}).build();
+
+
+ mDefaultVolumeDownToEnterSilent = mContext.getResources()
+ .getBoolean(R.bool.config_volume_down_to_enter_silent);
+
+ mVolumePolicy = new VolumePolicy(
+ mDefaultVolumeDownToEnterSilent, // volumeDownToEnterSilent
+ DEFAULT_VOLUME_UP_TO_EXIT_SILENT, // volumeUpToExitSilent
+ DEFAULT_DO_NOT_DISTURB_WHEN_SILENT, // doNotDisturbWhenSilent
+ 400 // vibrateToSilentDebounce
+ );
+
applyConfiguration();
+
tunerService.addTunable(this, VOLUME_DOWN_SILENT, VOLUME_UP_SILENT,
VOLUME_SILENT_DO_NOT_DISTURB);
demoModeController.addCallback(this);
@@ -121,7 +130,7 @@
if (VOLUME_DOWN_SILENT.equals(key)) {
volumeDownToEnterSilent =
- TunerService.parseIntegerSwitch(newValue, DEFAULT_VOLUME_DOWN_TO_ENTER_SILENT);
+ TunerService.parseIntegerSwitch(newValue, mDefaultVolumeDownToEnterSilent);
} else if (VOLUME_UP_SILENT.equals(key)) {
volumeUpToExitSilent =
TunerService.parseIntegerSwitch(newValue, DEFAULT_VOLUME_UP_TO_EXIT_SILENT);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
new file mode 100644
index 0000000..a1d42a0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.notetask.quickaffordance
+
+import android.test.suitebuilder.annotation.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.LockScreenState
+import com.android.systemui.notetask.NoteTaskController
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+/**
+ * Tests for [NoteTaskQuickAffordanceConfig].
+ *
+ * Build/Install/Run:
+ * - atest SystemUITests:NoteTaskQuickAffordanceConfigTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() {
+
+ @Mock lateinit var noteTaskController: NoteTaskController
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ whenever(noteTaskController.showNoteTask()).then {}
+ }
+
+ private fun createUnderTest(isEnabled: Boolean) =
+ NoteTaskQuickAffordanceConfig(
+ context = context,
+ noteTaskController = noteTaskController,
+ isEnabled = isEnabled,
+ )
+
+ @Test
+ fun lockScreenState_isNotEnabled_shouldEmitHidden() = runTest {
+ val underTest = createUnderTest(isEnabled = false)
+
+ val actual = collectLastValue(underTest.lockScreenState)
+
+ assertThat(actual()).isEqualTo(LockScreenState.Hidden)
+ }
+
+ @Test
+ fun lockScreenState_isEnabled_shouldEmitVisible() = runTest {
+ val stringResult = "Notetaking"
+ val underTest = createUnderTest(isEnabled = true)
+
+ val actual = collectLastValue(underTest.lockScreenState)
+
+ val expected =
+ LockScreenState.Visible(
+ icon =
+ Icon.Resource(
+ res = R.drawable.ic_note_task_shortcut_keyguard,
+ contentDescription = ContentDescription.Loaded(stringResult),
+ )
+ )
+ assertThat(actual()).isEqualTo(expected)
+ }
+
+ @Test
+ fun onTriggered_shouldLaunchNoteTask() {
+ val underTest = createUnderTest(isEnabled = false)
+
+ underTest.onTriggered(expandable = null)
+
+ verify(noteTaskController).showNoteTask()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt
index 3710281..57b6b2b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt
@@ -1,5 +1,6 @@
package com.android.systemui.settings
+import android.app.IActivityManager
import android.content.Context
import android.content.Intent
import android.content.pm.UserInfo
@@ -51,6 +52,7 @@
@Mock private lateinit var context: Context
@Mock private lateinit var userManager: UserManager
+ @Mock private lateinit var iActivityManager: IActivityManager
@Mock(stubOnly = true) private lateinit var dumpManager: DumpManager
@Mock(stubOnly = true) private lateinit var handler: Handler
@@ -67,7 +69,7 @@
`when`(context.user).thenReturn(UserHandle.SYSTEM)
`when`(context.createContextAsUser(ArgumentMatchers.any(), anyInt())).thenReturn(context)
- tracker = UserTrackerImpl(context, userManager, dumpManager, handler)
+ tracker = UserTrackerImpl(context, userManager, iActivityManager, dumpManager, handler)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
index e65bbb1..71ba215 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
@@ -16,11 +16,14 @@
package com.android.systemui.settings
+import android.app.IActivityManager
+import android.app.IUserSwitchObserver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.UserInfo
import android.os.Handler
+import android.os.IRemoteCallback
import android.os.UserHandle
import android.os.UserManager
import android.testing.AndroidTestingRunner
@@ -29,19 +32,20 @@
import com.android.systemui.dump.DumpManager
import com.android.systemui.util.mockito.capture
import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.Executor
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyString
import org.mockito.ArgumentMatchers.eq
import org.mockito.ArgumentMatchers.isNull
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
-import java.util.concurrent.Executor
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -51,6 +55,10 @@
private lateinit var context: Context
@Mock
private lateinit var userManager: UserManager
+ @Mock
+ private lateinit var iActivityManager: IActivityManager
+ @Mock
+ private lateinit var userSwitchingReply: IRemoteCallback
@Mock(stubOnly = true)
private lateinit var dumpManager: DumpManager
@Mock(stubOnly = true)
@@ -76,7 +84,7 @@
listOf(info)
}
- tracker = UserTrackerImpl(context, userManager, dumpManager, handler)
+ tracker = UserTrackerImpl(context, userManager, iActivityManager, dumpManager, handler)
}
@Test
@@ -125,8 +133,7 @@
verify(context).registerReceiverForAllUsers(
eq(tracker), capture(captor), isNull(), eq(handler))
with(captor.value) {
- assertThat(countActions()).isEqualTo(7)
- assertThat(hasAction(Intent.ACTION_USER_SWITCHED)).isTrue()
+ assertThat(countActions()).isEqualTo(6)
assertThat(hasAction(Intent.ACTION_USER_INFO_CHANGED)).isTrue()
assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)).isTrue()
assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)).isTrue()
@@ -158,8 +165,10 @@
tracker.initialize(0)
val newID = 5
- val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, newID)
- tracker.onReceive(context, intent)
+ val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
+ verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
+ captor.value.onUserSwitching(newID, userSwitchingReply)
+ verify(userSwitchingReply).sendResult(any())
verify(userManager).getProfiles(newID)
@@ -272,6 +281,24 @@
}
@Test
+ fun testCallbackCalledOnUserChanging() {
+ tracker.initialize(0)
+ val callback = TestCallback()
+ tracker.addCallback(callback, executor)
+
+ val newID = 5
+
+ val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
+ verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
+ captor.value.onUserSwitching(newID, userSwitchingReply)
+ verify(userSwitchingReply).sendResult(any())
+
+ assertThat(callback.calledOnUserChanging).isEqualTo(1)
+ assertThat(callback.lastUser).isEqualTo(newID)
+ assertThat(callback.lastUserContext?.userId).isEqualTo(newID)
+ }
+
+ @Test
fun testCallbackCalledOnUserChanged() {
tracker.initialize(0)
val callback = TestCallback()
@@ -279,8 +306,9 @@
val newID = 5
- val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, newID)
- tracker.onReceive(context, intent)
+ val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
+ verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
+ captor.value.onUserSwitchComplete(newID)
assertThat(callback.calledOnUserChanged).isEqualTo(1)
assertThat(callback.lastUser).isEqualTo(newID)
@@ -330,25 +358,36 @@
tracker.addCallback(callback, executor)
tracker.removeCallback(callback)
- val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, 5)
- tracker.onReceive(context, intent)
+ val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
+ verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
+ captor.value.onUserSwitching(newID, userSwitchingReply)
+ verify(userSwitchingReply).sendResult(any())
+ captor.value.onUserSwitchComplete(newID)
val intentProfiles = Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
.putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
tracker.onReceive(context, intentProfiles)
+ assertThat(callback.calledOnUserChanging).isEqualTo(0)
assertThat(callback.calledOnUserChanged).isEqualTo(0)
assertThat(callback.calledOnProfilesChanged).isEqualTo(0)
}
private class TestCallback : UserTracker.Callback {
+ var calledOnUserChanging = 0
var calledOnUserChanged = 0
var calledOnProfilesChanged = 0
var lastUser: Int? = null
var lastUserContext: Context? = null
var lastUserProfiles = emptyList<UserInfo>()
+ override fun onUserChanging(newUser: Int, userContext: Context) {
+ calledOnUserChanging++
+ lastUser = newUser
+ lastUserContext = userContext
+ }
+
override fun onUserChanged(newUser: Int, userContext: Context) {
calledOnUserChanged++
lastUser = newUser
@@ -360,4 +399,4 @@
lastUserProfiles = profiles
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 4c1b219..cf69515 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -321,6 +321,10 @@
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
+ // CentralSurfacesImpl's runtime flag check fails if the flag is absent.
+ // This value is unused, because test manifest is opted in.
+ mFeatureFlags.set(Flags.WM_ENABLE_PREDICTIVE_BACK_SYSUI, false);
+
IThermalService thermalService = mock(IThermalService.class);
mPowerManager = new PowerManager(mContext, mPowerManagerService, thermalService,
Handler.createAsync(Looper.myLooper()));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index c7a0582..c0537a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -630,6 +630,7 @@
@Test
public void transitionToUnlocked() {
+ mScrimController.setClipsQsScrim(false);
mScrimController.setRawPanelExpansionFraction(0f);
mScrimController.transitionTo(ScrimState.UNLOCKED);
finishAnimationsImmediately();
@@ -645,14 +646,21 @@
));
// Back scrim should be visible after start dragging
- mScrimController.setRawPanelExpansionFraction(0.3f);
+ mScrimController.setRawPanelExpansionFraction(0.29f);
assertScrimAlpha(Map.of(
mScrimInFront, TRANSPARENT,
mNotificationsScrim, TRANSPARENT,
mScrimBehind, SEMI_TRANSPARENT));
+ // Back scrim should be opaque at 30%
+ mScrimController.setRawPanelExpansionFraction(0.3f);
+ assertScrimAlpha(Map.of(
+ mScrimInFront, TRANSPARENT,
+ mNotificationsScrim, TRANSPARENT,
+ mScrimBehind, OPAQUE));
+
// Then, notification scrim should fade in
- mScrimController.setRawPanelExpansionFraction(0.7f);
+ mScrimController.setRawPanelExpansionFraction(0.31f);
assertScrimAlpha(Map.of(
mScrimInFront, TRANSPARENT,
mNotificationsScrim, SEMI_TRANSPARENT,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
index 6bfc2f1..7d0d57b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
@@ -19,9 +19,12 @@
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
+import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -35,6 +38,10 @@
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceTimeBy
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.yield
import org.junit.Test
import org.junit.runner.RunWith
@@ -231,6 +238,170 @@
}
}
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class ThrottleFlowTest : SysuiTestCase() {
+
+ @Test
+ fun doesNotAffectEmissions_whenDelayAtLeastEqualToPeriod() = runTest {
+ // Arrange
+ val choreographer = createChoreographer(this)
+ val output = mutableListOf<Int>()
+ val collectJob = backgroundScope.launch {
+ flow {
+ emit(1)
+ delay(1000)
+ emit(2)
+ }.throttle(1000, choreographer.fakeClock).toList(output)
+ }
+
+ // Act
+ choreographer.advanceAndRun(0)
+
+ // Assert
+ assertThat(output).containsExactly(1)
+
+ // Act
+ choreographer.advanceAndRun(999)
+
+ // Assert
+ assertThat(output).containsExactly(1)
+
+ // Act
+ choreographer.advanceAndRun(1)
+
+ // Assert
+ assertThat(output).containsExactly(1, 2)
+
+ // Cleanup
+ collectJob.cancel()
+ }
+
+ @Test
+ fun delaysEmissions_withShorterThanPeriodDelay_untilPeriodElapses() = runTest {
+ // Arrange
+ val choreographer = createChoreographer(this)
+ val output = mutableListOf<Int>()
+ val collectJob = backgroundScope.launch {
+ flow {
+ emit(1)
+ delay(500)
+ emit(2)
+ }.throttle(1000, choreographer.fakeClock).toList(output)
+ }
+
+ // Act
+ choreographer.advanceAndRun(0)
+
+ // Assert
+ assertThat(output).containsExactly(1)
+
+ // Act
+ choreographer.advanceAndRun(500)
+ choreographer.advanceAndRun(499)
+
+ // Assert
+ assertThat(output).containsExactly(1)
+
+ // Act
+ choreographer.advanceAndRun(1)
+
+ // Assert
+ assertThat(output).containsExactly(1, 2)
+
+ // Cleanup
+ collectJob.cancel()
+ }
+
+ @Test
+ fun filtersAllButLastEmission_whenMultipleEmissionsInPeriod() = runTest {
+ // Arrange
+ val choreographer = createChoreographer(this)
+ val output = mutableListOf<Int>()
+ val collectJob = backgroundScope.launch {
+ flow {
+ emit(1)
+ delay(500)
+ emit(2)
+ delay(500)
+ emit(3)
+ }.throttle(1000, choreographer.fakeClock).toList(output)
+ }
+
+ // Act
+ choreographer.advanceAndRun(0)
+
+ // Assert
+ assertThat(output).containsExactly(1)
+
+ // Act
+ choreographer.advanceAndRun(500)
+ choreographer.advanceAndRun(499)
+
+ // Assert
+ assertThat(output).containsExactly(1)
+
+ // Act
+ choreographer.advanceAndRun(1)
+
+ // Assert
+ assertThat(output).containsExactly(1, 3)
+
+ // Cleanup
+ collectJob.cancel()
+ }
+
+ @Test
+ fun filtersAllButLastEmission_andDelaysIt_whenMultipleEmissionsInShorterThanPeriod() = runTest {
+ // Arrange
+ val choreographer = createChoreographer(this)
+ val output = mutableListOf<Int>()
+ val collectJob = backgroundScope.launch {
+ flow {
+ emit(1)
+ delay(500)
+ emit(2)
+ delay(250)
+ emit(3)
+ }.throttle(1000, choreographer.fakeClock).toList(output)
+ }
+
+ // Act
+ choreographer.advanceAndRun(0)
+
+ // Assert
+ assertThat(output).containsExactly(1)
+
+ // Act
+ choreographer.advanceAndRun(500)
+ choreographer.advanceAndRun(250)
+ choreographer.advanceAndRun(249)
+
+ // Assert
+ assertThat(output).containsExactly(1)
+
+ // Act
+ choreographer.advanceAndRun(1)
+
+ // Assert
+ assertThat(output).containsExactly(1, 3)
+
+ // Cleanup
+ collectJob.cancel()
+ }
+
+ private fun createChoreographer(testScope: TestScope) = object {
+ val fakeClock = FakeSystemClock()
+
+ fun advanceAndRun(millis: Long) {
+ fakeClock.advanceTime(millis)
+ testScope.advanceTimeBy(millis)
+ testScope.runCurrent()
+ }
+ }
+}
+
private fun <T> assertThatFlow(flow: Flow<T>) =
object {
suspend fun emitsExactly(vararg emissions: T) =
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
index 0dd1fc7..251014f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
@@ -67,7 +67,10 @@
_userHandle = UserHandle.of(_userId)
val copy = callbacks.toList()
- copy.forEach { it.onUserChanged(_userId, userContext) }
+ copy.forEach {
+ it.onUserChanging(_userId, userContext)
+ it.onUserChanged(_userId, userContext)
+ }
}
fun onProfileChanged() {
diff --git a/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java b/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
index b20ff37..2ee9174 100644
--- a/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
+++ b/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
@@ -19,6 +19,7 @@
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
import static com.android.server.backup.BackupManagerService.TAG;
import static com.android.server.backup.UserBackupManagerService.PACKAGE_MANAGER_SENTINEL;
+import static com.android.server.backup.UserBackupManagerService.SETTINGS_PACKAGE;
import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
import static com.android.server.backup.UserBackupManagerService.WALLPAPER_PACKAGE;
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
@@ -56,8 +57,8 @@
public class BackupEligibilityRules {
private static final boolean DEBUG = false;
// List of system packages that are eligible for backup in non-system users.
- private static final Set<String> systemPackagesAllowedForAllUsers =
- Sets.newArraySet(PACKAGE_MANAGER_SENTINEL, PLATFORM_PACKAGE_NAME, WALLPAPER_PACKAGE);
+ private static final Set<String> systemPackagesAllowedForAllUsers = Sets.newArraySet(
+ PACKAGE_MANAGER_SENTINEL, PLATFORM_PACKAGE_NAME, WALLPAPER_PACKAGE, SETTINGS_PACKAGE);
private final PackageManager mPackageManager;
private final PackageManagerInternal mPackageManagerInternal;
diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java
index 67f5f1b..fce44f5 100644
--- a/services/core/java/com/android/server/BinaryTransparencyService.java
+++ b/services/core/java/com/android/server/BinaryTransparencyService.java
@@ -1074,11 +1074,6 @@
// register a package observer to detect updates to preloads
mPackageManagerInternal.getPackageList(new PackageManagerInternal.PackageListObserver() {
@Override
- public void onPackageAdded(String packageName, int uid) {
-
- }
-
- @Override
public void onPackageChanged(String packageName, int uid) {
// check if the updated package is a preloaded app.
PackageManager pm = mContext.getPackageManager();
@@ -1094,11 +1089,6 @@
UpdateMeasurementsJobService.scheduleBinaryMeasurements(mContext,
BinaryTransparencyService.this);
}
-
- @Override
- public void onPackageRemoved(String packageName, int uid) {
-
- }
});
// TODO(b/264428429): Register observer for updates to APEXs.
diff --git a/services/core/java/com/android/server/am/ActivityManagerLocal.java b/services/core/java/com/android/server/am/ActivityManagerLocal.java
index abaa8c7..31ea092 100644
--- a/services/core/java/com/android/server/am/ActivityManagerLocal.java
+++ b/services/core/java/com/android/server/am/ActivityManagerLocal.java
@@ -75,8 +75,9 @@
* @param conn Receives information as the service is started and stopped.
* This must be a valid ServiceConnection object; it must not be null.
* @param clientAppUid Uid of the app for which the sdk sandbox process needs to be spawned.
- * @param clientApplicationThread ApplicationThread object of the app for which the sdk sandboox
- * is spawned.
+ * @param clientAppProcessToken process token used to uniquely identify the client app
+ * process binding to the SDK sandbox. This is obtained using
+ * {@link Context#getProcessToken()}.
* @param clientAppPackage Package of the app for which the sdk sandbox process needs to
* be spawned. This package must belong to the clientAppUid.
* @param processName Unique identifier for the service instance. Each unique name here will
@@ -92,7 +93,7 @@
*/
@SuppressLint("RethrowRemoteException")
boolean bindSdkSandboxService(@NonNull Intent service, @NonNull ServiceConnection conn,
- int clientAppUid, @NonNull IBinder clientApplicationThread,
+ int clientAppUid, @NonNull IBinder clientAppProcessToken,
@NonNull String clientAppPackage, @NonNull String processName,
@Context.BindServiceFlags int flags)
throws RemoteException;
@@ -112,9 +113,9 @@
/**
* Kill an app process associated with an SDK sandbox.
*
- * @param clientApplicationThreadBinder binder value of the
- * {@link android.app.IApplicationThread} of a client app process associated with a
- * sandbox. This is obtained using {@link Context#getIApplicationThreadBinder()}.
+ * @param clientAppProcessToken process token used to uniquely identify the client app
+ * process associated with an SDK sandbox. This is obtained using
+ * {@link Context#getProcessToken()}.
*/
- void killSdkSandboxClientAppProcess(@NonNull IBinder clientApplicationThreadBinder);
+ void killSdkSandboxClientAppProcess(@NonNull IBinder clientAppProcessToken);
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 3df060b..67166b8 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -61,7 +61,6 @@
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.BroadcastOptions;
-import android.app.Dialog;
import android.app.IStopUserCallback;
import android.app.IUserSwitchObserver;
import android.app.KeyguardManager;
@@ -1677,6 +1676,7 @@
R.anim.screen_user_exit, R.anim.screen_user_enter);
t.traceEnd();
}
+ dismissUserSwitchDialog(); // so that we don't hold a reference to mUserSwitchingDialog
boolean needStart = false;
boolean updateUmState = false;
@@ -1863,6 +1863,8 @@
boolean success = startUser(targetUserId, USER_START_MODE_FOREGROUND);
if (!success) {
mInjector.getWindowManager().setSwitchingUser(false);
+ mTargetUserId = UserHandle.USER_NULL;
+ dismissUserSwitchDialog();
}
}
@@ -2011,6 +2013,10 @@
return true;
}
+ private void dismissUserSwitchDialog() {
+ mInjector.dismissUserSwitchingDialog();
+ }
+
private void showUserSwitchDialog(Pair<UserInfo, UserInfo> fromToUserPair) {
// The dialog will show and then initiate the user switch by calling startUserInForeground
mInjector.showUserSwitchingDialog(fromToUserPair.first, fromToUserPair.second,
@@ -3462,6 +3468,9 @@
private UserManagerService mUserManager;
private UserManagerInternal mUserManagerInternal;
private Handler mHandler;
+ private final Object mUserSwitchingDialogLock = new Object();
+ @GuardedBy("mUserSwitchingDialogLock")
+ private UserSwitchingDialog mUserSwitchingDialog;
Injector(ActivityManagerService service) {
mService = service;
@@ -3637,6 +3646,15 @@
mService.mCpHelper.installEncryptionUnawareProviders(userId);
}
+ void dismissUserSwitchingDialog() {
+ synchronized (mUserSwitchingDialogLock) {
+ if (mUserSwitchingDialog != null) {
+ mUserSwitchingDialog.dismiss();
+ mUserSwitchingDialog = null;
+ }
+ }
+ }
+
void showUserSwitchingDialog(UserInfo fromUser, UserInfo toUser,
String switchingFromSystemUserMessage, String switchingToSystemUserMessage) {
if (mService.mContext.getPackageManager()
@@ -3647,10 +3665,13 @@
Slogf.w(TAG, "Showing user switch dialog on UserController, it could cause a race "
+ "condition if it's shown by CarSystemUI as well");
}
- final Dialog d = new UserSwitchingDialog(mService, mService.mContext, fromUser,
- toUser, true /* above system */, switchingFromSystemUserMessage,
- switchingToSystemUserMessage);
- d.show();
+ synchronized (mUserSwitchingDialogLock) {
+ dismissUserSwitchingDialog();
+ mUserSwitchingDialog = new UserSwitchingDialog(mService, mService.mContext,
+ fromUser, toUser, true /* above system */, switchingFromSystemUserMessage,
+ switchingToSystemUserMessage);
+ mUserSwitchingDialog.show();
+ }
}
void reportGlobalUsageEvent(int event) {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 9877ed3..21bd7bc 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -1718,8 +1718,9 @@
mBrokerEventWakeLock.acquire(BROKER_WAKELOCK_TIMEOUT_MS);
} catch (Exception e) {
Log.e(TAG, "Exception acquiring wakelock", e);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
- Binder.restoreCallingIdentity(identity);
}
if (MESSAGES_MUTE_MUSIC.contains(msg)) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index cb98c66..92a9f46 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -908,9 +908,6 @@
private final SoundDoseHelper mSoundDoseHelper;
- @GuardedBy("mSettingsLock")
- private int mCurrentImeUid;
-
private final Object mSupportedSystemUsagesLock = new Object();
@GuardedBy("mSupportedSystemUsagesLock")
private @AttributeSystemUsage int[] mSupportedSystemUsages =
@@ -2856,10 +2853,14 @@
super.getPreferredDevicesForStrategy_enforcePermission();
List<AudioDeviceAttributes> devices = new ArrayList<>();
+ int status = AudioSystem.SUCCESS;
final long identity = Binder.clearCallingIdentity();
- final int status = AudioSystem.getDevicesForRoleAndStrategy(
- strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
- Binder.restoreCallingIdentity(identity);
+ try {
+ status = AudioSystem.getDevicesForRoleAndStrategy(
+ strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
if (status != AudioSystem.SUCCESS) {
Log.e(TAG, String.format("Error %d in getPreferredDeviceForStrategy(%d)",
status, strategy));
@@ -3045,10 +3046,14 @@
super.getPreferredDevicesForCapturePreset_enforcePermission();
List<AudioDeviceAttributes> devices = new ArrayList<>();
+ int status = AudioSystem.SUCCESS;
final long identity = Binder.clearCallingIdentity();
- final int status = AudioSystem.getDevicesForRoleAndCapturePreset(
- capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
- Binder.restoreCallingIdentity(identity);
+ try {
+ status = AudioSystem.getDevicesForRoleAndCapturePreset(
+ capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
if (status != AudioSystem.SUCCESS) {
Log.e(TAG, String.format("Error %d in getPreferredDeviceForCapturePreset(%d)",
status, capturePreset));
@@ -3653,11 +3658,14 @@
}
final long identity = Binder.clearCallingIdentity();
- mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(
- isMuteAdjust, getStreamVolume(AudioSystem.STREAM_MUSIC),
- getStreamMaxVolume(AudioSystem.STREAM_MUSIC),
- isStreamMute(AudioSystem.STREAM_MUSIC));
- Binder.restoreCallingIdentity(identity);
+ try {
+ mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(
+ isMuteAdjust, getStreamVolume(AudioSystem.STREAM_MUSIC),
+ getStreamMaxVolume(AudioSystem.STREAM_MUSIC),
+ isStreamMute(AudioSystem.STREAM_MUSIC));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
private int getNewRingerMode(int stream, int index, int flags) {
@@ -4985,26 +4993,26 @@
if (getCurrentUserId() == userId || userId == android.os.Process.SYSTEM_UID) {
final boolean currentMute = mAudioSystem.isMicrophoneMuted();
final long identity = Binder.clearCallingIdentity();
- final int ret = mAudioSystem.muteMicrophone(muted);
-
- // update cache with the real state independently from what was set
- mMicMuteFromSystemCached = mAudioSystem.isMicrophoneMuted();
- if (ret != AudioSystem.AUDIO_STATUS_OK) {
- Log.e(TAG, "Error changing mic mute state to " + muted + " current:"
- + mMicMuteFromSystemCached);
- }
-
- new MediaMetrics.Item(MediaMetrics.Name.AUDIO_MIC)
- .setUid(userId)
- .set(MediaMetrics.Property.EVENT, "setMicrophoneMuteNoCallerCheck")
- .set(MediaMetrics.Property.MUTE, mMicMuteFromSystemCached
- ? MediaMetrics.Value.ON : MediaMetrics.Value.OFF)
- .set(MediaMetrics.Property.REQUEST, muted
- ? MediaMetrics.Value.MUTE : MediaMetrics.Value.UNMUTE)
- .set(MediaMetrics.Property.STATUS, ret)
- .record();
-
try {
+ final int ret = mAudioSystem.muteMicrophone(muted);
+
+ // update cache with the real state independently from what was set
+ mMicMuteFromSystemCached = mAudioSystem.isMicrophoneMuted();
+ if (ret != AudioSystem.AUDIO_STATUS_OK) {
+ Log.e(TAG, "Error changing mic mute state to " + muted + " current:"
+ + mMicMuteFromSystemCached);
+ }
+
+ new MediaMetrics.Item(MediaMetrics.Name.AUDIO_MIC)
+ .setUid(userId)
+ .set(MediaMetrics.Property.EVENT, "setMicrophoneMuteNoCallerCheck")
+ .set(MediaMetrics.Property.MUTE, mMicMuteFromSystemCached
+ ? MediaMetrics.Value.ON : MediaMetrics.Value.OFF)
+ .set(MediaMetrics.Property.REQUEST, muted
+ ? MediaMetrics.Value.MUTE : MediaMetrics.Value.UNMUTE)
+ .set(MediaMetrics.Property.STATUS, ret)
+ .record();
+
// send the intent even if there was a failure to change the actual mute state:
// the AudioManager.setMicrophoneMute API doesn't have a return value to
// indicate if the call failed to successfully change the mute state, and receiving
@@ -5614,9 +5622,13 @@
+ mMode.get() + " requested mode: " + requestedMode);
}
if (mode != mMode.get() || force) {
+ int status = AudioSystem.SUCCESS;
final long identity = Binder.clearCallingIdentity();
- int status = mAudioSystem.setPhoneState(mode, uid);
- Binder.restoreCallingIdentity(identity);
+ try {
+ status = mAudioSystem.setPhoneState(mode, uid);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
if (status == AudioSystem.AUDIO_STATUS_OK) {
if (DEBUG_MODE) {
Log.v(TAG, "onUpdateAudioMode: mode successfully set to " + mode);
@@ -6023,17 +6035,22 @@
}
final long ident = Binder.clearCallingIdentity();
- boolean status =
- mDeviceBroker.setCommunicationDevice(cb, pid, device, eventSource);
- Binder.restoreCallingIdentity(ident);
- return status;
+ try {
+ return mDeviceBroker.setCommunicationDevice(cb, pid, device, eventSource);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
/** @see AudioManager#getCommunicationDevice() */
public int getCommunicationDevice() {
+ AudioDeviceInfo device = null;
final long ident = Binder.clearCallingIdentity();
- AudioDeviceInfo device = mDeviceBroker.getCommunicationDevice();
- Binder.restoreCallingIdentity(ident);
+ try {
+ device = mDeviceBroker.getCommunicationDevice();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
if (device == null) {
return 0;
}
@@ -6083,8 +6100,11 @@
? MediaMetrics.Value.ON : MediaMetrics.Value.OFF)
.record();
final long ident = Binder.clearCallingIdentity();
- mDeviceBroker.setSpeakerphoneOn(cb, pid, on, eventSource);
- Binder.restoreCallingIdentity(ident);
+ try {
+ mDeviceBroker.setSpeakerphoneOn(cb, pid, on, eventSource);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
/** @see AudioManager#isSpeakerphoneOn() */
@@ -6224,8 +6244,11 @@
return;
}
final long ident = Binder.clearCallingIdentity();
- mDeviceBroker.startBluetoothScoForClient(cb, pid, scoAudioMode, eventSource);
- Binder.restoreCallingIdentity(ident);
+ try {
+ mDeviceBroker.startBluetoothScoForClient(cb, pid, scoAudioMode, eventSource);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
mmi.record();
}
@@ -6241,8 +6264,11 @@
.append(") from u/pid:").append(uid).append("/")
.append(pid).toString();
final long ident = Binder.clearCallingIdentity();
- mDeviceBroker.stopBluetoothScoForClient(cb, pid, eventSource);
- Binder.restoreCallingIdentity(ident);
+ try {
+ mDeviceBroker.stopBluetoothScoForClient(cb, pid, eventSource);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
new MediaMetrics.Item(MediaMetrics.Name.AUDIO_BLUETOOTH)
.setUid(uid)
.setPid(pid)
@@ -6531,8 +6557,11 @@
(TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
final long ident = Binder.clearCallingIdentity();
- IsInCall = telecomManager.isInCall();
- Binder.restoreCallingIdentity(ident);
+ try {
+ IsInCall = telecomManager.isInCall();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
int mode = mMode.get();
return (IsInCall
@@ -6672,10 +6701,13 @@
private void queueMsgUnderWakeLock(Handler handler, int msg,
int arg1, int arg2, Object obj, int delay) {
final long ident = Binder.clearCallingIdentity();
- // Always acquire the wake lock as AudioService because it is released by the
- // message handler.
- mAudioEventWakeLock.acquire();
- Binder.restoreCallingIdentity(ident);
+ try {
+ // Always acquire the wake lock as AudioService because it is released by the
+ // message handler.
+ mAudioEventWakeLock.acquire();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
sendMsg(handler, msg, SENDMSG_QUEUE, arg1, arg2, obj, delay);
}
@@ -10075,6 +10107,55 @@
"com.android.server.audio", "AudioService");
}
+ @Override
+ @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
+ public float getRs2Value() {
+ super.getRs2Value_enforcePermission();
+ return mSoundDoseHelper.getRs2Value();
+ }
+
+ @Override
+ @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
+ public void setRs2Value(float rs2Value) {
+ super.setRs2Value_enforcePermission();
+ mSoundDoseHelper.setRs2Value(rs2Value);
+ }
+
+ @Override
+ @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
+ public float getCsd() {
+ super.getCsd_enforcePermission();
+ return mSoundDoseHelper.getCsd();
+ }
+
+ @Override
+ @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
+ public void setCsd(float csd) {
+ super.setCsd_enforcePermission();
+ mSoundDoseHelper.setCsd(csd);
+ }
+
+ @Override
+ @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
+ public void forceUseFrameworkMel(boolean useFrameworkMel) {
+ super.forceUseFrameworkMel_enforcePermission();
+ mSoundDoseHelper.forceUseFrameworkMel(useFrameworkMel);
+ }
+
+ @Override
+ @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
+ public void forceComputeCsdOnAllDevices(boolean computeCsdOnAllDevices) {
+ super.forceComputeCsdOnAllDevices_enforcePermission();
+ mSoundDoseHelper.forceComputeCsdOnAllDevices(computeCsdOnAllDevices);
+ }
+
+ @Override
+ @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
+ public boolean isCsdEnabled() {
+ super.isCsdEnabled_enforcePermission();
+ return mSoundDoseHelper.isCsdEnabled();
+ }
+
//==========================================================================================
// Hdmi CEC:
// - System audio mode:
@@ -10426,7 +10507,6 @@
+ " FromRestrictions=" + mMicMuteFromRestrictions
+ " FromApi=" + mMicMuteFromApi
+ " from system=" + mMicMuteFromSystemCached);
- pw.print(" mCurrentImeUid="); pw.println(mCurrentImeUid);
dumpAccessibilityServiceUids(pw);
dumpAssistantServicesUids(pw);
@@ -10561,6 +10641,15 @@
}
@Override
+ @Nullable
+ public IVolumeController getVolumeController() {
+ enforceVolumeController("get the volume controller");
+ if (DEBUG_VOL) Log.d(TAG, "Volume controller: " + mVolumeController);
+
+ return mVolumeController.getController();
+ }
+
+ @Override
public void notifyVolumeControllerVisible(final IVolumeController controller, boolean visible) {
enforceVolumeController("notify about volume controller visibility");
@@ -10605,6 +10694,10 @@
mVisible = false;
}
+ public IVolumeController getController() {
+ return mController;
+ }
+
public void loadSettings(ContentResolver cr) {
mLongPressTimeout = mSettings.getSecureIntForUser(cr,
Settings.Secure.LONG_PRESS_TIMEOUT, 500, UserHandle.USER_CURRENT);
@@ -11694,13 +11787,16 @@
int callingUid = Binder.getCallingUid();
int flags = AudioAttributes.capturePolicyToFlags(capturePolicy, 0x0);
final long identity = Binder.clearCallingIdentity();
- synchronized (mPlaybackMonitor) {
- int result = mAudioSystem.setAllowedCapturePolicy(callingUid, flags);
- if (result == AudioSystem.AUDIO_STATUS_OK) {
- mPlaybackMonitor.setAllowedCapturePolicy(callingUid, capturePolicy);
+ try {
+ synchronized (mPlaybackMonitor) {
+ int result = mAudioSystem.setAllowedCapturePolicy(callingUid, flags);
+ if (result == AudioSystem.AUDIO_STATUS_OK) {
+ mPlaybackMonitor.setAllowedCapturePolicy(callingUid, capturePolicy);
+ }
+ return result;
}
+ } finally {
Binder.restoreCallingIdentity(identity);
- return result;
}
}
@@ -11711,9 +11807,11 @@
public int getAllowedCapturePolicy() {
int callingUid = Binder.getCallingUid();
final long identity = Binder.clearCallingIdentity();
- int capturePolicy = mPlaybackMonitor.getAllowedCapturePolicy(callingUid);
- Binder.restoreCallingIdentity(identity);
- return capturePolicy;
+ try {
+ return mPlaybackMonitor.getAllowedCapturePolicy(callingUid);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
//======================
@@ -11855,8 +11953,11 @@
}
}
final long identity = Binder.clearCallingIdentity();
- mAudioSystem.registerPolicyMixes(mMixes, false);
- Binder.restoreCallingIdentity(identity);
+ try {
+ mAudioSystem.registerPolicyMixes(mMixes, false);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
synchronized (mAudioPolicies) {
mAudioPolicies.remove(mPolicyCallback.asBinder());
}
@@ -11915,9 +12016,12 @@
@AudioSystem.AudioSystemError int connectMixes() {
final long identity = Binder.clearCallingIdentity();
- int status = mAudioSystem.registerPolicyMixes(mMixes, true);
- Binder.restoreCallingIdentity(identity);
- return status;
+ try {
+ return mAudioSystem.registerPolicyMixes(mMixes, true);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+
}
int setUidDeviceAffinities(int uid, @NonNull int[] types, @NonNull String[] addresses) {
diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java
index 919b850..bc61b37 100644
--- a/services/core/java/com/android/server/audio/SoundDoseHelper.java
+++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java
@@ -53,6 +53,7 @@
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
/**
@@ -70,9 +71,6 @@
/*package*/ static final String ACTION_CHECK_MUSIC_ACTIVE =
"com.android.server.audio.action.CHECK_MUSIC_ACTIVE";
- /** Flag to enable/disable the sound dose computation. */
- private static final boolean USE_CSD_FOR_SAFE_HEARING = false;
-
// mSafeMediaVolumeState indicates whether the media volume is limited over headphones.
// It is SAFE_MEDIA_VOLUME_NOT_CONFIGURED at boot time until a network service is connected
// or the configure time is elapsed. It is then set to SAFE_MEDIA_VOLUME_ACTIVE or
@@ -96,8 +94,6 @@
private static final int MUSIC_ACTIVE_POLL_PERIOD_MS = 60000; // 1 minute polling interval
private static final int REQUEST_CODE_CHECK_MUSIC_ACTIVE = 1;
- private static final float CUSTOM_RS2_VALUE = 90;
-
// timeouts for the CSD warnings, -1 means no timeout (dialog must be ack'd by user)
private static final int CSD_WARNING_TIMEOUT_MS_DOSE_1X = 7000;
private static final int CSD_WARNING_TIMEOUT_MS_DOSE_5X = 5000;
@@ -233,6 +229,94 @@
initCsd();
}
+ float getRs2Value() {
+ if (!mEnableCsd) {
+ return 0.f;
+ }
+
+ Objects.requireNonNull(mSoundDose, "Sound dose interface not initialized");
+ try {
+ return mSoundDose.getOutputRs2();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception while getting the RS2 exposure value", e);
+ return 0.f;
+ }
+ }
+
+ void setRs2Value(float rs2Value) {
+ if (!mEnableCsd) {
+ return;
+ }
+
+ Objects.requireNonNull(mSoundDose, "Sound dose interface not initialized");
+ try {
+ mSoundDose.setOutputRs2(rs2Value);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception while setting the RS2 exposure value", e);
+ }
+ }
+
+ float getCsd() {
+ if (!mEnableCsd) {
+ return -1.f;
+ }
+
+ Objects.requireNonNull(mSoundDose, "Sound dose interface not initialized");
+ try {
+ return mSoundDose.getCsd();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception while getting the CSD value", e);
+ return -1.f;
+ }
+ }
+
+ void setCsd(float csd) {
+ if (!mEnableCsd) {
+ return;
+ }
+
+ Objects.requireNonNull(mSoundDose, "Sound dose interface not initialized");
+ try {
+ final SoundDoseRecord record = new SoundDoseRecord();
+ record.timestamp = System.currentTimeMillis();
+ record.value = csd;
+ final SoundDoseRecord[] recordArray = new SoundDoseRecord[] { record };
+ mSoundDose.resetCsd(csd, recordArray);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception while setting the CSD value", e);
+ }
+ }
+
+ void forceUseFrameworkMel(boolean useFrameworkMel) {
+ if (!mEnableCsd) {
+ return;
+ }
+
+ Objects.requireNonNull(mSoundDose, "Sound dose interface not initialized");
+ try {
+ mSoundDose.forceUseFrameworkMel(useFrameworkMel);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception while forcing the internal MEL computation", e);
+ }
+ }
+
+ void forceComputeCsdOnAllDevices(boolean computeCsdOnAllDevices) {
+ if (!mEnableCsd) {
+ return;
+ }
+
+ Objects.requireNonNull(mSoundDose, "Sound dose interface not initialized");
+ try {
+ mSoundDose.forceComputeCsdOnAllDevices(computeCsdOnAllDevices);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception while forcing CSD computation on all devices", e);
+ }
+ }
+
+ boolean isCsdEnabled() {
+ return mEnableCsd;
+ }
+
/*package*/ int safeMediaVolumeIndex(int device) {
if (!mSafeMediaVolumeDevices.contains(device)) {
return MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC];
@@ -293,7 +377,7 @@
|| (AudioService.mStreamVolumeAlias[streamType] != AudioSystem.STREAM_MUSIC)
|| (!mSafeMediaVolumeDevices.contains(device))
|| (index <= safeMediaVolumeIndex(device))
- || USE_CSD_FOR_SAFE_HEARING;
+ || mEnableCsd;
}
/*package*/ boolean willDisplayWarningAfterCheckVolume(int streamType, int index, int device,
@@ -329,7 +413,7 @@
/*package*/ void scheduleMusicActiveCheck() {
synchronized (mSafeMediaVolumeStateLock) {
cancelMusicActiveCheck();
- if (!USE_CSD_FOR_SAFE_HEARING) {
+ if (!mEnableCsd) {
mMusicActiveIntent = PendingIntent.getBroadcast(mContext,
REQUEST_CODE_CHECK_MUSIC_ACTIVE,
new Intent(ACTION_CHECK_MUSIC_ACTIVE),
@@ -460,14 +544,13 @@
private void initCsd() {
synchronized (mSafeMediaVolumeStateLock) {
- if (USE_CSD_FOR_SAFE_HEARING) {
+ if (mEnableCsd) {
Log.v(TAG, "Initializing sound dose");
mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_DISABLED;
mSoundDose = AudioSystem.getSoundDoseInterface(mSoundDoseCallback);
try {
if (mSoundDose != null && mSoundDose.asBinder().isBinderAlive()) {
- mSoundDose.setOutputRs2(CUSTOM_RS2_VALUE);
if (mCurrentCsd != 0.f) {
Log.d(TAG, "Resetting the saved sound dose value " + mCurrentCsd);
SoundDoseRecord[] records = mDoseRecords.toArray(
@@ -502,7 +585,7 @@
// The persisted state is either "disabled" or "active": this is the state applied
// next time we boot and cannot be "inactive"
int persistedState;
- if (safeMediaVolumeEnabled && !safeMediaVolumeBypass && !USE_CSD_FOR_SAFE_HEARING) {
+ if (safeMediaVolumeEnabled && !safeMediaVolumeBypass && !mEnableCsd) {
persistedState = SAFE_MEDIA_VOLUME_ACTIVE;
// The state can already be "inactive" here if the user has forced it before
// the 30 seconds timeout for forced configuration. In this case we don't reset
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 06b99f8..d558e69 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -109,7 +109,6 @@
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.text.TextUtils;
-import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
import android.util.IntArray;
@@ -261,13 +260,6 @@
final SparseArray<Pair<IVirtualDevice, DisplayWindowPolicyController>>
mDisplayWindowPolicyControllers = new SparseArray<>();
- /**
- * Map of every internal primary display device {@link HighBrightnessModeMetadata}s indexed by
- * {@link DisplayDevice#mUniqueId}.
- */
- public final ArrayMap<String, HighBrightnessModeMetadata> mHighBrightnessModeMetadataMap =
- new ArrayMap<>();
-
// List of all currently registered display adapters.
private final ArrayList<DisplayAdapter> mDisplayAdapters = new ArrayList<DisplayAdapter>();
@@ -1648,16 +1640,7 @@
DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
if (dpc != null) {
- final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
- if (device == null) {
- Slog.wtf(TAG, "Display Device is null in DisplayManagerService for display: "
- + display.getDisplayIdLocked());
- return;
- }
-
- final String uniqueId = device.getUniqueId();
- HighBrightnessModeMetadata hbmMetadata = mHighBrightnessModeMetadataMap.get(uniqueId);
- dpc.onDisplayChanged(hbmMetadata);
+ dpc.onDisplayChanged();
}
}
@@ -1715,15 +1698,7 @@
final int displayId = display.getDisplayIdLocked();
final DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
if (dpc != null) {
- final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
- if (device == null) {
- Slog.wtf(TAG, "Display Device is null in DisplayManagerService for display: "
- + display.getDisplayIdLocked());
- return;
- }
- final String uniqueId = device.getUniqueId();
- HighBrightnessModeMetadata hbmMetadata = mHighBrightnessModeMetadataMap.get(uniqueId);
- dpc.onDisplayChanged(hbmMetadata);
+ dpc.onDisplayChanged();
}
}
@@ -2676,31 +2651,6 @@
mLogicalDisplayMapper.forEachLocked(this::addDisplayPowerControllerLocked);
}
- private HighBrightnessModeMetadata getHighBrightnessModeMetadata(LogicalDisplay display) {
- final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
- if (device == null) {
- Slog.wtf(TAG, "Display Device is null in DisplayPowerController for display: "
- + display.getDisplayIdLocked());
- return null;
- }
-
- // HBM brightness mode is only applicable to internal physical displays.
- if (display.getDisplayInfoLocked().type != Display.TYPE_INTERNAL) {
- return null;
- }
-
- final String uniqueId = device.getUniqueId();
-
- if (mHighBrightnessModeMetadataMap.containsKey(uniqueId)) {
- return mHighBrightnessModeMetadataMap.get(uniqueId);
- }
-
- // HBM Time info not present. Create a new one for this physical display.
- HighBrightnessModeMetadata hbmInfo = new HighBrightnessModeMetadata();
- mHighBrightnessModeMetadataMap.put(uniqueId, hbmInfo);
- return hbmInfo;
- }
-
@RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
private void addDisplayPowerControllerLocked(LogicalDisplay display) {
if (mPowerHandler == null) {
@@ -2716,23 +2666,17 @@
display, mSyncRoot);
final DisplayPowerControllerInterface displayPowerController;
- // If display is internal and has a HighBrightnessModeMetadata mapping, use that.
- // Or create a new one and use that.
- // We also need to pass a mapping of the HighBrightnessModeTimeInfoMap to
- // displayPowerController, so the hbm info can be correctly associated
- // with the corresponding displaydevice.
- HighBrightnessModeMetadata hbmMetadata = getHighBrightnessModeMetadata(display);
if (DeviceConfig.getBoolean("display_manager",
"use_newly_structured_display_power_controller", true)) {
displayPowerController = new DisplayPowerController2(
mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
- () -> handleBrightnessChange(display), hbmMetadata);
+ () -> handleBrightnessChange(display));
} else {
displayPowerController = new DisplayPowerController(
mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
- () -> handleBrightnessChange(display), hbmMetadata);
+ () -> handleBrightnessChange(display));
}
mDisplayPowerControllers.append(display.getDisplayIdLocked(), displayPowerController);
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 142ec68..cdaa3d0 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -388,7 +388,6 @@
private float[] mNitsRange;
private final HighBrightnessModeController mHbmController;
- private final HighBrightnessModeMetadata mHighBrightnessModeMetadata;
private final BrightnessThrottler mBrightnessThrottler;
@@ -506,14 +505,13 @@
DisplayPowerCallbacks callbacks, Handler handler,
SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay,
BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting,
- Runnable onBrightnessChangeRunnable, HighBrightnessModeMetadata hbmMetadata) {
+ Runnable onBrightnessChangeRunnable) {
mInjector = injector != null ? injector : new Injector();
mClock = mInjector.getClock();
mLogicalDisplay = logicalDisplay;
mDisplayId = mLogicalDisplay.getDisplayIdLocked();
mTag = "DisplayPowerController[" + mDisplayId + "]";
- mHighBrightnessModeMetadata = hbmMetadata;
mSuspendBlockerIdUnfinishedBusiness = getSuspendBlockerUnfinishedBusinessId(mDisplayId);
mSuspendBlockerIdOnStateChanged = getSuspendBlockerOnStateChangedId(mDisplayId);
mSuspendBlockerIdProxPositive = getSuspendBlockerProxPositiveId(mDisplayId);
@@ -792,7 +790,7 @@
* Make sure DisplayManagerService.mSyncRoot is held when this is called
*/
@Override
- public void onDisplayChanged(HighBrightnessModeMetadata hbmMetadata) {
+ public void onDisplayChanged() {
final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
if (device == null) {
Slog.wtf(mTag, "Display Device is null in DisplayPowerController for display: "
@@ -814,7 +812,7 @@
mUniqueDisplayId = uniqueId;
mDisplayStatsId = mUniqueDisplayId.hashCode();
mDisplayDeviceConfig = config;
- loadFromDisplayDeviceConfig(token, info, hbmMetadata);
+ loadFromDisplayDeviceConfig(token, info);
/// Since the underlying display-device changed, we really don't know the
// last command that was sent to change it's state. Lets assume it is unknown so
@@ -866,8 +864,7 @@
}
}
- private void loadFromDisplayDeviceConfig(IBinder token, DisplayDeviceInfo info,
- HighBrightnessModeMetadata hbmMetadata) {
+ private void loadFromDisplayDeviceConfig(IBinder token, DisplayDeviceInfo info) {
// All properties that depend on the associated DisplayDevice and the DDC must be
// updated here.
loadBrightnessRampRates();
@@ -880,7 +877,6 @@
mBrightnessRampIncreaseMaxTimeMillis,
mBrightnessRampDecreaseMaxTimeMillis);
}
- mHbmController.setHighBrightnessModeMetadata(hbmMetadata);
mHbmController.resetHbmData(info.width, info.height, token, info.uniqueId,
mDisplayDeviceConfig.getHighBrightnessModeData(),
new HighBrightnessModeController.HdrBrightnessDeviceConfig() {
@@ -1965,7 +1961,7 @@
if (mAutomaticBrightnessController != null) {
mAutomaticBrightnessController.update();
}
- }, mHighBrightnessModeMetadata, mContext);
+ }, mContext);
}
private BrightnessThrottler createBrightnessThrottlerLocked() {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 96342f3..da59cca 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -328,7 +328,6 @@
private float[] mNitsRange;
private final HighBrightnessModeController mHbmController;
- private final HighBrightnessModeMetadata mHighBrightnessModeMetadata;
private final BrightnessThrottler mBrightnessThrottler;
@@ -433,7 +432,7 @@
DisplayPowerCallbacks callbacks, Handler handler,
SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay,
BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting,
- Runnable onBrightnessChangeRunnable, HighBrightnessModeMetadata hbmMetadata) {
+ Runnable onBrightnessChangeRunnable) {
mInjector = injector != null ? injector : new Injector();
mClock = mInjector.getClock();
@@ -449,7 +448,6 @@
mDisplayPowerProximityStateController = mInjector.getDisplayPowerProximityStateController(
mWakelockController, mDisplayDeviceConfig, mHandler.getLooper(),
() -> updatePowerState(), mDisplayId, mSensorManager);
- mHighBrightnessModeMetadata = hbmMetadata;
mDisplayStateController = new DisplayStateController(mDisplayPowerProximityStateController);
mTag = "DisplayPowerController2[" + mDisplayId + "]";
@@ -709,7 +707,7 @@
* Make sure DisplayManagerService.mSyncRoot lock is held when this is called
*/
@Override
- public void onDisplayChanged(HighBrightnessModeMetadata hbmMetadata) {
+ public void onDisplayChanged() {
final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
if (device == null) {
Slog.wtf(mTag, "Display Device is null in DisplayPowerController2 for display: "
@@ -723,7 +721,6 @@
final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
final boolean isEnabled = mLogicalDisplay.isEnabledLocked();
final boolean isInTransition = mLogicalDisplay.isInTransitionLocked();
-
mHandler.post(() -> {
boolean changed = false;
if (mDisplayDevice != device) {
@@ -732,7 +729,7 @@
mUniqueDisplayId = uniqueId;
mDisplayStatsId = mUniqueDisplayId.hashCode();
mDisplayDeviceConfig = config;
- loadFromDisplayDeviceConfig(token, info, hbmMetadata);
+ loadFromDisplayDeviceConfig(token, info);
mDisplayPowerProximityStateController.notifyDisplayDeviceChanged(config);
// Since the underlying display-device changed, we really don't know the
@@ -781,8 +778,7 @@
}
}
- private void loadFromDisplayDeviceConfig(IBinder token, DisplayDeviceInfo info,
- HighBrightnessModeMetadata hbmMetadata) {
+ private void loadFromDisplayDeviceConfig(IBinder token, DisplayDeviceInfo info) {
// All properties that depend on the associated DisplayDevice and the DDC must be
// updated here.
loadBrightnessRampRates();
@@ -794,7 +790,6 @@
mBrightnessRampIncreaseMaxTimeMillis,
mBrightnessRampDecreaseMaxTimeMillis);
}
- mHbmController.setHighBrightnessModeMetadata(hbmMetadata);
mHbmController.resetHbmData(info.width, info.height, token, info.uniqueId,
mDisplayDeviceConfig.getHighBrightnessModeData(),
new HighBrightnessModeController.HdrBrightnessDeviceConfig() {
@@ -1747,7 +1742,7 @@
if (mAutomaticBrightnessController != null) {
mAutomaticBrightnessController.update();
}
- }, mHighBrightnessModeMetadata, mContext);
+ }, mContext);
}
private BrightnessThrottler createBrightnessThrottlerLocked() {
diff --git a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
index 7b019846..e750ee2 100644
--- a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
+++ b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
@@ -31,14 +31,10 @@
public interface DisplayPowerControllerInterface {
/**
- * Notified when the display is changed.
- * We use this to apply any changes that might be needed
+ * Notified when the display is changed. We use this to apply any changes that might be needed
* when displays get swapped on foldable devices.
- * We also pass the High brightness mode metadata like
- * remaining time and hbm events for the corresponding
- * physical display, to update the values correctly.
*/
- void onDisplayChanged(HighBrightnessModeMetadata hbmInfo);
+ void onDisplayChanged();
/**
* Unregisters all listeners and interrupts all running threads; halting future work.
diff --git a/services/core/java/com/android/server/display/HbmEvent.java b/services/core/java/com/android/server/display/HbmEvent.java
deleted file mode 100644
index 5675e2f..0000000
--- a/services/core/java/com/android/server/display/HbmEvent.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.display;
-
-
-/**
- * Represents an event in which High Brightness Mode was enabled.
- */
-class HbmEvent {
- private long mStartTimeMillis;
- private long mEndTimeMillis;
-
- HbmEvent(long startTimeMillis, long endTimeMillis) {
- this.mStartTimeMillis = startTimeMillis;
- this.mEndTimeMillis = endTimeMillis;
- }
-
- public long getStartTimeMillis() {
- return mStartTimeMillis;
- }
-
- public long getEndTimeMillis() {
- return mEndTimeMillis;
- }
-
- @Override
- public String toString() {
- return "HbmEvent: {startTimeMillis:" + mStartTimeMillis + ", endTimeMillis: "
- + mEndTimeMillis + "}, total: "
- + ((mEndTimeMillis - mStartTimeMillis) / 1000) + "]";
- }
-}
diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java
index 2c843a4..f98c7df 100644
--- a/services/core/java/com/android/server/display/HighBrightnessModeController.java
+++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java
@@ -105,23 +105,30 @@
private int mHbmStatsState = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF;
/**
- * If HBM is currently running, this is the start time and set of all events,
- * for the current HBM session.
+ * If HBM is currently running, this is the start time for the current HBM session.
*/
- private HighBrightnessModeMetadata mHighBrightnessModeMetadata = null;
+ private long mRunningStartTimeMillis = -1;
+
+ /**
+ * Queue of previous HBM-events ordered from most recent to least recent.
+ * Meant to store only the events that fall into the most recent
+ * {@link HighBrightnessModeData#timeWindowMillis mHbmData.timeWindowMillis}.
+ */
+ private final ArrayDeque<HbmEvent> mEvents = new ArrayDeque<>();
+
HighBrightnessModeController(Handler handler, int width, int height, IBinder displayToken,
String displayUniqueId, float brightnessMin, float brightnessMax,
HighBrightnessModeData hbmData, HdrBrightnessDeviceConfig hdrBrightnessCfg,
- Runnable hbmChangeCallback, HighBrightnessModeMetadata hbmMetadata, Context context) {
+ Runnable hbmChangeCallback, Context context) {
this(new Injector(), handler, width, height, displayToken, displayUniqueId, brightnessMin,
- brightnessMax, hbmData, hdrBrightnessCfg, hbmChangeCallback, hbmMetadata, context);
+ brightnessMax, hbmData, hdrBrightnessCfg, hbmChangeCallback, context);
}
@VisibleForTesting
HighBrightnessModeController(Injector injector, Handler handler, int width, int height,
IBinder displayToken, String displayUniqueId, float brightnessMin, float brightnessMax,
HighBrightnessModeData hbmData, HdrBrightnessDeviceConfig hdrBrightnessCfg,
- Runnable hbmChangeCallback, HighBrightnessModeMetadata hbmMetadata, Context context) {
+ Runnable hbmChangeCallback, Context context) {
mInjector = injector;
mContext = context;
mClock = injector.getClock();
@@ -130,7 +137,6 @@
mBrightnessMin = brightnessMin;
mBrightnessMax = brightnessMax;
mHbmChangeCallback = hbmChangeCallback;
- mHighBrightnessModeMetadata = hbmMetadata;
mSkinThermalStatusObserver = new SkinThermalStatusObserver(mInjector, mHandler);
mSettingsObserver = new SettingsObserver(mHandler);
mRecalcRunnable = this::recalculateTimeAllowance;
@@ -216,22 +222,19 @@
// If we are starting or ending a high brightness mode session, store the current
// session in mRunningStartTimeMillis, or the old one in mEvents.
- final long runningStartTime = mHighBrightnessModeMetadata.getRunningStartTimeMillis();
- final boolean wasHbmDrainingAvailableTime = runningStartTime != -1;
+ final boolean wasHbmDrainingAvailableTime = mRunningStartTimeMillis != -1;
final boolean shouldHbmDrainAvailableTime = mBrightness > mHbmData.transitionPoint
&& !mIsHdrLayerPresent;
if (wasHbmDrainingAvailableTime != shouldHbmDrainAvailableTime) {
final long currentTime = mClock.uptimeMillis();
if (shouldHbmDrainAvailableTime) {
- mHighBrightnessModeMetadata.setRunningStartTimeMillis(currentTime);
+ mRunningStartTimeMillis = currentTime;
} else {
- final HbmEvent hbmEvent = new HbmEvent(runningStartTime, currentTime);
- mHighBrightnessModeMetadata.addHbmEvent(hbmEvent);
- mHighBrightnessModeMetadata.setRunningStartTimeMillis(-1);
+ mEvents.addFirst(new HbmEvent(mRunningStartTimeMillis, currentTime));
+ mRunningStartTimeMillis = -1;
if (DEBUG) {
- Slog.d(TAG, "New HBM event: "
- + mHighBrightnessModeMetadata.getHbmEventQueue().peekFirst());
+ Slog.d(TAG, "New HBM event: " + mEvents.peekFirst());
}
}
}
@@ -257,10 +260,6 @@
mSettingsObserver.stopObserving();
}
- void setHighBrightnessModeMetadata(HighBrightnessModeMetadata hbmInfo) {
- mHighBrightnessModeMetadata = hbmInfo;
- }
-
void resetHbmData(int width, int height, IBinder displayToken, String displayUniqueId,
HighBrightnessModeData hbmData, HdrBrightnessDeviceConfig hdrBrightnessCfg) {
mWidth = width;
@@ -317,22 +316,20 @@
pw.println(" mBrightnessMax=" + mBrightnessMax);
pw.println(" remainingTime=" + calculateRemainingTime(mClock.uptimeMillis()));
pw.println(" mIsTimeAvailable= " + mIsTimeAvailable);
- pw.println(" mRunningStartTimeMillis="
- + TimeUtils.formatUptime(mHighBrightnessModeMetadata.getRunningStartTimeMillis()));
+ pw.println(" mRunningStartTimeMillis=" + TimeUtils.formatUptime(mRunningStartTimeMillis));
pw.println(" mIsThermalStatusWithinLimit=" + mIsThermalStatusWithinLimit);
pw.println(" mIsBlockedByLowPowerMode=" + mIsBlockedByLowPowerMode);
pw.println(" width*height=" + mWidth + "*" + mHeight);
pw.println(" mEvents=");
final long currentTime = mClock.uptimeMillis();
long lastStartTime = currentTime;
- long runningStartTimeMillis = mHighBrightnessModeMetadata.getRunningStartTimeMillis();
- if (runningStartTimeMillis != -1) {
- lastStartTime = dumpHbmEvent(pw, new HbmEvent(runningStartTimeMillis, currentTime));
+ if (mRunningStartTimeMillis != -1) {
+ lastStartTime = dumpHbmEvent(pw, new HbmEvent(mRunningStartTimeMillis, currentTime));
}
- for (HbmEvent event : mHighBrightnessModeMetadata.getHbmEventQueue()) {
- if (lastStartTime > event.getEndTimeMillis()) {
+ for (HbmEvent event : mEvents) {
+ if (lastStartTime > event.endTimeMillis) {
pw.println(" event: [normal brightness]: "
- + TimeUtils.formatDuration(lastStartTime - event.getEndTimeMillis()));
+ + TimeUtils.formatDuration(lastStartTime - event.endTimeMillis));
}
lastStartTime = dumpHbmEvent(pw, event);
}
@@ -341,12 +338,12 @@
}
private long dumpHbmEvent(PrintWriter pw, HbmEvent event) {
- final long duration = event.getEndTimeMillis() - event.getStartTimeMillis();
+ final long duration = event.endTimeMillis - event.startTimeMillis;
pw.println(" event: ["
- + TimeUtils.formatUptime(event.getStartTimeMillis()) + ", "
- + TimeUtils.formatUptime(event.getEndTimeMillis()) + "] ("
+ + TimeUtils.formatUptime(event.startTimeMillis) + ", "
+ + TimeUtils.formatUptime(event.endTimeMillis) + "] ("
+ TimeUtils.formatDuration(duration) + ")");
- return event.getStartTimeMillis();
+ return event.startTimeMillis;
}
private boolean isCurrentlyAllowed() {
@@ -375,15 +372,13 @@
// First, lets see how much time we've taken for any currently running
// session of HBM.
- long runningStartTimeMillis = mHighBrightnessModeMetadata.getRunningStartTimeMillis();
- if (runningStartTimeMillis > 0) {
- if (runningStartTimeMillis > currentTime) {
+ if (mRunningStartTimeMillis > 0) {
+ if (mRunningStartTimeMillis > currentTime) {
Slog.e(TAG, "Start time set to the future. curr: " + currentTime
- + ", start: " + runningStartTimeMillis);
- mHighBrightnessModeMetadata.setRunningStartTimeMillis(currentTime);
- runningStartTimeMillis = currentTime;
+ + ", start: " + mRunningStartTimeMillis);
+ mRunningStartTimeMillis = currentTime;
}
- timeAlreadyUsed = currentTime - runningStartTimeMillis;
+ timeAlreadyUsed = currentTime - mRunningStartTimeMillis;
}
if (DEBUG) {
@@ -392,19 +387,18 @@
// Next, lets iterate through the history of previous sessions and add those times.
final long windowstartTimeMillis = currentTime - mHbmData.timeWindowMillis;
- Iterator<HbmEvent> it = mHighBrightnessModeMetadata.getHbmEventQueue().iterator();
+ Iterator<HbmEvent> it = mEvents.iterator();
while (it.hasNext()) {
final HbmEvent event = it.next();
// If this event ended before the current Timing window, discard forever and ever.
- if (event.getEndTimeMillis() < windowstartTimeMillis) {
+ if (event.endTimeMillis < windowstartTimeMillis) {
it.remove();
continue;
}
- final long startTimeMillis = Math.max(event.getStartTimeMillis(),
- windowstartTimeMillis);
- timeAlreadyUsed += event.getEndTimeMillis() - startTimeMillis;
+ final long startTimeMillis = Math.max(event.startTimeMillis, windowstartTimeMillis);
+ timeAlreadyUsed += event.endTimeMillis - startTimeMillis;
}
if (DEBUG) {
@@ -431,18 +425,17 @@
// Calculate the time at which we want to recalculate mIsTimeAvailable in case a lux or
// brightness change doesn't happen before then.
long nextTimeout = -1;
- final ArrayDeque<HbmEvent> hbmEvents = mHighBrightnessModeMetadata.getHbmEventQueue();
if (mBrightness > mHbmData.transitionPoint) {
// if we're in high-lux now, timeout when we run out of allowed time.
nextTimeout = currentTime + remainingTime;
- } else if (!mIsTimeAvailable && hbmEvents.size() > 0) {
+ } else if (!mIsTimeAvailable && mEvents.size() > 0) {
// If we are not allowed...timeout when the oldest event moved outside of the timing
// window by at least minTime. Basically, we're calculating the soonest time we can
// get {@code timeMinMillis} back to us.
final long windowstartTimeMillis = currentTime - mHbmData.timeWindowMillis;
- final HbmEvent lastEvent = hbmEvents.peekLast();
+ final HbmEvent lastEvent = mEvents.peekLast();
final long startTimePlusMinMillis =
- Math.max(windowstartTimeMillis, lastEvent.getStartTimeMillis())
+ Math.max(windowstartTimeMillis, lastEvent.startTimeMillis)
+ mHbmData.timeMinMillis;
final long timeWhenMinIsGainedBack =
currentTime + (startTimePlusMinMillis - windowstartTimeMillis) - remainingTime;
@@ -466,10 +459,9 @@
+ ", mUnthrottledBrightness: " + mUnthrottledBrightness
+ ", mThrottlingReason: "
+ BrightnessInfo.briMaxReasonToString(mThrottlingReason)
- + ", RunningStartTimeMillis: "
- + mHighBrightnessModeMetadata.getRunningStartTimeMillis()
+ + ", RunningStartTimeMillis: " + mRunningStartTimeMillis
+ ", nextTimeout: " + (nextTimeout != -1 ? (nextTimeout - currentTime) : -1)
- + ", events: " + hbmEvents);
+ + ", events: " + mEvents);
}
if (nextTimeout != -1) {
@@ -596,6 +588,25 @@
}
}
+ /**
+ * Represents an event in which High Brightness Mode was enabled.
+ */
+ private static class HbmEvent {
+ public long startTimeMillis;
+ public long endTimeMillis;
+
+ HbmEvent(long startTimeMillis, long endTimeMillis) {
+ this.startTimeMillis = startTimeMillis;
+ this.endTimeMillis = endTimeMillis;
+ }
+
+ @Override
+ public String toString() {
+ return "[Event: {" + startTimeMillis + ", " + endTimeMillis + "}, total: "
+ + ((endTimeMillis - startTimeMillis) / 1000) + "]";
+ }
+ }
+
@VisibleForTesting
class HdrListener extends SurfaceControlHdrLayerInfoListener {
@Override
diff --git a/services/core/java/com/android/server/display/HighBrightnessModeMetadata.java b/services/core/java/com/android/server/display/HighBrightnessModeMetadata.java
deleted file mode 100644
index 37234ff..0000000
--- a/services/core/java/com/android/server/display/HighBrightnessModeMetadata.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.display;
-
-import java.util.ArrayDeque;
-
-
-/**
- * Represents High Brightness Mode metadata associated
- * with a specific internal physical display.
- * Required for separately storing data like time information,
- * and related events when display was in HBM mode per
- * physical internal display.
- */
-class HighBrightnessModeMetadata {
- /**
- * Queue of previous HBM-events ordered from most recent to least recent.
- * Meant to store only the events that fall into the most recent
- * {@link HighBrightnessModeData#timeWindowMillis mHbmData.timeWindowMillis}.
- */
- private final ArrayDeque<HbmEvent> mEvents = new ArrayDeque<>();
-
- /**
- * If HBM is currently running, this is the start time for the current HBM session.
- */
- private long mRunningStartTimeMillis = -1;
-
- public long getRunningStartTimeMillis() {
- return mRunningStartTimeMillis;
- }
-
- public void setRunningStartTimeMillis(long setTime) {
- mRunningStartTimeMillis = setTime;
- }
-
- public ArrayDeque<HbmEvent> getHbmEventQueue() {
- return mEvents;
- }
-
- public void addHbmEvent(HbmEvent hbmEvent) {
- mEvents.addFirst(hbmEvent);
- }
-}
-
diff --git a/services/core/java/com/android/server/inputmethod/ImeTrackerService.java b/services/core/java/com/android/server/inputmethod/ImeTrackerService.java
index 60167b4..da65f27 100644
--- a/services/core/java/com/android/server/inputmethod/ImeTrackerService.java
+++ b/services/core/java/com/android/server/inputmethod/ImeTrackerService.java
@@ -59,13 +59,16 @@
private static final long TIMEOUT_MS = 10_000;
/** Handler for registering timeouts for live entries. */
- private final Handler mHandler =
- new Handler(Looper.myLooper(), null /* callback */, true /* async */);
+ private final Handler mHandler;
/** Singleton instance of the History. */
@GuardedBy("ImeTrackerService.this")
private final History mHistory = new History();
+ ImeTrackerService(@NonNull Looper looper) {
+ mHandler = new Handler(looper, null /* callback */, true /* async */);
+ }
+
@NonNull
@Override
public synchronized IBinder onRequestShow(int uid, @ImeTracker.Origin int origin,
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index a63b95c..2ced988 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -89,6 +89,7 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.LocaleList;
+import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
import android.os.Process;
@@ -1105,7 +1106,7 @@
new SoftInputShowHideHistory();
@NonNull
- private final ImeTrackerService mImeTrackerService = new ImeTrackerService();
+ private final ImeTrackerService mImeTrackerService;
class SettingsObserver extends ContentObserver {
int mUserId;
@@ -1662,6 +1663,8 @@
true /* allowIo */);
thread.start();
mHandler = Handler.createAsync(thread.getLooper(), this);
+ mImeTrackerService = new ImeTrackerService(serviceThreadForTesting != null
+ ? serviceThreadForTesting.getLooper() : Looper.getMainLooper());
// Note: SettingsObserver doesn't register observers in its constructor.
mSettingsObserver = new SettingsObserver(mHandler);
mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
@@ -4613,7 +4616,9 @@
info.requestWindowName, info.imeControlTargetName, info.imeLayerTargetName,
info.imeSurfaceParentName));
- mImeTrackerService.onImmsUpdate(statsToken.mBinder, info.requestWindowName);
+ if (statsToken != null) {
+ mImeTrackerService.onImmsUpdate(statsToken.mBinder, info.requestWindowName);
+ }
}
@BinderThread
@@ -4655,13 +4660,10 @@
}
@VisibleForTesting
- ImeVisibilityStateComputer getVisibilityStateComputer() {
- return mVisibilityStateComputer;
- }
-
- @VisibleForTesting
ImeVisibilityApplier getVisibilityApplier() {
- return mVisibilityApplier;
+ synchronized (ImfLock.class) {
+ return mVisibilityApplier;
+ }
}
@GuardedBy("ImfLock.class")
diff --git a/services/core/java/com/android/server/inputmethod/TEST_MAPPING b/services/core/java/com/android/server/inputmethod/TEST_MAPPING
index 0ccd75d..bd95c5b 100644
--- a/services/core/java/com/android/server/inputmethod/TEST_MAPPING
+++ b/services/core/java/com/android/server/inputmethod/TEST_MAPPING
@@ -2,6 +2,9 @@
"imports": [
{
"path": "frameworks/base/core/java/android/view/inputmethod"
+ },
+ {
+ "path": "frameworks/base/services/tests/InputMethodSystemServerTests"
}
]
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index 7f6c2d6..8f65775 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -1155,6 +1155,34 @@
return nanoappIds;
}
+ @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
+ /**
+ * Puts the context hub in and out of test mode. Test mode is a clean state
+ * where tests can be executed in the same environment. If enable is true,
+ * this will enable test mode by unloading all nanoapps. If enable is false,
+ * this will disable test mode and reverse the actions of enabling test mode
+ * by loading all preloaded nanoapps. This puts CHRE in a normal state.
+ *
+ * This should only be used for a test environment, either through a
+ * @TestApi or development tools. This should not be used in a production
+ * environment.
+ *
+ * @param enable If true, put the context hub in test mode. If false, disable
+ * test mode.
+ * @return If true, the operation was successful; false otherwise.
+ */
+ @Override
+ public boolean setTestMode(boolean enable) {
+ super.setTestMode_enforcePermission();
+ boolean status = mContextHubWrapper.setTestMode(enable);
+
+ // Query nanoapps to update service state after test mode state change.
+ for (int contextHubId: mDefaultClientMap.keySet()) {
+ queryNanoAppsInternal(contextHubId);
+ }
+ return status;
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
index 3581b02..ca184ee 100644
--- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
+++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
@@ -382,6 +382,23 @@
*/
public abstract void registerExistingCallback(int contextHubId) throws RemoteException;
+ /**
+ * Puts the context hub in and out of test mode. Test mode is a clean state
+ * where tests can be executed in the same environment. If enable is true,
+ * this will enable test mode by unloading all nanoapps. If enable is false,
+ * this will disable test mode and reverse the actions of enabling test mode
+ * by loading all preloaded nanoapps. This puts CHRE in a normal state.
+ *
+ * This should only be used for a test environment, either through a
+ * @TestApi or development tools. This should not be used in a production
+ * environment.
+ *
+ * @param enable If true, put the context hub in test mode. If false, disable
+ * test mode.
+ * @return If true, the operation was successful; false otherwise.
+ */
+ public abstract boolean setTestMode(boolean enable);
+
private static class ContextHubWrapperAidl extends IContextHubWrapper
implements IBinder.DeathRecipient {
private android.hardware.contexthub.IContextHub mHub;
@@ -741,6 +758,22 @@
registerExistingCallback(contextHubId);
}
+ public boolean setTestMode(boolean enable) {
+ android.hardware.contexthub.IContextHub hub = getHub();
+ if (hub == null) {
+ return false;
+ }
+
+ try {
+ hub.setTestMode(enable);
+ return true;
+ } catch (RemoteException | ServiceSpecificException e) {
+ Log.e(TAG, "Exception while setting test mode (enable: "
+ + (enable ? "true" : "false") + "): " + e.getMessage());
+ return false;
+ }
+ }
+
private void onSettingChanged(byte setting, boolean enabled) {
android.hardware.contexthub.IContextHub hub = getHub();
if (hub == null) {
@@ -911,6 +944,10 @@
mHub.registerCallback(contextHubId, callback);
}
+ public boolean setTestMode(boolean enable) {
+ return false;
+ }
+
public boolean supportsBtSettingNotifications() {
return false;
}
diff --git a/services/core/java/com/android/server/om/IdmapDaemon.java b/services/core/java/com/android/server/om/IdmapDaemon.java
index 56390a9..15cfca5 100644
--- a/services/core/java/com/android/server/om/IdmapDaemon.java
+++ b/services/core/java/com/android/server/om/IdmapDaemon.java
@@ -274,20 +274,16 @@
}
}
- final long endMillis = SystemClock.elapsedRealtime() + SERVICE_CONNECT_TIMEOUT_MS;
- while (SystemClock.elapsedRealtime() <= endMillis) {
+ final long endMillis = SystemClock.uptimeMillis() + SERVICE_CONNECT_TIMEOUT_MS;
+ do {
final IBinder binder = ServiceManager.getService(IDMAP_SERVICE);
if (binder != null) {
binder.linkToDeath(
() -> Slog.w(TAG, String.format("service '%s' died", IDMAP_SERVICE)), 0);
return binder;
}
-
- try {
- Thread.sleep(SERVICE_CONNECT_INTERVAL_SLEEP_MS);
- } catch (InterruptedException ignored) {
- }
- }
+ SystemClock.sleep(SERVICE_CONNECT_INTERVAL_SLEEP_MS);
+ } while (SystemClock.uptimeMillis() <= endMillis);
throw new TimeoutException(
String.format("Failed to connect to '%s' in %d milliseconds", IDMAP_SERVICE,
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 5c4c7c9..faa06f7 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -331,6 +331,7 @@
/** Adds listener for package update */
public void addPackagesUpdatedListener(PackagesUpdatedListener listener)
throws LegacyDexoptDisabledException {
+ // TODO(b/251903639): Evaluate whether this needs to support ART Service or not.
Installer.checkLegacyDexoptDisabled();
synchronized (mLock) {
mPackagesUpdatedListeners.add(listener);
@@ -629,6 +630,8 @@
/** Gets the size of a package. */
private long getPackageSize(@NonNull Computer snapshot, String pkg) {
+ // TODO(b/251903639): Make this in line with the calculation in
+ // `DexOptHelper.DexoptDoneHandler`.
PackageInfo info = snapshot.getPackageInfo(pkg, 0, UserHandle.USER_SYSTEM);
long size = 0;
if (info != null && info.applicationInfo != null) {
@@ -723,6 +726,8 @@
return optimizePackages(pkgs, lowStorageThreshold, updatedPackages, isPostBootUpdate);
} finally {
// Always let the pinner service know about changes.
+ // TODO(b/251903639): ART Service does this for all dexopts, while the code below only
+ // runs for background jobs. We should try to make them behave the same.
notifyPinService(updatedPackages);
// Only notify IORap the primary dex opt, because we don't want to
// invalidate traces unnecessary due to b/161633001 and that it's
diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java
index 382ae26..de37080 100644
--- a/services/core/java/com/android/server/pm/DexOptHelper.java
+++ b/services/core/java/com/android/server/pm/DexOptHelper.java
@@ -57,8 +57,11 @@
import android.util.Slog;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalManagerRegistry;
+import com.android.server.LocalServices;
+import com.android.server.PinnerService;
import com.android.server.art.ArtManagerLocal;
import com.android.server.art.DexUseManagerLocal;
import com.android.server.art.ReasonMapping;
@@ -970,6 +973,35 @@
mPm.getPackageUsage().maybeWriteAsync(mPm.mSettings.getPackagesLocked());
mPm.mCompilerStats.maybeWriteAsync();
}
+
+ if (result.getReason().equals(ReasonMapping.REASON_INACTIVE)) {
+ for (DexoptResult.PackageDexoptResult pkgRes : result.getPackageDexoptResults()) {
+ if (pkgRes.getStatus() == DexoptResult.DEXOPT_PERFORMED) {
+ long pkgSizeBytes = 0;
+ long pkgSizeBeforeBytes = 0;
+ for (DexoptResult.DexContainerFileDexoptResult dexRes :
+ pkgRes.getDexContainerFileDexoptResults()) {
+ long dexContainerSize = new File(dexRes.getDexContainerFile()).length();
+ pkgSizeBytes += dexRes.getSizeBytes() + dexContainerSize;
+ pkgSizeBeforeBytes += dexRes.getSizeBeforeBytes() + dexContainerSize;
+ }
+ FrameworkStatsLog.write(FrameworkStatsLog.APP_DOWNGRADED,
+ pkgRes.getPackageName(), pkgSizeBeforeBytes, pkgSizeBytes,
+ false /* aggressive */);
+ }
+ }
+ }
+
+ var updatedPackages = new ArraySet<String>();
+ for (DexoptResult.PackageDexoptResult pkgRes : result.getPackageDexoptResults()) {
+ if (pkgRes.hasUpdatedArtifacts()) {
+ updatedPackages.add(pkgRes.getPackageName());
+ }
+ }
+ if (!updatedPackages.isEmpty()) {
+ LocalServices.getService(PinnerService.class)
+ .update(updatedPackages, false /* force */);
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 9a15e11..94e96b0 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2950,15 +2950,6 @@
}
}
if (doTrim) {
- if (!isFirstBoot()) {
- try {
- ActivityManager.getService().showBootMessage(
- mContext.getResources().getString(
- R.string.android_upgrading_fstrim),
- true);
- } catch (RemoteException e) {
- }
- }
sm.runMaintenance();
}
} else {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 328fc8b..3340e12 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1481,36 +1481,45 @@
@Override
public void setUserAdmin(@UserIdInt int userId) {
checkManageUserAndAcrossUsersFullPermission("set user admin");
-
+ final long sessionId = logGrantAdminJourneyBegin(userId);
+ UserInfo info;
synchronized (mPackagesLock) {
- UserInfo info;
synchronized (mUsersLock) {
info = getUserInfoLU(userId);
}
if (info == null || info.isAdmin()) {
// Exit if no user found with that id, or the user is already an Admin.
+ logUserJourneyError(sessionId,
+ FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__GRANT_ADMIN,
+ userId);
return;
}
-
info.flags ^= UserInfo.FLAG_ADMIN;
writeUserLP(getUserDataLU(info.id));
}
+ logGrantAdminJourneyFinish(sessionId, userId, info.userType, info.flags);
}
@Override
public void revokeUserAdmin(@UserIdInt int userId) {
checkManageUserAndAcrossUsersFullPermission("revoke admin privileges");
+ final long sessionId = logRevokeAdminJourneyBegin(userId);
+ UserData user;
synchronized (mPackagesLock) {
synchronized (mUsersLock) {
- UserData user = getUserDataLU(userId);
+ user = getUserDataLU(userId);
if (user == null || !user.info.isAdmin()) {
// Exit if no user found with that id, or the user is not an Admin.
+ logUserJourneyError(sessionId, FrameworkStatsLog
+ .USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__REVOKE_ADMIN,
+ userId);
return;
}
user.info.flags ^= UserInfo.FLAG_ADMIN;
writeUserLP(user);
}
}
+ logRevokeAdminJourneyFinish(sessionId, userId, user.info.userType, user.info.flags);
}
/**
@@ -5147,12 +5156,38 @@
userId, userType, flags, finish);
}
+ private long logGrantAdminJourneyBegin(@UserIdInt int userId) {
+ return logUserJourneyBegin(
+ FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__GRANT_ADMIN,
+ userId);
+ }
+
+ private void logGrantAdminJourneyFinish(long sessionId, @UserIdInt int userId, String userType,
+ @UserInfoFlag int flags) {
+ logUserJourneyFinish(sessionId,
+ FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__GRANT_ADMIN,
+ userId, userType, flags, true);
+ }
+
+ private long logRevokeAdminJourneyBegin(@UserIdInt int userId) {
+ return logUserJourneyBegin(
+ FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__REVOKE_ADMIN,
+ userId);
+ }
+
+ private void logRevokeAdminJourneyFinish(long sessionId, @UserIdInt int userId, String userType,
+ @UserInfoFlag int flags) {
+ logUserJourneyFinish(sessionId,
+ FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__REVOKE_ADMIN,
+ userId, userType, flags, true);
+ }
+
private void logUserJourneyFinish(long sessionId, int journey, @UserIdInt int userId,
String userType, @UserInfoFlag int flags, boolean finish) {
// log the journey atom with the user metadata
FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED, sessionId,
- journey, /* origin_user= */ -1, userId,
+ journey, /* origin_user= */ getCurrentUserId(), userId,
UserManager.getUserTypeForStatsd(userType), flags);
int event;
@@ -5163,6 +5198,12 @@
case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_REMOVE:
event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__REMOVE_USER;
break;
+ case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__GRANT_ADMIN:
+ event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__GRANT_ADMIN;
+ break;
+ case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__REVOKE_ADMIN:
+ event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__REVOKE_ADMIN;
+ break;
default:
throw new IllegalArgumentException("Journey " + journey + " not expected.");
}
@@ -5184,6 +5225,12 @@
case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_REMOVE:
event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__REMOVE_USER;
break;
+ case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__GRANT_ADMIN:
+ event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__GRANT_ADMIN;
+ break;
+ case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__REVOKE_ADMIN:
+ event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__REVOKE_ADMIN;
+ break;
default:
throw new IllegalArgumentException("Journey " + journey + " not expected.");
}
@@ -5193,6 +5240,27 @@
return sessionId;
}
+ private void logUserJourneyError(long sessionId, int journey, @UserIdInt int userId) {
+
+ // log the journey atom with the user metadata
+ FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED, sessionId,
+ journey, /* origin_user= */ getCurrentUserId(), userId);
+
+ int event;
+ switch (journey) {
+ case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__GRANT_ADMIN:
+ event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__GRANT_ADMIN;
+ break;
+ case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__REVOKE_ADMIN:
+ event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__REVOKE_ADMIN;
+ break;
+ default:
+ throw new IllegalArgumentException("Journey " + journey + " not expected.");
+ }
+ FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, sessionId, userId,
+ event, FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__ERROR);
+ }
+
/** Register callbacks for statsd pulled atoms. */
private void registerStatsCallbacks() {
final StatsManager statsManager = mContext.getSystemService(StatsManager.class);
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 6c616e0..b3e915a 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -170,6 +170,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.procstats.IProcessStats;
import com.android.internal.app.procstats.ProcessStats;
+import com.android.internal.app.procstats.StatsEventOutput;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.BinderCallsStats.ExportedCallStat;
import com.android.internal.os.KernelAllocationStats;
@@ -613,12 +614,19 @@
}
case FrameworkStatsLog.PROC_STATS:
synchronized (mProcStatsLock) {
- return pullProcStatsLocked(ProcessStats.REPORT_ALL, atomTag, data);
+ return pullProcStatsLocked(atomTag, data);
}
case FrameworkStatsLog.PROC_STATS_PKG_PROC:
synchronized (mProcStatsLock) {
- return pullProcStatsLocked(ProcessStats.REPORT_PKG_PROC_STATS, atomTag,
- data);
+ return pullProcStatsLocked(atomTag, data);
+ }
+ case FrameworkStatsLog.PROCESS_STATE:
+ synchronized (mProcStatsLock) {
+ return pullProcessStateLocked(atomTag, data);
+ }
+ case FrameworkStatsLog.PROCESS_ASSOCIATION:
+ synchronized (mProcStatsLock) {
+ return pullProcessAssociationLocked(atomTag, data);
}
case FrameworkStatsLog.DISK_IO:
synchronized (mDiskIoLock) {
@@ -891,6 +899,8 @@
registerNumFacesEnrolled();
registerProcStats();
registerProcStatsPkgProc();
+ registerProcessState();
+ registerProcessAssociation();
registerDiskIO();
registerPowerProfile();
registerProcessCpuTime();
@@ -2884,59 +2894,138 @@
);
}
- private int pullProcStatsLocked(int section, int atomTag, List<StatsEvent> pulledData) {
+ private void registerProcessState() {
+ int tagId = FrameworkStatsLog.PROCESS_STATE;
+ mStatsManager.setPullAtomCallback(
+ tagId,
+ null, // use default PullAtomMetadata values
+ DIRECT_EXECUTOR,
+ mStatsCallbackImpl);
+ }
+
+ private void registerProcessAssociation() {
+ int tagId = FrameworkStatsLog.PROCESS_ASSOCIATION;
+ mStatsManager.setPullAtomCallback(
+ tagId,
+ null, // use default PullAtomMetadata values
+ DIRECT_EXECUTOR,
+ mStatsCallbackImpl);
+ }
+
+ @GuardedBy("mProcStatsLock")
+ private ProcessStats getStatsFromProcessStatsService(int atomTag) {
IProcessStats processStatsService = getIProcessStatsService();
if (processStatsService == null) {
- return StatsManager.PULL_SKIP;
+ return null;
}
-
final long token = Binder.clearCallingIdentity();
try {
// force procstats to flush & combine old files into one store
- long lastHighWaterMark = readProcStatsHighWaterMark(section);
-
- ProtoOutputStream[] protoStreams = new ProtoOutputStream[MAX_PROCSTATS_SHARDS];
- for (int i = 0; i < protoStreams.length; i++) {
- protoStreams[i] = new ProtoOutputStream();
- }
-
+ long lastHighWaterMark = readProcStatsHighWaterMark(atomTag);
ProcessStats procStats = new ProcessStats(false);
// Force processStatsService to aggregate all in-storage and in-memory data.
- long highWaterMark = processStatsService.getCommittedStatsMerged(
- lastHighWaterMark, section, true, null, procStats);
- procStats.dumpAggregatedProtoForStatsd(protoStreams, MAX_PROCSTATS_RAW_SHARD_SIZE);
-
- for (int i = 0; i < protoStreams.length; i++) {
- byte[] bytes = protoStreams[i].getBytes(); // cache the value
- if (bytes.length > 0) {
- pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, bytes,
- // This is a shard ID, and is specified in the metric definition to be
- // a dimension. This will result in statsd using RANDOM_ONE_SAMPLE to
- // keep all the shards, as it thinks each shard is a different dimension
- // of data.
- i));
- }
- }
-
- new File(mBaseDir.getAbsolutePath() + "/" + section + "_" + lastHighWaterMark)
+ long highWaterMark =
+ processStatsService.getCommittedStatsMerged(
+ lastHighWaterMark,
+ ProcessStats.REPORT_ALL, // ignored since committedStats below is null.
+ true,
+ null, // committedStats
+ procStats);
+ new File(
+ mBaseDir.getAbsolutePath()
+ + "/"
+ + highWaterMarkFilePrefix(atomTag)
+ + "_"
+ + lastHighWaterMark)
.delete();
- new File(mBaseDir.getAbsolutePath() + "/" + section + "_" + highWaterMark)
+ new File(
+ mBaseDir.getAbsolutePath()
+ + "/"
+ + highWaterMarkFilePrefix(atomTag)
+ + "_"
+ + highWaterMark)
.createNewFile();
+ return procStats;
} catch (RemoteException | IOException e) {
Slog.e(TAG, "Getting procstats failed: ", e);
- return StatsManager.PULL_SKIP;
+ return null;
} finally {
Binder.restoreCallingIdentity(token);
}
+ }
+
+ @GuardedBy("mProcStatsLock")
+ private int pullProcStatsLocked(int atomTag, List<StatsEvent> pulledData) {
+ ProcessStats procStats = getStatsFromProcessStatsService(atomTag);
+ if (procStats == null) {
+ return StatsManager.PULL_SKIP;
+ }
+ ProtoOutputStream[] protoStreams = new ProtoOutputStream[MAX_PROCSTATS_SHARDS];
+ for (int i = 0; i < protoStreams.length; i++) {
+ protoStreams[i] = new ProtoOutputStream();
+ }
+ procStats.dumpAggregatedProtoForStatsd(protoStreams, MAX_PROCSTATS_RAW_SHARD_SIZE);
+ for (int i = 0; i < protoStreams.length; i++) {
+ byte[] bytes = protoStreams[i].getBytes(); // cache the value
+ if (bytes.length > 0) {
+ pulledData.add(
+ FrameworkStatsLog.buildStatsEvent(
+ atomTag,
+ bytes,
+ // This is a shard ID, and is specified in the metric definition to
+ // be
+ // a dimension. This will result in statsd using RANDOM_ONE_SAMPLE
+ // to
+ // keep all the shards, as it thinks each shard is a different
+ // dimension
+ // of data.
+ i));
+ }
+ }
return StatsManager.PULL_SUCCESS;
}
+ @GuardedBy("mProcStatsLock")
+ private int pullProcessStateLocked(int atomTag, List<StatsEvent> pulledData) {
+ ProcessStats procStats = getStatsFromProcessStatsService(atomTag);
+ if (procStats == null) {
+ return StatsManager.PULL_SKIP;
+ }
+ procStats.dumpProcessState(atomTag, new StatsEventOutput(pulledData));
+ return StatsManager.PULL_SUCCESS;
+ }
+
+ @GuardedBy("mProcStatsLock")
+ private int pullProcessAssociationLocked(int atomTag, List<StatsEvent> pulledData) {
+ ProcessStats procStats = getStatsFromProcessStatsService(atomTag);
+ if (procStats == null) {
+ return StatsManager.PULL_SKIP;
+ }
+ procStats.dumpProcessAssociation(atomTag, new StatsEventOutput(pulledData));
+ return StatsManager.PULL_SUCCESS;
+ }
+
+ private String highWaterMarkFilePrefix(int atomTag) {
+ // For backward compatibility, use the legacy ProcessStats enum value as the prefix for
+ // PROC_STATS and PROC_STATS_PKG_PROC.
+ if (atomTag == FrameworkStatsLog.PROC_STATS) {
+ return String.valueOf(ProcessStats.REPORT_ALL);
+ }
+ if (atomTag == FrameworkStatsLog.PROC_STATS_PKG_PROC) {
+ return String.valueOf(ProcessStats.REPORT_PKG_PROC_STATS);
+ }
+ return "atom-" + atomTag;
+ }
+
// read high watermark for section
- private long readProcStatsHighWaterMark(int section) {
+ private long readProcStatsHighWaterMark(int atomTag) {
try {
- File[] files = mBaseDir.listFiles((d, name) -> {
- return name.toLowerCase().startsWith(String.valueOf(section) + '_');
- });
+ File[] files =
+ mBaseDir.listFiles(
+ (d, name) -> {
+ return name.toLowerCase()
+ .startsWith(highWaterMarkFilePrefix(atomTag) + '_');
+ });
if (files == null || files.length == 0) {
return 0;
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 4f87b1a..90ac1aa 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -6578,12 +6578,29 @@
updateReportedVisibilityLocked();
}
+ /**
+ * Sets whether something has been visible in the task and returns {@code true} if the state
+ * is changed from invisible to visible.
+ */
+ private boolean setTaskHasBeenVisible() {
+ final boolean wasTaskVisible = task.getHasBeenVisible();
+ if (wasTaskVisible) {
+ return false;
+ }
+ if (inTransition()) {
+ // The deferring will be canceled until transition is ready so it won't dispatch
+ // intermediate states to organizer.
+ task.setDeferTaskAppear(true);
+ }
+ task.setHasBeenVisible(true);
+ return true;
+ }
+
void onStartingWindowDrawn() {
boolean wasTaskVisible = false;
if (task != null) {
mSplashScreenStyleSolidColor = true;
- wasTaskVisible = task.getHasBeenVisible();
- task.setHasBeenVisible(true);
+ wasTaskVisible = !setTaskHasBeenVisible();
}
// The transition may not be executed if the starting process hasn't attached. But if the
@@ -6621,7 +6638,7 @@
}
finishLaunchTickingLocked();
if (task != null) {
- task.setHasBeenVisible(true);
+ setTaskHasBeenVisible();
}
// Clear indicated launch root task because there's no trampoline activity to expect after
// the windows are drawn.
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index f916ee4..800fe09 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -197,6 +197,12 @@
// Whether using split screen aspect ratio as a default aspect ratio for unresizable apps.
private boolean mIsSplitScreenAspectRatioForUnresizableAppsEnabled;
+ // Whether using display aspect ratio as a default aspect ratio for all letterboxed apps.
+ // mIsSplitScreenAspectRatioForUnresizableAppsEnabled and
+ // config_letterboxDefaultMinAspectRatioForUnresizableApps take priority over this for
+ // unresizable apps
+ private boolean mIsDisplayAspectRatioEnabledForFixedOrientationLetterbox;
+
// Whether letterboxing strategy is enabled for translucent activities. If {@value false}
// all the feature is disabled
private boolean mTranslucentLetterboxingEnabled;
@@ -288,6 +294,9 @@
R.dimen.config_letterboxDefaultMinAspectRatioForUnresizableApps));
mIsSplitScreenAspectRatioForUnresizableAppsEnabled = mContext.getResources().getBoolean(
R.bool.config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled);
+ mIsDisplayAspectRatioEnabledForFixedOrientationLetterbox = mContext.getResources()
+ .getBoolean(R.bool
+ .config_letterboxIsDisplayAspectRatioForFixedOrientationLetterboxEnabled);
mTranslucentLetterboxingEnabled = mContext.getResources().getBoolean(
R.bool.config_letterboxIsEnabledForTranslucentActivities);
mIsCameraCompatTreatmentEnabled = mContext.getResources().getBoolean(
@@ -943,6 +952,13 @@
}
/**
+ * Whether using display aspect ratio as a default aspect ratio for all letterboxed apps.
+ */
+ boolean getIsDisplayAspectRatioEnabledForFixedOrientationLetterbox() {
+ return mIsDisplayAspectRatioEnabledForFixedOrientationLetterbox;
+ }
+
+ /**
* Overrides whether using split screen aspect ratio as a default aspect ratio for unresizable
* apps.
*/
@@ -951,6 +967,14 @@
}
/**
+ * Overrides whether using display aspect ratio as a default aspect ratio for all letterboxed
+ * apps.
+ */
+ void setIsDisplayAspectRatioEnabledForFixedOrientationLetterbox(boolean enabled) {
+ mIsDisplayAspectRatioEnabledForFixedOrientationLetterbox = enabled;
+ }
+
+ /**
* Resets whether using split screen aspect ratio as a default aspect ratio for unresizable
* apps {@link R.bool.config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled}.
*/
@@ -959,6 +983,16 @@
R.bool.config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled);
}
+ /**
+ * Resets whether using display aspect ratio as a default aspect ratio for all letterboxed
+ * apps {@link R.bool.config_letterboxIsDisplayAspectRatioForFixedOrientationLetterboxEnabled}.
+ */
+ void resetIsDisplayAspectRatioEnabledForFixedOrientationLetterbox() {
+ mIsDisplayAspectRatioEnabledForFixedOrientationLetterbox = mContext.getResources()
+ .getBoolean(R.bool
+ .config_letterboxIsDisplayAspectRatioForFixedOrientationLetterboxEnabled);
+ }
+
boolean isTranslucentLetterboxingEnabled() {
return mTranslucentLetterboxingOverrideEnabled || (mTranslucentLetterboxingEnabled
&& isTranslucentLetterboxingAllowed());
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 9c43c1d..67e188f 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -584,7 +584,7 @@
? getSplitScreenAspectRatio()
: mActivityRecord.shouldCreateCompatDisplayInsets()
? getDefaultMinAspectRatioForUnresizableApps()
- : mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio();
+ : getDefaultMinAspectRatio();
}
private float getDefaultMinAspectRatioForUnresizableApps() {
@@ -593,7 +593,7 @@
return mLetterboxConfiguration.getDefaultMinAspectRatioForUnresizableApps()
> MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO
? mLetterboxConfiguration.getDefaultMinAspectRatioForUnresizableApps()
- : mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio();
+ : getDefaultMinAspectRatio();
}
return getSplitScreenAspectRatio();
@@ -621,6 +621,16 @@
return computeAspectRatio(bounds);
}
+ private float getDefaultMinAspectRatio() {
+ final DisplayContent displayContent = mActivityRecord.getDisplayContent();
+ if (displayContent == null
+ || !mLetterboxConfiguration
+ .getIsDisplayAspectRatioEnabledForFixedOrientationLetterbox()) {
+ return mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio();
+ }
+ return computeAspectRatio(new Rect(displayContent.getBounds()));
+ }
+
Resources getResources() {
return mActivityRecord.mWmService.mContext.getResources();
}
@@ -1014,6 +1024,9 @@
+ mLetterboxConfiguration.getDefaultMinAspectRatioForUnresizableApps());
pw.println(prefix + " isSplitScreenAspectRatioForUnresizableAppsEnabled="
+ mLetterboxConfiguration.getIsSplitScreenAspectRatioForUnresizableAppsEnabled());
+ pw.println(prefix + " isDisplayAspectRatioEnabledForFixedOrientationLetterbox="
+ + mLetterboxConfiguration
+ .getIsDisplayAspectRatioEnabledForFixedOrientationLetterbox());
}
/**
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 15a5ebf..97e0b1e 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -310,6 +310,11 @@
}
}
+ @Override
+ public void performHapticFeedbackAsync(int effectId, boolean always) {
+ performHapticFeedback(effectId, always);
+ }
+
/* Drag/drop */
@Override
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index b7021c8..a76d836 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -4148,21 +4148,28 @@
void setHasBeenVisible(boolean hasBeenVisible) {
mHasBeenVisible = hasBeenVisible;
- if (hasBeenVisible) {
- if (!mDeferTaskAppear) sendTaskAppeared();
- if (!isRootTask()) {
- getRootTask().setHasBeenVisible(true);
+ if (!hasBeenVisible || mDeferTaskAppear) {
+ return;
+ }
+ sendTaskAppeared();
+ for (WindowContainer<?> parent = getParent(); parent != null; parent = parent.getParent()) {
+ final Task parentTask = parent.asTask();
+ if (parentTask == null) {
+ break;
}
+ parentTask.setHasBeenVisible(true);
}
}
+
boolean getHasBeenVisible() {
return mHasBeenVisible;
}
void setDeferTaskAppear(boolean deferTaskAppear) {
+ final boolean wasDeferred = mDeferTaskAppear;
mDeferTaskAppear = deferTaskAppear;
- if (!mDeferTaskAppear) {
+ if (wasDeferred && !deferTaskAppear) {
sendTaskAppeared();
}
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 8b1fb51..7d3367f 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1156,12 +1156,15 @@
private void commitVisibleActivities(SurfaceControl.Transaction transaction) {
for (int i = mParticipants.size() - 1; i >= 0; --i) {
final ActivityRecord ar = mParticipants.valueAt(i).asActivityRecord();
- if (ar == null || !ar.isVisibleRequested()) {
+ if (ar == null || ar.getTask() == null) {
continue;
}
- ar.commitVisibility(true /* visible */, false /* performLayout */,
- true /* fromTransition */);
- ar.commitFinishDrawing(transaction);
+ if (ar.isVisibleRequested()) {
+ ar.commitVisibility(true /* visible */, false /* performLayout */,
+ true /* fromTransition */);
+ ar.commitFinishDrawing(transaction);
+ }
+ ar.getTask().setDeferTaskAppear(false);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index e2c9c17..e931175 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -970,6 +970,10 @@
runSetBooleanFlag(pw, mLetterboxConfiguration
::setIsSplitScreenAspectRatioForUnresizableAppsEnabled);
break;
+ case "--isDisplayAspectRatioEnabledForFixedOrientationLetterbox":
+ runSetBooleanFlag(pw, mLetterboxConfiguration
+ ::setIsDisplayAspectRatioEnabledForFixedOrientationLetterbox);
+ break;
case "--isTranslucentLetterboxingEnabled":
runSetBooleanFlag(pw, mLetterboxConfiguration
::setTranslucentLetterboxingOverrideEnabled);
@@ -1045,6 +1049,10 @@
mLetterboxConfiguration
.resetIsSplitScreenAspectRatioForUnresizableAppsEnabled();
break;
+ case "IsDisplayAspectRatioEnabledForFixedOrientationLetterbox":
+ mLetterboxConfiguration
+ .resetIsDisplayAspectRatioEnabledForFixedOrientationLetterbox();
+ break;
case "isTranslucentLetterboxingEnabled":
mLetterboxConfiguration.resetTranslucentLetterboxingEnabled();
break;
@@ -1155,6 +1163,7 @@
mLetterboxConfiguration.resetDefaultPositionForVerticalReachability();
mLetterboxConfiguration.resetIsEducationEnabled();
mLetterboxConfiguration.resetIsSplitScreenAspectRatioForUnresizableAppsEnabled();
+ mLetterboxConfiguration.resetIsDisplayAspectRatioEnabledForFixedOrientationLetterbox();
mLetterboxConfiguration.resetTranslucentLetterboxingEnabled();
mLetterboxConfiguration.resetCameraCompatRefreshEnabled();
mLetterboxConfiguration.resetCameraCompatRefreshCycleThroughStopEnabled();
@@ -1202,7 +1211,9 @@
pw.println("Is using split screen aspect ratio as aspect ratio for unresizable apps: "
+ mLetterboxConfiguration
.getIsSplitScreenAspectRatioForUnresizableAppsEnabled());
-
+ pw.println("Is using display aspect ratio as aspect ratio for all letterboxed apps: "
+ + mLetterboxConfiguration
+ .getIsDisplayAspectRatioEnabledForFixedOrientationLetterbox());
pw.println(" Is activity \"refresh\" in camera compatibility treatment enabled: "
+ mLetterboxConfiguration.isCameraCompatRefreshEnabled());
pw.println(" Refresh using \"stopped -> resumed\" cycle: "
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index ce67f3c..1406a396 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -30,6 +30,7 @@
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_DEFAULT;
import static android.app.AppOpsManager.OPSTR_SYSTEM_EXEMPT_FROM_APP_STANDBY;
+import static android.app.admin.DeviceAdminInfo.HEADLESS_DEVICE_OWNER_MODE_AFFILIATED;
import static android.app.admin.DeviceAdminReceiver.ACTION_COMPLIANCE_ACKNOWLEDGEMENT_REQUIRED;
import static android.app.admin.DeviceAdminReceiver.EXTRA_TRANSFER_OWNERSHIP_ADMIN_EXTRAS_BUNDLE;
import static android.app.admin.DevicePolicyManager.ACTION_CHECK_POLICY_COMPLIANCE;
@@ -105,6 +106,7 @@
import static android.app.admin.DevicePolicyManager.STATUS_DEVICE_ADMIN_NOT_SUPPORTED;
import static android.app.admin.DevicePolicyManager.STATUS_HAS_DEVICE_OWNER;
import static android.app.admin.DevicePolicyManager.STATUS_HAS_PAIRED;
+import static android.app.admin.DevicePolicyManager.STATUS_HEADLESS_SYSTEM_USER_MODE_NOT_SUPPORTED;
import static android.app.admin.DevicePolicyManager.STATUS_MANAGED_USERS_NOT_SUPPORTED;
import static android.app.admin.DevicePolicyManager.STATUS_NONSYSTEM_USER_EXISTS;
import static android.app.admin.DevicePolicyManager.STATUS_NOT_SYSTEM_USER;
@@ -140,6 +142,7 @@
import static android.app.admin.ProvisioningException.ERROR_SET_DEVICE_OWNER_FAILED;
import static android.app.admin.ProvisioningException.ERROR_STARTING_PROFILE_FAILED;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.pm.PackageManager.GET_META_DATA;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
@@ -2929,7 +2932,7 @@
final ActivityInfo ai = mInjector.binderWithCleanCallingIdentity(() -> {
try {
return mIPackageManager.getReceiverInfo(adminName,
- PackageManager.GET_META_DATA
+ GET_META_DATA
| PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
| PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userHandle);
@@ -7957,13 +7960,11 @@
Preconditions.checkCallAuthorization(hasCrossUsersPermission(caller, userId));
synchronized (getLockObject()) {
- if (mOwners.hasProfileOwner(userId) || mOwners.hasDeviceOwner()) {
- final ActiveAdmin admin = getDeviceOrProfileOwnerAdminLocked(userId);
- return admin.mNearbyNotificationStreamingPolicy;
- }
+ final ActiveAdmin admin = getDeviceOrProfileOwnerAdminLocked(userId);
+ return admin != null
+ ? admin.mNearbyNotificationStreamingPolicy
+ : NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY;
}
-
- return NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY;
}
@Override
@@ -7998,13 +7999,11 @@
Preconditions.checkCallAuthorization(hasCrossUsersPermission(caller, userId));
synchronized (getLockObject()) {
- if (mOwners.hasProfileOwner(userId) || mOwners.hasDeviceOwner()) {
- final ActiveAdmin admin = getDeviceOrProfileOwnerAdminLocked(userId);
- return admin.mNearbyAppStreamingPolicy;
- }
+ final ActiveAdmin admin = getDeviceOrProfileOwnerAdminLocked(userId);
+ return admin != null
+ ? admin.mNearbyAppStreamingPolicy
+ : NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY;
}
-
- return NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY;
}
/**
@@ -14758,7 +14757,14 @@
Preconditions.checkCallAuthorization(
hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
- return checkProvisioningPreconditionSkipPermission(action, packageName, caller.getUserId());
+ long originalId = mInjector.binderClearCallingIdentity();
+ try {
+ return checkProvisioningPreconditionSkipPermission(
+ action, packageName, caller.getUserId());
+ } finally {
+ mInjector.binderRestoreCallingIdentity(originalId);
+ }
+
}
private int checkProvisioningPreconditionSkipPermission(String action,
String packageName, int userId) {
@@ -14824,22 +14830,31 @@
return STATUS_USER_HAS_PROFILE_OWNER;
}
- boolean isHeadlessSystemUserMode = mInjector.userManagerIsHeadlessSystemUserMode();
- // System user is always running in headless system user mode.
- if (!isHeadlessSystemUserMode
- && !mUserManager.isUserRunning(new UserHandle(deviceOwnerUserId))) {
+ if (!mUserManager.isUserRunning(new UserHandle(deviceOwnerUserId))) {
return STATUS_USER_NOT_RUNNING;
}
if (mIsWatch && hasPaired(UserHandle.USER_SYSTEM)) {
return STATUS_HAS_PAIRED;
}
+ boolean isHeadlessSystemUserMode = mInjector.userManagerIsHeadlessSystemUserMode();
+
if (isHeadlessSystemUserMode) {
if (deviceOwnerUserId != UserHandle.USER_SYSTEM) {
Slogf.e(LOG_TAG, "In headless system user mode, "
+ "device owner can only be set on headless system user.");
return STATUS_NOT_SYSTEM_USER;
}
+
+ if (owner != null) {
+ DeviceAdminInfo adminInfo = findAdmin(
+ owner, deviceOwnerUserId, /* throwForMissingPermission= */ false);
+
+ if (adminInfo.getHeadlessDeviceOwnerMode()
+ != HEADLESS_DEVICE_OWNER_MODE_AFFILIATED) {
+ return STATUS_HEADLESS_SYSTEM_USER_MODE_NOT_SUPPORTED;
+ }
+ }
}
if (isAdb) {
@@ -18971,11 +18986,12 @@
"Provisioning preconditions failed with result: " + result);
}
onProvisionFullyManagedDeviceStarted(provisioningParams);
+
+ // These properties are global so will apply on all users
setTimeAndTimezone(provisioningParams.getTimeZone(), provisioningParams.getLocalTime());
setLocale(provisioningParams.getLocale());
- final int deviceOwnerUserId = mInjector.userManagerIsHeadlessSystemUserMode()
- ? UserHandle.USER_SYSTEM : caller.getUserId();
+ int deviceOwnerUserId = UserHandle.USER_SYSTEM;
if (!removeNonRequiredAppsForManagedDevice(
deviceOwnerUserId,
provisioningParams.isLeaveAllSystemAppsEnabled(),
@@ -19101,9 +19117,12 @@
}
private void disallowAddUser() {
- if (mInjector.userManagerIsHeadlessSystemUserMode()) {
- Slogf.i(LOG_TAG, "Not setting DISALLOW_ADD_USER on headless system user mode.");
- return;
+ if (!isHeadlessFlagEnabled() || mIsAutomotive) {
+ // Auto still enables adding users due to the communal nature of those devices
+ if (mInjector.userManagerIsHeadlessSystemUserMode()) {
+ Slogf.i(LOG_TAG, "Not setting DISALLOW_ADD_USER on headless system user mode.");
+ return;
+ }
}
for (UserInfo userInfo : mUserManager.getUsers()) {
UserHandle userHandle = userInfo.getUserHandle();
diff --git a/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt
index 694efbb..730cac9 100644
--- a/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt
@@ -440,7 +440,8 @@
Log.w(
LOG_TAG, "Ignoring permission $permissionName declared in system package" +
" $newPackageName: already declared in another system package" +
- " $oldPackageName")
+ " $oldPackageName"
+ )
return@forEachIndexed
}
} else {
@@ -516,15 +517,20 @@
if (packageState != null && androidPackage == null) {
return
}
- // TODO: STOPSHIP: We may need to retain permission definitions by disabled system packages
- // to retain their permission state.
-
+ val disabledSystemPackage = systemState.disabledSystemPackageStates[packageName]
+ ?.androidPackage
+ // Unlike in the previous implementation, we now also retain permission trees defined by
+ // disabled system packages for consistency with permissions.
val isPermissionTreeRemoved = systemState.permissionTrees.removeAllIndexed {
_, permissionTreeName, permissionTree ->
permissionTree.packageName == packageName && (
packageState == null || androidPackage!!.permissions.noneIndexed { _, it ->
it.isTree && it.name == permissionTreeName
}
+ ) && (
+ disabledSystemPackage?.permissions?.anyIndexed { _, it ->
+ it.isTree && it.name == permissionTreeName
+ } != true
)
}
if (isPermissionTreeRemoved) {
@@ -538,6 +544,10 @@
packageState == null || androidPackage!!.permissions.noneIndexed { _, it ->
!it.isTree && it.name == permissionName
}
+ ) && (
+ disabledSystemPackage?.permissions?.anyIndexed { _, it ->
+ !it.isTree && it.name == permissionName
+ } != true
)) {
// Different from the old implementation where we keep the permission state if the
// permission is declared by a disabled system package (ag/15189282), we now
@@ -574,8 +584,14 @@
private fun MutateStateScope.trimPermissionStates(appId: Int) {
val requestedPermissions = IndexedSet<String>()
forEachPackageInAppId(appId) {
+ // Note that we still trim the permission states requested by disabled system packages.
+ // Because in the previous implementation:
+ // despite revokeSharedUserPermissionsForLeavingPackageInternal() retains permissions
+ // requested by disabled system packages, revokeUnusedSharedUserPermissionsLocked(),
+ // which is call upon app update installation, didn't do such preservation.
+ // Hence, permissions only requested by disabled system packages were still trimmed in
+ // the previous implementation.
requestedPermissions += it.androidPackage!!.requestedPermissions
- // TODO: STOPSHIP: Retain permissions requested by disabled system packages.
}
newState.userStates.forEachIndexed { _, userId, userState ->
userState.uidPermissionFlags[appId]?.forEachReversedIndexed { _, permissionName, _ ->
diff --git a/services/tests/InputMethodSystemServerTests/Android.bp b/services/tests/InputMethodSystemServerTests/Android.bp
index 70a5c3f..05a8b11 100644
--- a/services/tests/InputMethodSystemServerTests/Android.bp
+++ b/services/tests/InputMethodSystemServerTests/Android.bp
@@ -28,7 +28,7 @@
],
srcs: [
- "src/server/**/*.java",
+ "src/com/android/server/inputmethod/**/*.java",
],
static_libs: [
diff --git a/services/tests/InputMethodSystemServerTests/TEST_MAPPING b/services/tests/InputMethodSystemServerTests/TEST_MAPPING
new file mode 100644
index 0000000..77e32a7
--- /dev/null
+++ b/services/tests/InputMethodSystemServerTests/TEST_MAPPING
@@ -0,0 +1,13 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksInputMethodSystemServerTests",
+ "options": [
+ {"include-filter": "com.android.server.inputmethod"},
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+ {"exclude-annotation": "org.junit.Ignore"}
+ ]
+ }
+ ]
+}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
index 73d04c6..720f486 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
@@ -65,19 +65,24 @@
@Test
public void testPerformShowIme() throws Exception {
- mVisibilityApplier.performShowIme(mWindowToken, null, null, SHOW_SOFT_INPUT);
+ synchronized (ImfLock.class) {
+ mVisibilityApplier.performShowIme(mWindowToken, null /* statsToken */,
+ InputMethodManager.SHOW_IMPLICIT, null, SHOW_SOFT_INPUT);
+ }
verifyShowSoftInput(false, true, InputMethodManager.SHOW_IMPLICIT);
}
@Test
public void testPerformHideIme() throws Exception {
- mVisibilityApplier.performHideIme(mWindowToken, null, null, HIDE_SOFT_INPUT);
+ synchronized (ImfLock.class) {
+ mVisibilityApplier.performHideIme(mWindowToken, null /* statsToken */, null,
+ HIDE_SOFT_INPUT);
+ }
verifyHideSoftInput(false, true);
}
@Test
public void testApplyImeVisibility_throwForInvalidState() {
- mVisibilityApplier.applyImeVisibility(mWindowToken, null, STATE_INVALID);
assertThrows(IllegalArgumentException.class,
() -> mVisibilityApplier.applyImeVisibility(mWindowToken, null, STATE_INVALID));
}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
index 8415fe1..a1b9b98 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
@@ -30,6 +30,7 @@
import static com.google.common.truth.Truth.assertThat;
+import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.inputmethod.InputMethodManager;
@@ -177,9 +178,28 @@
assertThat(state.getImeDisplayId()).isEqualTo(FALLBACK_DISPLAY_ID);
}
- private void initImeTargetWindowState(IBinder windowToken) {
+ @Test
+ public void testComputeState_lastImeRequestedVisible_preserved_When_StateUnChanged() {
+ // Assume the last IME targeted window has requested IME visible
+ final IBinder lastImeTargetWindowToken = new Binder();
+ mInputMethodManagerService.mLastImeTargetWindow = lastImeTargetWindowToken;
+ mComputer.requestImeVisibility(lastImeTargetWindowToken, true);
+ final ImeTargetWindowState lastState = mComputer.getWindowStateOrNull(
+ lastImeTargetWindowToken);
+ assertThat(lastState.isRequestedImeVisible()).isTrue();
+
+ // Verify when focusing the next window with STATE_UNCHANGED flag, the last IME
+ // visibility state will be preserved to the current window state.
+ final ImeTargetWindowState stateWithUnChangedFlag = initImeTargetWindowState(mWindowToken);
+ mComputer.computeState(stateWithUnChangedFlag, true /* allowVisible */);
+ assertThat(stateWithUnChangedFlag.isRequestedImeVisible()).isEqualTo(
+ lastState.isRequestedImeVisible());
+ }
+
+ private ImeTargetWindowState initImeTargetWindowState(IBinder windowToken) {
final ImeTargetWindowState state = new ImeTargetWindowState(SOFT_INPUT_STATE_UNCHANGED,
0, true, true, true);
mComputer.setWindowState(windowToken, state);
+ return state;
}
}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
index 804bb49..dbdffd0 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
@@ -71,6 +71,8 @@
/** Base class for testing {@link InputMethodManagerService}. */
public class InputMethodManagerServiceTestBase {
+ private static final int NO_VERIFY_SHOW_FLAGS = -1;
+
protected static final String TEST_SELECTED_IME_ID = "test.ime";
protected static final String TEST_EDITOR_PKG_NAME = "test.editor";
protected static final String TEST_FOCUSED_WINDOW_NAME = "test.editor/activity";
@@ -239,7 +241,7 @@
protected void verifyShowSoftInput(boolean setVisible, boolean showSoftInput)
throws RemoteException {
- verifyShowSoftInput(setVisible, showSoftInput, anyInt());
+ verifyShowSoftInput(setVisible, showSoftInput, NO_VERIFY_SHOW_FLAGS);
}
protected void verifyShowSoftInput(boolean setVisible, boolean showSoftInput, int showFlags)
@@ -249,7 +251,8 @@
.setCurrentMethodVisible();
}
verify(mMockInputMethod, times(showSoftInput ? 1 : 0))
- .showSoftInput(any(), any(), eq(showFlags), any());
+ .showSoftInput(any(), any(),
+ showFlags != NO_VERIFY_SHOW_FLAGS ? eq(showFlags) : anyInt(), any());
}
protected void verifyHideSoftInput(boolean setNotVisible, boolean hideSoftInput)
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
index 2a790a1..f2cba40 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
@@ -80,8 +80,6 @@
@Mock
private DisplayBlanker mDisplayBlankerMock;
@Mock
- private HighBrightnessModeMetadata mHighBrightnessModeMetadataMock;
- @Mock
private LogicalDisplay mLogicalDisplayMock;
@Mock
private DisplayDevice mDisplayDeviceMock;
@@ -171,7 +169,7 @@
mContextSpy, mInjector, mDisplayPowerCallbacksMock, mHandler,
mSensorManagerMock, mDisplayBlankerMock, mLogicalDisplayMock,
mBrightnessTrackerMock, mBrightnessSettingMock, () -> {
- }, mHighBrightnessModeMetadataMock);
+ });
when(mDisplayPowerStateMock.getScreenState()).thenReturn(Display.STATE_ON);
// send a display power request
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
index d99ed78..4f8cb88 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -84,8 +84,6 @@
@Mock
private DisplayDevice mDisplayDeviceMock;
@Mock
- private HighBrightnessModeMetadata mHighBrightnessModeMetadataMock;
- @Mock
private BrightnessTracker mBrightnessTrackerMock;
@Mock
private BrightnessSetting mBrightnessSettingMock;
@@ -153,7 +151,7 @@
mContextSpy, mInjector, mDisplayPowerCallbacksMock, mHandler,
mSensorManagerMock, mDisplayBlankerMock, mLogicalDisplayMock,
mBrightnessTrackerMock, mBrightnessSettingMock, () -> {
- }, mHighBrightnessModeMetadataMock);
+ });
when(mDisplayPowerStateMock.getScreenState()).thenReturn(Display.STATE_ON);
// send a display power request
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 210aeef..4998a6c 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -1085,27 +1085,6 @@
// DPMS.getApplicationLabel() because Context.createPackageContextAsUser() is not mockable.
}
- /**
- * TODO(b/174859111): move to automotive-only section
- * Test for {@link DevicePolicyManager#setDeviceOwner} in headless system user mode.
- */
- @Test
- public void testSetDeviceOwner_headlessSystemUserMode() throws Exception {
- when(getServices().userManagerForMock.isHeadlessSystemUserMode()).thenReturn(true);
- setDeviceOwner_headlessSystemUser();
-
- // Try to set a profile owner on the same user, which should fail.
- setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_UID);
- dpm.setActiveAdmin(admin2, /* refreshing= */ true, CALLER_USER_HANDLE);
- assertExpectException(IllegalStateException.class,
- /* messageRegex= */ "profile owner is already set",
- () -> dpm.setProfileOwner(admin2, CALLER_USER_HANDLE));
-
- // DO admin can't be deactivated.
- dpm.removeActiveAdmin(admin1);
- assertThat(dpm.isAdminActive(admin1)).isTrue();
- }
-
private void setDeviceOwner() throws Exception {
mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
mContext.callerPermissions.add(permission.MANAGE_USERS);
diff --git a/services/tests/servicestests/src/com/android/server/display/HbmEventTest.java b/services/tests/servicestests/src/com/android/server/display/HbmEventTest.java
deleted file mode 100644
index 24fc348..0000000
--- a/services/tests/servicestests/src/com/android/server/display/HbmEventTest.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.display;
-
-import static org.junit.Assert.assertEquals;
-
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public final class HbmEventTest {
- private long mStartTimeMillis;
- private long mEndTimeMillis;
- private HbmEvent mHbmEvent;
-
- @Before
- public void setUp() {
- mStartTimeMillis = 10;
- mEndTimeMillis = 20;
- mHbmEvent = new HbmEvent(mStartTimeMillis, mEndTimeMillis);
- }
-
- @Test
- public void getCorrectValues() {
- assertEquals(mHbmEvent.getStartTimeMillis(), mStartTimeMillis);
- assertEquals(mHbmEvent.getEndTimeMillis(), mEndTimeMillis);
- }
-
- @Test
- public void toStringGeneratesExpectedString() {
- String actualString = mHbmEvent.toString();
- String expectedString = "HbmEvent: {startTimeMillis:" + mStartTimeMillis
- + ", endTimeMillis: " + mEndTimeMillis + "}, total: "
- + ((mEndTimeMillis - mStartTimeMillis) / 1000) + "]";
- assertEquals(actualString, expectedString);
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
index 2655c3f..a1e5ce7 100644
--- a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
@@ -96,7 +96,6 @@
private Binder mDisplayToken;
private String mDisplayUniqueId;
private Context mContextSpy;
- private HighBrightnessModeMetadata mHighBrightnessModeMetadata;
@Rule
public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
@@ -119,7 +118,6 @@
mTestLooper = new TestLooper(mClock::now);
mDisplayToken = null;
mDisplayUniqueId = "unique_id";
-
mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
final MockContentResolver resolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
when(mContextSpy.getContentResolver()).thenReturn(resolver);
@@ -136,8 +134,7 @@
initHandler(null);
final HighBrightnessModeController hbmc = new HighBrightnessModeController(
mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken,
- mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, null, null, () -> {},
- null, mContextSpy);
+ mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, null, null, () -> {}, mContextSpy);
assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF);
assertEquals(hbmc.getTransitionPoint(), HBM_TRANSITION_POINT_INVALID, 0.0f);
}
@@ -147,8 +144,7 @@
initHandler(null);
final HighBrightnessModeController hbmc = new HighBrightnessModeController(
mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken,
- mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, null, null, () -> {},
- null, mContextSpy);
+ mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, null, null, () -> {}, mContextSpy);
hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
hbmc.onAmbientLuxChange(MINIMUM_LUX - 1); // below allowed range
assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF);
@@ -703,12 +699,9 @@
// Creates instance with standard initialization values.
private HighBrightnessModeController createDefaultHbm(OffsettableClock clock) {
initHandler(clock);
- if (mHighBrightnessModeMetadata == null) {
- mHighBrightnessModeMetadata = new HighBrightnessModeMetadata();
- }
return new HighBrightnessModeController(mInjectorMock, mHandler, DISPLAY_WIDTH,
DISPLAY_HEIGHT, mDisplayToken, mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX,
- DEFAULT_HBM_DATA, null, () -> {}, mHighBrightnessModeMetadata, mContextSpy);
+ DEFAULT_HBM_DATA, null, () -> {}, mContextSpy);
}
private void initHandler(OffsettableClock clock) {
diff --git a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeMetadataTest.java b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeMetadataTest.java
deleted file mode 100644
index ede54e0..0000000
--- a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeMetadataTest.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.display;
-
-import static org.junit.Assert.assertEquals;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public final class HighBrightnessModeMetadataTest {
- private HighBrightnessModeMetadata mHighBrightnessModeMetadata;
-
- private long mRunningStartTimeMillis = -1;
-
- @Before
- public void setUp() {
- mHighBrightnessModeMetadata = new HighBrightnessModeMetadata();
- }
-
- @Test
- public void checkDefaultValues() {
- assertEquals(mHighBrightnessModeMetadata.getRunningStartTimeMillis(),
- mRunningStartTimeMillis);
- assertEquals(mHighBrightnessModeMetadata.getHbmEventQueue().size(), 0);
- }
-
- @Test
- public void checkSetValues() {
- mRunningStartTimeMillis = 10;
- mHighBrightnessModeMetadata.setRunningStartTimeMillis(mRunningStartTimeMillis);
- assertEquals(mHighBrightnessModeMetadata.getRunningStartTimeMillis(),
- mRunningStartTimeMillis);
- HbmEvent expectedHbmEvent = new HbmEvent(10, 20);
- mHighBrightnessModeMetadata.addHbmEvent(expectedHbmEvent);
- HbmEvent actualHbmEvent = mHighBrightnessModeMetadata.getHbmEventQueue().peekFirst();
- assertEquals(expectedHbmEvent.toString(), actualHbmEvent.toString());
- }
-}
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 352e498..3b7b5eb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -1931,6 +1931,132 @@
}
@Test
+ public void testDisplayAspectRatioForResizablePortraitApps() {
+ // Set up a display in portrait and ignoring orientation request.
+ int displayWidth = 1400;
+ int displayHeight = 1600;
+ setUpDisplaySizeWithApp(displayWidth, displayHeight);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mWm.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(2f);
+
+ // Enable display aspect ratio to take precedence before
+ // fixedOrientationLetterboxAspectRatio
+ mWm.mLetterboxConfiguration
+ .setIsDisplayAspectRatioEnabledForFixedOrientationLetterbox(true);
+
+ // Set up resizable app in portrait
+ prepareLimitedBounds(mActivity, SCREEN_ORIENTATION_PORTRAIT, false /* isUnresizable */);
+
+ final TestSplitOrganizer organizer =
+ new TestSplitOrganizer(mAtm, mActivity.getDisplayContent());
+ // Move activity to split screen which takes half of the screen.
+ mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test");
+ organizer.mPrimary.setBounds(0, 0, displayWidth, getExpectedSplitSize(displayHeight));
+ assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode());
+ assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
+
+ // App should launch in fixed orientation letterbox.
+ assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
+ // Checking that there is no size compat mode.
+ assertFitted();
+ // Check that the display aspect ratio is used by the app.
+ final float targetMinAspectRatio = 1f * displayHeight / displayWidth;
+ final float delta = 0.01f;
+ assertEquals(targetMinAspectRatio, ActivityRecord
+ .computeAspectRatio(mActivity.getBounds()), delta);
+ }
+
+ @Test
+ public void testDisplayAspectRatioForResizableLandscapeApps() {
+ // Set up a display in landscape and ignoring orientation request.
+ int displayWidth = 1600;
+ int displayHeight = 1400;
+ setUpDisplaySizeWithApp(displayWidth, displayHeight);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mWm.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(2f);
+
+ // Enable display aspect ratio to take precedence before
+ // fixedOrientationLetterboxAspectRatio
+ mWm.mLetterboxConfiguration
+ .setIsDisplayAspectRatioEnabledForFixedOrientationLetterbox(true);
+
+ // Set up resizable app in landscape
+ prepareLimitedBounds(mActivity, SCREEN_ORIENTATION_LANDSCAPE, false /* isUnresizable */);
+
+ final TestSplitOrganizer organizer =
+ new TestSplitOrganizer(mAtm, mActivity.getDisplayContent());
+ // Move activity to split screen which takes half of the screen.
+ mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test");
+ organizer.mPrimary.setBounds(0, 0, getExpectedSplitSize(displayWidth), displayHeight);
+ assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode());
+ assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
+
+ // App should launch in fixed orientation letterbox.
+ assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
+ // Checking that there is no size compat mode.
+ assertFitted();
+ // Check that the display aspect ratio is used by the app.
+ final float targetMinAspectRatio = 1f * displayWidth / displayHeight;
+ final float delta = 0.01f;
+ assertEquals(targetMinAspectRatio, ActivityRecord
+ .computeAspectRatio(mActivity.getBounds()), delta);
+ }
+
+ @Test
+ public void testDisplayAspectRatioForUnresizableLandscapeApps() {
+ // Set up a display in portrait and ignoring orientation request.
+ int displayWidth = 1400;
+ int displayHeight = 1600;
+ setUpDisplaySizeWithApp(displayWidth, displayHeight);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+ mActivity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.1f);
+ // Enable display aspect ratio to take precedence before
+ // fixedOrientationLetterboxAspectRatio
+ mWm.mLetterboxConfiguration
+ .setIsDisplayAspectRatioEnabledForFixedOrientationLetterbox(true);
+
+ prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
+
+ // App should launch in fixed orientation letterbox.
+ assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
+ // Checking that there is no size compat mode.
+ assertFitted();
+ // Check that the display aspect ratio is used by the app.
+ final float targetMinAspectRatio = 1f * displayHeight / displayWidth;
+ final float delta = 0.01f;
+ assertEquals(targetMinAspectRatio, ActivityRecord
+ .computeAspectRatio(mActivity.getBounds()), delta);
+ }
+
+ @Test
+ public void testDisplayAspectRatioForUnresizablePortraitApps() {
+ // Set up a display in landscape and ignoring orientation request.
+ int displayWidth = 1600;
+ int displayHeight = 1400;
+ setUpDisplaySizeWithApp(displayWidth, displayHeight);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+ mActivity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.1f);
+ // Enable display aspect ratio to take precedence before
+ // fixedOrientationLetterboxAspectRatio
+ mWm.mLetterboxConfiguration
+ .setIsDisplayAspectRatioEnabledForFixedOrientationLetterbox(true);
+
+ prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+
+ // App should launch in fixed orientation letterbox.
+ assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
+ // Checking that there is no size compat mode.
+ assertFitted();
+ // Check that the display aspect ratio is used by the app.
+ final float targetMinAspectRatio = 1f * displayWidth / displayHeight;
+ final float delta = 0.01f;
+ assertEquals(targetMinAspectRatio, ActivityRecord
+ .computeAspectRatio(mActivity.getBounds()), delta);
+ }
+
+ @Test
public void
testDisplayIgnoreOrientationRequest_orientationLetterboxBecameSizeCompatAfterRotate() {
// Set up a display in landscape and ignoring orientation request.
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 21135e0..9018138 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -566,6 +566,8 @@
doReturn(mock(IBinder.class)).when(delegateProc.getThread()).asBinder();
final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true)
.setVisible(false).build();
+ final Task task = app.getTask();
+ task.setTaskOrganizer(mock(ITaskOrganizer.class), true /* skipTaskAppeared */);
app.setVisibleRequested(true);
final TransitionController controller = app.mTransitionController;
final Transition transition = controller.createTransition(TRANSIT_OPEN);
@@ -576,7 +578,11 @@
controller.requestStartTransition(transition, null /* startTask */, remoteTransition,
null /* displayChange */);
testPlayer.startTransition();
+ app.onStartingWindowDrawn();
+ // The task appeared event should be deferred until transition ready.
+ assertFalse(task.taskAppearedReady());
testPlayer.onTransactionReady(app.getSyncTransaction());
+ assertTrue(task.taskAppearedReady());
assertTrue(playerProc.isRunningRemoteTransition());
assertTrue(delegateProc.isRunningRemoteTransition());
assertTrue(controller.mRemotePlayer.reportRunning(delegateProc.getThread()));
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index bbdf621..e5efe05 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -262,6 +262,12 @@
// device form factors.
mAtm.mWindowManager.mLetterboxConfiguration
.setIsSplitScreenAspectRatioForUnresizableAppsEnabled(false);
+ // Ensure aspect ratio for al apps isn't overridden on any device target.
+ // {@link com.android.internal.R.bool
+ // .config_letterboxIsDisplayAspectRatioForFixedOrientationLetterboxEnabled}, may be set on
+ // some device form factors.
+ mAtm.mWindowManager.mLetterboxConfiguration
+ .setIsDisplayAspectRatioEnabledForFixedOrientationLetterbox(false);
checkDeviceSpecificOverridesNotApplied();
}
@@ -276,6 +282,8 @@
mAtm.mWindowManager.mLetterboxConfiguration.resetIsVerticalReachabilityEnabled();
mAtm.mWindowManager.mLetterboxConfiguration
.resetIsSplitScreenAspectRatioForUnresizableAppsEnabled();
+ mAtm.mWindowManager.mLetterboxConfiguration
+ .resetIsDisplayAspectRatioEnabledForFixedOrientationLetterbox();
}
/**
diff --git a/telecomm/TEST_MAPPING b/telecomm/TEST_MAPPING
index 0dcf1b6..775f1b8 100644
--- a/telecomm/TEST_MAPPING
+++ b/telecomm/TEST_MAPPING
@@ -25,14 +25,6 @@
]
},
{
- "name": "CtsTelephonyTestCases",
- "options": [
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- }
- ]
- },
- {
"name": "CtsTelephony2TestCases",
"options": [
{
diff --git a/telephony/java/android/telephony/CarrierRestrictionRules.java b/telephony/java/android/telephony/CarrierRestrictionRules.java
index ceea94b..eac4d16 100644
--- a/telephony/java/android/telephony/CarrierRestrictionRules.java
+++ b/telephony/java/android/telephony/CarrierRestrictionRules.java
@@ -104,12 +104,15 @@
private int mCarrierRestrictionDefault;
@MultiSimPolicy
private int mMultiSimPolicy;
+ @TelephonyManager.CarrierRestrictionStatus
+ private int mCarrierRestrictionStatus;
private CarrierRestrictionRules() {
mAllowedCarriers = new ArrayList<CarrierIdentifier>();
mExcludedCarriers = new ArrayList<CarrierIdentifier>();
mCarrierRestrictionDefault = CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED;
mMultiSimPolicy = MULTISIM_POLICY_NONE;
+ mCarrierRestrictionStatus = TelephonyManager.CARRIER_RESTRICTION_STATUS_UNKNOWN;
}
private CarrierRestrictionRules(Parcel in) {
@@ -120,6 +123,7 @@
in.readTypedList(mExcludedCarriers, CarrierIdentifier.CREATOR);
mCarrierRestrictionDefault = in.readInt();
mMultiSimPolicy = in.readInt();
+ mCarrierRestrictionStatus = in.readInt();
}
/**
@@ -289,6 +293,11 @@
return true;
}
+ /** @hide */
+ public int getCarrierRestrictionStatus() {
+ return mCarrierRestrictionStatus;
+ }
+
/**
* {@link Parcelable#writeToParcel}
*/
@@ -298,6 +307,7 @@
out.writeTypedList(mExcludedCarriers);
out.writeInt(mCarrierRestrictionDefault);
out.writeInt(mMultiSimPolicy);
+ out.writeInt(mCarrierRestrictionStatus);
}
/**
@@ -399,5 +409,17 @@
mRules.mMultiSimPolicy = multiSimPolicy;
return this;
}
+
+ /**
+ * Set the device's carrier restriction status
+ *
+ * @param carrierRestrictionStatus device restriction status
+ * @hide
+ */
+ public @NonNull
+ Builder setCarrierRestrictionStatus(int carrierRestrictionStatus) {
+ mRules.mCarrierRestrictionStatus = carrierRestrictionStatus;
+ return this;
+ }
}
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index c47c67e..83b9098 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -13162,6 +13162,81 @@
}
/**
+ * Carrier restriction status value is unknown, in case modem did not provide any
+ * information about carrier restriction status.
+ */
+ public static final int CARRIER_RESTRICTION_STATUS_UNKNOWN = 0;
+
+ /** The device is not restricted to a carrier */
+ public static final int CARRIER_RESTRICTION_STATUS_NOT_RESTRICTED = 1;
+
+ /** The device is restricted to a carrier. */
+ public static final int CARRIER_RESTRICTION_STATUS_RESTRICTED = 2;
+
+ /** The device is restricted to the carrier of the calling application. */
+ public static final int CARRIER_RESTRICTION_STATUS_RESTRICTED_TO_CALLER = 3;
+
+ /** @hide */
+ @IntDef(prefix = {"CARRIER_RESTRICTION_STATUS_"}, value = {
+ CARRIER_RESTRICTION_STATUS_UNKNOWN,
+ CARRIER_RESTRICTION_STATUS_NOT_RESTRICTED,
+ CARRIER_RESTRICTION_STATUS_RESTRICTED,
+ CARRIER_RESTRICTION_STATUS_RESTRICTED_TO_CALLER
+ })
+
+ public @interface CarrierRestrictionStatus {
+ }
+
+ /**
+ * Get the carrier restriction status of the device.
+ * <p>To fetch the carrier restriction status of the device the calling application needs to be
+ * allowlisted to Android at <a href="https://android.googlesource.com/platform/packages/services/Telephony/+/master/assets/CarrierRestrictionOperatorDetails.json">here</a>.
+ * The calling application also needs the READ_PHONE_STATE permission.
+ * The return value of the API is as follows.
+ * <ul>
+ * <li>return {@link #CARRIER_RESTRICTION_STATUS_RESTRICTED_TO_CALLER} if the caller
+ * and the device locked by the network are same</li>
+ * <li>return {@link #CARRIER_RESTRICTION_STATUS_RESTRICTED} if the caller and the
+ * device locked by the network are different</li>
+ * <li>return {@link #CARRIER_RESTRICTION_STATUS_NOT_RESTRICTED} if the device is
+ * not locked</li>
+ * <li>return {@link #CARRIER_RESTRICTION_STATUS_UNKNOWN} if the device locking
+ * state is unavailable or radio does not supports the feature</li>
+ * </ul>
+ *
+ * @param executor The executor on which the result listener will be called.
+ * @param resultListener {@link Consumer} that will be called with the result fetched
+ * from the radio of type {@link CarrierRestrictionStatus}
+ * @throws SecurityException if the caller does not have the required permission/privileges or
+ * if the caller is not pre-registered.
+ */
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public void getCarrierRestrictionStatus(@NonNull Executor executor,
+ @NonNull @CarrierRestrictionStatus
+ Consumer<Integer> resultListener) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(resultListener);
+
+ IIntegerConsumer internalCallback = new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(@CarrierRestrictionStatus int result) {
+ executor.execute(() -> Binder.withCleanCallingIdentity(
+ () -> resultListener.accept(result)));
+ }
+ };
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ service.getCarrierRestrictionStatus(internalCallback, getOpPackageName());
+ }
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "getCarrierRestrictionStatus: RemoteException = " + ex);
+ throw ex.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
* Used to enable or disable carrier data by the system based on carrier signalling or
* carrier privileged apps. Different from {@link #setDataEnabled(boolean)} which is linked to
* user settings, carrier data on/off won't affect user settings but will bypass the
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index c7ed347..6099cb1 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2687,4 +2687,9 @@
* @return {@code true} if the domain selection service is supported.
*/
boolean isDomainSelectionSupported();
+
+ /**
+ * Get the carrier restriction status of the device.
+ */
+ void getCarrierRestrictionStatus(IIntegerConsumer internalCallback, String packageName);
}
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 616f21cb..fdba9a6 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -1163,5 +1163,13 @@
<category android:name="com.android.test.hwui.TEST"/>
</intent-filter>
</activity>
+ <activity android:name="MeshLargeActivity"
+ android:label="Mesh/LargeMesh"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="com.android.test.hwui.TEST"/>
+ </intent-filter>
+ </activity>
</application>
</manifest>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MeshLargeActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/MeshLargeActivity.java
new file mode 100644
index 0000000..f97d942
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/MeshLargeActivity.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.BlendMode;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Mesh;
+import android.graphics.MeshSpecification;
+import android.graphics.MeshSpecification.Attribute;
+import android.graphics.MeshSpecification.Varying;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.view.View;
+
+import java.nio.FloatBuffer;
+import java.nio.ShortBuffer;
+import java.util.ArrayList;
+
+public class MeshLargeActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(new MeshView(this));
+ }
+
+ static class MeshView extends View {
+ MeshView(Context c) {
+ super(c);
+ this.setOnTouchListener((v, event) -> {
+ invalidate();
+ return true;
+ });
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ MeshSpecification meshSpec = createMeshSpecification();
+ int numTriangles = 10000;
+ // number of triangles plus first 2 vertices
+ FloatBuffer vertexBuffer = FloatBuffer.allocate((numTriangles + 2) * 30);
+ ShortBuffer indexBuffer = ShortBuffer.allocate(numTriangles * 3);
+
+ float origin = 500.0f;
+ int radius = 200;
+
+ // origin
+ vertexBuffer.put(0, origin);
+ vertexBuffer.put(1, origin);
+ for (int i = 0; i < 7; i++) {
+ vertexBuffer.put(2 + (i * 4), 1.0f);
+ vertexBuffer.put(2 + (i * 4) + 1, 1.0f);
+ vertexBuffer.put(2 + (i * 4) + 2, 1.0f);
+ vertexBuffer.put(2 + (i * 4) + 3, 1.0f);
+ }
+
+ // first point
+ vertexBuffer.put(30, origin + radius);
+ vertexBuffer.put(31, origin);
+ for (int i = 0; i < 7; i++) {
+ vertexBuffer.put(32 + (i * 4), 1.0f);
+ vertexBuffer.put(32 + (i * 4) + 1, 1.0f);
+ vertexBuffer.put(32 + (i * 4) + 2, 1.0f);
+ vertexBuffer.put(32 + (i * 4) + 3, 1.0f);
+ }
+
+ int nVert = 2;
+ int nInd = 0;
+ for (int i = 2; i <= numTriangles + 1; i++) {
+ double angle = 2 * Math.PI * i / numTriangles;
+ double x = radius * Math.cos(angle);
+ double y = radius * Math.sin(angle);
+ // position
+ vertexBuffer.put(i * 30, origin + (float) x);
+ vertexBuffer.put(i * 30 + 1, origin + (float) y);
+
+ // test through test7
+ for (int j = 0; j < 7; j++) {
+ vertexBuffer.put((i * 30 + 2) + (j * 4), 1.0f);
+ vertexBuffer.put((i * 30 + 2) + (j * 4) + 1, 1.0f);
+ vertexBuffer.put((i * 30 + 2) + (j * 4) + 2, 1.0f);
+ vertexBuffer.put((i * 30 + 2) + (j * 4) + 3, 1.0f);
+ }
+
+ indexBuffer.put(nInd++, (short) 0);
+ indexBuffer.put(nInd++, (short) (nVert - 1));
+ indexBuffer.put(nInd++, (short) nVert);
+ nVert++;
+ }
+ vertexBuffer.rewind();
+ indexBuffer.rewind();
+ Mesh mesh = Mesh.makeIndexed(
+ meshSpec, Mesh.TRIANGLES, vertexBuffer, numTriangles + 2, indexBuffer,
+ new Rect(0, 0, 1000, 1000)
+ );
+ mesh.setFloatUniform("test", 1.0f, 2.0f);
+ Paint paint = new Paint();
+ paint.setColor(Color.BLUE);
+
+ canvas.drawMesh(mesh, BlendMode.SRC, paint);
+ }
+
+ private MeshSpecification createMeshSpecification() {
+ String vs = "Varyings main(const Attributes attributes) { "
+ + " Varyings varyings;"
+ + " varyings.position = attributes.position;"
+ + " return varyings;"
+ + "}";
+ String fs = "uniform float2 test;"
+ + "float2 main(const Varyings varyings, out float4 color) {\n"
+ + " color = vec4(1.0, 0.0, 0.0, 1.0);"
+ + " return varyings.position;\n"
+ + "}";
+ ArrayList<Attribute> attList = new ArrayList<>();
+ attList.add(new Attribute(MeshSpecification.FLOAT2, 0, "position"));
+ attList.add(new Attribute(
+ MeshSpecification.FLOAT4,
+ 8,
+ "test"
+ ));
+ attList.add(new Attribute(
+ MeshSpecification.FLOAT4,
+ 24,
+ "test2"
+ ));
+ attList.add(new Attribute(
+ MeshSpecification.FLOAT4,
+ 40,
+ "test3"
+ ));
+ attList.add(new Attribute(
+ MeshSpecification.FLOAT4,
+ 56,
+ "test4"
+ ));
+ attList.add(new Attribute(
+ MeshSpecification.FLOAT4,
+ 72,
+ "test5"
+ ));
+ attList.add(new Attribute(
+ MeshSpecification.FLOAT4,
+ 88,
+ "test6"
+ ));
+ attList.add(new Attribute(
+ MeshSpecification.FLOAT4,
+ 104,
+ "test7"
+ ));
+ ArrayList<Varying> varyList = new ArrayList<>();
+ return MeshSpecification.make(attList, 120, varyList, vs, fs);
+ }
+ }
+}