Merge "Bouncer - User switcher for large screens"
diff --git a/core/api/current.txt b/core/api/current.txt
index 5f7e330..a65cf949 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -31935,7 +31935,7 @@
method public <K, V> void readMap(@NonNull java.util.Map<? super K,? super V>, @Nullable ClassLoader, @NonNull Class<K>, @NonNull Class<V>);
method @Deprecated @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader);
method @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader, @NonNull Class<T>);
- method @Nullable public android.os.Parcelable[] readParcelableArray(@Nullable ClassLoader);
+ method @Deprecated @Nullable public android.os.Parcelable[] readParcelableArray(@Nullable ClassLoader);
method @Nullable public <T> T[] readParcelableArray(@Nullable ClassLoader, @NonNull Class<T>);
method @Deprecated @Nullable public android.os.Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader);
method @Nullable public <T> android.os.Parcelable.Creator<T> readParcelableCreator(@Nullable ClassLoader, @NonNull Class<T>);
@@ -32503,6 +32503,7 @@
field public static final String ALLOW_PARENT_PROFILE_APP_LINKING = "allow_parent_profile_app_linking";
field @Deprecated public static final String DISALLOW_ADD_MANAGED_PROFILE = "no_add_managed_profile";
field public static final String DISALLOW_ADD_USER = "no_add_user";
+ field public static final String DISALLOW_ADD_WIFI_CONFIG = "no_add_wifi_config";
field public static final String DISALLOW_ADJUST_VOLUME = "no_adjust_volume";
field public static final String DISALLOW_AIRPLANE_MODE = "no_airplane_mode";
field public static final String DISALLOW_AMBIENT_DISPLAY = "no_ambient_display";
@@ -32558,6 +32559,7 @@
field public static final String DISALLOW_UNMUTE_MICROPHONE = "no_unmute_microphone";
field public static final String DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer";
field public static final String DISALLOW_USER_SWITCH = "no_user_switch";
+ field public static final String DISALLOW_WIFI_DIRECT = "no_wifi_direct";
field public static final String DISALLOW_WIFI_TETHERING = "no_wifi_tethering";
field public static final String ENSURE_VERIFY_APPS = "ensure_verify_apps";
field public static final String KEY_RESTRICTIONS_PENDING = "restrictions_pending";
@@ -40453,6 +40455,7 @@
field public static final String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS";
field public static final String EXTRA_IS_RTT_AUDIO_PRESENT = "android.telecom.extra.IS_RTT_AUDIO_PRESENT";
field public static final String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER";
+ field public static final String EXTRA_LAST_KNOWN_CELL_IDENTITY = "android.telecom.extra.LAST_KNOWN_CELL_IDENTITY";
field public static final String EXTRA_SIP_INVITE = "android.telecom.extra.SIP_INVITE";
field public static final int PROPERTY_ASSISTED_DIALING = 512; // 0x200
field public static final int PROPERTY_CROSS_SIM = 8192; // 0x2000
@@ -51344,7 +51347,7 @@
method public void addAccessibilityServicesStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener);
method public boolean addAccessibilityStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener);
method public void addAccessibilityStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener, @Nullable android.os.Handler);
- method public void addAudioDescriptionByDefaultStateChangeListener(@NonNull java.util.concurrent.Executor, @NonNull android.view.accessibility.AccessibilityManager.AudioDescriptionByDefaultStateChangeListener);
+ method public void addAudioDescriptionRequestedChangeListener(@NonNull java.util.concurrent.Executor, @NonNull android.view.accessibility.AccessibilityManager.AudioDescriptionRequestedChangeListener);
method public boolean addTouchExplorationStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener);
method public void addTouchExplorationStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener, @Nullable android.os.Handler);
method @ColorInt public int getAccessibilityFocusColor();
@@ -51361,7 +51364,7 @@
method public void removeAccessibilityRequestPreparer(android.view.accessibility.AccessibilityRequestPreparer);
method public boolean removeAccessibilityServicesStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener);
method public boolean removeAccessibilityStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener);
- method public boolean removeAudioDescriptionByDefaultStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.AudioDescriptionByDefaultStateChangeListener);
+ method public boolean removeAudioDescriptionRequestedChangeListener(@NonNull android.view.accessibility.AccessibilityManager.AudioDescriptionRequestedChangeListener);
method public boolean removeTouchExplorationStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener);
method public void sendAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
field public static final int FLAG_CONTENT_CONTROLS = 4; // 0x4
@@ -51377,8 +51380,8 @@
method public void onAccessibilityStateChanged(boolean);
}
- public static interface AccessibilityManager.AudioDescriptionByDefaultStateChangeListener {
- method public void onAudioDescriptionByDefaultStateChanged(boolean);
+ public static interface AccessibilityManager.AudioDescriptionRequestedChangeListener {
+ method public void onAudioDescriptionRequestedChanged(boolean);
}
public static interface AccessibilityManager.TouchExplorationStateChangeListener {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index dd951b4..c1d1518 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -224,6 +224,7 @@
field public static final String READ_APP_SPECIFIC_LOCALES = "android.permission.READ_APP_SPECIFIC_LOCALES";
field public static final String READ_CARRIER_APP_INFO = "android.permission.READ_CARRIER_APP_INFO";
field public static final String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS";
+ field public static final String READ_COMMUNAL_STATE = "android.permission.READ_COMMUNAL_STATE";
field public static final String READ_CONTENT_RATING_SYSTEMS = "android.permission.READ_CONTENT_RATING_SYSTEMS";
field public static final String READ_DEVICE_CONFIG = "android.permission.READ_DEVICE_CONFIG";
field public static final String READ_DREAM_STATE = "android.permission.READ_DREAM_STATE";
@@ -393,6 +394,7 @@
field public static final int config_helpPackageNameValue = 17039388; // 0x104001c
field public static final int config_systemActivityRecognizer = 17039416; // 0x1040038
field public static final int config_systemAmbientAudioIntelligence = 17039411; // 0x1040033
+ field public static final int config_systemAppProtectionService;
field public static final int config_systemAudioIntelligence = 17039412; // 0x1040034
field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028
field public static final int config_systemAutomotiveProjection = 17039401; // 0x1040029
@@ -1309,6 +1311,20 @@
}
+package android.app.communal {
+
+ public final class CommunalManager {
+ method @RequiresPermission(android.Manifest.permission.READ_COMMUNAL_STATE) public void addCommunalModeListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.communal.CommunalManager.CommunalModeListener);
+ method @RequiresPermission(android.Manifest.permission.READ_COMMUNAL_STATE) public boolean isCommunalMode();
+ method @RequiresPermission(android.Manifest.permission.READ_COMMUNAL_STATE) public void removeCommunalModeListener(@NonNull android.app.communal.CommunalManager.CommunalModeListener);
+ }
+
+ @java.lang.FunctionalInterface public static interface CommunalManager.CommunalModeListener {
+ method public void onCommunalModeChanged(boolean);
+ }
+
+}
+
package android.app.compat {
public final class CompatChanges {
@@ -2491,6 +2507,7 @@
field public static final String BATTERY_STATS_SERVICE = "batterystats";
field @Deprecated public static final int BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS = 1048576; // 0x100000
field public static final int BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND = 262144; // 0x40000
+ field public static final String COMMUNAL_SERVICE = "communal";
field public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions";
field public static final String CONTEXTHUB_SERVICE = "contexthub";
field public static final String ETHERNET_SERVICE = "ethernet";
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index aa791aa..bf06db0 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -42,6 +42,7 @@
field public static final String TEST_BIOMETRIC = "android.permission.TEST_BIOMETRIC";
field public static final String TEST_MANAGE_ROLLBACKS = "android.permission.TEST_MANAGE_ROLLBACKS";
field public static final String UPGRADE_RUNTIME_PERMISSIONS = "android.permission.UPGRADE_RUNTIME_PERMISSIONS";
+ field public static final String WRITE_COMMUNAL_STATE = "android.permission.WRITE_COMMUNAL_STATE";
field public static final String WRITE_DEVICE_CONFIG = "android.permission.WRITE_DEVICE_CONFIG";
field @Deprecated public static final String WRITE_MEDIA_STORAGE = "android.permission.WRITE_MEDIA_STORAGE";
field public static final String WRITE_OBB = "android.permission.WRITE_OBB";
@@ -608,6 +609,14 @@
}
+package android.app.communal {
+
+ public final class CommunalManager {
+ method @RequiresPermission(android.Manifest.permission.WRITE_COMMUNAL_STATE) public void setCommunalViewShowing(boolean);
+ }
+
+}
+
package android.app.contentsuggestions {
public final class ContentSuggestionsManager {
@@ -819,6 +828,7 @@
method public void holdLock(android.os.IBinder, int);
method @RequiresPermission(android.Manifest.permission.KEEP_UNINSTALLED_PACKAGES) public void setKeepUninstalledPackages(@NonNull java.util.List<java.lang.String>);
field public static final String FEATURE_ADOPTABLE_STORAGE = "android.software.adoptable_storage";
+ field public static final String FEATURE_COMMUNAL_MODE = "android.software.communal_mode";
field public static final String FEATURE_FILE_BASED_ENCRYPTION = "android.software.file_based_encryption";
field public static final String FEATURE_HDMI_CEC = "android.hardware.hdmi.cec";
field public static final int FLAG_PERMISSION_REVOKE_WHEN_REQUESTED = 128; // 0x80
diff --git a/core/java/android/app/NotificationHistory.java b/core/java/android/app/NotificationHistory.java
index eb9ec86..b40fbee 100644
--- a/core/java/android/app/NotificationHistory.java
+++ b/core/java/android/app/NotificationHistory.java
@@ -497,7 +497,11 @@
p.writeLong(notification.getPostedTimeMs());
p.writeString(notification.getTitle());
p.writeString(notification.getText());
- notification.getIcon().writeToParcel(p, flags);
+ p.writeBoolean(false);
+ // The current design does not display icons, so don't bother adding them to the parcel
+ //if (notification.getIcon() != null) {
+ // notification.getIcon().writeToParcel(p, flags);
+ //}
}
/**
@@ -539,7 +543,9 @@
notificationOut.setPostedTimeMs(p.readLong());
notificationOut.setTitle(p.readString());
notificationOut.setText(p.readString());
- notificationOut.setIcon(Icon.CREATOR.createFromParcel(p));
+ if (p.readBoolean()) {
+ notificationOut.setIcon(Icon.CREATOR.createFromParcel(p));
+ }
return notificationOut.build();
}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 089c269..81e6ae4 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -1513,7 +1513,7 @@
}
});
- registerService(Context.COMMUNAL_MANAGER_SERVICE, CommunalManager.class,
+ registerService(Context.COMMUNAL_SERVICE, CommunalManager.class,
new CachedServiceFetcher<CommunalManager>() {
@Override
public CommunalManager createService(ContextImpl ctx) {
@@ -1522,7 +1522,7 @@
return null;
}
IBinder iBinder =
- ServiceManager.getService(Context.COMMUNAL_MANAGER_SERVICE);
+ ServiceManager.getService(Context.COMMUNAL_SERVICE);
return iBinder != null ? new CommunalManager(
ICommunalManager.Stub.asInterface(iBinder)) : null;
}
diff --git a/core/java/android/app/communal/CommunalManager.java b/core/java/android/app/communal/CommunalManager.java
index 60730ad..22f07693 100644
--- a/core/java/android/app/communal/CommunalManager.java
+++ b/core/java/android/app/communal/CommunalManager.java
@@ -17,15 +17,21 @@
package android.app.communal;
import android.Manifest;
+import android.annotation.NonNull;
import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled;
import android.compat.annotation.Overridable;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.RemoteException;
+import android.util.ArrayMap;
+
+import java.util.concurrent.Executor;
/**
* System private class for talking with the
@@ -33,10 +39,12 @@
*
* @hide
*/
-@SystemService(Context.COMMUNAL_MANAGER_SERVICE)
+@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+@SystemService(Context.COMMUNAL_SERVICE)
@RequiresFeature(PackageManager.FEATURE_COMMUNAL_MODE)
public final class CommunalManager {
private final ICommunalManager mService;
+ private final ArrayMap<CommunalModeListener, ICommunalModeListener> mCommunalModeListeners;
/**
* This change id is used to annotate packages which can run in communal mode by default,
@@ -59,15 +67,20 @@
@Disabled
public static final long ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT = 200324021L;
+ /** @hide */
public CommunalManager(ICommunalManager service) {
mService = service;
+ mCommunalModeListeners = new ArrayMap<CommunalModeListener, ICommunalModeListener>();
}
/**
* Updates whether or not the communal view is currently showing over the lockscreen.
*
* @param isShowing Whether communal view is showing.
+ *
+ * @hide
*/
+ @TestApi
@RequiresPermission(Manifest.permission.WRITE_COMMUNAL_STATE)
public void setCommunalViewShowing(boolean isShowing) {
try {
@@ -76,4 +89,72 @@
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Checks whether or not the communal view is currently showing over the lockscreen.
+ */
+ @RequiresPermission(Manifest.permission.READ_COMMUNAL_STATE)
+ public boolean isCommunalMode() {
+ try {
+ return mService.isCommunalMode();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Listener for communal state changes.
+ */
+ @FunctionalInterface
+ public interface CommunalModeListener {
+ /**
+ * Callback function that executes when the communal state changes.
+ */
+ void onCommunalModeChanged(boolean isCommunalMode);
+ }
+
+ /**
+ * Registers a callback to execute when the communal state changes.
+ *
+ * @param listener The listener to add to receive communal state changes.
+ * @param executor {@link Executor} to dispatch to. To dispatch the callback to the main
+ * thread of your application, use
+ * {@link android.content.Context#getMainExecutor()}.
+ */
+ @RequiresPermission(Manifest.permission.READ_COMMUNAL_STATE)
+ public void addCommunalModeListener(@NonNull Executor executor,
+ @NonNull CommunalModeListener listener) {
+ synchronized (mCommunalModeListeners) {
+ try {
+ ICommunalModeListener iListener = new ICommunalModeListener.Stub() {
+ @Override
+ public void onCommunalModeChanged(boolean isCommunalMode) {
+ executor.execute(() -> listener.onCommunalModeChanged(isCommunalMode));
+ }
+ };
+ mService.addCommunalModeListener(iListener);
+ mCommunalModeListeners.put(listener, iListener);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Unregisters a callback that executes when communal state changes.
+ */
+ @RequiresPermission(Manifest.permission.READ_COMMUNAL_STATE)
+ public void removeCommunalModeListener(@NonNull CommunalModeListener listener) {
+ synchronized (mCommunalModeListeners) {
+ ICommunalModeListener iListener = mCommunalModeListeners.get(listener);
+ if (iListener != null) {
+ try {
+ mService.removeCommunalModeListener(iListener);
+ mCommunalModeListeners.remove(listener);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+ }
}
diff --git a/core/java/android/app/communal/ICommunalManager.aidl b/core/java/android/app/communal/ICommunalManager.aidl
index 02e8a65..869891e 100644
--- a/core/java/android/app/communal/ICommunalManager.aidl
+++ b/core/java/android/app/communal/ICommunalManager.aidl
@@ -16,12 +16,17 @@
package android.app.communal;
+import android.app.communal.ICommunalModeListener;
+
/**
* System private API for talking with the communal manager service that handles communal mode
* state.
*
* @hide
*/
-oneway interface ICommunalManager {
- void setCommunalViewShowing(boolean isShowing);
+interface ICommunalManager {
+ oneway void setCommunalViewShowing(boolean isShowing);
+ boolean isCommunalMode();
+ void addCommunalModeListener(in ICommunalModeListener listener);
+ void removeCommunalModeListener(in ICommunalModeListener listener);
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java b/core/java/android/app/communal/ICommunalModeListener.aidl
similarity index 74%
rename from packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
rename to core/java/android/app/communal/ICommunalModeListener.aidl
index fbb78c8..006e782 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
+++ b/core/java/android/app/communal/ICommunalModeListener.aidl
@@ -14,12 +14,13 @@
* limitations under the License.
*/
-package com.android.systemui.flags;
+package android.app.communal;
/**
- * Class to manage simple DeviceConfig-based feature flags.
+ * System private API to be notified about communal mode changes.
*
- * See {@link Flags} for instructions on defining new flags.
+ * @hide
*/
-public interface FeatureFlags extends FlagReader {
-}
+oneway interface ICommunalModeListener {
+ void onCommunalModeChanged(boolean isCommunalMode);
+}
\ No newline at end of file
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
index c000e56..8ee38d3 100644
--- a/core/java/android/bluetooth/BluetoothPbap.java
+++ b/core/java/android/bluetooth/BluetoothPbap.java
@@ -23,7 +23,6 @@
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Attributable;
import android.content.AttributionSource;
@@ -170,7 +169,7 @@
mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- UserHandle.CURRENT_OR_SELF)) {
+ UserHandle.CURRENT)) {
Log.e(TAG, "Could not bind to Bluetooth Pbap Service with " + intent);
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothProfileConnector.java b/core/java/android/bluetooth/BluetoothProfileConnector.java
index a254291..ecd5e40 100644
--- a/core/java/android/bluetooth/BluetoothProfileConnector.java
+++ b/core/java/android/bluetooth/BluetoothProfileConnector.java
@@ -103,7 +103,7 @@
mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- UserHandle.CURRENT_OR_SELF)) {
+ UserHandle.CURRENT)) {
logError("Could not bind to Bluetooth Service with " + intent);
return false;
}
diff --git a/core/java/android/bluetooth/le/TransportBlock.java b/core/java/android/bluetooth/le/TransportBlock.java
index b388bed..18bad9c 100644
--- a/core/java/android/bluetooth/le/TransportBlock.java
+++ b/core/java/android/bluetooth/le/TransportBlock.java
@@ -24,6 +24,7 @@
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
+import java.util.Arrays;
/**
* Wrapper for Transport Discovery Data Transport Blocks.
@@ -59,8 +60,12 @@
mOrgId = in.readInt();
mTdsFlags = in.readInt();
mTransportDataLength = in.readInt();
- mTransportData = new byte[mTransportDataLength];
- in.readByteArray(mTransportData);
+ if (mTransportDataLength > 0) {
+ mTransportData = new byte[mTransportDataLength];
+ in.readByteArray(mTransportData);
+ } else {
+ mTransportData = null;
+ }
}
@Override
@@ -68,7 +73,9 @@
dest.writeInt(mOrgId);
dest.writeInt(mTdsFlags);
dest.writeInt(mTransportDataLength);
- dest.writeByteArray(mTransportData);
+ if (mTransportData != null) {
+ dest.writeByteArray(mTransportData);
+ }
}
/**
@@ -79,6 +86,21 @@
return 0;
}
+ /**
+ * @hide
+ */
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ TransportBlock other = (TransportBlock) obj;
+ return Arrays.equals(toByteArray(), other.toByteArray());
+ }
+
public static final @NonNull Creator<TransportBlock> CREATOR = new Creator<TransportBlock>() {
@Override
public TransportBlock createFromParcel(Parcel in) {
diff --git a/core/java/android/bluetooth/le/TransportDiscoveryData.java b/core/java/android/bluetooth/le/TransportDiscoveryData.java
index c8e97f9..2b52f19 100644
--- a/core/java/android/bluetooth/le/TransportDiscoveryData.java
+++ b/core/java/android/bluetooth/le/TransportDiscoveryData.java
@@ -26,6 +26,7 @@
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -96,6 +97,21 @@
return 0;
}
+ /**
+ * @hide
+ */
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ TransportDiscoveryData other = (TransportDiscoveryData) obj;
+ return Arrays.equals(toByteArray(), other.toByteArray());
+ }
+
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mTransportDataType);
diff --git a/core/java/android/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java
index 1d4d30d..f335ae4 100644
--- a/core/java/android/content/BroadcastReceiver.java
+++ b/core/java/android/content/BroadcastReceiver.java
@@ -27,6 +27,7 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.Trace;
import android.os.UserHandle;
import android.util.Log;
import android.util.Slog;
@@ -98,6 +99,7 @@
boolean mAbortBroadcast;
@UnsupportedAppUsage
boolean mFinished;
+ String mReceiverClassName;
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
@@ -219,6 +221,12 @@
* next broadcast will proceed.
*/
public final void finish() {
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+ Trace.traceCounter(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ "PendingResult#finish#ClassName:" + mReceiverClassName,
+ 1);
+ }
+
if (mType == TYPE_COMPONENT) {
final IActivityManager mgr = ActivityManager.getService();
if (QueuedWork.hasPendingWork()) {
@@ -383,6 +391,14 @@
public final PendingResult goAsync() {
PendingResult res = mPendingResult;
mPendingResult = null;
+
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+ res.mReceiverClassName = getClass().getName();
+ Trace.traceCounter(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ "BroadcastReceiver#goAsync#ClassName:" + res.mReceiverClassName,
+ 1);
+ }
+
return res;
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 73740d2c..543239b 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -5862,13 +5862,14 @@
/**
* Use with {@link #getSystemService(String)} to retrieve a
- * {@link android.app.CommunalManager} for interacting with the global system state.
+ * {@link android.app.communal.CommunalManager} for interacting with the global system state.
*
* @see #getSystemService(String)
- * @see android.app.CommunalManager
+ * @see android.app.communal.CommunalManager
* @hide
*/
- public static final String COMMUNAL_MANAGER_SERVICE = "communal_manager";
+ @SystemApi
+ public static final String COMMUNAL_SERVICE = "communal";
/**
* Use with {@link #getSystemService(String)} to retrieve a
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 45d6ddd..ffe4ea4 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1374,18 +1374,18 @@
* Returns if the activity should never be sandboxed to the activity window bounds.
* @hide
*/
- public boolean neverSandboxDisplayApis() {
+ public boolean neverSandboxDisplayApis(ConstrainDisplayApisConfig constrainDisplayApisConfig) {
return isChangeEnabled(NEVER_SANDBOX_DISPLAY_APIS)
- || ConstrainDisplayApisConfig.neverConstrainDisplayApis(applicationInfo);
+ || constrainDisplayApisConfig.getNeverConstrainDisplayApis(applicationInfo);
}
/**
* Returns if the activity should always be sandboxed to the activity window bounds.
* @hide
*/
- public boolean alwaysSandboxDisplayApis() {
+ public boolean alwaysSandboxDisplayApis(ConstrainDisplayApisConfig constrainDisplayApisConfig) {
return isChangeEnabled(ALWAYS_SANDBOX_DISPLAY_APIS)
- || ConstrainDisplayApisConfig.alwaysConstrainDisplayApis(applicationInfo);
+ || constrainDisplayApisConfig.getAlwaysConstrainDisplayApis(applicationInfo);
}
/** @hide */
diff --git a/core/java/android/content/pm/ConstrainDisplayApisConfig.java b/core/java/android/content/pm/ConstrainDisplayApisConfig.java
index 11ba3d4..98b73aa 100644
--- a/core/java/android/content/pm/ConstrainDisplayApisConfig.java
+++ b/core/java/android/content/pm/ConstrainDisplayApisConfig.java
@@ -19,10 +19,15 @@
import static android.provider.DeviceConfig.NAMESPACE_CONSTRAIN_DISPLAY_APIS;
import android.provider.DeviceConfig;
+import android.util.ArrayMap;
+import android.util.Pair;
import android.util.Slog;
+import com.android.internal.os.BackgroundThread;
+
import java.util.Arrays;
import java.util.List;
+import java.util.Map;
/**
* Class for processing flags in the Device Config namespace 'constrain_display_apis'.
@@ -55,19 +60,45 @@
"always_constrain_display_apis";
/**
+ * Indicates that display APIs should never be constrained to the activity window bounds for all
+ * packages.
+ */
+ private boolean mNeverConstrainDisplayApisAllPackages;
+
+ /**
+ * Indicates that display APIs should never be constrained to the activity window bounds for
+ * a set of defined packages. Map keys are package names, and entries are a
+ * 'Pair(<min-version-code>, <max-version-code>)'.
+ */
+ private ArrayMap<String, Pair<Long, Long>> mNeverConstrainConfigMap;
+
+ /**
+ * Indicates that display APIs should always be constrained to the activity window bounds for
+ * a set of defined packages. Map keys are package names, and entries are a
+ * 'Pair(<min-version-code>, <max-version-code>)'.
+ */
+ private ArrayMap<String, Pair<Long, Long>> mAlwaysConstrainConfigMap;
+
+ public ConstrainDisplayApisConfig() {
+ updateCache();
+
+ DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_CONSTRAIN_DISPLAY_APIS,
+ BackgroundThread.getExecutor(), properties -> updateCache());
+ }
+
+ /**
* Returns true if either the flag 'never_constrain_display_apis_all_packages' is true or the
* flag 'never_constrain_display_apis' contains a package entry that matches the given {@code
* applicationInfo}.
*
* @param applicationInfo Information about the application/package.
*/
- public static boolean neverConstrainDisplayApis(ApplicationInfo applicationInfo) {
- if (DeviceConfig.getBoolean(NAMESPACE_CONSTRAIN_DISPLAY_APIS,
- FLAG_NEVER_CONSTRAIN_DISPLAY_APIS_ALL_PACKAGES, /* defaultValue= */ false)) {
+ public boolean getNeverConstrainDisplayApis(ApplicationInfo applicationInfo) {
+ if (mNeverConstrainDisplayApisAllPackages) {
return true;
}
- return flagHasMatchingPackageEntry(FLAG_NEVER_CONSTRAIN_DISPLAY_APIS, applicationInfo);
+ return flagHasMatchingPackageEntry(mNeverConstrainConfigMap, applicationInfo);
}
/**
@@ -76,73 +107,106 @@
*
* @param applicationInfo Information about the application/package.
*/
- public static boolean alwaysConstrainDisplayApis(ApplicationInfo applicationInfo) {
- return flagHasMatchingPackageEntry(FLAG_ALWAYS_CONSTRAIN_DISPLAY_APIS, applicationInfo);
+ public boolean getAlwaysConstrainDisplayApis(ApplicationInfo applicationInfo) {
+ return flagHasMatchingPackageEntry(mAlwaysConstrainConfigMap, applicationInfo);
+ }
+
+
+ /**
+ * Updates {@link #mNeverConstrainDisplayApisAllPackages}, {@link #mNeverConstrainConfigMap},
+ * and {@link #mAlwaysConstrainConfigMap} from the {@link DeviceConfig}.
+ */
+ private void updateCache() {
+ mNeverConstrainDisplayApisAllPackages = DeviceConfig.getBoolean(
+ NAMESPACE_CONSTRAIN_DISPLAY_APIS,
+ FLAG_NEVER_CONSTRAIN_DISPLAY_APIS_ALL_PACKAGES, /* defaultValue= */ false);
+
+ final String neverConstrainConfigStr = DeviceConfig.getString(
+ NAMESPACE_CONSTRAIN_DISPLAY_APIS,
+ FLAG_NEVER_CONSTRAIN_DISPLAY_APIS, /* defaultValue= */ "");
+ mNeverConstrainConfigMap = buildConfigMap(neverConstrainConfigStr);
+
+ final String alwaysConstrainConfigStr = DeviceConfig.getString(
+ NAMESPACE_CONSTRAIN_DISPLAY_APIS,
+ FLAG_ALWAYS_CONSTRAIN_DISPLAY_APIS, /* defaultValue= */ "");
+ mAlwaysConstrainConfigMap = buildConfigMap(alwaysConstrainConfigStr);
+ }
+
+ /**
+ * Processes the configuration string into a map of version codes, for the given
+ * configuration to be applied to the specified packages. If the given package
+ * entry string is invalid, then the map will not contain an entry for the package.
+ *
+ * @param configStr A configuration string expected to be in the format of a list of package
+ * entries separated by ','. A package entry expected to be in the format
+ * '<package-name>:<min-version-code>?:<max-version-code>?'.
+ * @return a map of configuration entries, where each key is a package name. Each value is
+ * a pair of version codes, in the format 'Pair(<min-version-code>, <max-version-code>)'.
+ */
+ private static ArrayMap<String, Pair<Long, Long>> buildConfigMap(String configStr) {
+ ArrayMap<String, Pair<Long, Long>> configMap = new ArrayMap<>();
+ // String#split returns a non-empty array given an empty string.
+ if (configStr.isEmpty()) {
+ return configMap;
+ }
+ for (String packageEntryString : configStr.split(",")) {
+ List<String> packageAndVersions = Arrays.asList(packageEntryString.split(":", 3));
+ if (packageAndVersions.size() != 3) {
+ Slog.w(TAG, "Invalid package entry in flag 'never/always_constrain_display_apis': "
+ + packageEntryString);
+ // Skip this entry.
+ continue;
+ }
+ String packageName = packageAndVersions.get(0);
+ String minVersionCodeStr = packageAndVersions.get(1);
+ String maxVersionCodeStr = packageAndVersions.get(2);
+ try {
+ final long minVersion =
+ minVersionCodeStr.isEmpty() ? Long.MIN_VALUE : Long.parseLong(
+ minVersionCodeStr);
+ final long maxVersion =
+ maxVersionCodeStr.isEmpty() ? Long.MAX_VALUE : Long.parseLong(
+ maxVersionCodeStr);
+ Pair<Long, Long> minMaxVersionCodes = new Pair<>(minVersion, maxVersion);
+ configMap.put(packageName, minMaxVersionCodes);
+ } catch (NumberFormatException e) {
+ Slog.w(TAG, "Invalid APK version code in package entry: " + packageEntryString);
+ // Skip this entry.
+ }
+ }
+ return configMap;
}
/**
* Returns true if the flag with the given {@code flagName} contains a package entry that
* matches the given {@code applicationInfo}.
*
+ * @param configMap the map representing the current configuration value to examine
* @param applicationInfo Information about the application/package.
*/
- private static boolean flagHasMatchingPackageEntry(String flagName,
+ private static boolean flagHasMatchingPackageEntry(Map<String, Pair<Long, Long>> configMap,
ApplicationInfo applicationInfo) {
- String configStr = DeviceConfig.getString(NAMESPACE_CONSTRAIN_DISPLAY_APIS,
- flagName, /* defaultValue= */ "");
-
- // String#split returns a non-empty array given an empty string.
- if (configStr.isEmpty()) {
+ if (configMap.isEmpty()) {
return false;
}
-
- for (String packageEntryString : configStr.split(",")) {
- if (matchesApplicationInfo(packageEntryString, applicationInfo)) {
- return true;
- }
+ if (!configMap.containsKey(applicationInfo.packageName)) {
+ return false;
}
-
- return false;
+ return matchesApplicationInfo(configMap.get(applicationInfo.packageName), applicationInfo);
}
/**
- * Parses the given {@code packageEntryString} and returns true if {@code
- * applicationInfo.packageName} matches the package name in the config and {@code
- * applicationInfo.longVersionCode} is within the version range in the config.
+ * Parses the given {@code minMaxVersionCodes} and returns true if {@code
+ * applicationInfo.longVersionCode} is within the version range in the pair.
+ * Returns false otherwise.
*
- * <p>Logs a warning and returns false in case the given {@code packageEntryString} is invalid.
- *
- * @param packageEntryStr A package entry expected to be in the format
- * '<package-name>:<min-version-code>?:<max-version-code>?'.
+ * @param minMaxVersionCodes A pair expected to be in the format
+ * 'Pair(<min-version-code>, <max-version-code>)'.
* @param applicationInfo Information about the application/package.
*/
- private static boolean matchesApplicationInfo(String packageEntryStr,
+ private static boolean matchesApplicationInfo(Pair<Long, Long> minMaxVersionCodes,
ApplicationInfo applicationInfo) {
- List<String> packageAndVersions = Arrays.asList(packageEntryStr.split(":", 3));
- if (packageAndVersions.size() != 3) {
- Slog.w(TAG, "Invalid package entry in flag 'never_constrain_display_apis': "
- + packageEntryStr);
- return false;
- }
- String packageName = packageAndVersions.get(0);
- String minVersionCodeStr = packageAndVersions.get(1);
- String maxVersionCodeStr = packageAndVersions.get(2);
-
- if (!packageName.equals(applicationInfo.packageName)) {
- return false;
- }
- long version = applicationInfo.longVersionCode;
- try {
- if (!minVersionCodeStr.isEmpty() && version < Long.parseLong(minVersionCodeStr)) {
- return false;
- }
- if (!maxVersionCodeStr.isEmpty() && version > Long.parseLong(maxVersionCodeStr)) {
- return false;
- }
- } catch (NumberFormatException e) {
- Slog.w(TAG, "Invalid APK version code in package entry: " + packageEntryStr);
- return false;
- }
- return true;
+ return applicationInfo.longVersionCode >= minMaxVersionCodes.first
+ && applicationInfo.longVersionCode <= minMaxVersionCodes.second;
}
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 3bf5f31..1c35b47 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2762,6 +2762,16 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: The device supports FeliCa communication, which is based on
+ * ISO/IEC 18092 and JIS X 6319-4.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_FELICA = "android.hardware.felica";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device's
* {@link ActivityManager#isLowRamDevice() ActivityManager.isLowRamDevice()} method returns
* true.
@@ -3971,6 +3981,7 @@
* @hide
*/
@SdkConstant(SdkConstantType.FEATURE)
+ @TestApi
public static final String FEATURE_COMMUNAL_MODE = "android.software.communal_mode";
/** @hide */
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 4c81f9c..0037464 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -30,6 +30,7 @@
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.app.KeyguardManager;
+import android.companion.virtual.IVirtualDevice;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Resources;
@@ -902,8 +903,16 @@
@NonNull VirtualDisplayConfig virtualDisplayConfig,
@Nullable VirtualDisplay.Callback callback, @Nullable Handler handler,
@Nullable Context windowContext) {
- return mGlobal.createVirtualDisplay(mContext, projection, virtualDisplayConfig, callback,
- handler, windowContext);
+ return mGlobal.createVirtualDisplay(mContext, projection, null /* virtualDevice */,
+ virtualDisplayConfig, callback, handler, windowContext);
+ }
+
+ /** @hide */
+ public VirtualDisplay createVirtualDisplay(@Nullable IVirtualDevice virtualDevice,
+ @NonNull VirtualDisplayConfig virtualDisplayConfig,
+ @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) {
+ return mGlobal.createVirtualDisplay(mContext, null /* projection */, virtualDevice,
+ virtualDisplayConfig, callback, handler, null);
}
/**
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 75155bb..01833fd 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -24,6 +24,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PropertyInvalidatedCache;
+import android.companion.virtual.IVirtualDevice;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.pm.ParceledListSlice;
@@ -582,14 +583,14 @@
}
public VirtualDisplay createVirtualDisplay(@NonNull Context context, MediaProjection projection,
- @NonNull VirtualDisplayConfig virtualDisplayConfig, VirtualDisplay.Callback callback,
- Handler handler, @Nullable Context windowContext) {
+ IVirtualDevice virtualDevice, @NonNull VirtualDisplayConfig virtualDisplayConfig,
+ VirtualDisplay.Callback callback, Handler handler, @Nullable Context windowContext) {
VirtualDisplayCallback callbackWrapper = new VirtualDisplayCallback(callback, handler);
IMediaProjection projectionToken = projection != null ? projection.getProjection() : null;
int displayId;
try {
displayId = mDm.createVirtualDisplay(virtualDisplayConfig, callbackWrapper,
- projectionToken, context.getPackageName());
+ projectionToken, virtualDevice, context.getPackageName());
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 2985c75..83e1061 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -20,7 +20,6 @@
import android.annotation.Nullable;
import android.graphics.Point;
import android.hardware.SensorManager;
-import android.media.projection.IMediaProjection;
import android.os.Handler;
import android.os.IBinder;
import android.os.PowerManager;
@@ -381,31 +380,6 @@
public abstract void onEarlyInteractivityChange(boolean interactive);
/**
- * A special API for creates a virtual display with a DisplayPolicyController in system_server.
- * <p>
- * If this method is called without original calling uid, the caller must enforce the
- * corresponding permissions according to the flags.
- * {@link android.Manifest.permission#CAPTURE_VIDEO_OUTPUT}
- * {@link android.Manifest.permission#CAPTURE_SECURE_VIDEO_OUTPUT}
- * {@link android.Manifest.permission#ADD_TRUSTED_DISPLAY}
- * {@link android.Manifest.permission#INTERNAL_SYSTEM_WINDOW}
- * </p>
- *
- * @param virtualDisplayConfig The arguments for the virtual display configuration. See
- * {@link VirtualDisplayConfig} for using it.
- * @param callback Callback to call when the virtual display's state changes, or null if none.
- * @param projection MediaProjection token.
- * @param packageName The package name of the app.
- * @param controller The DisplayWindowPolicyControl that can control what contents are
- * allowed to be displayed.
- * @return The newly created virtual display id , or {@link Display#INVALID_DISPLAY} if the
- * virtual display cannot be created.
- */
- public abstract int createVirtualDisplay(VirtualDisplayConfig virtualDisplayConfig,
- IVirtualDisplayCallback callback, IMediaProjection projection, String packageName,
- DisplayWindowPolicyController controller);
-
- /**
* Get {@link DisplayWindowPolicyController} associated to the {@link DisplayInfo#displayId}
*
* @param displayId The id of the display.
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 0d5f1af..82b31d4 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -16,6 +16,7 @@
package android.hardware.display;
+import android.companion.virtual.IVirtualDevice;
import android.content.pm.ParceledListSlice;
import android.graphics.Point;
import android.hardware.display.BrightnessConfiguration;
@@ -86,10 +87,10 @@
void requestColorMode(int displayId, int colorMode);
// Requires CAPTURE_VIDEO_OUTPUT, CAPTURE_SECURE_VIDEO_OUTPUT, or an appropriate
- // MediaProjection token for certain combinations of flags.
+ // MediaProjection token or VirtualDevice for certain combinations of flags.
int createVirtualDisplay(in VirtualDisplayConfig virtualDisplayConfig,
in IVirtualDisplayCallback callback, in IMediaProjection projectionToken,
- String packageName);
+ in IVirtualDevice virtualDevice, String packageName);
// No permissions required, but must be same Uid as the creator.
void resizeVirtualDisplay(in IVirtualDisplayCallback token,
diff --git a/core/java/android/net/vcn/VcnCellUnderlyingNetworkPriority.java b/core/java/android/net/vcn/VcnCellUnderlyingNetworkPriority.java
index 50a6bfc..b3f7345 100644
--- a/core/java/android/net/vcn/VcnCellUnderlyingNetworkPriority.java
+++ b/core/java/android/net/vcn/VcnCellUnderlyingNetworkPriority.java
@@ -151,7 +151,7 @@
/** Retrieve the allowed PLMN IDs, or an empty set if any PLMN ID is acceptable. */
@NonNull
- public Set<String> getAllowedPlmnIds() {
+ public Set<String> getAllowedOperatorPlmnIds() {
return Collections.unmodifiableSet(mAllowedNetworkPlmnIds);
}
@@ -211,7 +211,7 @@
}
/** This class is used to incrementally build WifiNetworkPriority objects. */
- public static class Builder extends VcnUnderlyingNetworkPriority.Builder<Builder> {
+ public static final class Builder extends VcnUnderlyingNetworkPriority.Builder<Builder> {
@NonNull private final Set<String> mAllowedNetworkPlmnIds = new ArraySet<>();
@NonNull private final Set<Integer> mAllowedSpecificCarrierIds = new ArraySet<>();
@@ -233,7 +233,7 @@
* and {@link SubscriptionInfo#getMncString()}.
*/
@NonNull
- public Builder setAllowedPlmnIds(@NonNull Set<String> allowedNetworkPlmnIds) {
+ public Builder setAllowedOperatorPlmnIds(@NonNull Set<String> allowedNetworkPlmnIds) {
validatePlmnIds(allowedNetworkPlmnIds);
mAllowedNetworkPlmnIds.clear();
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index 31e38c0..55d3ecd 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -198,7 +198,10 @@
private static final String EXPOSED_CAPABILITIES_KEY = "mExposedCapabilities";
@NonNull private final SortedSet<Integer> mExposedCapabilities;
- private static final String UNDERLYING_NETWORK_PRIORITIES_KEY = "mUnderlyingNetworkPriorities";
+ /** @hide */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public static final String UNDERLYING_NETWORK_PRIORITIES_KEY = "mUnderlyingNetworkPriorities";
+
@NonNull private final LinkedHashSet<VcnUnderlyingNetworkPriority> mUnderlyingNetworkPriorities;
private static final String MAX_MTU_KEY = "mMaxMtu";
@@ -229,6 +232,8 @@
validate();
}
+ // Null check MUST be done for all new fields added to VcnGatewayConnectionConfig, to avoid
+ // crashes when parsing PersistableBundle built on old platforms.
/** @hide */
@VisibleForTesting(visibility = Visibility.PRIVATE)
public VcnGatewayConnectionConfig(@NonNull PersistableBundle in) {
@@ -239,19 +244,30 @@
final PersistableBundle exposedCapsBundle =
in.getPersistableBundle(EXPOSED_CAPABILITIES_KEY);
- final PersistableBundle networkPrioritiesBundle =
- in.getPersistableBundle(UNDERLYING_NETWORK_PRIORITIES_KEY);
-
mGatewayConnectionName = in.getString(GATEWAY_CONNECTION_NAME_KEY);
mTunnelConnectionParams =
TunnelConnectionParamsUtils.fromPersistableBundle(tunnelConnectionParamsBundle);
mExposedCapabilities = new TreeSet<>(PersistableBundleUtils.toList(
exposedCapsBundle, PersistableBundleUtils.INTEGER_DESERIALIZER));
- mUnderlyingNetworkPriorities =
- new LinkedHashSet<>(
- PersistableBundleUtils.toList(
- networkPrioritiesBundle,
- VcnUnderlyingNetworkPriority::fromPersistableBundle));
+
+ final PersistableBundle networkPrioritiesBundle =
+ in.getPersistableBundle(UNDERLYING_NETWORK_PRIORITIES_KEY);
+
+ if (networkPrioritiesBundle == null) {
+ // UNDERLYING_NETWORK_PRIORITIES_KEY was added in Android T. Thus
+ // VcnGatewayConnectionConfig created on old platforms will not have this data and will
+ // be assigned with the default value
+ mUnderlyingNetworkPriorities =
+ new LinkedHashSet<>(DEFAULT_UNDERLYING_NETWORK_PRIORITIES);
+
+ } else {
+ mUnderlyingNetworkPriorities =
+ new LinkedHashSet<>(
+ PersistableBundleUtils.toList(
+ networkPrioritiesBundle,
+ VcnUnderlyingNetworkPriority::fromPersistableBundle));
+ }
+
mRetryIntervalsMs = in.getLongArray(RETRY_INTERVAL_MS_KEY);
mMaxMtu = in.getInt(MAX_MTU_KEY);
@@ -420,6 +436,7 @@
mGatewayConnectionName,
mTunnelConnectionParams,
mExposedCapabilities,
+ mUnderlyingNetworkPriorities,
Arrays.hashCode(mRetryIntervalsMs),
mMaxMtu);
}
@@ -434,6 +451,7 @@
return mGatewayConnectionName.equals(rhs.mGatewayConnectionName)
&& mTunnelConnectionParams.equals(rhs.mTunnelConnectionParams)
&& mExposedCapabilities.equals(rhs.mExposedCapabilities)
+ && mUnderlyingNetworkPriorities.equals(rhs.mUnderlyingNetworkPriorities)
&& Arrays.equals(mRetryIntervalsMs, rhs.mRetryIntervalsMs)
&& mMaxMtu == rhs.mMaxMtu;
}
diff --git a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkPriority.java b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkPriority.java
index 2ba9169..85eb100 100644
--- a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkPriority.java
+++ b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkPriority.java
@@ -79,7 +79,7 @@
}
final VcnWifiUnderlyingNetworkPriority rhs = (VcnWifiUnderlyingNetworkPriority) other;
- return mSsid == rhs.mSsid;
+ return mSsid.equals(rhs.mSsid);
}
/** @hide */
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 53484d2..584f3c4 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -634,7 +634,7 @@
*/
public static int mapToInternalProcessState(int procState) {
if (procState == ActivityManager.PROCESS_STATE_NONEXISTENT) {
- return ActivityManager.PROCESS_STATE_NONEXISTENT;
+ return Uid.PROCESS_STATE_NONEXISTENT;
} else if (procState == ActivityManager.PROCESS_STATE_TOP) {
return Uid.PROCESS_STATE_TOP;
} else if (ActivityManager.isForegroundService(procState)) {
@@ -911,6 +911,11 @@
* Total number of process states we track.
*/
public static final int NUM_PROCESS_STATE = 7;
+ /**
+ * State of the UID when it has no running processes. It is intentionally out of
+ * bounds 0..NUM_PROCESS_STATE.
+ */
+ public static final int PROCESS_STATE_NONEXISTENT = NUM_PROCESS_STATE;
// Used in dump
static final String[] PROCESS_STATE_NAMES = {
@@ -930,16 +935,6 @@
"C" // CACHED
};
- /**
- * When the process exits one of these states, we need to make sure cpu time in this state
- * is not attributed to any non-critical process states.
- */
- public static final int[] CRITICAL_PROC_STATES = {
- Uid.PROCESS_STATE_TOP,
- Uid.PROCESS_STATE_FOREGROUND_SERVICE,
- Uid.PROCESS_STATE_FOREGROUND
- };
-
public abstract long getProcessStateTime(int state, long elapsedRealtimeUs, int which);
public abstract Timer getProcessStateTimer(int state);
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index b3416e9..8292f26 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -67,6 +67,9 @@
private static final String SYSTEM_DRIVER_NAME = "system";
private static final String SYSTEM_DRIVER_VERSION_NAME = "";
private static final long SYSTEM_DRIVER_VERSION_CODE = 0;
+ private static final String ANGLE_DRIVER_NAME = "angle";
+ private static final String ANGLE_DRIVER_VERSION_NAME = "";
+ private static final long ANGLE_DRIVER_VERSION_CODE = 0;
// System properties related to updatable graphics drivers.
private static final String PROPERTY_GFX_DRIVER_PRODUCTION = "ro.gfx.driver.0";
@@ -134,14 +137,24 @@
Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "setupAngle");
- setupAngle(context, coreSettings, pm, packageName);
+ boolean useAngle = false;
+ if (setupAngle(context, coreSettings, pm, packageName)) {
+ if (shouldUseAngle(context, coreSettings, packageName)) {
+ useAngle = true;
+ setGpuStats(ANGLE_DRIVER_NAME, ANGLE_DRIVER_VERSION_NAME, ANGLE_DRIVER_VERSION_CODE,
+ 0, packageName, getVulkanVersion(pm));
+ }
+ }
Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "chooseDriver");
if (!chooseDriver(context, coreSettings, pm, packageName, appInfoWithMetaData)) {
- setGpuStats(SYSTEM_DRIVER_NAME, SYSTEM_DRIVER_VERSION_NAME, SYSTEM_DRIVER_VERSION_CODE,
- SystemProperties.getLong(PROPERTY_GFX_DRIVER_BUILD_TIME, 0), packageName,
- getVulkanVersion(pm));
+ if (!useAngle) {
+ setGpuStats(SYSTEM_DRIVER_NAME, SYSTEM_DRIVER_VERSION_NAME,
+ SYSTEM_DRIVER_VERSION_CODE,
+ SystemProperties.getLong(PROPERTY_GFX_DRIVER_BUILD_TIME, 0),
+ packageName, getVulkanVersion(pm));
+ }
}
Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
}
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 8894c85..4d5f97c 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -4388,9 +4388,16 @@
* The given class loader will be used to load any enclosed
* Parcelables.
* @return the Parcelable array, or null if the array is null
+ *
+ * @deprecated Use the type-safer version {@link #readParcelableArray(ClassLoader, Class)}
+ * starting from Android {@link Build.VERSION_CODES#TIRAMISU}. Also consider changing the
+ * format to use {@link #createTypedArray(Parcelable.Creator)} if possible (eg. if the
+ * items' class is final) since this is also more performant. Note that changing to the
+ * latter also requires changing the writes.
*/
+ @Deprecated
@Nullable
- public final Parcelable[] readParcelableArray(@Nullable ClassLoader loader) {
+ public Parcelable[] readParcelableArray(@Nullable ClassLoader loader) {
int N = readInt();
if (N < 0) {
return null;
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index bc6dbd8..29d4d78 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -327,6 +327,43 @@
"no_sharing_admin_configured_wifi";
/**
+ * Specifies if a user is disallowed from using Wi-Fi Direct.
+ *
+ * <p>This restriction can only be set by a device owner,
+ * a profile owner of an organization-owned managed profile on the parent profile.
+ * When it is set by any of these owners, it prevents all users from using
+ * Wi-Fi Direct.
+ *
+ * <p>The default value is <code>false</code>.
+ *
+ * <p>Key for user restrictions.
+ * <p>Type: Boolean
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+ * @see #getUserRestrictions()
+ */
+ public static final String DISALLOW_WIFI_DIRECT = "no_wifi_direct";
+
+ /**
+ * Specifies if a user is disallowed from adding a new Wi-Fi configuration.
+ *
+ * <p>This restriction can only be set by a device owner,
+ * a profile owner of an organization-owned managed profile on the parent profile.
+ * When it is set by any of these owners, it prevents all users from adding
+ * a new Wi-Fi configuration. This does not limit the owner and carrier's ability
+ * to add a new configuration.
+ *
+ * <p>The default value is <code>false</code>.
+ *
+ * <p>Key for user restrictions.
+ * <p>Type: Boolean
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+ * @see #getUserRestrictions()
+ */
+ public static final String DISALLOW_ADD_WIFI_CONFIG = "no_add_wifi_config";
+
+ /**
* Specifies if a user is disallowed from changing the device
* language. The default value is <code>false</code>.
*
@@ -1500,6 +1537,8 @@
DISALLOW_CHANGE_WIFI_STATE,
DISALLOW_WIFI_TETHERING,
DISALLOW_SHARING_ADMIN_CONFIGURED_WIFI,
+ DISALLOW_WIFI_DIRECT,
+ DISALLOW_ADD_WIFI_CONFIG,
})
@Retention(RetentionPolicy.SOURCE)
public @interface UserRestrictionKey {}
diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java
index 20f6c10..f0e6624 100644
--- a/core/java/android/permission/PermissionUsageHelper.java
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -52,6 +52,8 @@
import android.util.ArrayMap;
import android.util.ArraySet;
+import com.android.internal.annotations.GuardedBy;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -150,7 +152,9 @@
private ArrayMap<UserHandle, Context> mUserContexts;
private PackageManager mPkgManager;
private AppOpsManager mAppOpsManager;
- private ArrayMap<Integer, ArrayList<AccessChainLink>> mAttributionChains = new ArrayMap<>();
+ @GuardedBy("mAttributionChains")
+ private final ArrayMap<Integer, ArrayList<AccessChainLink>> mAttributionChains =
+ new ArrayMap<>();
/**
* Constructor for PermissionUsageHelper
@@ -199,22 +203,24 @@
// if any link in the chain is finished, remove the chain. Then, find any other chains that
// contain this op/package/uid/tag combination, and remove them, as well.
// TODO ntmyren: be smarter about this
- mAttributionChains.remove(attributionChainId);
- int numChains = mAttributionChains.size();
- ArrayList<Integer> toRemove = new ArrayList<>();
- for (int i = 0; i < numChains; i++) {
- int chainId = mAttributionChains.keyAt(i);
- ArrayList<AccessChainLink> chain = mAttributionChains.valueAt(i);
- int chainSize = chain.size();
- for (int j = 0; j < chainSize; j++) {
- AccessChainLink link = chain.get(j);
- if (link.packageAndOpEquals(op, packageName, attributionTag, uid)) {
- toRemove.add(chainId);
- break;
+ synchronized (mAttributionChains) {
+ mAttributionChains.remove(attributionChainId);
+ int numChains = mAttributionChains.size();
+ ArrayList<Integer> toRemove = new ArrayList<>();
+ for (int i = 0; i < numChains; i++) {
+ int chainId = mAttributionChains.keyAt(i);
+ ArrayList<AccessChainLink> chain = mAttributionChains.valueAt(i);
+ int chainSize = chain.size();
+ for (int j = 0; j < chainSize; j++) {
+ AccessChainLink link = chain.get(j);
+ if (link.packageAndOpEquals(op, packageName, attributionTag, uid)) {
+ toRemove.add(chainId);
+ break;
+ }
}
}
+ mAttributionChains.removeAll(toRemove);
}
- mAttributionChains.removeAll(toRemove);
}
@Override
@@ -234,11 +240,13 @@
// If this is not a successful start, or it is not a chain, or it is untrusted, return
return;
}
- addLinkToChainIfNotPresent(AppOpsManager.opToPublicName(op), packageName, uid,
- attributionTag, attributionFlags, attributionChainId);
+ synchronized (mAttributionChains) {
+ addLinkToChainIfNotPresentLocked(AppOpsManager.opToPublicName(op), packageName, uid,
+ attributionTag, attributionFlags, attributionChainId);
+ }
}
- private void addLinkToChainIfNotPresent(String op, String packageName, int uid,
+ private void addLinkToChainIfNotPresentLocked(String op, String packageName, int uid,
String attributionTag, int attributionFlags, int attributionChainId) {
ArrayList<AccessChainLink> currentChain = mAttributionChains.computeIfAbsent(
@@ -544,42 +552,44 @@
}
}
- for (int i = 0; i < mAttributionChains.size(); i++) {
- List<AccessChainLink> usageList = mAttributionChains.valueAt(i);
- int lastVisible = usageList.size() - 1;
- // TODO ntmyren: remove this mic code once camera is converted to AttributionSource
- // if the list is empty or incomplete, do not show it.
- if (usageList.isEmpty() || !usageList.get(lastVisible).isEnd()
- || !usageList.get(0).isStart()
- || !usageList.get(lastVisible).usage.op.equals(OPSTR_RECORD_AUDIO)) {
- continue;
- }
-
- //TODO ntmyren: remove once camera etc. etc.
- for (AccessChainLink link: usageList) {
- proxyPackages.add(link.usage.getPackageIdHash());
- }
-
- AccessChainLink start = usageList.get(0);
- AccessChainLink lastVisibleLink = usageList.get(lastVisible);
- while (lastVisible > 0 && !shouldShowPackage(lastVisibleLink.usage.packageName)) {
- lastVisible--;
- lastVisibleLink = usageList.get(lastVisible);
- }
- String proxyLabel = null;
- if (!lastVisibleLink.usage.packageName.equals(start.usage.packageName)) {
- try {
- PackageManager userPkgManager =
- getUserContext(lastVisibleLink.usage.getUser()).getPackageManager();
- ApplicationInfo appInfo = userPkgManager.getApplicationInfo(
- lastVisibleLink.usage.packageName, 0);
- proxyLabel = appInfo.loadLabel(userPkgManager).toString();
- } catch (PackageManager.NameNotFoundException e) {
- // do nothing
+ synchronized (mAttributionChains) {
+ for (int i = 0; i < mAttributionChains.size(); i++) {
+ List<AccessChainLink> usageList = mAttributionChains.valueAt(i);
+ int lastVisible = usageList.size() - 1;
+ // TODO ntmyren: remove this mic code once camera is converted to AttributionSource
+ // if the list is empty or incomplete, do not show it.
+ if (usageList.isEmpty() || !usageList.get(lastVisible).isEnd()
+ || !usageList.get(0).isStart()
+ || !usageList.get(lastVisible).usage.op.equals(OPSTR_RECORD_AUDIO)) {
+ continue;
}
+ //TODO ntmyren: remove once camera etc. etc.
+ for (AccessChainLink link : usageList) {
+ proxyPackages.add(link.usage.getPackageIdHash());
+ }
+
+ AccessChainLink start = usageList.get(0);
+ AccessChainLink lastVisibleLink = usageList.get(lastVisible);
+ while (lastVisible > 0 && !shouldShowPackage(lastVisibleLink.usage.packageName)) {
+ lastVisible--;
+ lastVisibleLink = usageList.get(lastVisible);
+ }
+ String proxyLabel = null;
+ if (!lastVisibleLink.usage.packageName.equals(start.usage.packageName)) {
+ try {
+ PackageManager userPkgManager =
+ getUserContext(lastVisibleLink.usage.getUser()).getPackageManager();
+ ApplicationInfo appInfo = userPkgManager.getApplicationInfo(
+ lastVisibleLink.usage.packageName, 0);
+ proxyLabel = appInfo.loadLabel(userPkgManager).toString();
+ } catch (PackageManager.NameNotFoundException e) {
+ // do nothing
+ }
+
+ }
+ usagesAndLabels.put(start.usage, proxyLabel);
}
- usagesAndLabels.put(start.usage, proxyLabel);
}
for (int packageHash : mostRecentUsages.keySet()) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 49a211f..ad6d85f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9327,6 +9327,16 @@
"emergency_gesture_sound_enabled";
/**
+ * The power button "cooldown" period in milliseconds after the Emergency gesture is
+ * triggered, during which single-key actions on the power button are suppressed. Cooldown
+ * period is disabled if set to zero.
+ *
+ * @hide
+ */
+ public static final String EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS =
+ "emergency_gesture_power_button_cooldown_period_ms";
+
+ /**
* Whether the camera launch gesture to double tap the power button when the screen is off
* should be disabled.
*
@@ -16988,6 +16998,14 @@
"wear_activity_auto_resume_timeout_ms";
/**
+ * If the current {@code WEAR_ACTIVITY_AUTO_RESUME_TIMEOUT_MS} value is set by user.
+ * 1 for true, 0 for false.
+ * @hide
+ */
+ public static final String WEAR_ACTIVITY_AUTO_RESUME_TIMEOUT_SET_BY_USER =
+ "wear_activity_auto_resume_timeout_set_by_user";
+
+ /**
* If burn in protection is enabled.
* @hide
*/
diff --git a/core/java/android/text/style/StyleSpan.java b/core/java/android/text/style/StyleSpan.java
index ba0ac82..176a068 100644
--- a/core/java/android/text/style/StyleSpan.java
+++ b/core/java/android/text/style/StyleSpan.java
@@ -67,7 +67,8 @@
* include bold, italic, and normal. Values are constants defined
* in {@link Typeface}.
* @param fontWeightAdjustment An integer describing the adjustment to be made to the font
- * weight.
+ * weight. This is added to the value of the current weight returned by
+ * {@link Typeface#getWeight()}.
* @see Configuration#fontWeightAdjustment This is the adjustment in text font weight
* that is used to reflect the current user's preference for increasing font weight.
*/
@@ -123,6 +124,9 @@
/**
* Returns the font weight adjustment specified by this span.
+ * <p>
+ * This can be {@link Configuration#FONT_WEIGHT_ADJUSTMENT_UNDEFINED}. This is added to the
+ * value of the current weight returned by {@link Typeface#getWeight()}.
*/
public int getFontWeightAdjustment() {
return mFontWeightAdjustment;
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index ba436e1..3c0597c 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -948,9 +948,10 @@
// When the listener is updated, we will get at least a single position update call so we can
// guarantee any changes we post will be applied.
private void replacePositionUpdateListener(int surfaceWidth, int surfaceHeight,
- @Nullable Transaction geometryTransaction) {
+ Transaction geometryTransaction) {
if (mPositionListener != null) {
mRenderNode.removePositionUpdateListener(mPositionListener);
+ geometryTransaction = mPositionListener.getTransaction().merge(geometryTransaction);
}
mPositionListener = new SurfaceViewPositionUpdateListener(surfaceWidth, surfaceHeight,
geometryTransaction);
@@ -958,7 +959,8 @@
}
private boolean performSurfaceTransaction(ViewRootImpl viewRoot, Translator translator,
- boolean creating, boolean sizeChanged, boolean hintChanged) {
+ boolean creating, boolean sizeChanged, boolean hintChanged,
+ Transaction geometryTransaction) {
boolean realSizeChanged = false;
mSurfaceLock.lock();
@@ -996,10 +998,6 @@
mSurfaceAlpha = alpha;
}
- // While creating the surface, we will set it's initial
- // geometry. Outside of that though, we should generally
- // leave it to the RenderThread.
- Transaction geometryTransaction = new Transaction();
geometryTransaction.setCornerRadius(mSurfaceControl, mCornerRadius);
if ((sizeChanged || hintChanged) && !creating) {
setBufferSize(geometryTransaction);
@@ -1022,20 +1020,18 @@
mSurfaceHeight);
}
- boolean applyChangesOnRenderThread =
- sizeChanged && !creating && isHardwareAccelerated();
if (isHardwareAccelerated()) {
// This will consume the passed in transaction and the transaction will be
// applied on a render worker thread.
replacePositionUpdateListener(mSurfaceWidth, mSurfaceHeight,
- applyChangesOnRenderThread ? geometryTransaction : null);
+ geometryTransaction);
}
if (DEBUG_POSITION) {
Log.d(TAG, String.format(
- "%d updateSurfacePosition %s"
+ "%d performSurfaceTransaction %s "
+ "position = [%d, %d, %d, %d] surfaceSize = %dx%d",
System.identityHashCode(this),
- applyChangesOnRenderThread ? "RenderWorker" : "UiThread",
+ isHardwareAccelerated() ? "RenderWorker" : "UI Thread",
mScreenRect.left, mScreenRect.top, mScreenRect.right,
mScreenRect.bottom, mSurfaceWidth, mSurfaceHeight));
}
@@ -1147,12 +1143,14 @@
final Rect surfaceInsets = viewRoot.mWindowAttributes.surfaceInsets;
mScreenRect.offset(surfaceInsets.left, surfaceInsets.top);
-
+ // Collect all geometry changes and apply these changes on the RenderThread worker
+ // via the RenderNode.PositionUpdateListener.
+ final Transaction geometryTransaction = new Transaction();
if (creating) {
updateOpaqueFlag();
final String name = "SurfaceView[" + viewRoot.getTitle().toString() + "]";
if (mUseBlastAdapter) {
- createBlastSurfaceControls(viewRoot, name);
+ createBlastSurfaceControls(viewRoot, name, geometryTransaction);
} else {
mDeferredDestroySurfaceControl = createSurfaceControls(viewRoot, name);
}
@@ -1161,7 +1159,7 @@
}
final boolean realSizeChanged = performSurfaceTransaction(viewRoot,
- translator, creating, sizeChanged, hintChanged);
+ translator, creating, sizeChanged, hintChanged, geometryTransaction);
final boolean redrawNeeded = sizeChanged || creating || hintChanged
|| (mVisible && !mDrawFinished);
@@ -1335,7 +1333,8 @@
// is still alive, the old buffers will continue to be presented until replaced by buffers from
// the new adapter. This means we do not need to track the old surface control and destroy it
// after the client has drawn to avoid any flickers.
- private void createBlastSurfaceControls(ViewRootImpl viewRoot, String name) {
+ private void createBlastSurfaceControls(ViewRootImpl viewRoot, String name,
+ Transaction geometryTransaction) {
if (mSurfaceControl == null) {
mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
.setName(name)
@@ -1376,8 +1375,9 @@
}
mTransformHint = viewRoot.getBufferTransformHint();
mBlastSurfaceControl.setTransformHint(mTransformHint);
- mBlastBufferQueue = new BLASTBufferQueue(name, mBlastSurfaceControl, mSurfaceWidth,
- mSurfaceHeight, mFormat);
+ mBlastBufferQueue = new BLASTBufferQueue(name);
+ mBlastBufferQueue.update(mBlastSurfaceControl, mSurfaceWidth, mSurfaceHeight, mFormat,
+ geometryTransaction);
}
private void onDrawFinished(Transaction t) {
@@ -1558,6 +1558,10 @@
applyOrMergeTransaction(mRtTransaction, frameNumber);
}
}
+
+ public Transaction getTransaction() {
+ return mPositionChangedTransaction;
+ }
}
private SurfaceViewPositionUpdateListener mPositionListener = null;
@@ -1651,6 +1655,11 @@
@Override
public void setFixedSize(int width, int height) {
if (mRequestedWidth != width || mRequestedHeight != height) {
+ if (DEBUG_POSITION) {
+ Log.d(TAG, String.format("%d setFixedSize %dx%d -> %dx%d",
+ System.identityHashCode(this), mRequestedWidth, mRequestedHeight, width,
+ height));
+ }
mRequestedWidth = width;
mRequestedHeight = height;
requestLayout();
@@ -1660,6 +1669,10 @@
@Override
public void setSizeFromLayout() {
if (mRequestedWidth != -1 || mRequestedHeight != -1) {
+ if (DEBUG_POSITION) {
+ Log.d(TAG, String.format("%d setSizeFromLayout was %dx%d",
+ System.identityHashCode(this), mRequestedWidth, mRequestedHeight));
+ }
mRequestedWidth = mRequestedHeight = -1;
requestLayout();
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index c76245b..75d5ecf 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -533,10 +533,11 @@
boolean mReportNextDraw;
/**
- * Set if the reportDraw was requested from WM. If just a local report draw was invoked, there's
- * no need to report back to system server and can just apply immediately on the client.
+ * Set whether the draw should use blast sync. This is in case the draw is canceled,
+ * but will be rescheduled. We still want the next draw to be sync.
*/
- boolean mReportDrawToWm;
+ boolean mNextDrawUseBlastSync;
+
boolean mFullRedrawNeeded;
boolean mNewSurfaceNeeded;
boolean mForceNextWindowRelayout;
@@ -2761,7 +2762,7 @@
}
}
final boolean wasReportNextDraw = mReportNextDraw;
- boolean useBlastSync = false;
+ boolean useBlastSync = mNextDrawUseBlastSync;
if (mFirst || windowShouldResize || viewVisibilityChanged || params != null
|| mForceNextWindowRelayout) {
@@ -3292,9 +3293,11 @@
mPendingTransitions.clear();
}
performDraw(useBlastSync);
+ mNextDrawUseBlastSync = false;
} else {
if (isViewVisible) {
// Try again
+ mNextDrawUseBlastSync = useBlastSync;
scheduleTraversals();
} else {
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
@@ -3984,17 +3987,8 @@
}
mDrawsNeededToReport = 0;
- if (!mReportDrawToWm) {
- if (DEBUG_BLAST) {
- Log.d(mTag, "No need to report finishDrawing. Apply immediately");
- }
- mSurfaceChangedTransaction.apply();
- return;
- }
-
try {
mWindowSession.finishDrawing(mWindow, mSurfaceChangedTransaction);
- mReportDrawToWm = false;
} catch (RemoteException e) {
Log.e(mTag, "Unable to report draw finished", e);
mSurfaceChangedTransaction.apply();
@@ -9604,7 +9598,6 @@
if (mReportNextDraw == false) {
drawPending();
}
- mReportDrawToWm = true;
mReportNextDraw = true;
}
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 078b767..7a33507 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -273,8 +273,8 @@
private final ArrayMap<AccessibilityServicesStateChangeListener, Executor>
mServicesStateChangeListeners = new ArrayMap<>();
- private final ArrayMap<AudioDescriptionByDefaultStateChangeListener, Executor>
- mAudioDescriptionByDefaultStateChangeListeners = new ArrayMap<>();
+ private final ArrayMap<AudioDescriptionRequestedChangeListener, Executor>
+ mAudioDescriptionRequestedChangeListeners = new ArrayMap<>();
/**
* Map from a view's accessibility id to the list of request preparers set for that view
@@ -359,15 +359,15 @@
* Listener for the audio description by default state. To listen for
* changes to the audio description by default state on the device,
* implement this interface and register it with the system by calling
- * {@link #addAudioDescriptionByDefaultStateChangeListener}.
+ * {@link #addAudioDescriptionRequestedChangeListener}.
*/
- public interface AudioDescriptionByDefaultStateChangeListener {
+ public interface AudioDescriptionRequestedChangeListener {
/**
* Called when the audio description enabled state changes.
*
* @param enabled Whether audio description by default is enabled.
*/
- void onAudioDescriptionByDefaultStateChanged(boolean enabled);
+ void onAudioDescriptionRequestedChanged(boolean enabled);
}
/**
@@ -1177,31 +1177,31 @@
}
/**
- * Registers a {@link AudioDescriptionByDefaultStateChangeListener}
+ * Registers a {@link AudioDescriptionRequestedChangeListener}
* for changes in the audio description by default state of the system.
* The value could be read via {@link #isAudioDescriptionRequested}.
*
* @param executor The executor on which the listener should be called back.
* @param listener The listener.
*/
- public void addAudioDescriptionByDefaultStateChangeListener(
+ public void addAudioDescriptionRequestedChangeListener(
@NonNull Executor executor,
- @NonNull AudioDescriptionByDefaultStateChangeListener listener) {
+ @NonNull AudioDescriptionRequestedChangeListener listener) {
synchronized (mLock) {
- mAudioDescriptionByDefaultStateChangeListeners.put(listener, executor);
+ mAudioDescriptionRequestedChangeListeners.put(listener, executor);
}
}
/**
- * Unregisters a {@link AudioDescriptionByDefaultStateChangeListener}.
+ * Unregisters a {@link AudioDescriptionRequestedChangeListener}.
*
* @param listener The listener.
* @return True if listener was previously registered.
*/
- public boolean removeAudioDescriptionByDefaultStateChangeListener(
- @NonNull AudioDescriptionByDefaultStateChangeListener listener) {
+ public boolean removeAudioDescriptionRequestedChangeListener(
+ @NonNull AudioDescriptionRequestedChangeListener listener) {
synchronized (mLock) {
- return (mAudioDescriptionByDefaultStateChangeListeners.remove(listener) != null);
+ return (mAudioDescriptionRequestedChangeListeners.remove(listener) != null);
}
}
@@ -1752,7 +1752,7 @@
* </p>
* <p>
* Add listener to detect the state change via
- * {@link #addAudioDescriptionByDefaultStateChangeListener}
+ * {@link #addAudioDescriptionRequestedChangeListener}
* </p>
* @return {@code true} if the audio description is enabled, {@code false} otherwise.
*/
@@ -1865,20 +1865,20 @@
*/
private void notifyAudioDescriptionbyDefaultStateChanged() {
final boolean isAudioDescriptionByDefaultRequested;
- final ArrayMap<AudioDescriptionByDefaultStateChangeListener, Executor> listeners;
+ final ArrayMap<AudioDescriptionRequestedChangeListener, Executor> listeners;
synchronized (mLock) {
- if (mAudioDescriptionByDefaultStateChangeListeners.isEmpty()) {
+ if (mAudioDescriptionRequestedChangeListeners.isEmpty()) {
return;
}
isAudioDescriptionByDefaultRequested = mIsAudioDescriptionByDefaultRequested;
- listeners = new ArrayMap<>(mAudioDescriptionByDefaultStateChangeListeners);
+ listeners = new ArrayMap<>(mAudioDescriptionRequestedChangeListeners);
}
final int numListeners = listeners.size();
for (int i = 0; i < numListeners; i++) {
- final AudioDescriptionByDefaultStateChangeListener listener = listeners.keyAt(i);
+ final AudioDescriptionRequestedChangeListener listener = listeners.keyAt(i);
listeners.valueAt(i).execute(() ->
- listener.onAudioDescriptionByDefaultStateChanged(
+ listener.onAudioDescriptionRequestedChanged(
isAudioDescriptionByDefaultRequested));
}
}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 3d4d9ec..dfd853a 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -3696,18 +3696,21 @@
}
private void initializeFrom(@NonNull RemoteViews src, @Nullable RemoteViews hierarchyRoot) {
+ if (hierarchyRoot == null) {
+ mBitmapCache = src.mBitmapCache;
+ mApplicationInfoCache = src.mApplicationInfoCache;
+ } else {
+ mBitmapCache = hierarchyRoot.mBitmapCache;
+ mApplicationInfoCache = hierarchyRoot.mApplicationInfoCache;
+ }
if (hierarchyRoot == null || src.mIsRoot) {
// If there's no provided root, or if src was itself a root, then this RemoteViews is
// the root of the new hierarchy.
mIsRoot = true;
- mBitmapCache = new BitmapCache();
- mApplicationInfoCache = new ApplicationInfoCache();
hierarchyRoot = this;
} else {
// Otherwise, we're a descendant in the hierarchy.
mIsRoot = false;
- mBitmapCache = hierarchyRoot.mBitmapCache;
- mApplicationInfoCache = hierarchyRoot.mApplicationInfoCache;
}
mApplication = src.mApplication;
mLayoutId = src.mLayoutId;
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 359c382..52122ee 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -2736,7 +2736,7 @@
}
@Override
- public void onListRebuilt(ResolverListAdapter listAdapter) {
+ public void onListRebuilt(ResolverListAdapter listAdapter, boolean rebuildComplete) {
setupScrollListener();
maybeSetupGlobalLayoutListener();
@@ -2756,15 +2756,20 @@
chooserListAdapter.updateAlphabeticalList();
}
+ if (rebuildComplete) {
+ getChooserActivityLogger().logSharesheetAppLoadComplete();
+ maybeQueryAdditionalPostProcessingTargets(chooserListAdapter);
+ }
+ }
+
+ private void maybeQueryAdditionalPostProcessingTargets(ChooserListAdapter chooserListAdapter) {
// don't support direct share on low ram devices
if (ActivityManager.isLowRamDeviceStatic()) {
- getChooserActivityLogger().logSharesheetAppLoadComplete();
return;
}
// no need to query direct share for work profile when its locked or disabled
if (!shouldQueryShortcutManager(chooserListAdapter.getUserHandle())) {
- getChooserActivityLogger().logSharesheetAppLoadComplete();
return;
}
@@ -2775,8 +2780,6 @@
queryDirectShareTargets(chooserListAdapter, false);
}
-
- getChooserActivityLogger().logSharesheetAppLoadComplete();
}
@VisibleForTesting
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index fd8637a..b273f6d 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1159,11 +1159,11 @@
if (doPostProcessing) {
maybeCreateHeader(listAdapter);
resetButtonBar();
- onListRebuilt(listAdapter);
+ onListRebuilt(listAdapter, rebuildCompleted);
}
}
- protected void onListRebuilt(ResolverListAdapter listAdapter) {
+ protected void onListRebuilt(ResolverListAdapter listAdapter, boolean rebuildCompleted) {
final ItemClickListener listener = new ItemClickListener();
setupAdapterListView((ListView) mMultiProfilePagerAdapter.getActiveAdapterView(), listener);
if (shouldShowTabs() && isIntentPicker()) {
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index aba43d8..c2224b4 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -18,6 +18,7 @@
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.os.BatteryStats.Uid.NUM_PROCESS_STATE;
import static android.os.BatteryStatsManager.NUM_WIFI_STATES;
import static android.os.BatteryStatsManager.NUM_WIFI_SUPPL_STATES;
@@ -241,32 +242,16 @@
MeasuredEnergyStats.POWER_BUCKET_CPU,
};
+ // TimeInState counters need NUM_PROCESS_STATE states in order to accommodate
+ // Uid.PROCESS_STATE_NONEXISTENT, which is outside the range of legitimate proc states.
+ private static final int PROC_STATE_TIME_COUNTER_STATE_COUNT = NUM_PROCESS_STATE + 1;
+
@GuardedBy("this")
public boolean mPerProcStateCpuTimesAvailable = true;
- /**
- * When per process state cpu times tracking is off, cpu times in KernelSingleUidTimeReader are
- * not updated. So, when the setting is turned on later, we would end up with huge cpu time
- * deltas. This flag tracks the case where tracking is turned on from off so that we won't
- * end up attributing the huge deltas to wrong buckets.
- */
- @GuardedBy("this")
- private boolean mIsPerProcessStateCpuDataStale;
-
- /**
- * Uids for which per-procstate cpu times need to be updated.
- *
- * Contains uid -> procState mappings.
- */
- @GuardedBy("this")
- @VisibleForTesting
- protected final SparseIntArray mPendingUids = new SparseIntArray();
-
@GuardedBy("this")
private long mNumSingleUidCpuTimeReads;
@GuardedBy("this")
- private long mNumBatchedSingleUidCpuTimeReads;
- @GuardedBy("this")
private long mCpuTimeReadsTrackingStartTimeMs = SystemClock.uptimeMillis();
@GuardedBy("this")
private int mNumUidsRemoved;
@@ -443,88 +428,42 @@
}
/**
- * Update per-freq cpu times for all the uids in {@link #mPendingUids}.
+ * Update per-freq cpu times for the supplied UID.
*/
- public void updateProcStateCpuTimes(boolean onBattery, boolean onBatteryScreenOff) {
- final SparseIntArray uidStates;
- synchronized (BatteryStatsImpl.this) {
- if (!mConstants.TRACK_CPU_TIMES_BY_PROC_STATE) {
- return;
- }
- if(!initKernelSingleUidTimeReaderLocked()) {
- return;
- }
- // If the KernelSingleUidTimeReader has stale cpu times, then we shouldn't try to
- // compute deltas since it might result in mis-attributing cpu times to wrong states.
- if (mIsPerProcessStateCpuDataStale) {
- mPendingUids.clear();
- return;
- }
-
- if (mPendingUids.size() == 0) {
- return;
- }
- uidStates = mPendingUids.clone();
- mPendingUids.clear();
+ @GuardedBy("this")
+ @SuppressWarnings("GuardedBy") // errorprone false positive on getProcStateTimeCounter
+ @VisibleForTesting
+ public void updateProcStateCpuTimesLocked(int uid, long timestampMs) {
+ if (!initKernelSingleUidTimeReaderLocked()) {
+ return;
}
- final long timestampMs = mClock.elapsedRealtime();
- LongArrayMultiStateCounter.LongArrayContainer deltaContainer = null;
- for (int i = uidStates.size() - 1; i >= 0; --i) {
- final int uid = uidStates.keyAt(i);
- final int procState = uidStates.valueAt(i);
- final int[] isolatedUids;
- final LongArrayMultiStateCounter[] isolatedUidTimeInFreqCounters;
- final Uid u;
- synchronized (BatteryStatsImpl.this) {
- // It's possible that uid no longer exists and any internal references have
- // already been deleted, so using {@link #getAvailableUidStatsLocked} to avoid
- // creating an UidStats object if it doesn't already exist.
- u = getAvailableUidStatsLocked(uid);
- if (u == null) {
- continue;
- }
- if (u.mChildUids == null) {
- isolatedUids = null;
- isolatedUidTimeInFreqCounters = null;
- } else {
- int childUidCount = u.mChildUids.size();
- isolatedUids = new int[childUidCount];
- isolatedUidTimeInFreqCounters = new LongArrayMultiStateCounter[childUidCount];
- for (int j = childUidCount - 1; j >= 0; --j) {
- isolatedUids[j] = u.mChildUids.keyAt(j);
- isolatedUidTimeInFreqCounters[j] =
- u.mChildUids.valueAt(j).cpuTimeInFreqCounter;
- if (deltaContainer == null && isolatedUidTimeInFreqCounters[j] != null) {
- deltaContainer = getCpuTimeInFreqContainer();
- }
- }
+
+ final Uid u = getUidStatsLocked(uid);
+
+ mNumSingleUidCpuTimeReads++;
+
+ LongArrayMultiStateCounter onBatteryCounter =
+ u.getProcStateTimeCounter().getCounter();
+ LongArrayMultiStateCounter onBatteryScreenOffCounter =
+ u.getProcStateScreenOffTimeCounter().getCounter();
+
+ mKernelSingleUidTimeReader.addDelta(uid, onBatteryCounter, timestampMs);
+ mKernelSingleUidTimeReader.addDelta(uid, onBatteryScreenOffCounter, timestampMs);
+
+ if (u.mChildUids != null) {
+ LongArrayMultiStateCounter.LongArrayContainer deltaContainer =
+ getCpuTimeInFreqContainer();
+ int childUidCount = u.mChildUids.size();
+ for (int j = childUidCount - 1; j >= 0; --j) {
+ LongArrayMultiStateCounter cpuTimeInFreqCounter =
+ u.mChildUids.valueAt(j).cpuTimeInFreqCounter;
+ if (cpuTimeInFreqCounter != null) {
+ mKernelSingleUidTimeReader.addDelta(u.mChildUids.keyAt(j),
+ cpuTimeInFreqCounter, timestampMs, deltaContainer);
+ onBatteryCounter.addCounts(deltaContainer);
+ onBatteryScreenOffCounter.addCounts(deltaContainer);
}
}
-
- LongArrayMultiStateCounter onBatteryCounter =
- u.getProcStateTimeCounter().getCounter();
- LongArrayMultiStateCounter onBatteryScreenOffCounter =
- u.getProcStateScreenOffTimeCounter().getCounter();
-
- onBatteryCounter.setState(procState, timestampMs);
- mKernelSingleUidTimeReader.addDelta(uid, onBatteryCounter, timestampMs);
-
- onBatteryScreenOffCounter.setState(procState, timestampMs);
- mKernelSingleUidTimeReader.addDelta(uid, onBatteryScreenOffCounter, timestampMs);
-
- if (isolatedUids != null) {
- for (int j = isolatedUids.length - 1; j >= 0; --j) {
- if (isolatedUidTimeInFreqCounters[j] != null) {
- mKernelSingleUidTimeReader.addDelta(isolatedUids[j],
- isolatedUidTimeInFreqCounters[j], timestampMs, deltaContainer);
- onBatteryCounter.addCounts(deltaContainer);
- onBatteryScreenOffCounter.addCounts(deltaContainer);
- }
- }
- }
-
- onBatteryCounter.setState(u.mProcessState, timestampMs);
- onBatteryScreenOffCounter.setState(u.mProcessState, timestampMs);
}
}
@@ -542,24 +481,17 @@
}
}
- public void copyFromAllUidsCpuTimes() {
- synchronized (BatteryStatsImpl.this) {
- copyFromAllUidsCpuTimes(
- mOnBatteryTimeBase.isRunning(), mOnBatteryScreenOffTimeBase.isRunning());
- }
- }
-
/**
* When the battery/screen state changes, we don't attribute the cpu times to any process
- * but we still need to snapshots of all uids to get correct deltas later on. Since we
- * already read this data for updating per-freq cpu times, we can use the same data for
- * per-procstate cpu times.
+ * but we still need to take snapshots of all uids to get correct deltas later on.
*/
- public void copyFromAllUidsCpuTimes(boolean onBattery, boolean onBatteryScreenOff) {
+ @SuppressWarnings("GuardedBy") // errorprone false positive on getProcStateTimeCounter
+ public void updateCpuTimesForAllUids() {
synchronized (BatteryStatsImpl.this) {
- if (!mConstants.TRACK_CPU_TIMES_BY_PROC_STATE) {
+ if (!trackPerProcStateCpuTimes()) {
return;
}
+
if(!initKernelSingleUidTimeReaderLocked()) {
return;
}
@@ -567,14 +499,6 @@
// TODO(b/197162116): just get a list of UIDs
final SparseArray<long[]> allUidCpuFreqTimesMs =
mCpuUidFreqTimeReader.getAllUidCpuFreqTimeMs();
- // If the KernelSingleUidTimeReader has stale cpu times, then we shouldn't try to
- // compute deltas since it might result in mis-attributing cpu times to wrong states.
- if (mIsPerProcessStateCpuDataStale) {
- mKernelSingleUidTimeReader.setAllUidsCpuTimesMs(allUidCpuFreqTimesMs);
- mIsPerProcessStateCpuDataStale = false;
- mPendingUids.clear();
- return;
- }
for (int i = allUidCpuFreqTimesMs.size() - 1; i >= 0; --i) {
final int uid = allUidCpuFreqTimesMs.keyAt(i);
final int parentUid = mapUid(uid);
@@ -583,16 +507,8 @@
continue;
}
- final int procState;
- final int idx = mPendingUids.indexOfKey(uid);
- if (idx >= 0) {
- procState = mPendingUids.valueAt(idx);
- mPendingUids.removeAt(idx);
- } else {
- procState = u.mProcessState;
- }
-
- if (procState == ActivityManager.PROCESS_STATE_NONEXISTENT) {
+ final int procState = u.mProcessState;
+ if (procState == Uid.PROCESS_STATE_NONEXISTENT) {
continue;
}
@@ -602,27 +518,19 @@
final LongArrayMultiStateCounter onBatteryScreenOffCounter =
u.getProcStateScreenOffTimeCounter().getCounter();
- onBatteryCounter.setState(procState, timestampMs);
if (uid == parentUid) {
mKernelSingleUidTimeReader.addDelta(uid, onBatteryCounter, timestampMs);
- }
-
- onBatteryScreenOffCounter.setState(procState, timestampMs);
- if (uid == parentUid) {
mKernelSingleUidTimeReader.addDelta(uid, onBatteryScreenOffCounter,
timestampMs);
- }
-
- if (u.mChildUids != null) {
- for (int j = u.mChildUids.size() - 1; j >= 0; --j) {
- final LongArrayMultiStateCounter counter =
- u.mChildUids.valueAt(j).cpuTimeInFreqCounter;
+ } else {
+ Uid.ChildUid childUid = u.getChildUid(uid);
+ if (childUid != null) {
+ final LongArrayMultiStateCounter counter = childUid.cpuTimeInFreqCounter;
if (counter != null) {
- final int isolatedUid = u.mChildUids.keyAt(j);
final LongArrayMultiStateCounter.LongArrayContainer deltaContainer =
getCpuTimeInFreqContainer();
- mKernelSingleUidTimeReader.addDelta(isolatedUid,
- counter, timestampMs, deltaContainer);
+ mKernelSingleUidTimeReader.addDelta(uid, counter, timestampMs,
+ deltaContainer);
onBatteryCounter.addCounts(deltaContainer);
onBatteryScreenOffCounter.addCounts(deltaContainer);
}
@@ -689,9 +597,6 @@
Future<?> scheduleSync(String reason, int flags);
Future<?> scheduleCpuSyncDueToRemovedUid(int uid);
- Future<?> scheduleReadProcStateCpuTimes(boolean onBattery, boolean onBatteryScreenOff,
- long delayMillis);
- Future<?> scheduleCopyFromAllUidsCpuTimes(boolean onBattery, boolean onBatteryScreenOff);
Future<?> scheduleCpuSyncDueToSettingChange();
/**
* Schedule a sync because of a screen state change.
@@ -1839,10 +1744,6 @@
return mCounter.getStateCount();
}
- public void setTrackingEnabled(boolean enabled, long timestampMs) {
- mCounter.setEnabled(enabled && mTimeBase.isRunning(), timestampMs);
- }
-
private void setState(@BatteryConsumer.ProcessState int processState,
long elapsedRealtimeMs) {
mCounter.setState(processState, elapsedRealtimeMs);
@@ -8073,7 +7974,7 @@
Counter mBluetoothScanResultCounter;
Counter mBluetoothScanResultBgCounter;
- int mProcessState = ActivityManager.PROCESS_STATE_NONEXISTENT;
+ int mProcessState = Uid.PROCESS_STATE_NONEXISTENT;
StopwatchTimer[] mProcessStateTimer;
boolean mInForegroundService = false;
@@ -8415,6 +8316,11 @@
mChildUids.remove(idx);
}
+ @GuardedBy("mBsi")
+ ChildUid getChildUid(int childUid) {
+ return mChildUids == null ? null : mChildUids.get(childUid);
+ }
+
private long[] nullIfAllZeros(LongSamplingCounterArray cpuTimesMs, int which) {
if (cpuTimesMs == null) {
return null;
@@ -8432,6 +8338,7 @@
return null;
}
+ @GuardedBy("mBsi")
private void ensureMultiStateCounters() {
if (mProcStateTimeMs != null) {
return;
@@ -8440,31 +8347,26 @@
final long timestampMs = mBsi.mClock.elapsedRealtime();
mProcStateTimeMs =
new TimeInFreqMultiStateCounter(mBsi.mOnBatteryTimeBase,
- NUM_PROCESS_STATE, mBsi.getCpuFreqCount(), timestampMs);
+ PROC_STATE_TIME_COUNTER_STATE_COUNT, mBsi.getCpuFreqCount(),
+ timestampMs);
mProcStateScreenOffTimeMs =
new TimeInFreqMultiStateCounter(mBsi.mOnBatteryScreenOffTimeBase,
- NUM_PROCESS_STATE, mBsi.getCpuFreqCount(), timestampMs);
+ PROC_STATE_TIME_COUNTER_STATE_COUNT, mBsi.getCpuFreqCount(),
+ timestampMs);
}
+ @GuardedBy("mBsi")
private TimeInFreqMultiStateCounter getProcStateTimeCounter() {
ensureMultiStateCounters();
return mProcStateTimeMs;
}
+ @GuardedBy("mBsi")
private TimeInFreqMultiStateCounter getProcStateScreenOffTimeCounter() {
ensureMultiStateCounters();
return mProcStateScreenOffTimeMs;
}
- private void setProcStateTimesTrackingEnabled(boolean enabled, long timestampMs) {
- if (mProcStateTimeMs != null) {
- mProcStateTimeMs.setTrackingEnabled(enabled, timestampMs);
- }
- if (mProcStateScreenOffTimeMs != null) {
- mProcStateScreenOffTimeMs.setTrackingEnabled(enabled, timestampMs);
- }
- }
-
@Override
public Timer getAggregatedPartialWakelockTimer() {
return mAggregatedPartialWakelockTimer;
@@ -8774,6 +8676,7 @@
processState);
}
+ @GuardedBy("mBsi")
@Override
public long getGnssMeasuredBatteryConsumptionUC() {
return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_GNSS);
@@ -9553,7 +9456,7 @@
for (int i = 0; i < NUM_PROCESS_STATE; i++) {
active |= !resetIfNotNull(mProcessStateTimer[i], false, realtimeUs);
}
- active |= (mProcessState != ActivityManager.PROCESS_STATE_NONEXISTENT);
+ active |= (mProcessState != Uid.PROCESS_STATE_NONEXISTENT);
}
if (mVibratorOnTimer != null) {
if (mVibratorOnTimer.reset(false, realtimeUs)) {
@@ -10270,7 +10173,7 @@
} else {
mBluetoothScanResultBgCounter = null;
}
- mProcessState = ActivityManager.PROCESS_STATE_NONEXISTENT;
+ mProcessState = Uid.PROCESS_STATE_NONEXISTENT;
for (int i = 0; i < NUM_PROCESS_STATE; i++) {
if (in.readInt() != 0) {
makeProcessState(i, in);
@@ -10360,7 +10263,7 @@
// Read the object from the Parcel, whether it's usable or not
TimeInFreqMultiStateCounter counter = new TimeInFreqMultiStateCounter(
mBsi.mOnBatteryTimeBase, in, timestampMs);
- if (stateCount == NUM_PROCESS_STATE) {
+ if (stateCount == PROC_STATE_TIME_COUNTER_STATE_COUNT) {
mProcStateTimeMs = counter;
}
} else {
@@ -10373,7 +10276,7 @@
TimeInFreqMultiStateCounter counter =
new TimeInFreqMultiStateCounter(
mBsi.mOnBatteryScreenOffTimeBase, in, timestampMs);
- if (stateCount == NUM_PROCESS_STATE) {
+ if (stateCount == PROC_STATE_TIME_COUNTER_STATE_COUNT) {
mProcStateScreenOffTimeMs = counter;
}
} else {
@@ -11244,12 +11147,6 @@
}
@GuardedBy("mBsi")
- public void updateUidProcessStateLocked(int procState) {
- updateUidProcessStateLocked(procState,
- mBsi.mClock.elapsedRealtime(), mBsi.mClock.uptimeMillis());
- }
-
- @GuardedBy("mBsi")
public void updateUidProcessStateLocked(int procState,
long elapsedRealtimeMs, long uptimeMs) {
int uidRunningState;
@@ -11263,40 +11160,35 @@
}
if (mProcessState != uidRunningState) {
- if (mProcessState != ActivityManager.PROCESS_STATE_NONEXISTENT) {
+ if (mProcessState != Uid.PROCESS_STATE_NONEXISTENT) {
mProcessStateTimer[mProcessState].stopRunningLocked(elapsedRealtimeMs);
-
- if (mBsi.trackPerProcStateCpuTimes()) {
- if (mBsi.mPendingUids.size() == 0) {
- mBsi.mExternalSync.scheduleReadProcStateCpuTimes(
- mBsi.mOnBatteryTimeBase.isRunning(),
- mBsi.mOnBatteryScreenOffTimeBase.isRunning(),
- mBsi.mConstants.PROC_STATE_CPU_TIMES_READ_DELAY_MS);
- mBsi.mNumSingleUidCpuTimeReads++;
- } else {
- mBsi.mNumBatchedSingleUidCpuTimeReads++;
- }
- if (mBsi.mPendingUids.indexOfKey(mUid) < 0
- || ArrayUtils.contains(CRITICAL_PROC_STATES, mProcessState)) {
- mBsi.mPendingUids.put(mUid, mProcessState);
- }
- } else {
- mBsi.mPendingUids.clear();
- }
}
- mProcessState = uidRunningState;
- if (uidRunningState != ActivityManager.PROCESS_STATE_NONEXISTENT) {
+ if (uidRunningState != Uid.PROCESS_STATE_NONEXISTENT) {
if (mProcessStateTimer[uidRunningState] == null) {
makeProcessState(uidRunningState, null);
}
mProcessStateTimer[uidRunningState].startRunningLocked(elapsedRealtimeMs);
}
+ if (mBsi.trackPerProcStateCpuTimes()) {
+ mBsi.updateProcStateCpuTimesLocked(mUid, elapsedRealtimeMs);
+
+ LongArrayMultiStateCounter onBatteryCounter =
+ getProcStateTimeCounter().getCounter();
+ LongArrayMultiStateCounter onBatteryScreenOffCounter =
+ getProcStateScreenOffTimeCounter().getCounter();
+
+ onBatteryCounter.setState(uidRunningState, elapsedRealtimeMs);
+ onBatteryScreenOffCounter.setState(uidRunningState, elapsedRealtimeMs);
+ }
+
+ mProcessState = uidRunningState;
+
updateOnBatteryBgTimeBase(uptimeMs * 1000, elapsedRealtimeMs * 1000);
updateOnBatteryScreenOffBgTimeBase(uptimeMs * 1000, elapsedRealtimeMs * 1000);
final int batteryConsumerProcessState =
- mapUidProcessStateToBatteryConsumerProcessState(mProcessState);
+ mapUidProcessStateToBatteryConsumerProcessState(uidRunningState);
getCpuActiveTimeCounter().setState(batteryConsumerProcessState, elapsedRealtimeMs);
final MeasuredEnergyStats energyStats =
@@ -11318,7 +11210,7 @@
/** Whether to consider Uid to be in the background for background timebase purposes. */
public boolean isInBackground() {
- // Note that PROCESS_STATE_CACHED and ActivityManager.PROCESS_STATE_NONEXISTENT is
+ // Note that PROCESS_STATE_CACHED and Uid.PROCESS_STATE_NONEXISTENT is
// also considered to be 'background' for our purposes, because it's not foreground.
return mProcessState >= PROCESS_STATE_BACKGROUND;
}
@@ -15660,7 +15552,7 @@
@GuardedBy("this")
public boolean trackPerProcStateCpuTimes() {
- return mConstants.TRACK_CPU_TIMES_BY_PROC_STATE && mPerProcStateCpuTimesAvailable;
+ return mCpuUidFreqTimeReader.isFastCpuTimesReader();
}
@GuardedBy("this")
@@ -15747,8 +15639,6 @@
@VisibleForTesting
public final class Constants extends ContentObserver {
- public static final String KEY_TRACK_CPU_TIMES_BY_PROC_STATE
- = "track_cpu_times_by_proc_state";
public static final String KEY_TRACK_CPU_ACTIVE_CLUSTER_TIME
= "track_cpu_active_cluster_time";
public static final String KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS
@@ -15766,9 +15656,7 @@
public static final String KEY_BATTERY_CHARGED_DELAY_MS =
"battery_charged_delay_ms";
- private static final boolean DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE = false;
private static final boolean DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME = true;
- private static final long DEFAULT_PROC_STATE_CPU_TIMES_READ_DELAY_MS = 5_000;
private static final long DEFAULT_KERNEL_UID_READERS_THROTTLE_TIME = 1_000;
private static final long DEFAULT_UID_REMOVE_DELAY_MS = 5L * 60L * 1000L;
private static final long DEFAULT_EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS = 600_000;
@@ -15779,9 +15667,7 @@
private static final int DEFAULT_MAX_HISTORY_BUFFER_LOW_RAM_DEVICE_KB = 64; /*Kilo Bytes*/
private static final int DEFAULT_BATTERY_CHARGED_DELAY_MS = 900000; /* 15 min */
- public boolean TRACK_CPU_TIMES_BY_PROC_STATE = DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE;
public boolean TRACK_CPU_ACTIVE_CLUSTER_TIME = DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME;
- public long PROC_STATE_CPU_TIMES_READ_DELAY_MS = DEFAULT_PROC_STATE_CPU_TIMES_READ_DELAY_MS;
/* Do not set default value for KERNEL_UID_READERS_THROTTLE_TIME. Need to trigger an
* update when startObserving. */
public long KERNEL_UID_READERS_THROTTLE_TIME;
@@ -15843,14 +15729,8 @@
Slog.e(TAG, "Bad batterystats settings", e);
}
- updateTrackCpuTimesByProcStateLocked(TRACK_CPU_TIMES_BY_PROC_STATE,
- mParser.getBoolean(KEY_TRACK_CPU_TIMES_BY_PROC_STATE,
- DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE));
TRACK_CPU_ACTIVE_CLUSTER_TIME = mParser.getBoolean(
KEY_TRACK_CPU_ACTIVE_CLUSTER_TIME, DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME);
- updateProcStateCpuTimesReadDelayMs(PROC_STATE_CPU_TIMES_READ_DELAY_MS,
- mParser.getLong(KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS,
- DEFAULT_PROC_STATE_CPU_TIMES_READ_DELAY_MS));
updateKernelUidReadersThrottleTime(KERNEL_UID_READERS_THROTTLE_TIME,
mParser.getLong(KEY_KERNEL_UID_READERS_THROTTLE_TIME,
DEFAULT_KERNEL_UID_READERS_THROTTLE_TIME));
@@ -15887,33 +15767,6 @@
DEFAULT_BATTERY_CHARGED_DELAY_MS);
}
- @GuardedBy("BatteryStatsImpl.this")
- private void updateTrackCpuTimesByProcStateLocked(boolean wasEnabled, boolean isEnabled) {
- TRACK_CPU_TIMES_BY_PROC_STATE = isEnabled;
- if (isEnabled && !wasEnabled) {
- mIsPerProcessStateCpuDataStale = true;
- mExternalSync.scheduleCpuSyncDueToSettingChange();
-
- mNumSingleUidCpuTimeReads = 0;
- mNumBatchedSingleUidCpuTimeReads = 0;
- mCpuTimeReadsTrackingStartTimeMs = mClock.uptimeMillis();
- }
- final long timestampMs = mClock.elapsedRealtime();
- for (int i = mUidStats.size() - 1; i >= 0; i--) {
- mUidStats.valueAt(i).setProcStateTimesTrackingEnabled(isEnabled, timestampMs);
- }
- }
-
- @GuardedBy("BatteryStatsImpl.this")
- private void updateProcStateCpuTimesReadDelayMs(long oldDelayMillis, long newDelayMillis) {
- PROC_STATE_CPU_TIMES_READ_DELAY_MS = newDelayMillis;
- if (oldDelayMillis != newDelayMillis) {
- mNumSingleUidCpuTimeReads = 0;
- mNumBatchedSingleUidCpuTimeReads = 0;
- mCpuTimeReadsTrackingStartTimeMs = mClock.uptimeMillis();
- }
- }
-
private void updateKernelUidReadersThrottleTime(long oldTimeMs, long newTimeMs) {
KERNEL_UID_READERS_THROTTLE_TIME = newTimeMs;
if (oldTimeMs != newTimeMs) {
@@ -15932,12 +15785,8 @@
}
public void dumpLocked(PrintWriter pw) {
- pw.print(KEY_TRACK_CPU_TIMES_BY_PROC_STATE); pw.print("=");
- pw.println(TRACK_CPU_TIMES_BY_PROC_STATE);
pw.print(KEY_TRACK_CPU_ACTIVE_CLUSTER_TIME); pw.print("=");
pw.println(TRACK_CPU_ACTIVE_CLUSTER_TIME);
- pw.print(KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS); pw.print("=");
- pw.println(PROC_STATE_CPU_TIMES_READ_DELAY_MS);
pw.print(KEY_KERNEL_UID_READERS_THROTTLE_TIME); pw.print("=");
pw.println(KERNEL_UID_READERS_THROTTLE_TIME);
pw.print(KEY_EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS); pw.print("=");
@@ -16606,8 +16455,8 @@
if (in.readInt() != 0) {
u.createBluetoothScanResultBgCounterLocked().readSummaryFromParcelLocked(in);
}
- u.mProcessState = ActivityManager.PROCESS_STATE_NONEXISTENT;
- for (int i = 0; i < Uid.NUM_PROCESS_STATE; i++) {
+ u.mProcessState = Uid.PROCESS_STATE_NONEXISTENT;
+ for (int i = 0; i < NUM_PROCESS_STATE; i++) {
if (in.readInt() != 0) {
u.makeProcessState(i, null);
u.mProcessStateTimer[i].readSummaryFromParcelLocked(in);
@@ -16699,7 +16548,7 @@
// Read the object from the Parcel, whether it's usable or not
TimeInFreqMultiStateCounter counter = new TimeInFreqMultiStateCounter(
mOnBatteryTimeBase, in, mClock.elapsedRealtime());
- if (stateCount == Uid.NUM_PROCESS_STATE) {
+ if (stateCount == PROC_STATE_TIME_COUNTER_STATE_COUNT) {
u.mProcStateTimeMs = counter;
}
}
@@ -16714,7 +16563,7 @@
TimeInFreqMultiStateCounter counter =
new TimeInFreqMultiStateCounter(
mOnBatteryScreenOffTimeBase, in, mClock.elapsedRealtime());
- if (stateCount == Uid.NUM_PROCESS_STATE) {
+ if (stateCount == PROC_STATE_TIME_COUNTER_STATE_COUNT) {
u.mProcStateScreenOffTimeMs = counter;
}
}
@@ -17152,7 +17001,7 @@
} else {
out.writeInt(0);
}
- for (int i = 0; i < Uid.NUM_PROCESS_STATE; i++) {
+ for (int i = 0; i < NUM_PROCESS_STATE; i++) {
if (u.mProcessStateTimer[i] != null) {
out.writeInt(1);
u.mProcessStateTimer[i].writeSummaryFromParcelLocked(out, nowRealtime);
@@ -17982,10 +17831,10 @@
}
super.dumpLocked(context, pw, flags, reqUid, histStart);
+ pw.print("Per process state tracking available: ");
+ pw.println(trackPerProcStateCpuTimes());
pw.print("Total cpu time reads: ");
pw.println(mNumSingleUidCpuTimeReads);
- pw.print("Batched cpu time reads: ");
- pw.println(mNumBatchedSingleUidCpuTimeReads);
pw.print("Batching Duration (min): ");
pw.println((mClock.uptimeMillis() - mCpuTimeReadsTrackingStartTimeMs) / (60 * 1000));
pw.print("All UID cpu time reads since the later of device start or stats reset: ");
diff --git a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
index faeb8fc..c801be0 100644
--- a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
+++ b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
@@ -620,6 +620,10 @@
}
return numClusterFreqs;
}
+
+ public boolean isFastCpuTimesReader() {
+ return mBpfTimesAvailable;
+ }
}
/**
diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp
index a059dd6..78e5adc 100644
--- a/core/jni/android_graphics_BLASTBufferQueue.cpp
+++ b/core/jni/android_graphics_BLASTBufferQueue.cpp
@@ -30,21 +30,19 @@
namespace android {
-static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring jName, jlong surfaceControl,
- jlong width, jlong height, jint format) {
- String8 str8;
- if (jName) {
- const jchar* str16 = env->GetStringCritical(jName, nullptr);
- if (str16) {
- str8 = String8(reinterpret_cast<const char16_t*>(str16), env->GetStringLength(jName));
- env->ReleaseStringCritical(jName, str16);
- str16 = nullptr;
- }
- }
- std::string name = str8.string();
+static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring jName) {
+ ScopedUtfChars name(env, jName);
+ sp<BLASTBufferQueue> queue = new BLASTBufferQueue(name.c_str());
+ queue->incStrong((void*)nativeCreate);
+ return reinterpret_cast<jlong>(queue.get());
+}
+
+static jlong nativeCreateAndUpdate(JNIEnv* env, jclass clazz, jstring jName, jlong surfaceControl,
+ jlong width, jlong height, jint format) {
+ ScopedUtfChars name(env, jName);
sp<BLASTBufferQueue> queue =
- new BLASTBufferQueue(name, reinterpret_cast<SurfaceControl*>(surfaceControl), width,
- height, format);
+ new BLASTBufferQueue(name.c_str(), reinterpret_cast<SurfaceControl*>(surfaceControl),
+ width, height, format);
queue->incStrong((void*)nativeCreate);
return reinterpret_cast<jlong>(queue.get());
}
@@ -96,7 +94,8 @@
static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
// clang-format off
- {"nativeCreate", "(Ljava/lang/String;JJJI)J", (void*)nativeCreate},
+ {"nativeCreate", "(Ljava/lang/String;)J", (void*)nativeCreate},
+ {"nativeCreateAndUpdate", "(Ljava/lang/String;JJJI)J", (void*)nativeCreateAndUpdate},
{"nativeGetSurface", "(JZ)Landroid/view/Surface;", (void*)nativeGetSurface},
{"nativeDestroy", "(J)V", (void*)nativeDestroy},
{"nativeSetSyncTransaction", "(JJZ)V", (void*)nativeSetSyncTransaction},
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 211f78e..23453876 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -380,6 +380,7 @@
optional bool pip_auto_enter_enabled = 31;
optional bool in_size_compat_mode = 32;
optional float min_aspect_ratio = 33;
+ optional bool provides_max_bounds = 34;
}
/* represents WindowToken */
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 04d6171..7018df2 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2798,7 +2798,7 @@
<!-- @SystemApi @hide Allows an application to set the profile owners and the device owner.
This permission is not available to third party applications.-->
<permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"
- android:protectionLevel="signature|role"
+ android:protectionLevel="signature|role|setup"
android:label="@string/permlab_manageProfileAndDeviceOwners"
android:description="@string/permdesc_manageProfileAndDeviceOwners" />
@@ -5531,10 +5531,18 @@
<!-- Allows an application to interact with the currently active
{@link com.android.server.communal.CommunalManagerService}.
- @hide -->
+ @hide
+ @TestApi -->
<permission android:name="android.permission.WRITE_COMMUNAL_STATE"
android:protectionLevel="signature" />
+ <!-- Allows an application to view information from the currently active
+ {@link com.android.server.communal.CommunalManagerService}.
+ @hide
+ @SystemApi -->
+ <permission android:name="android.permission.READ_COMMUNAL_STATE"
+ android:protectionLevel="signature|privileged"/>
+
<!-- Allows the holder to manage whether the system can bind to services
provided by instant apps. This permission is intended to protect
test/development fucntionality and should be used only in such cases.
@@ -5870,6 +5878,10 @@
<!-- Allows input events to be monitored. Very dangerous! @hide -->
<permission android:name="android.permission.MONITOR_INPUT"
android:protectionLevel="signature|recents" />
+ <!-- Allows the use of FLAG_SLIPPERY, which permits touch events to slip from the current
+ window to the window where the touch currently is on top of. @hide -->
+ <permission android:name="android.permission.ALLOW_SLIPPERY_TOUCHES"
+ android:protectionLevel="signature|recents" />
<!-- Allows the caller to change the associations between input devices and displays.
Very dangerous! @hide -->
<permission android:name="android.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY"
diff --git a/core/res/res/values-television/config.xml b/core/res/res/values-television/config.xml
index 606d0f2..92bea34 100644
--- a/core/res/res/values-television/config.xml
+++ b/core/res/res/values-television/config.xml
@@ -27,11 +27,11 @@
<!-- The percentage of the screen width to use for the default width or height of
picture-in-picture windows. Regardless of the percent set here, calculated size will never
be smaller than @dimen/default_minimal_size_pip_resizable_task. -->
- <item name="config_pictureInPictureDefaultSizePercent" format="float" type="dimen">0.14</item>
+ <item name="config_pictureInPictureDefaultSizePercent" format="float" type="dimen">0.2</item>
<!-- Default insets [LEFT/RIGHTxTOP/BOTTOM] from the screen edge for picture-in-picture windows.
These values are in DPs and will be converted to pixel sizes internally. -->
- <string translatable="false" name="config_defaultPictureInPictureScreenEdgeInsets">56x27</string>
+ <string translatable="false" name="config_defaultPictureInPictureScreenEdgeInsets">24x24</string>
<!-- The default gravity for the picture-in-picture window.
Currently, this maps to Gravity.BOTTOM | Gravity.RIGHT -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index ccde348..480c679 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2123,6 +2123,8 @@
<string name="config_systemTelevisionRemoteService" translatable="false">@string/config_tvRemoteServicePackage</string>
<!-- The name of the package that will hold the device management role -->
<string name="config_deviceManager" translatable="false"></string>
+ <!-- The name of the package that will hold the app protection service role. -->
+ <string name="config_systemAppProtectionService" translatable="false"></string>
<!-- The name of the package that will be allowed to change its components' label/icon. -->
<string name="config_overrideComponentUiPackage" translatable="false">com.android.stk</string>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index ca80def..1aa3ac2 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3267,6 +3267,8 @@
<public name="config_systemSupervision" />
<!-- @hide @SystemApi -->
<public name="config_deviceManager" />
+ <!-- @hide @SystemApi -->
+ <public name="config_systemAppProtectionService" />
</staging-public-group>
<staging-public-group type="dimen" first-id="0x01db0000">
diff --git a/core/tests/coretests/src/android/content/pm/ConstrainDisplayApisConfigTest.java b/core/tests/coretests/src/android/content/pm/ConstrainDisplayApisConfigTest.java
index 0456029..98485c0 100644
--- a/core/tests/coretests/src/android/content/pm/ConstrainDisplayApisConfigTest.java
+++ b/core/tests/coretests/src/android/content/pm/ConstrainDisplayApisConfigTest.java
@@ -18,8 +18,7 @@
import static android.provider.DeviceConfig.NAMESPACE_CONSTRAIN_DISPLAY_APIS;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
import android.annotation.Nullable;
import android.platform.test.annotations.Presubmit;
@@ -146,24 +145,17 @@
private static void testNeverConstrainDisplayApis(String packageName, long version,
boolean expected) {
- boolean result = ConstrainDisplayApisConfig.neverConstrainDisplayApis(
- buildApplicationInfo(packageName, version));
- if (expected) {
- assertTrue(result);
- } else {
- assertFalse(result);
- }
+ ConstrainDisplayApisConfig config = new ConstrainDisplayApisConfig();
+ assertEquals(expected,
+ config.getNeverConstrainDisplayApis(buildApplicationInfo(packageName, version)));
}
private static void testAlwaysConstrainDisplayApis(String packageName, long version,
boolean expected) {
- boolean result = ConstrainDisplayApisConfig.alwaysConstrainDisplayApis(
- buildApplicationInfo(packageName, version));
- if (expected) {
- assertTrue(result);
- } else {
- assertFalse(result);
- }
+ ConstrainDisplayApisConfig config = new ConstrainDisplayApisConfig();
+
+ assertEquals(expected,
+ config.getAlwaysConstrainDisplayApis(buildApplicationInfo(packageName, version)));
}
private static ApplicationInfo buildApplicationInfo(String packageName, long version) {
diff --git a/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java b/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java
index 6be9306..bd4d80d 100644
--- a/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java
+++ b/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java
@@ -169,13 +169,13 @@
assertEquals("19/1/2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + HOUR,
FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
- assertEquals("19/1/2009–22/1/2009",
+ assertEquals("19/1/2009 – 22/1/2009",
formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY,
FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
- assertEquals("19/1/2009–22/4/2009",
+ assertEquals("19/1/2009 – 22/4/2009",
formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH,
FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
- assertEquals("19/1/2009–9/2/2012",
+ assertEquals("19/1/2009 – 9/2/2012",
formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR,
FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
@@ -251,35 +251,35 @@
assertEquals("19–22 de enero de 2009",
formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, 0));
- assertEquals("19–22 de ene. de 2009",
+ assertEquals("19–22 de ene de 2009",
formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY,
FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
- assertEquals("lun, 19 de ene. – jue, 22 de ene. de 2009",
+ assertEquals("lun, 19 de ene – jue, 22 de ene de 2009",
formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY,
FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
- assertEquals("lunes, 19 de enero–jueves, 22 de enero de 2009",
+ assertEquals("lunes, 19 de enero – jueves, 22 de enero de 2009",
formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY));
- assertEquals("19 de enero–22 de abril de 2009",
+ assertEquals("19 de enero – 22 de abril de 2009",
formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, 0));
- assertEquals("19 de ene. – 22 de abr. 2009",
+ assertEquals("19 de ene – 22 de abr 2009",
formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH,
FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
- assertEquals("lun, 19 de ene. – mié, 22 de abr. de 2009",
+ assertEquals("lun, 19 de ene – mié, 22 de abr de 2009",
formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH,
FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
assertEquals("enero–abril de 2009",
formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY));
- assertEquals("19 de ene. de 2009 – 9 de feb. de 2012",
+ assertEquals("19 de ene de 2009 – 9 de feb de 2012",
formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR,
FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
- assertEquals("ene. de 2009 – feb. de 2012",
+ assertEquals("ene de 2009 – feb de 2012",
formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR,
FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
- assertEquals("19 de enero de 2009–9 de febrero de 2012",
+ assertEquals("19 de enero de 2009 – 9 de febrero de 2012",
formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, 0));
- assertEquals("lunes, 19 de enero de 2009–jueves, 9 de febrero de 2012",
+ assertEquals("lunes, 19 de enero de 2009 – jueves, 9 de febrero de 2012",
formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY));
// The same tests but for es_ES.
@@ -291,10 +291,10 @@
assertEquals("lun, 19 ene – jue, 22 ene 2009",
formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY,
FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
- assertEquals("lunes, 19 de enero–jueves, 22 de enero de 2009",
+ assertEquals("lunes, 19 de enero – jueves, 22 de enero de 2009",
formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY));
- assertEquals("19 de enero–22 de abril de 2009",
+ assertEquals("19 de enero – 22 de abril de 2009",
formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, 0));
assertEquals("19 ene – 22 abr 2009",
formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH,
@@ -311,9 +311,9 @@
assertEquals("ene 2009 – feb 2012",
formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR,
FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
- assertEquals("19 de enero de 2009–9 de febrero de 2012",
+ assertEquals("19 de enero de 2009 – 9 de febrero de 2012",
formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, 0));
- assertEquals("lunes, 19 de enero de 2009–jueves, 9 de febrero de 2012",
+ assertEquals("lunes, 19 de enero de 2009 – jueves, 9 de febrero de 2012",
formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY));
}
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index cfcbc7d..45504c0 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -132,6 +132,8 @@
});
}
+ private static final String TEST_MIME_TYPE = "application/TestType";
+
private static final int CONTENT_PREVIEW_IMAGE = 1;
private static final int CONTENT_PREVIEW_FILE = 2;
private static final int CONTENT_PREVIEW_TEXT = 3;
@@ -564,7 +566,7 @@
assertEquals(mActivityRule.getActivityResult().getResultCode(), RESULT_OK);
}
- @Test @Ignore
+ @Test
public void copyTextToClipboardLogging() throws Exception {
Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
@@ -585,17 +587,15 @@
onView(withId(R.id.chooser_copy_button)).perform(click());
verify(mockLogger, atLeastOnce()).write(logMakerCaptor.capture());
- // First is Activity shown, Second is "with preview"
- assertThat(logMakerCaptor.getAllValues().get(2).getCategory(),
+
+ // The last captured event is the selection of the target.
+ assertThat(logMakerCaptor.getValue().getCategory(),
is(MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_SYSTEM_TARGET));
- assertThat(logMakerCaptor
- .getAllValues().get(2)
- .getSubtype(),
- is(1));
+ assertThat(logMakerCaptor.getValue().getSubtype(), is(1));
}
- @Test @Ignore
+ @Test
public void testNearbyShareLogging() throws Exception {
Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
@@ -614,12 +614,12 @@
ChooserActivityLoggerFake logger =
(ChooserActivityLoggerFake) activity.getChooserActivityLogger();
- assertThat(logger.numCalls(), is(6));
- // first one should be SHARESHEET_TRIGGERED uievent
- assertThat(logger.get(0).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
- assertThat(logger.get(0).event.getId(),
+
+ // SHARESHEET_TRIGGERED:
+ assertThat(logger.event(0).getId(),
is(ChooserActivityLogger.SharesheetStandardEvent.SHARESHEET_TRIGGERED.getId()));
- // second one should be SHARESHEET_STARTED event
+
+ // SHARESHEET_STARTED:
assertThat(logger.get(1).atomId, is(FrameworkStatsLog.SHARESHEET_STARTED));
assertThat(logger.get(1).intent, is(Intent.ACTION_SEND));
assertThat(logger.get(1).mimeType, is("text/plain"));
@@ -628,22 +628,37 @@
assertThat(logger.get(1).appProvidedDirect, is(0));
assertThat(logger.get(1).isWorkprofile, is(false));
assertThat(logger.get(1).previewType, is(3));
- // third one should be SHARESHEET_APP_LOAD_COMPLETE uievent
- assertThat(logger.get(2).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
- assertThat(logger.get(2).event.getId(),
+
+ // SHARESHEET_APP_LOAD_COMPLETE:
+ assertThat(logger.event(2).getId(),
is(ChooserActivityLogger
.SharesheetStandardEvent.SHARESHEET_APP_LOAD_COMPLETE.getId()));
- // fourth and fifth are just artifacts of test set-up
- // sixth one should be ranking atom with SHARESHEET_NEARBY_TARGET_SELECTED event
- assertThat(logger.get(5).atomId, is(FrameworkStatsLog.RANKING_SELECTED));
- assertThat(logger.get(5).targetType,
+
+ // SHARESHEET_DIRECT_LOAD_COMPLETE:
+ assertThat(logger.event(3).getId(),
+ is(ChooserActivityLogger
+ .SharesheetStandardEvent.SHARESHEET_DIRECT_LOAD_COMPLETE.getId()));
+
+ // Fifth and sixth are just artifacts of test set-up:
+ assertThat(logger.event(4).getId(),
+ is(ChooserActivityLogger
+ .SharesheetStandardEvent.SHARESHEET_EMPTY_DIRECT_SHARE_ROW.getId()));
+ assertThat(logger.event(5).getId(),
+ is(ChooserActivityLogger.SharesheetStandardEvent.SHARESHEET_EXPANDED.getId()));
+
+ // SHARESHEET_EDIT_TARGET_SELECTED:
+ assertThat(logger.get(6).atomId, is(FrameworkStatsLog.RANKING_SELECTED));
+ assertThat(logger.get(6).targetType,
is(ChooserActivityLogger
.SharesheetTargetSelectedEvent.SHARESHEET_NEARBY_TARGET_SELECTED.getId()));
+
+ // No more events.
+ assertThat(logger.numCalls(), is(7));
}
- @Test @Ignore
+ @Test
public void testEditImageLogs() throws Exception {
Intent sendIntent = createSendImageIntent(
Uri.parse("android.resource://com.android.frameworks.coretests/"
@@ -668,11 +683,12 @@
ChooserActivityLoggerFake logger =
(ChooserActivityLoggerFake) activity.getChooserActivityLogger();
- // first one should be SHARESHEET_TRIGGERED uievent
- assertThat(logger.get(0).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
- assertThat(logger.get(0).event.getId(),
+
+ // SHARESHEET_TRIGGERED:
+ assertThat(logger.event(0).getId(),
is(ChooserActivityLogger.SharesheetStandardEvent.SHARESHEET_TRIGGERED.getId()));
- // second one should be SHARESHEET_STARTED event
+
+ // SHARESHEET_STARTED:
assertThat(logger.get(1).atomId, is(FrameworkStatsLog.SHARESHEET_STARTED));
assertThat(logger.get(1).intent, is(Intent.ACTION_SEND));
assertThat(logger.get(1).mimeType, is("image/png"));
@@ -681,17 +697,32 @@
assertThat(logger.get(1).appProvidedDirect, is(0));
assertThat(logger.get(1).isWorkprofile, is(false));
assertThat(logger.get(1).previewType, is(1));
- // third one should be SHARESHEET_APP_LOAD_COMPLETE uievent
- assertThat(logger.get(2).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
- assertThat(logger.get(2).event.getId(),
+
+ // SHARESHEET_APP_LOAD_COMPLETE:
+ assertThat(logger.event(2).getId(),
is(ChooserActivityLogger
.SharesheetStandardEvent.SHARESHEET_APP_LOAD_COMPLETE.getId()));
- // fourth and fifth are just artifacts of test set-up
- // sixth one should be ranking atom with SHARESHEET_EDIT_TARGET_SELECTED event
- assertThat(logger.get(5).atomId, is(FrameworkStatsLog.RANKING_SELECTED));
- assertThat(logger.get(5).targetType,
+
+ // SHARESHEET_DIRECT_LOAD_COMPLETE:
+ assertThat(logger.event(3).getId(),
+ is(ChooserActivityLogger
+ .SharesheetStandardEvent.SHARESHEET_DIRECT_LOAD_COMPLETE.getId()));
+
+ // Fifth and sixth are just artifacts of test set-up:
+ assertThat(logger.event(4).getId(),
+ is(ChooserActivityLogger
+ .SharesheetStandardEvent.SHARESHEET_EMPTY_DIRECT_SHARE_ROW.getId()));
+ assertThat(logger.event(5).getId(),
+ is(ChooserActivityLogger.SharesheetStandardEvent.SHARESHEET_EXPANDED.getId()));
+
+ // SHARESHEET_EDIT_TARGET_SELECTED:
+ assertThat(logger.get(6).atomId, is(FrameworkStatsLog.RANKING_SELECTED));
+ assertThat(logger.get(6).targetType,
is(ChooserActivityLogger
.SharesheetTargetSelectedEvent.SHARESHEET_EDIT_TARGET_SELECTED.getId()));
+
+ // No more events.
+ assertThat(logger.numCalls(), is(7));
}
@@ -778,7 +809,7 @@
@Test
public void testOnCreateLogging() {
Intent sendIntent = createSendTextIntent();
- sendIntent.setType("TestType");
+ sendIntent.setType(TEST_MIME_TYPE);
MetricsLogger mockLogger = sOverrides.metricsLogger;
ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class);
@@ -794,7 +825,7 @@
assertThat(logMakerCaptor
.getAllValues().get(0)
.getTaggedData(MetricsEvent.FIELD_SHARESHEET_MIMETYPE),
- is("TestType"));
+ is(TEST_MIME_TYPE));
assertThat(logMakerCaptor
.getAllValues().get(0)
.getSubtype(),
@@ -804,7 +835,7 @@
@Test
public void testOnCreateLoggingFromWorkProfile() {
Intent sendIntent = createSendTextIntent();
- sendIntent.setType("TestType");
+ sendIntent.setType(TEST_MIME_TYPE);
sOverrides.alternateProfileSetting = MetricsEvent.MANAGED_PROFILE;
MetricsLogger mockLogger = sOverrides.metricsLogger;
ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class);
@@ -820,7 +851,7 @@
assertThat(logMakerCaptor
.getAllValues().get(0)
.getTaggedData(MetricsEvent.FIELD_SHARESHEET_MIMETYPE),
- is("TestType"));
+ is(TEST_MIME_TYPE));
assertThat(logMakerCaptor
.getAllValues().get(0)
.getSubtype(),
@@ -1476,7 +1507,7 @@
// enable the work tab feature flag
ResolverActivity.ENABLE_TABBED_VIEW = true;
Intent sendIntent = createSendTextIntent();
- sendIntent.setType("TestType");
+ sendIntent.setType(TEST_MIME_TYPE);
markWorkProfileUserAvailable();
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
@@ -1490,7 +1521,7 @@
// enable the work tab feature flag
ResolverActivity.ENABLE_TABBED_VIEW = true;
Intent sendIntent = createSendTextIntent();
- sendIntent.setType("TestType");
+ sendIntent.setType(TEST_MIME_TYPE);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
waitForIdle();
@@ -1512,7 +1543,7 @@
workProfileTargets);
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendTextIntent();
- sendIntent.setType("TestType");
+ sendIntent.setType(TEST_MIME_TYPE);
markWorkProfileUserAvailable();
final ChooserWrapperActivity activity =
@@ -1538,7 +1569,7 @@
createResolvedComponentsForTest(workProfileTargets);
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendTextIntent();
- sendIntent.setType("TestType");
+ sendIntent.setType(TEST_MIME_TYPE);
final ChooserWrapperActivity activity =
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
@@ -1561,7 +1592,7 @@
createResolvedComponentsForTest(workProfileTargets);
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendTextIntent();
- sendIntent.setType("TestType");
+ sendIntent.setType(TEST_MIME_TYPE);
ResolveInfo[] chosen = new ResolveInfo[1];
sOverrides.onSafelyStartCallback = targetInfo -> {
chosen[0] = targetInfo.getResolveInfo();
@@ -1597,7 +1628,7 @@
sOverrides.hasCrossProfileIntents = false;
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendTextIntent();
- sendIntent.setType("TestType");
+ sendIntent.setType(TEST_MIME_TYPE);
final ChooserWrapperActivity activity =
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
@@ -1623,7 +1654,7 @@
sOverrides.isQuietModeEnabled = true;
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendTextIntent();
- sendIntent.setType("TestType");
+ sendIntent.setType(TEST_MIME_TYPE);
ResolverActivity.ENABLE_TABBED_VIEW = true;
final ChooserWrapperActivity activity =
@@ -1649,7 +1680,7 @@
createResolvedComponentsForTest(0);
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendTextIntent();
- sendIntent.setType("TestType");
+ sendIntent.setType(TEST_MIME_TYPE);
final ChooserWrapperActivity activity =
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
@@ -1676,7 +1707,7 @@
sOverrides.isQuietModeEnabled = true;
sOverrides.hasCrossProfileIntents = false;
Intent sendIntent = createSendTextIntent();
- sendIntent.setType("TestType");
+ sendIntent.setType(TEST_MIME_TYPE);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
waitForIdle();
@@ -1701,7 +1732,7 @@
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
sOverrides.isQuietModeEnabled = true;
Intent sendIntent = createSendTextIntent();
- sendIntent.setType("TestType");
+ sendIntent.setType(TEST_MIME_TYPE);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
waitForIdle();
@@ -1714,7 +1745,7 @@
.check(matches(isDisplayed()));
}
- @Test @Ignore
+ @Test
public void testAppTargetLogging() throws InterruptedException {
Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
@@ -1743,12 +1774,12 @@
ChooserActivityLoggerFake logger =
(ChooserActivityLoggerFake) activity.getChooserActivityLogger();
- assertThat(logger.numCalls(), is(6));
- // first one should be SHARESHEET_TRIGGERED uievent
- assertThat(logger.get(0).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
- assertThat(logger.get(0).event.getId(),
+
+ // SHARESHEET_TRIGGERED:
+ assertThat(logger.event(0).getId(),
is(ChooserActivityLogger.SharesheetStandardEvent.SHARESHEET_TRIGGERED.getId()));
- // second one should be SHARESHEET_STARTED event
+
+ // SHARESHEET_STARTED:
assertThat(logger.get(1).atomId, is(FrameworkStatsLog.SHARESHEET_STARTED));
assertThat(logger.get(1).intent, is(Intent.ACTION_SEND));
assertThat(logger.get(1).mimeType, is("text/plain"));
@@ -1757,17 +1788,32 @@
assertThat(logger.get(1).appProvidedDirect, is(0));
assertThat(logger.get(1).isWorkprofile, is(false));
assertThat(logger.get(1).previewType, is(3));
- // third one should be SHARESHEET_APP_LOAD_COMPLETE uievent
- assertThat(logger.get(2).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
- assertThat(logger.get(2).event.getId(),
+
+ // SHARESHEET_APP_LOAD_COMPLETE:
+ assertThat(logger.event(2).getId(),
is(ChooserActivityLogger
.SharesheetStandardEvent.SHARESHEET_APP_LOAD_COMPLETE.getId()));
- // fourth and fifth are just artifacts of test set-up
- // sixth one should be ranking atom with SHARESHEET_APP_TARGET_SELECTED event
- assertThat(logger.get(5).atomId, is(FrameworkStatsLog.RANKING_SELECTED));
- assertThat(logger.get(5).targetType,
+
+ // SHARESHEET_DIRECT_LOAD_COMPLETE:
+ assertThat(logger.event(3).getId(),
+ is(ChooserActivityLogger
+ .SharesheetStandardEvent.SHARESHEET_DIRECT_LOAD_COMPLETE.getId()));
+
+ // Fifth and sixth are just artifacts of test set-up:
+ assertThat(logger.event(4).getId(),
+ is(ChooserActivityLogger
+ .SharesheetStandardEvent.SHARESHEET_EMPTY_DIRECT_SHARE_ROW.getId()));
+ assertThat(logger.event(5).getId(),
+ is(ChooserActivityLogger.SharesheetStandardEvent.SHARESHEET_EXPANDED.getId()));
+
+ // SHARESHEET_EDIT_TARGET_SELECTED:
+ assertThat(logger.get(6).atomId, is(FrameworkStatsLog.RANKING_SELECTED));
+ assertThat(logger.get(6).targetType,
is(ChooserActivityLogger
.SharesheetTargetSelectedEvent.SHARESHEET_APP_TARGET_SELECTED.getId()));
+
+ // No more events.
+ assertThat(logger.numCalls(), is(7));
}
@Test @Ignore
@@ -1850,7 +1896,7 @@
.SharesheetTargetSelectedEvent.SHARESHEET_SERVICE_TARGET_SELECTED.getId()));
}
- @Test @Ignore
+ @Test
public void testEmptyDirectRowLogging() throws InterruptedException {
Intent sendIntent = createSendTextIntent();
// We need app targets for direct targets to get displayed
@@ -1874,12 +1920,12 @@
ChooserActivityLoggerFake logger =
(ChooserActivityLoggerFake) activity.getChooserActivityLogger();
- assertThat(logger.numCalls(), is(6));
- // first one should be SHARESHEET_TRIGGERED uievent
- assertThat(logger.get(0).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
- assertThat(logger.get(0).event.getId(),
+
+ // SHARESHEET_TRIGGERED:
+ assertThat(logger.event(0).getId(),
is(ChooserActivityLogger.SharesheetStandardEvent.SHARESHEET_TRIGGERED.getId()));
- // second one should be SHARESHEET_STARTED event
+
+ // SHARESHEET_STARTED:
assertThat(logger.get(1).atomId, is(FrameworkStatsLog.SHARESHEET_STARTED));
assertThat(logger.get(1).intent, is(Intent.ACTION_SEND));
assertThat(logger.get(1).mimeType, is("text/plain"));
@@ -1888,20 +1934,30 @@
assertThat(logger.get(1).appProvidedDirect, is(0));
assertThat(logger.get(1).isWorkprofile, is(false));
assertThat(logger.get(1).previewType, is(3));
- // third one should be SHARESHEET_APP_LOAD_COMPLETE uievent
- assertThat(logger.get(2).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
- assertThat(logger.get(2).event.getId(),
+
+ // SHARESHEET_APP_LOAD_COMPLETE:
+ assertThat(logger.event(2).getId(),
is(ChooserActivityLogger
.SharesheetStandardEvent.SHARESHEET_APP_LOAD_COMPLETE.getId()));
- // fourth and fifth are just artifacts of test set-up
- // sixth one should be ranking atom with SHARESHEET_EMPTY_DIRECT_SHARE_ROW event
- assertThat(logger.get(5).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
- assertThat(logger.get(5).event.getId(),
+
+ // SHARESHEET_DIRECT_LOAD_COMPLETE:
+ assertThat(logger.event(3).getId(),
+ is(ChooserActivityLogger
+ .SharesheetStandardEvent.SHARESHEET_DIRECT_LOAD_COMPLETE.getId()));
+
+ // SHARESHEET_EMPTY_DIRECT_SHARE_ROW:
+ assertThat(logger.event(4).getId(),
is(ChooserActivityLogger
.SharesheetStandardEvent.SHARESHEET_EMPTY_DIRECT_SHARE_ROW.getId()));
+
+ // Sixth is just an artifact of test set-up:
+ assertThat(logger.event(5).getId(),
+ is(ChooserActivityLogger.SharesheetStandardEvent.SHARESHEET_EXPANDED.getId()));
+
+ assertThat(logger.numCalls(), is(6));
}
- @Test @Ignore
+ @Test
public void testCopyTextToClipboardLogging() throws Exception {
Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
@@ -1920,12 +1976,12 @@
ChooserActivityLoggerFake logger =
(ChooserActivityLoggerFake) activity.getChooserActivityLogger();
- assertThat(logger.numCalls(), is(6));
- // first one should be SHARESHEET_TRIGGERED uievent
- assertThat(logger.get(0).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
- assertThat(logger.get(0).event.getId(),
+
+ // SHARESHEET_TRIGGERED:
+ assertThat(logger.event(0).getId(),
is(ChooserActivityLogger.SharesheetStandardEvent.SHARESHEET_TRIGGERED.getId()));
- // second one should be SHARESHEET_STARTED event
+
+ // SHARESHEET_STARTED:
assertThat(logger.get(1).atomId, is(FrameworkStatsLog.SHARESHEET_STARTED));
assertThat(logger.get(1).intent, is(Intent.ACTION_SEND));
assertThat(logger.get(1).mimeType, is("text/plain"));
@@ -1934,20 +1990,35 @@
assertThat(logger.get(1).appProvidedDirect, is(0));
assertThat(logger.get(1).isWorkprofile, is(false));
assertThat(logger.get(1).previewType, is(3));
- // third one should be SHARESHEET_APP_LOAD_COMPLETE uievent
- assertThat(logger.get(2).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
- assertThat(logger.get(2).event.getId(),
+
+ // SHARESHEET_APP_LOAD_COMPLETE:
+ assertThat(logger.event(2).getId(),
is(ChooserActivityLogger
.SharesheetStandardEvent.SHARESHEET_APP_LOAD_COMPLETE.getId()));
- // fourth and fifth are just artifacts of test set-up
- // sixth one should be ranking atom with SHARESHEET_COPY_TARGET_SELECTED event
- assertThat(logger.get(5).atomId, is(FrameworkStatsLog.RANKING_SELECTED));
- assertThat(logger.get(5).targetType,
+
+ // SHARESHEET_DIRECT_LOAD_COMPLETE:
+ assertThat(logger.event(3).getId(),
+ is(ChooserActivityLogger
+ .SharesheetStandardEvent.SHARESHEET_DIRECT_LOAD_COMPLETE.getId()));
+
+ // Fifth and sixth are just artifacts of test set-up:
+ assertThat(logger.event(4).getId(),
+ is(ChooserActivityLogger
+ .SharesheetStandardEvent.SHARESHEET_EMPTY_DIRECT_SHARE_ROW.getId()));
+ assertThat(logger.event(5).getId(),
+ is(ChooserActivityLogger.SharesheetStandardEvent.SHARESHEET_EXPANDED.getId()));
+
+ // SHARESHEET_EDIT_TARGET_SELECTED:
+ assertThat(logger.get(6).atomId, is(FrameworkStatsLog.RANKING_SELECTED));
+ assertThat(logger.get(6).targetType,
is(ChooserActivityLogger
.SharesheetTargetSelectedEvent.SHARESHEET_COPY_TARGET_SELECTED.getId()));
+
+ // No more events.
+ assertThat(logger.numCalls(), is(7));
}
- @Test @Ignore
+ @Test
public void testSwitchProfileLogging() throws InterruptedException {
// enable the work tab feature flag
ResolverActivity.ENABLE_TABBED_VIEW = true;
@@ -1959,7 +2030,7 @@
createResolvedComponentsForTest(workProfileTargets);
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendTextIntent();
- sendIntent.setType("TestType");
+ sendIntent.setType(TEST_MIME_TYPE);
final ChooserWrapperActivity activity =
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
@@ -1971,42 +2042,65 @@
ChooserActivityLoggerFake logger =
(ChooserActivityLoggerFake) activity.getChooserActivityLogger();
- assertThat(logger.numCalls(), is(8));
- // first one should be SHARESHEET_TRIGGERED uievent
- assertThat(logger.get(0).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
- assertThat(logger.get(0).event.getId(),
+
+ // SHARESHEET_TRIGGERED:
+ assertThat(logger.event(0).getId(),
is(ChooserActivityLogger.SharesheetStandardEvent.SHARESHEET_TRIGGERED.getId()));
- // second one should be SHARESHEET_STARTED event
+
+ // SHARESHEET_STARTED:
assertThat(logger.get(1).atomId, is(FrameworkStatsLog.SHARESHEET_STARTED));
assertThat(logger.get(1).intent, is(Intent.ACTION_SEND));
- assertThat(logger.get(1).mimeType, is("TestType"));
+ assertThat(logger.get(1).mimeType, is(TEST_MIME_TYPE));
assertThat(logger.get(1).packageName, is("com.android.frameworks.coretests"));
assertThat(logger.get(1).appProvidedApp, is(0));
assertThat(logger.get(1).appProvidedDirect, is(0));
assertThat(logger.get(1).isWorkprofile, is(false));
assertThat(logger.get(1).previewType, is(3));
- // third one should be SHARESHEET_APP_LOAD_COMPLETE uievent
- assertThat(logger.get(2).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
- assertThat(logger.get(2).event.getId(),
+
+ // SHARESHEET_APP_LOAD_COMPLETE:
+ assertThat(logger.event(2).getId(),
is(ChooserActivityLogger
.SharesheetStandardEvent.SHARESHEET_APP_LOAD_COMPLETE.getId()));
- // fourth one is artifact of test setup
- // fifth one is switch to work profile
- assertThat(logger.get(4).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
- assertThat(logger.get(4).event.getId(),
+
+ // SHARESHEET_DIRECT_LOAD_COMPLETE:
+ assertThat(logger.event(3).getId(),
is(ChooserActivityLogger
- .SharesheetStandardEvent.SHARESHEET_PROFILE_CHANGED.getId()));
- // sixth one should be SHARESHEET_APP_LOAD_COMPLETE uievent
- assertThat(logger.get(5).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
- assertThat(logger.get(5).event.getId(),
+ .SharesheetStandardEvent.SHARESHEET_DIRECT_LOAD_COMPLETE.getId()));
+
+ // Fifth is just an artifact of test set-up:
+ assertThat(logger.event(4).getId(),
+ is(ChooserActivityLogger
+ .SharesheetStandardEvent.SHARESHEET_EMPTY_DIRECT_SHARE_ROW.getId()));
+
+ // SHARESHEET_PROFILE_CHANGED:
+ assertThat(logger.event(5).getId(),
+ is(ChooserActivityLogger.SharesheetStandardEvent
+ .SHARESHEET_PROFILE_CHANGED.getId()));
+
+ // Repeat the loading steps in the new profile:
+
+ // SHARESHEET_APP_LOAD_COMPLETE:
+ assertThat(logger.event(6).getId(),
is(ChooserActivityLogger
.SharesheetStandardEvent.SHARESHEET_APP_LOAD_COMPLETE.getId()));
- // seventh one is artifact of test setup
- // eigth one is switch to work profile
- assertThat(logger.get(7).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
- assertThat(logger.get(7).event.getId(),
+
+ // SHARESHEET_DIRECT_LOAD_COMPLETE:
+ assertThat(logger.event(7).getId(),
is(ChooserActivityLogger
- .SharesheetStandardEvent.SHARESHEET_PROFILE_CHANGED.getId()));
+ .SharesheetStandardEvent.SHARESHEET_DIRECT_LOAD_COMPLETE.getId()));
+
+ // Ninth is again an artifact of test set-up:
+ assertThat(logger.event(8).getId(),
+ is(ChooserActivityLogger
+ .SharesheetStandardEvent.SHARESHEET_EMPTY_DIRECT_SHARE_ROW.getId()));
+
+ // SHARESHEET_PROFILE_CHANGED:
+ assertThat(logger.event(9).getId(),
+ is(ChooserActivityLogger.SharesheetStandardEvent
+ .SHARESHEET_PROFILE_CHANGED.getId()));
+
+ // No more events (this profile was already loaded).
+ assertThat(logger.numCalls(), is(10));
}
@Test
@@ -2019,7 +2113,7 @@
Mockito.isA(List.class)))
.thenReturn(new ArrayList<>(personalResolvedComponentInfos));
Intent sendIntent = createSendTextIntent();
- sendIntent.setType("TestType");
+ sendIntent.setType(TEST_MIME_TYPE);
ResolveInfo[] chosen = new ResolveInfo[1];
sOverrides.onSafelyStartCallback = targetInfo -> {
chosen[0] = targetInfo.getResolveInfo();
@@ -2043,7 +2137,7 @@
Mockito.isA(List.class)))
.thenReturn(new ArrayList<>(personalResolvedComponentInfos));
Intent sendIntent = createSendTextIntent();
- sendIntent.setType("TestType");
+ sendIntent.setType(TEST_MIME_TYPE);
ResolveInfo[] chosen = new ResolveInfo[1];
sOverrides.onSafelyStartCallback = targetInfo -> {
chosen[0] = targetInfo.getResolveInfo();
@@ -2244,7 +2338,7 @@
return null;
};
Intent sendIntent = createSendTextIntent();
- sendIntent.setType("TestType");
+ sendIntent.setType(TEST_MIME_TYPE);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
waitForIdle();
@@ -2275,7 +2369,7 @@
return null;
};
Intent sendIntent = createSendTextIntent();
- sendIntent.setType("TestType");
+ sendIntent.setType(TEST_MIME_TYPE);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
waitForIdle();
@@ -2299,7 +2393,7 @@
createResolvedComponentsForTest(3);
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendTextIntent();
- sendIntent.setType("TestType");
+ sendIntent.setType(TEST_MIME_TYPE);
sOverrides.isWorkProfileUserRunning = false;
final ChooserWrapperActivity activity =
@@ -2331,7 +2425,7 @@
return null;
};
Intent sendIntent = createSendTextIntent();
- sendIntent.setType("TestType");
+ sendIntent.setType(TEST_MIME_TYPE);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
waitForIdle();
@@ -2355,7 +2449,7 @@
createResolvedComponentsForTest(3);
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendTextIntent();
- sendIntent.setType("TestType");
+ sendIntent.setType(TEST_MIME_TYPE);
sOverrides.isWorkProfileUserUnlocked = false;
final ChooserWrapperActivity activity =
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
index 8ec33bf..c1a45c4 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
@@ -37,7 +37,6 @@
import android.app.ActivityManager;
import android.os.BatteryStats;
import android.util.SparseArray;
-import android.util.SparseIntArray;
import android.view.Display;
import androidx.test.filters.LargeTest;
@@ -53,6 +52,7 @@
@LargeTest
@RunWith(AndroidJUnit4.class)
+@SuppressWarnings("GuardedBy")
public class BatteryStatsImplTest {
private static final long[] CPU_FREQS = {1, 2, 3, 4, 5};
private static final int NUM_CPU_FREQS = CPU_FREQS.length;
@@ -62,29 +62,26 @@
@Mock
private KernelSingleUidTimeReader mKernelSingleUidTimeReader;
+ private final MockClock mMockClock = new MockClock();
private MockBatteryStatsImpl mBatteryStatsImpl;
- private MockClock mMockClock = new MockClock();
-
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ when(mKernelUidCpuFreqTimeReader.isFastCpuTimesReader()).thenReturn(true);
when(mKernelUidCpuFreqTimeReader.readFreqs(any())).thenReturn(CPU_FREQS);
when(mKernelUidCpuFreqTimeReader.allUidTimesAvailable()).thenReturn(true);
when(mKernelSingleUidTimeReader.singleUidCpuTimesAvailable()).thenReturn(true);
mBatteryStatsImpl = new MockBatteryStatsImpl(mMockClock)
.setKernelCpuUidFreqTimeReader(mKernelUidCpuFreqTimeReader)
- .setKernelSingleUidTimeReader(mKernelSingleUidTimeReader)
- .setTrackingCpuByProcStateEnabled(true);
+ .setKernelSingleUidTimeReader(mKernelSingleUidTimeReader);
}
@Test
public void testUpdateProcStateCpuTimes() {
mBatteryStatsImpl.setOnBatteryInternal(true);
- synchronized (mBatteryStatsImpl) {
- mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
- }
+ mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
final int[] testUids = {10032, 10048, 10145, 10139};
final int[] activityManagerProcStates = {
@@ -99,15 +96,24 @@
PROCESS_STATE_TOP,
PROCESS_STATE_CACHED
};
- addPendingUids(testUids, testProcStates);
// Initialize time-in-freq counters
mMockClock.realtime = 1000;
for (int i = 0; i < testUids.length; ++i) {
- mBatteryStatsImpl.noteUidProcessStateLocked(testUids[i], activityManagerProcStates[i]);
mockKernelSingleUidTimeReader(testUids[i], new long[5]);
+ mBatteryStatsImpl.noteUidProcessStateLocked(testUids[i], activityManagerProcStates[i]);
}
- mBatteryStatsImpl.updateProcStateCpuTimes(true, false);
+
+ final long[] timeInFreqs = new long[NUM_CPU_FREQS];
+
+ // Verify there are no cpu times initially.
+ for (int i = 0; i < testUids.length; ++i) {
+ final BatteryStats.Uid u = mBatteryStatsImpl.getUidStatsLocked(testUids[i]);
+ for (int procState = 0; procState < NUM_PROCESS_STATE; ++procState) {
+ assertFalse(u.getCpuFreqTimes(timeInFreqs, procState));
+ assertFalse(u.getScreenOffCpuFreqTimes(timeInFreqs, procState));
+ }
+ }
// Obtain initial CPU time-in-freq counts
final long[][] cpuTimes = {
@@ -117,24 +123,14 @@
{4859048, 348903, 4578967, 5973894, 298549}
};
- final long[] timeInFreqs = new long[NUM_CPU_FREQS];
+ mMockClock.realtime += 1000;
for (int i = 0; i < testUids.length; ++i) {
mockKernelSingleUidTimeReader(testUids[i], cpuTimes[i]);
-
- // Verify there are no cpu times initially.
- final BatteryStats.Uid u = mBatteryStatsImpl.getUidStatsLocked(testUids[i]);
- for (int procState = 0; procState < NUM_PROCESS_STATE; ++procState) {
- assertFalse(u.getCpuFreqTimes(timeInFreqs, procState));
- assertFalse(u.getScreenOffCpuFreqTimes(timeInFreqs, procState));
- }
+ mBatteryStatsImpl.updateProcStateCpuTimesLocked(testUids[i],
+ mMockClock.realtime);
}
- addPendingUids(testUids, testProcStates);
- mMockClock.realtime += 1000;
- mBatteryStatsImpl.updateProcStateCpuTimes(true, false);
-
- verifyNoPendingUids();
for (int i = 0; i < testUids.length; ++i) {
final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
for (int procState = 0; procState < NUM_PROCESS_STATE; ++procState) {
@@ -155,19 +151,19 @@
{945894, 9089432, 19478, 3834, 7845},
{843895, 43948, 949582, 99, 384}
};
+
+ mMockClock.realtime += 1000;
+
for (int i = 0; i < testUids.length; ++i) {
long[] newCpuTimes = new long[cpuTimes[i].length];
for (int j = 0; j < cpuTimes[i].length; j++) {
newCpuTimes[j] = cpuTimes[i][j] + delta1[i][j];
}
mockKernelSingleUidTimeReader(testUids[i], newCpuTimes);
+ mBatteryStatsImpl.updateProcStateCpuTimesLocked(testUids[i],
+ mMockClock.realtime);
}
- addPendingUids(testUids, testProcStates);
- mMockClock.realtime += 1000;
- mBatteryStatsImpl.updateProcStateCpuTimes(true, false);
-
- verifyNoPendingUids();
for (int i = 0; i < testUids.length; ++i) {
final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
for (int procState = 0; procState < NUM_PROCESS_STATE; ++procState) {
@@ -186,10 +182,8 @@
}
// Validate the on-battery-screen-off counter
- synchronized (mBatteryStatsImpl) {
- mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_OFF, 0,
- mMockClock.realtime * 1000);
- }
+ mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_OFF, 0,
+ mMockClock.realtime * 1000);
final long[][] delta2 = {
{95932, 2943, 49834, 89034, 139},
@@ -197,19 +191,19 @@
{678, 7498, 9843, 889, 4894},
{488, 998, 8498, 394, 574}
};
+
+ mMockClock.realtime += 1000;
+
for (int i = 0; i < testUids.length; ++i) {
long[] newCpuTimes = new long[cpuTimes[i].length];
for (int j = 0; j < cpuTimes[i].length; j++) {
newCpuTimes[j] = cpuTimes[i][j] + delta1[i][j] + delta2[i][j];
}
mockKernelSingleUidTimeReader(testUids[i], newCpuTimes);
+ mBatteryStatsImpl.updateProcStateCpuTimesLocked(testUids[i],
+ mMockClock.realtime);
}
- addPendingUids(testUids, testProcStates);
- mMockClock.realtime += 1000;
- mBatteryStatsImpl.updateProcStateCpuTimes(true, true);
-
- verifyNoPendingUids();
for (int i = 0; i < testUids.length; ++i) {
final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
for (int procState = 0; procState < NUM_PROCESS_STATE; ++procState) {
@@ -239,24 +233,25 @@
{3049509483598l, 4597834, 377654, 94589035, 7854},
{9493, 784, 99895, 8974893, 9879843}
};
- for (int i = 0; i < testUids.length; ++i) {
- long[] newCpuTimes = new long[cpuTimes[i].length];
- for (int j = 0; j < cpuTimes[i].length; j++) {
- newCpuTimes[j] = cpuTimes[i][j] + delta1[i][j] + delta2[i][j] + delta3[i][j];
- }
- mockKernelSingleUidTimeReader(testUids[i], newCpuTimes);
- }
- addPendingUids(testUids, testProcStates);
+
+ mMockClock.realtime += 1000;
+
final int parentUid = testUids[1];
final int childUid = 99099;
addIsolatedUid(parentUid, childUid);
final long[] isolatedUidCpuTimes = {495784, 398473, 4895, 4905, 30984093};
mockKernelSingleUidTimeReader(childUid, isolatedUidCpuTimes, isolatedUidCpuTimes);
- mMockClock.realtime += 1000;
- mBatteryStatsImpl.updateProcStateCpuTimes(true, true);
+ for (int i = 0; i < testUids.length; ++i) {
+ long[] newCpuTimes = new long[cpuTimes[i].length];
+ for (int j = 0; j < cpuTimes[i].length; j++) {
+ newCpuTimes[j] = cpuTimes[i][j] + delta1[i][j] + delta2[i][j] + delta3[i][j];
+ }
+ mockKernelSingleUidTimeReader(testUids[i], newCpuTimes);
+ mBatteryStatsImpl.updateProcStateCpuTimesLocked(testUids[i],
+ mMockClock.realtime);
+ }
- verifyNoPendingUids();
for (int i = 0; i < testUids.length; ++i) {
final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
for (int procState = 0; procState < NUM_PROCESS_STATE; ++procState) {
@@ -284,11 +279,9 @@
}
@Test
- public void testCopyFromAllUidsCpuTimes() {
+ public void testUpdateCpuTimesForAllUids() {
mBatteryStatsImpl.setOnBatteryInternal(false);
- synchronized (mBatteryStatsImpl) {
- mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
- }
+ mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
mMockClock.realtime = 1000;
@@ -299,14 +292,14 @@
PROCESS_STATE_TOP,
PROCESS_STATE_CACHED
};
- addPendingUids(testUids, testProcStates);
for (int i = 0; i < testUids.length; ++i) {
BatteryStatsImpl.Uid uid = mBatteryStatsImpl.getUidStatsLocked(testUids[i]);
uid.setProcessStateForTest(testProcStates[i], mMockClock.elapsedRealtime());
mockKernelSingleUidTimeReader(testUids[i], new long[NUM_CPU_FREQS]);
+ mBatteryStatsImpl.updateProcStateCpuTimesLocked(testUids[i],
+ mMockClock.elapsedRealtime());
}
- mBatteryStatsImpl.updateProcStateCpuTimes(true, false);
final SparseArray<long[]> allUidCpuTimes = new SparseArray<>();
long[][] allCpuTimes = {
@@ -330,9 +323,8 @@
}
mMockClock.realtime += 1000;
- mBatteryStatsImpl.copyFromAllUidsCpuTimes(true, false);
- verifyNoPendingUids();
+ mBatteryStatsImpl.updateCpuTimesForAllUids();
final long[] timeInFreqs = new long[NUM_CPU_FREQS];
@@ -411,9 +403,7 @@
final int releaseTimeMs = 1005;
final int currentTimeMs = 1011;
- synchronized (mBatteryStatsImpl) {
- mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
- }
+ mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
// Create a Uid Object
final BatteryStats.Uid u = mBatteryStatsImpl.getUidStatsLocked(testUid);
@@ -436,9 +426,7 @@
final int acquireTimeMs = 1000;
final int currentTimeMs = 1011;
- synchronized (mBatteryStatsImpl) {
- mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
- }
+ mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
// Create a Uid Object
final BatteryStats.Uid u = mBatteryStatsImpl.getUidStatsLocked(testUid);
@@ -464,9 +452,7 @@
final int releaseTimeMs_2 = 1009;
final int currentTimeMs = 1011;
- synchronized (mBatteryStatsImpl) {
- mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
- }
+ mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
// Create a Uid Object
final BatteryStats.Uid u = mBatteryStatsImpl.getUidStatsLocked(testUid);
@@ -496,9 +482,7 @@
final int releaseTimeMs_2 = 1009;
final int currentTimeMs = 1011;
- synchronized (mBatteryStatsImpl) {
- mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
- }
+ mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
// Create a Uid Object
final BatteryStats.Uid u = mBatteryStatsImpl.getUidStatsLocked(testUid);
@@ -523,17 +507,4 @@
final BatteryStatsImpl.Uid u = mBatteryStatsImpl.getUidStatsLocked(parentUid);
u.addIsolatedUid(childUid);
}
-
- private void addPendingUids(int[] uids, int[] procStates) {
- final SparseIntArray pendingUids = mBatteryStatsImpl.getPendingUids();
- for (int i = 0; i < uids.length; ++i) {
- pendingUids.put(uids[i], procStates[i]);
- }
- }
-
- private void verifyNoPendingUids() {
- final SparseIntArray pendingUids = mBatteryStatsImpl.getPendingUids();
- assertEquals("There shouldn't be any pending uids left: " + pendingUids,
- 0, pendingUids.size());
- }
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java b/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java
index 441e85d..9172d34 100644
--- a/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java
@@ -26,9 +26,6 @@
import static android.os.BatteryStats.Uid.PROCESS_STATE_TOP_SLEEPING;
import static android.os.BatteryStats.Uid.UID_PROCESS_TYPES;
-import static com.android.internal.os.BatteryStatsImpl.Constants.KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS;
-import static com.android.internal.os.BatteryStatsImpl.Constants.KEY_TRACK_CPU_TIMES_BY_PROC_STATE;
-
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
@@ -51,7 +48,6 @@
import android.os.SystemClock;
import android.provider.Settings;
import android.support.test.uiautomator.UiDevice;
-import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.DebugUtils;
import android.util.KeyValueListParser;
@@ -125,11 +121,6 @@
PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
sTestPkgUid = sContext.getPackageManager().getPackageUid(TEST_PKG, 0);
executeCmd("cmd deviceidle whitelist +" + TEST_PKG);
-
- final ArrayMap<String, String> desiredConstants = new ArrayMap<>();
- desiredConstants.put(KEY_TRACK_CPU_TIMES_BY_PROC_STATE, Boolean.toString(true));
- desiredConstants.put(KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS, Integer.toString(0));
- updateBatteryStatsConstants(desiredConstants);
checkCpuTimesAvailability();
}
@@ -517,125 +508,6 @@
batteryOffScreenOn();
}
- @Test
- public void testCpuFreqTimes_trackingDisabled() throws Exception {
- if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) {
- Log.w(TAG, "Skipping " + testName.getMethodName()
- + "; freqTimesAvailable=" + sCpuFreqTimesAvailable
- + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable);
- return;
- }
-
- final String bstatsConstants = Settings.Global.getString(sContext.getContentResolver(),
- Settings.Global.BATTERY_STATS_CONSTANTS);
- try {
- batteryOnScreenOn();
- forceStop();
- resetBatteryStats();
- final long[] initialSnapshot = getAllCpuFreqTimes(sTestPkgUid);
- assertNull("Initial snapshot should be null, initial="
- + Arrays.toString(initialSnapshot), initialSnapshot);
- assertNull("Initial top state snapshot should be null",
- getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP));
-
- doSomeWork(PROCESS_STATE_TOP);
- forceStop();
-
- final long[] cpuTimesMs = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP);
- final String msgCpuTimes = getAllCpuTimesMsg();
- assertCpuTimesValid(cpuTimesMs);
- long actualCpuTimeMs = 0;
- for (int i = 0; i < cpuTimesMs.length / 2; ++i) {
- actualCpuTimeMs += cpuTimesMs[i];
- }
- assertApproximateValue("Incorrect total cpu time, " + msgCpuTimes,
- WORK_DURATION_MS, actualCpuTimeMs);
-
- updateTrackPerProcStateCpuTimesSetting(bstatsConstants, false);
-
- doSomeWork(PROCESS_STATE_TOP);
- forceStop();
-
- final long[] cpuTimesMs2 = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP);
- assertCpuTimesValid(cpuTimesMs2);
- assertCpuTimesEqual(cpuTimesMs2, cpuTimesMs, 20,
- "Unexpected cpu times with tracking off");
-
- updateTrackPerProcStateCpuTimesSetting(bstatsConstants, true);
-
- final long[] cpuTimesMs3 = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP);
- assertCpuTimesValid(cpuTimesMs3);
- assertCpuTimesEqual(cpuTimesMs3, cpuTimesMs, 500,
- "Unexpected cpu times after turning on tracking");
-
- doSomeWork(PROCESS_STATE_TOP);
- forceStop();
-
- final long[] cpuTimesMs4 = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP);
- assertCpuTimesValid(cpuTimesMs4);
- actualCpuTimeMs = 0;
- for (int i = 0; i < cpuTimesMs4.length / 2; ++i) {
- actualCpuTimeMs += cpuTimesMs4[i];
- }
- assertApproximateValue("Incorrect total cpu time, " + msgCpuTimes,
- 2 * WORK_DURATION_MS, actualCpuTimeMs);
-
- batteryOffScreenOn();
- } finally {
- Settings.Global.putString(sContext.getContentResolver(),
- Settings.Global.BATTERY_STATS_CONSTANTS, bstatsConstants);
- }
- }
-
- private void assertCpuTimesEqual(long[] actual, long[] expected, long delta, String errMsg) {
- for (int i = actual.length - 1; i >= 0; --i) {
- if (actual[i] > expected[i] + delta || actual[i] < expected[i]) {
- fail(errMsg + ", actual=" + Arrays.toString(actual)
- + ", expected=" + Arrays.toString(expected) + ", delta=" + delta);
- }
- }
- }
-
- private void updateTrackPerProcStateCpuTimesSetting(String originalConstants, boolean enabled)
- throws Exception {
- final String newConstants;
- final String setting = KEY_TRACK_CPU_TIMES_BY_PROC_STATE + "=" + enabled;
- if (originalConstants == null || "null".equals(originalConstants)) {
- newConstants = setting;
- } else if (originalConstants.contains(KEY_TRACK_CPU_TIMES_BY_PROC_STATE)) {
- newConstants = originalConstants.replaceAll(
- KEY_TRACK_CPU_TIMES_BY_PROC_STATE + "=(true|false)", setting);
- } else {
- newConstants = originalConstants + "," + setting;
- }
- Settings.Global.putString(sContext.getContentResolver(),
- Settings.Global.BATTERY_STATS_CONSTANTS, newConstants);
- assertTrackPerProcStateCpuTimesSetting(enabled);
- }
-
- private void assertTrackPerProcStateCpuTimesSetting(boolean enabled) throws Exception {
- final String expectedValue = Boolean.toString(enabled);
- assertDelayedCondition("Unexpected value for " + KEY_TRACK_CPU_TIMES_BY_PROC_STATE, () -> {
- final String actualValue = getSettingValueFromDump(KEY_TRACK_CPU_TIMES_BY_PROC_STATE);
- return expectedValue.equals(actualValue)
- ? null : "expected=" + expectedValue + ", actual=" + actualValue;
- }, SETTING_UPDATE_TIMEOUT_MS, SETTING_UPDATE_CHECK_INTERVAL_MS);
- }
-
- private String getSettingValueFromDump(String key) throws Exception {
- final String settingsDump = executeCmdSilent("dumpsys batterystats --settings");
- final TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter('\n');
- splitter.setString(settingsDump);
- String next;
- while (splitter.hasNext()) {
- next = splitter.next().trim();
- if (next.startsWith(key)) {
- return next.split("=")[1];
- }
- }
- return null;
- }
-
private void assertCpuTimesValid(long[] cpuTimes) {
assertNotNull(cpuTimes);
for (int i = 0; i < cpuTimes.length; ++i) {
diff --git a/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java
index 1ae30db..d5b0f0a 100644
--- a/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java
@@ -98,6 +98,8 @@
new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS];
supportedPowerBuckets[MeasuredEnergyStats.POWER_BUCKET_CPU] = true;
+ when(mMockCpuUidFreqTimeReader.isFastCpuTimesReader()).thenReturn(true);
+
mStatsRule.getBatteryStats()
.setUserInfoProvider(mMockUserInfoProvider)
.setKernelCpuSpeedReaders(mMockKernelCpuSpeedReaders)
@@ -277,8 +279,6 @@
@Test
public void testTimerBasedModel_byProcessState() {
- mStatsRule.getBatteryStats().setTrackingCpuByProcStateEnabled(true);
-
when(mMockUserInfoProvider.exists(anyInt())).thenReturn(true);
when(mMockCpuUidFreqTimeReader.allUidTimesAvailable()).thenReturn(true);
@@ -311,7 +311,7 @@
}).when(mMockKerneCpuUidActiveTimeReader).readAbsolute(any());
mStatsRule.getBatteryStats().updateCpuTimeLocked(true, true, null);
- mStatsRule.getBatteryStats().copyFromAllUidsCpuTimes(true, true);
+ mStatsRule.getBatteryStats().updateCpuTimesForAllUids();
mockSingleUidTimeReader(APP_UID1, new long[]{1000, 2000, 3000, 4000});
mockSingleUidTimeReader(APP_UID2, new long[]{1111, 2222, 3333, 4444});
@@ -326,7 +326,7 @@
}).when(mMockKerneCpuUidActiveTimeReader).readAbsolute(any());
mStatsRule.getBatteryStats().updateCpuTimeLocked(true, true, null);
- mStatsRule.getBatteryStats().copyFromAllUidsCpuTimes(true, true);
+ mStatsRule.getBatteryStats().updateCpuTimesForAllUids();
mockSingleUidTimeReader(APP_UID1, new long[] {5000, 6000, 7000, 8000});
mockSingleUidTimeReader(APP_UID2, new long[]{5555, 6666, 7777, 8888});
@@ -346,7 +346,7 @@
}).when(mMockKerneCpuUidActiveTimeReader).readAbsolute(any());
mStatsRule.getBatteryStats().updateCpuTimeLocked(true, true, null);
- mStatsRule.getBatteryStats().copyFromAllUidsCpuTimes(true, true);
+ mStatsRule.getBatteryStats().updateCpuTimesForAllUids();
CpuPowerCalculator calculator =
new CpuPowerCalculator(mStatsRule.getPowerProfile());
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index d16689c..e4c83f1 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -16,10 +16,13 @@
package com.android.internal.os;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
import android.net.NetworkStats;
import android.os.Handler;
import android.os.Looper;
-import android.util.SparseIntArray;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader;
@@ -59,6 +62,9 @@
// A no-op handler.
mHandler = new Handler(Looper.getMainLooper()) {
};
+
+ mCpuUidFreqTimeReader = mock(KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader.class);
+ when(mCpuUidFreqTimeReader.readFreqs(any())).thenReturn(new long[]{100, 200});
}
public void initMeasuredEnergyStats(String[] customBucketNames) {
@@ -178,15 +184,6 @@
return this;
}
- public MockBatteryStatsImpl setTrackingCpuByProcStateEnabled(boolean enabled) {
- mConstants.TRACK_CPU_TIMES_BY_PROC_STATE = enabled;
- return this;
- }
-
- public SparseIntArray getPendingUids() {
- return mPendingUids;
- }
-
public int getAndClearExternalStatsSyncFlags() {
final int flags = mExternalStatsSync.flags;
mExternalStatsSync.flags = 0;
@@ -217,18 +214,6 @@
}
@Override
- public Future<?> scheduleReadProcStateCpuTimes(boolean onBattery,
- boolean onBatteryScreenOff, long delayMillis) {
- return null;
- }
-
- @Override
- public Future<?> scheduleCopyFromAllUidsCpuTimes(
- boolean onBattery, boolean onBatteryScreenOff) {
- return null;
- }
-
- @Override
public Future<?> scheduleSyncDueToScreenStateChange(int flag, boolean onBattery,
boolean onBatteryScreenOff, int screenState, int[] perDisplayScreenStates) {
flags |= flag;
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 60cb9d3..81db63e 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -519,6 +519,9 @@
<permission name="android.permission.LOCK_DEVICE" />
<!-- Permission required for CTS test - CtsSafetyCenterTestCases -->
<permission name="android.permission.SEND_SAFETY_CENTER_UPDATE" />
+ <!-- Permission required for CTS test - CommunalManagerTest -->
+ <permission name="android.permission.WRITE_COMMUNAL_STATE" />
+ <permission name="android.permission.READ_COMMUNAL_STATE" />
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/RequiresPermissionChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/RequiresPermissionChecker.java
index 9a41cb4..fa173072 100644
--- a/errorprone/java/com/google/errorprone/bugpatterns/android/RequiresPermissionChecker.java
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/RequiresPermissionChecker.java
@@ -189,7 +189,10 @@
if (!actualPerm.containsAll(expectedPerm)) {
return buildDescription(tree)
.setMessage("Method " + method.name.toString() + "() annotated " + expectedPerm
- + " but too wide; only invokes methods requiring " + actualPerm)
+ + " but too wide; only invokes methods requiring " + actualPerm
+ + "\n If calling an AIDL interface, it can be annotated by adding:"
+ + "\n @JavaPassthrough(annotation=\""
+ + "@android.annotation.RequiresPermission(...)\")")
.build();
}
diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java
index 8844370..8d3eadb 100644
--- a/graphics/java/android/graphics/BLASTBufferQueue.java
+++ b/graphics/java/android/graphics/BLASTBufferQueue.java
@@ -27,8 +27,9 @@
// Note: This field is accessed by native code.
public long mNativeObject; // BLASTBufferQueue*
- private static native long nativeCreate(String name, long surfaceControl, long width,
+ private static native long nativeCreateAndUpdate(String name, long surfaceControl, long width,
long height, int format);
+ private static native long nativeCreate(String name);
private static native void nativeDestroy(long ptr);
private static native Surface nativeGetSurface(long ptr, boolean includeSurfaceControlHandle);
private static native void nativeSetSyncTransaction(long ptr, long transactionPtr,
@@ -43,7 +44,11 @@
/** Create a new connection with the surface flinger. */
public BLASTBufferQueue(String name, SurfaceControl sc, int width, int height,
@PixelFormat.Format int format) {
- mNativeObject = nativeCreate(name, sc.mNativeObject, width, height, format);
+ mNativeObject = nativeCreateAndUpdate(name, sc.mNativeObject, width, height, format);
+ }
+
+ public BLASTBufferQueue(String name) {
+ mNativeObject = nativeCreate(name);
}
public void destroy() {
diff --git a/libs/WindowManager/Shell/res/color/tv_pip_menu_icon.xml b/libs/WindowManager/Shell/res/color/tv_pip_menu_icon.xml
new file mode 100644
index 0000000..2758704
--- /dev/null
+++ b/libs/WindowManager/Shell/res/color/tv_pip_menu_icon.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_focused="true"
+ android:color="@color/tv_pip_menu_icon_focused" />
+ <item android:state_enabled="false"
+ android:color="@color/tv_pip_menu_icon_disabled" />
+ <item android:color="@color/tv_pip_menu_icon_unfocused" />
+</selector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/color/tv_pip_menu_icon_bg.xml b/libs/WindowManager/Shell/res/color/tv_pip_menu_icon_bg.xml
new file mode 100644
index 0000000..4f5e63d
--- /dev/null
+++ b/libs/WindowManager/Shell/res/color/tv_pip_menu_icon_bg.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_focused="true"
+ android:color="@color/tv_pip_menu_icon_bg_focused" />
+ <item android:color="@color/tv_pip_menu_icon_bg_unfocused" />
+</selector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/tv_pip_button_focused.xml b/libs/WindowManager/Shell/res/drawable/tv_pip_button_bg.xml
similarity index 74%
rename from libs/WindowManager/Shell/res/drawable/tv_pip_button_focused.xml
rename to libs/WindowManager/Shell/res/drawable/tv_pip_button_bg.xml
index cce1303..1938f45 100644
--- a/libs/WindowManager/Shell/res/drawable/tv_pip_button_focused.xml
+++ b/libs/WindowManager/Shell/res/drawable/tv_pip_button_bg.xml
@@ -14,5 +14,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="#9AFFFFFF" android:radius="17dp" />
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <corners android:radius="@dimen/pip_menu_button_radius" />
+ <solid android:color="@color/tv_pip_menu_icon_bg" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/screenrecord_button_background_outline.xml b/libs/WindowManager/Shell/res/drawable/tv_pip_menu_border.xml
similarity index 60%
rename from packages/SystemUI/res/drawable/screenrecord_button_background_outline.xml
rename to libs/WindowManager/Shell/res/drawable/tv_pip_menu_border.xml
index 59a31e8..9bc0311 100644
--- a/packages/SystemUI/res/drawable/screenrecord_button_background_outline.xml
+++ b/libs/WindowManager/Shell/res/drawable/tv_pip_menu_border.xml
@@ -12,19 +12,11 @@
~ 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
+ ~ limitations under the License.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- android:shape="rectangle">
- <stroke
- android:color="?androidprv:attr/colorAccentPrimary"
- android:width="1dp"/>
- <corners android:radius="24dp"/>
- <padding
- android:left="16dp"
- android:right="16dp"
- android:top="8dp"
- android:bottom="8dp" />
- <solid android:color="@android:color/transparent" />
-</shape>
+ android:shape="rectangle">
+ <corners android:radius="@dimen/pip_menu_border_radius" />
+ <stroke android:width="@dimen/pip_menu_border_width"
+ android:color="@color/tv_pip_menu_focus_border" />
+</shape>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
index 49e2379..5b90c99 100644
--- a/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
@@ -18,35 +18,54 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/tv_pip_menu"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="#CC000000">
+ android:layout_height="match_parent">
- <LinearLayout
- android:id="@+id/tv_pip_menu_action_buttons"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:layout_marginTop="350dp"
- android:orientation="horizontal"
- android:alpha="0">
+ <FrameLayout
+ android:id="@+id/tv_pip_menu_frame"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:alpha="0" >
- <com.android.wm.shell.pip.tv.TvPipMenuActionButton
- android:id="@+id/tv_pip_menu_fullscreen_button"
- android:layout_width="@dimen/picture_in_picture_button_width"
- android:layout_height="wrap_content"
- android:src="@drawable/pip_ic_fullscreen_white"
- android:text="@string/pip_fullscreen" />
+ <HorizontalScrollView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_centerHorizontal="true"
+ android:gravity="center"
+ android:scrollbars="none"
+ android:requiresFadingEdge="vertical"
+ android:fadingEdgeLength="30dp">
- <com.android.wm.shell.pip.tv.TvPipMenuActionButton
- android:id="@+id/tv_pip_menu_close_button"
- android:layout_width="@dimen/picture_in_picture_button_width"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/picture_in_picture_button_start_margin"
- android:src="@drawable/pip_ic_close_white"
- android:text="@string/pip_close" />
+ <LinearLayout
+ android:id="@+id/tv_pip_menu_action_buttons"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingStart="@dimen/pip_menu_button_wrapper_margin"
+ android:paddingEnd="@dimen/pip_menu_button_wrapper_margin"
+ android:gravity="center"
+ android:orientation="horizontal">
- <!-- More TvPipMenuActionButtons may be added here at runtime. -->
+ <com.android.wm.shell.pip.tv.TvPipMenuActionButton
+ android:id="@+id/tv_pip_menu_fullscreen_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/pip_ic_fullscreen_white"
+ android:text="@string/pip_fullscreen" />
- </LinearLayout>
+ <com.android.wm.shell.pip.tv.TvPipMenuActionButton
+ android:id="@+id/tv_pip_menu_close_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/pip_ic_close_white"
+ android:text="@string/pip_close" />
+ <!-- More TvPipMenuActionButtons may be added here at runtime. -->
+
+ </LinearLayout>
+ </HorizontalScrollView>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/tv_pip_menu_border"/>
+ </FrameLayout>
</FrameLayout>
diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_menu_action_button.xml b/libs/WindowManager/Shell/res/layout/tv_pip_menu_action_button.xml
index 5925008..f9d0968 100644
--- a/libs/WindowManager/Shell/res/layout/tv_pip_menu_action_button.xml
+++ b/libs/WindowManager/Shell/res/layout/tv_pip_menu_action_button.xml
@@ -15,36 +15,20 @@
limitations under the License.
-->
<!-- Layout for TvPipMenuActionButton -->
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/button"
+ android:layout_width="@dimen/pip_menu_button_size"
+ android:layout_height="@dimen/pip_menu_button_size"
+ android:layout_marginStart="@dimen/pip_menu_button_margin"
+ android:layout_marginEnd="@dimen/pip_menu_button_margin"
+ android:background="@drawable/tv_pip_button_bg"
+ android:focusable="true">
- <ImageView android:id="@+id/button"
- android:layout_width="34dp"
- android:layout_height="34dp"
- android:layout_alignParentTop="true"
- android:layout_centerHorizontal="true"
- android:focusable="true"
- android:src="@drawable/tv_pip_button_focused"
- android:importantForAccessibility="yes" />
-
- <ImageView android:id="@+id/icon"
- android:layout_width="34dp"
- android:layout_height="34dp"
- android:layout_alignParentTop="true"
- android:layout_centerHorizontal="true"
- android:padding="5dp"
- android:importantForAccessibility="no" />
-
- <TextView android:id="@+id/desc"
- android:layout_width="100dp"
- android:layout_height="wrap_content"
- android:layout_below="@id/icon"
- android:layout_centerHorizontal="true"
- android:layout_marginTop="3dp"
- android:gravity="center"
- android:text="@string/pip_fullscreen"
- android:alpha="0"
- android:fontFamily="sans-serif"
- android:textSize="12sp"
- android:textColor="#EEEEEE"
- android:importantForAccessibility="no" />
-</merge>
+ <ImageView android:id="@+id/icon"
+ android:layout_width="@dimen/pip_menu_icon_size"
+ android:layout_height="@dimen/pip_menu_icon_size"
+ android:layout_gravity="center"
+ android:duplicateParentState="true"
+ android:tint="@color/tv_pip_menu_icon" />
+</FrameLayout>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_menu_additional_action_button.xml b/libs/WindowManager/Shell/res/layout/tv_pip_menu_additional_action_button.xml
deleted file mode 100644
index bf4eb26..0000000
--- a/libs/WindowManager/Shell/res/layout/tv_pip_menu_additional_action_button.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2020 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<com.android.wm.shell.pip.tv.TvPipMenuActionButton
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="@dimen/picture_in_picture_button_width"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/picture_in_picture_button_start_margin" />
diff --git a/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml b/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml
index 7920fd2..e41ebc4 100644
--- a/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml
+++ b/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml
@@ -16,7 +16,12 @@
-->
<resources>
<!-- The dimensions to user for picture-in-picture action buttons. -->
- <dimen name="picture_in_picture_button_width">100dp</dimen>
- <dimen name="picture_in_picture_button_start_margin">-50dp</dimen>
+ <dimen name="pip_menu_button_size">40dp</dimen>
+ <dimen name="pip_menu_button_radius">20dp</dimen>
+ <dimen name="pip_menu_icon_size">20dp</dimen>
+ <dimen name="pip_menu_button_margin">4dp</dimen>
+ <dimen name="pip_menu_button_wrapper_margin">26dp</dimen>
+ <dimen name="pip_menu_border_width">2dp</dimen>
+ <dimen name="pip_menu_border_radius">0dp</dimen>
</resources>
diff --git a/libs/WindowManager/Shell/res/values/colors_tv.xml b/libs/WindowManager/Shell/res/values/colors_tv.xml
new file mode 100644
index 0000000..17387fa
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values/colors_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<resources>
+ <color name="tv_pip_menu_icon_focused">#0E0E0F</color>
+ <color name="tv_pip_menu_icon_unfocused">#E8EAED</color>
+ <color name="tv_pip_menu_icon_disabled">#80868B</color>
+ <color name="tv_pip_menu_icon_bg_focused">#E8EAED</color>
+ <color name="tv_pip_menu_icon_bg_unfocused">#777777</color>
+ <color name="tv_pip_menu_focus_border">#CCE8EAED</color>
+</resources>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
index 8467cc5..92a3598 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
@@ -122,7 +122,7 @@
if (mTargetViewContainer != null) {
// init can be called multiple times, remove the old one from view hierarchy first.
- mWindowManager.removeViewImmediate(mTargetViewContainer);
+ cleanUpDismissTarget();
}
mTargetView = new DismissCircleView(mContext);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index 83390a5..b165706 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -253,9 +253,6 @@
final Rect newBounds;
switch (mState) {
case STATE_PIP_MENU:
- newBounds = mPipBoundsState.getExpandedBounds();
- break;
-
case STATE_PIP:
// Let PipBoundsAlgorithm figure out what the correct bounds are at the moment.
// Internally, it will get the "default" bounds from PipBoundsState and adjust them
@@ -336,11 +333,6 @@
private void loadConfigurations() {
final Resources res = mContext.getResources();
mResizeAnimationDuration = res.getInteger(R.integer.config_pipResizeAnimationDuration);
- // "Cache" bounds for the Pip menu as "expanded" bounds in PipBoundsState. We'll refer back
- // to this value in resizePinnedStack(), when we are adjusting Pip task/window position for
- // the menu.
- mPipBoundsState.setExpandedBounds(
- Rect.unflattenFromString(res.getString(R.string.pip_menu_bounds)));
}
private DisplayInfo getDisplayInfo() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuActionButton.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuActionButton.java
index 6f7cd82..bda685e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuActionButton.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuActionButton.java
@@ -16,8 +16,6 @@
package com.android.wm.shell.pip.tv;
-import android.animation.Animator;
-import android.animation.AnimatorInflater;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
@@ -26,7 +24,6 @@
import android.view.View;
import android.widget.ImageView;
import android.widget.RelativeLayout;
-import android.widget.TextView;
import com.android.wm.shell.R;
@@ -36,12 +33,7 @@
*/
public class TvPipMenuActionButton extends RelativeLayout implements View.OnClickListener {
private final ImageView mIconImageView;
- private final ImageView mButtonImageView;
- private final TextView mDescriptionTextView;
- private Animator mTextFocusGainAnimator;
- private Animator mButtonFocusGainAnimator;
- private Animator mTextFocusLossAnimator;
- private Animator mButtonFocusLossAnimator;
+ private final View mButtonView;
private OnClickListener mOnClickListener;
public TvPipMenuActionButton(Context context) {
@@ -64,8 +56,7 @@
inflater.inflate(R.layout.tv_pip_menu_action_button, this);
mIconImageView = findViewById(R.id.icon);
- mButtonImageView = findViewById(R.id.button);
- mDescriptionTextView = findViewById(R.id.desc);
+ mButtonView = findViewById(R.id.button);
final int[] values = new int[]{android.R.attr.src, android.R.attr.text};
final TypedArray typedArray = context.obtainStyledAttributes(attrs, values, defStyleAttr,
@@ -76,43 +67,16 @@
if (textResId != 0) {
setTextAndDescription(getContext().getString(textResId));
}
-
typedArray.recycle();
}
@Override
- public void onFinishInflate() {
- super.onFinishInflate();
- mButtonImageView.setOnFocusChangeListener((v, hasFocus) -> {
- if (hasFocus) {
- startFocusGainAnimation();
- } else {
- startFocusLossAnimation();
- }
- });
-
- mTextFocusGainAnimator = AnimatorInflater.loadAnimator(getContext(),
- R.anim.tv_pip_controls_focus_gain_animation);
- mTextFocusGainAnimator.setTarget(mDescriptionTextView);
- mButtonFocusGainAnimator = AnimatorInflater.loadAnimator(getContext(),
- R.anim.tv_pip_controls_focus_gain_animation);
- mButtonFocusGainAnimator.setTarget(mButtonImageView);
-
- mTextFocusLossAnimator = AnimatorInflater.loadAnimator(getContext(),
- R.anim.tv_pip_controls_focus_loss_animation);
- mTextFocusLossAnimator.setTarget(mDescriptionTextView);
- mButtonFocusLossAnimator = AnimatorInflater.loadAnimator(getContext(),
- R.anim.tv_pip_controls_focus_loss_animation);
- mButtonFocusLossAnimator.setTarget(mButtonImageView);
- }
-
- @Override
public void setOnClickListener(OnClickListener listener) {
// We do not want to set an OnClickListener to the TvPipMenuActionButton itself, but only to
// the ImageView. So let's "cash" the listener we've been passed here and set a "proxy"
// listener to the ImageView.
mOnClickListener = listener;
- mButtonImageView.setOnClickListener(listener != null ? this : null);
+ mButtonView.setOnClickListener(listener != null ? this : null);
}
@Override
@@ -143,55 +107,16 @@
* Sets the text for description the with the given string.
*/
public void setTextAndDescription(CharSequence text) {
- mButtonImageView.setContentDescription(text);
- mDescriptionTextView.setText(text);
+ mButtonView.setContentDescription(text);
}
- private static void cancelAnimator(Animator animator) {
- if (animator.isStarted()) {
- animator.cancel();
- }
+ @Override
+ public void setEnabled(boolean enabled) {
+ mButtonView.setEnabled(enabled);
}
- /**
- * Starts the focus gain animation.
- */
- public void startFocusGainAnimation() {
- cancelAnimator(mButtonFocusLossAnimator);
- cancelAnimator(mTextFocusLossAnimator);
- mTextFocusGainAnimator.start();
- if (mButtonImageView.getAlpha() < 1f) {
- // If we had faded out the ripple drawable, run our manual focus change animation.
- // See the comment at {@link #startFocusLossAnimation()} for the reason of manual
- // animator.
- mButtonFocusGainAnimator.start();
- }
- }
-
- /**
- * Starts the focus loss animation.
- */
- public void startFocusLossAnimation() {
- cancelAnimator(mButtonFocusGainAnimator);
- cancelAnimator(mTextFocusGainAnimator);
- mTextFocusLossAnimator.start();
- if (mButtonImageView.hasFocus()) {
- // Button uses ripple that has the default animation for the focus changes.
- // However, it doesn't expose the API to fade out while it is focused, so we should
- // manually run the fade out animation when PIP controls row loses focus.
- mButtonFocusLossAnimator.start();
- }
- }
-
- /**
- * Resets to initial state.
- */
- public void reset() {
- cancelAnimator(mButtonFocusGainAnimator);
- cancelAnimator(mTextFocusGainAnimator);
- cancelAnimator(mButtonFocusLossAnimator);
- cancelAnimator(mTextFocusLossAnimator);
- mButtonImageView.setAlpha(1f);
- mDescriptionTextView.setAlpha(mButtonImageView.hasFocus() ? 1f : 0f);
+ @Override
+ public boolean isEnabled() {
+ return mButtonView.isEnabled();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
index ee41b41..77bfa07 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
@@ -24,6 +24,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ParceledListSlice;
+import android.graphics.Rect;
import android.os.Handler;
import android.util.Log;
import android.view.SurfaceControl;
@@ -122,19 +123,19 @@
if (DEBUG) Log.d(TAG, "showMenu()");
if (mMenuView != null) {
- mSystemWindows.updateViewLayout(mMenuView, getPipMenuLayoutParams(MENU_WINDOW_TITLE,
- mPipBoundsState.getDisplayBounds().width(),
- mPipBoundsState.getDisplayBounds().height()));
+ Rect pipBounds = mPipBoundsState.getBounds();
+ mSystemWindows.updateViewLayout(mMenuView, getPipMenuLayoutParams(
+ MENU_WINDOW_TITLE, pipBounds.width(), pipBounds.height()));
maybeUpdateMenuViewActions();
- mMenuView.show();
- // By default, SystemWindows views are above everything else.
- // Set the relative z-order so the menu is below PiP.
- if (mMenuView.getWindowSurfaceControl() != null && mLeash != null) {
+ SurfaceControl menuSurfaceControl = mSystemWindows.getViewSurface(mMenuView);
+ if (menuSurfaceControl != null) {
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- t.setRelativeLayer(mMenuView.getWindowSurfaceControl(), mLeash, -1);
+ t.setRelativeLayer(mMenuView.getWindowSurfaceControl(), mLeash, 1);
+ t.setPosition(menuSurfaceControl, pipBounds.left, pipBounds.top);
t.apply();
}
+ mMenuView.show();
}
}
@@ -181,7 +182,15 @@
private void onMediaActionsChanged(List<RemoteAction> actions) {
if (DEBUG) Log.d(TAG, "onMediaActionsChanged()");
- updateAdditionalActionsList(mMediaActions, actions);
+
+ // Hide disabled actions.
+ List<RemoteAction> enabledActions = new ArrayList<>();
+ for (RemoteAction remoteAction : actions) {
+ if (remoteAction.isEnabled()) {
+ enabledActions.add(remoteAction);
+ }
+ }
+ updateAdditionalActionsList(mMediaActions, enabledActions);
}
private void updateAdditionalActionsList(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
index d6cd9ea..4327f15 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
@@ -24,9 +24,7 @@
import android.app.PendingIntent;
import android.app.RemoteAction;
import android.content.Context;
-import android.graphics.Color;
import android.os.Handler;
-import android.os.Looper;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
@@ -55,13 +53,13 @@
private static final String TAG = "TvPipMenuView";
private static final boolean DEBUG = TvPipController.DEBUG;
- private static final float DISABLED_ACTION_ALPHA = 0.54f;
-
private final Animator mFadeInAnimation;
private final Animator mFadeOutAnimation;
- @Nullable private Listener mListener;
+ @Nullable
+ private Listener mListener;
private final LinearLayout mActionButtonsContainer;
+ private final View mMenuFrameView;
private final List<TvPipMenuActionButton> mAdditionalButtons = new ArrayList<>();
public TvPipMenuView(@NonNull Context context) {
@@ -88,11 +86,12 @@
mActionButtonsContainer.findViewById(R.id.tv_pip_menu_close_button)
.setOnClickListener(this);
+ mMenuFrameView = findViewById(R.id.tv_pip_menu_frame);
mFadeInAnimation = loadAnimator(mContext, R.anim.tv_pip_menu_fade_in_animation);
- mFadeInAnimation.setTarget(mActionButtonsContainer);
+ mFadeInAnimation.setTarget(mMenuFrameView);
mFadeOutAnimation = loadAnimator(mContext, R.anim.tv_pip_menu_fade_out_animation);
- mFadeOutAnimation.setTarget(mActionButtonsContainer);
+ mFadeOutAnimation.setTarget(mMenuFrameView);
}
void setListener(@Nullable Listener listener) {
@@ -103,7 +102,6 @@
if (DEBUG) Log.d(TAG, "show()");
mFadeInAnimation.start();
- setAlpha(1.0f);
grantWindowFocus(true);
}
@@ -111,12 +109,11 @@
if (DEBUG) Log.d(TAG, "hide()");
mFadeOutAnimation.start();
- setAlpha(0.0f);
grantWindowFocus(false);
}
boolean isVisible() {
- return getAlpha() == 1.0f;
+ return mMenuFrameView != null && mMenuFrameView.getAlpha() != 0.0f;
}
private void grantWindowFocus(boolean grantFocus) {
@@ -140,9 +137,7 @@
final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
// Add buttons until we have enough to display all of the actions.
while (actionsNumber > buttonsNumber) {
- final TvPipMenuActionButton button = (TvPipMenuActionButton) layoutInflater.inflate(
- R.layout.tv_pip_menu_additional_action_button, mActionButtonsContainer,
- false);
+ TvPipMenuActionButton button = new TvPipMenuActionButton(mContext);
button.setOnClickListener(this);
mActionButtonsContainer.addView(button);
@@ -168,13 +163,8 @@
button.setVisibility(View.VISIBLE); // Ensure the button is visible.
button.setTextAndDescription(action.getContentDescription());
button.setEnabled(action.isEnabled());
- button.setAlpha(action.isEnabled() ? 1f : DISABLED_ACTION_ALPHA);
button.setTag(action);
-
- action.getIcon().loadDrawableAsync(mContext, drawable -> {
- drawable.setTint(Color.WHITE);
- button.setImageDrawable(drawable);
- }, mainHandler);
+ action.getIcon().loadDrawableAsync(mContext, button::setImageDrawable, mainHandler);
}
}
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 a0d9d03..e404724 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
@@ -346,6 +346,15 @@
}
if (change.getMode() == TRANSIT_CHANGE) {
+ // If task is child task, only set position in parent.
+ if (isTask && change.getParent() != null
+ && info.getChange(change.getParent()).getTaskInfo() != null) {
+ final Point positionInParent = change.getTaskInfo().positionInParent;
+ startTransaction.setPosition(change.getLeash(),
+ positionInParent.x, positionInParent.y);
+ continue;
+ }
+
// No default animation for this, so just update bounds/position.
startTransaction.setPosition(change.getLeash(),
change.getEndAbsBounds().left - change.getEndRelOffset().x,
diff --git a/libs/androidfw/LocaleDataTables.cpp b/libs/androidfw/LocaleDataTables.cpp
index 2c3567a..2c005fd 100644
--- a/libs/androidfw/LocaleDataTables.cpp
+++ b/libs/androidfw/LocaleDataTables.cpp
@@ -34,206 +34,209 @@
/* 30 */ {'H', 'a', 'n', 't'},
/* 31 */ {'H', 'e', 'b', 'r'},
/* 32 */ {'H', 'l', 'u', 'w'},
- /* 33 */ {'H', 'm', 'n', 'g'},
- /* 34 */ {'H', 'm', 'n', 'p'},
- /* 35 */ {'I', 't', 'a', 'l'},
- /* 36 */ {'J', 'p', 'a', 'n'},
- /* 37 */ {'K', 'a', 'l', 'i'},
- /* 38 */ {'K', 'a', 'n', 'a'},
- /* 39 */ {'K', 'h', 'a', 'r'},
- /* 40 */ {'K', 'h', 'm', 'r'},
- /* 41 */ {'K', 'i', 't', 's'},
- /* 42 */ {'K', 'n', 'd', 'a'},
- /* 43 */ {'K', 'o', 'r', 'e'},
- /* 44 */ {'L', 'a', 'n', 'a'},
- /* 45 */ {'L', 'a', 'o', 'o'},
- /* 46 */ {'L', 'a', 't', 'n'},
- /* 47 */ {'L', 'e', 'p', 'c'},
- /* 48 */ {'L', 'i', 'n', 'a'},
- /* 49 */ {'L', 'i', 's', 'u'},
- /* 50 */ {'L', 'y', 'c', 'i'},
- /* 51 */ {'L', 'y', 'd', 'i'},
- /* 52 */ {'M', 'a', 'n', 'd'},
- /* 53 */ {'M', 'a', 'n', 'i'},
- /* 54 */ {'M', 'e', 'd', 'f'},
- /* 55 */ {'M', 'e', 'r', 'c'},
- /* 56 */ {'M', 'l', 'y', 'm'},
- /* 57 */ {'M', 'o', 'n', 'g'},
- /* 58 */ {'M', 'r', 'o', 'o'},
- /* 59 */ {'M', 'y', 'm', 'r'},
- /* 60 */ {'N', 'a', 'r', 'b'},
- /* 61 */ {'N', 'k', 'o', 'o'},
- /* 62 */ {'N', 's', 'h', 'u'},
- /* 63 */ {'O', 'g', 'a', 'm'},
- /* 64 */ {'O', 'l', 'c', 'k'},
- /* 65 */ {'O', 'r', 'k', 'h'},
- /* 66 */ {'O', 'r', 'y', 'a'},
- /* 67 */ {'O', 's', 'g', 'e'},
+ /* 33 */ {'H', 'm', 'n', 'p'},
+ /* 34 */ {'I', 't', 'a', 'l'},
+ /* 35 */ {'J', 'p', 'a', 'n'},
+ /* 36 */ {'K', 'a', 'l', 'i'},
+ /* 37 */ {'K', 'a', 'n', 'a'},
+ /* 38 */ {'K', 'h', 'a', 'r'},
+ /* 39 */ {'K', 'h', 'm', 'r'},
+ /* 40 */ {'K', 'i', 't', 's'},
+ /* 41 */ {'K', 'n', 'd', 'a'},
+ /* 42 */ {'K', 'o', 'r', 'e'},
+ /* 43 */ {'L', 'a', 'n', 'a'},
+ /* 44 */ {'L', 'a', 'o', 'o'},
+ /* 45 */ {'L', 'a', 't', 'n'},
+ /* 46 */ {'L', 'e', 'p', 'c'},
+ /* 47 */ {'L', 'i', 'n', 'a'},
+ /* 48 */ {'L', 'i', 's', 'u'},
+ /* 49 */ {'L', 'y', 'c', 'i'},
+ /* 50 */ {'L', 'y', 'd', 'i'},
+ /* 51 */ {'M', 'a', 'n', 'd'},
+ /* 52 */ {'M', 'a', 'n', 'i'},
+ /* 53 */ {'M', 'e', 'd', 'f'},
+ /* 54 */ {'M', 'e', 'r', 'c'},
+ /* 55 */ {'M', 'l', 'y', 'm'},
+ /* 56 */ {'M', 'o', 'n', 'g'},
+ /* 57 */ {'M', 'r', 'o', 'o'},
+ /* 58 */ {'M', 'y', 'm', 'r'},
+ /* 59 */ {'N', 'a', 'r', 'b'},
+ /* 60 */ {'N', 'k', 'o', 'o'},
+ /* 61 */ {'N', 's', 'h', 'u'},
+ /* 62 */ {'O', 'g', 'a', 'm'},
+ /* 63 */ {'O', 'l', 'c', 'k'},
+ /* 64 */ {'O', 'r', 'k', 'h'},
+ /* 65 */ {'O', 'r', 'y', 'a'},
+ /* 66 */ {'O', 's', 'g', 'e'},
+ /* 67 */ {'O', 'u', 'g', 'r'},
/* 68 */ {'P', 'a', 'u', 'c'},
/* 69 */ {'P', 'h', 'l', 'i'},
/* 70 */ {'P', 'h', 'n', 'x'},
/* 71 */ {'P', 'l', 'r', 'd'},
/* 72 */ {'P', 'r', 't', 'i'},
- /* 73 */ {'R', 'u', 'n', 'r'},
- /* 74 */ {'S', 'a', 'm', 'r'},
- /* 75 */ {'S', 'a', 'r', 'b'},
- /* 76 */ {'S', 'a', 'u', 'r'},
- /* 77 */ {'S', 'g', 'n', 'w'},
- /* 78 */ {'S', 'i', 'n', 'h'},
- /* 79 */ {'S', 'o', 'g', 'd'},
- /* 80 */ {'S', 'o', 'r', 'a'},
- /* 81 */ {'S', 'o', 'y', 'o'},
- /* 82 */ {'S', 'y', 'r', 'c'},
- /* 83 */ {'T', 'a', 'l', 'e'},
- /* 84 */ {'T', 'a', 'l', 'u'},
- /* 85 */ {'T', 'a', 'm', 'l'},
- /* 86 */ {'T', 'a', 'n', 'g'},
- /* 87 */ {'T', 'a', 'v', 't'},
- /* 88 */ {'T', 'e', 'l', 'u'},
- /* 89 */ {'T', 'f', 'n', 'g'},
- /* 90 */ {'T', 'h', 'a', 'a'},
- /* 91 */ {'T', 'h', 'a', 'i'},
- /* 92 */ {'T', 'i', 'b', 't'},
- /* 93 */ {'U', 'g', 'a', 'r'},
- /* 94 */ {'V', 'a', 'i', 'i'},
- /* 95 */ {'W', 'c', 'h', 'o'},
- /* 96 */ {'X', 'p', 'e', 'o'},
- /* 97 */ {'X', 's', 'u', 'x'},
- /* 98 */ {'Y', 'i', 'i', 'i'},
- /* 99 */ {'~', '~', '~', 'A'},
- /* 100 */ {'~', '~', '~', 'B'},
+ /* 73 */ {'R', 'o', 'h', 'g'},
+ /* 74 */ {'R', 'u', 'n', 'r'},
+ /* 75 */ {'S', 'a', 'm', 'r'},
+ /* 76 */ {'S', 'a', 'r', 'b'},
+ /* 77 */ {'S', 'a', 'u', 'r'},
+ /* 78 */ {'S', 'g', 'n', 'w'},
+ /* 79 */ {'S', 'i', 'n', 'h'},
+ /* 80 */ {'S', 'o', 'g', 'd'},
+ /* 81 */ {'S', 'o', 'r', 'a'},
+ /* 82 */ {'S', 'o', 'y', 'o'},
+ /* 83 */ {'S', 'y', 'r', 'c'},
+ /* 84 */ {'T', 'a', 'l', 'e'},
+ /* 85 */ {'T', 'a', 'l', 'u'},
+ /* 86 */ {'T', 'a', 'm', 'l'},
+ /* 87 */ {'T', 'a', 'n', 'g'},
+ /* 88 */ {'T', 'a', 'v', 't'},
+ /* 89 */ {'T', 'e', 'l', 'u'},
+ /* 90 */ {'T', 'f', 'n', 'g'},
+ /* 91 */ {'T', 'h', 'a', 'a'},
+ /* 92 */ {'T', 'h', 'a', 'i'},
+ /* 93 */ {'T', 'i', 'b', 't'},
+ /* 94 */ {'T', 'n', 's', 'a'},
+ /* 95 */ {'T', 'o', 't', 'o'},
+ /* 96 */ {'U', 'g', 'a', 'r'},
+ /* 97 */ {'V', 'a', 'i', 'i'},
+ /* 98 */ {'W', 'c', 'h', 'o'},
+ /* 99 */ {'X', 'p', 'e', 'o'},
+ /* 100 */ {'X', 's', 'u', 'x'},
+ /* 101 */ {'Y', 'i', 'i', 'i'},
+ /* 102 */ {'~', '~', '~', 'A'},
+ /* 103 */ {'~', '~', '~', 'B'},
};
const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({
- {0x61610000u, 46u}, // aa -> Latn
- {0xA0000000u, 46u}, // aai -> Latn
- {0xA8000000u, 46u}, // aak -> Latn
- {0xD0000000u, 46u}, // aau -> Latn
+ {0x61610000u, 45u}, // aa -> Latn
+ {0xA0000000u, 45u}, // aai -> Latn
+ {0xA8000000u, 45u}, // aak -> Latn
+ {0xD0000000u, 45u}, // aau -> Latn
{0x61620000u, 18u}, // ab -> Cyrl
- {0xA0200000u, 46u}, // abi -> Latn
+ {0xA0200000u, 45u}, // abi -> Latn
{0xC0200000u, 18u}, // abq -> Cyrl
- {0xC4200000u, 46u}, // abr -> Latn
- {0xCC200000u, 46u}, // abt -> Latn
- {0xE0200000u, 46u}, // aby -> Latn
- {0x8C400000u, 46u}, // acd -> Latn
- {0x90400000u, 46u}, // ace -> Latn
- {0x9C400000u, 46u}, // ach -> Latn
- {0x80600000u, 46u}, // ada -> Latn
- {0x90600000u, 46u}, // ade -> Latn
- {0xA4600000u, 46u}, // adj -> Latn
- {0xBC600000u, 92u}, // adp -> Tibt
+ {0xC4200000u, 45u}, // abr -> Latn
+ {0xCC200000u, 45u}, // abt -> Latn
+ {0xE0200000u, 45u}, // aby -> Latn
+ {0x8C400000u, 45u}, // acd -> Latn
+ {0x90400000u, 45u}, // ace -> Latn
+ {0x9C400000u, 45u}, // ach -> Latn
+ {0x80600000u, 45u}, // ada -> Latn
+ {0x90600000u, 45u}, // ade -> Latn
+ {0xA4600000u, 45u}, // adj -> Latn
+ {0xBC600000u, 93u}, // adp -> Tibt
{0xE0600000u, 18u}, // ady -> Cyrl
- {0xE4600000u, 46u}, // adz -> Latn
+ {0xE4600000u, 45u}, // adz -> Latn
{0x61650000u, 5u}, // ae -> Avst
{0x84800000u, 2u}, // aeb -> Arab
- {0xE0800000u, 46u}, // aey -> Latn
- {0x61660000u, 46u}, // af -> Latn
- {0x88C00000u, 46u}, // agc -> Latn
- {0x8CC00000u, 46u}, // agd -> Latn
- {0x98C00000u, 46u}, // agg -> Latn
- {0xB0C00000u, 46u}, // agm -> Latn
- {0xB8C00000u, 46u}, // ago -> Latn
- {0xC0C00000u, 46u}, // agq -> Latn
- {0x80E00000u, 46u}, // aha -> Latn
- {0xACE00000u, 46u}, // ahl -> Latn
+ {0xE0800000u, 45u}, // aey -> Latn
+ {0x61660000u, 45u}, // af -> Latn
+ {0x88C00000u, 45u}, // agc -> Latn
+ {0x8CC00000u, 45u}, // agd -> Latn
+ {0x98C00000u, 45u}, // agg -> Latn
+ {0xB0C00000u, 45u}, // agm -> Latn
+ {0xB8C00000u, 45u}, // ago -> Latn
+ {0xC0C00000u, 45u}, // agq -> Latn
+ {0x80E00000u, 45u}, // aha -> Latn
+ {0xACE00000u, 45u}, // ahl -> Latn
{0xB8E00000u, 1u}, // aho -> Ahom
- {0x99200000u, 46u}, // ajg -> Latn
- {0x616B0000u, 46u}, // ak -> Latn
- {0xA9400000u, 97u}, // akk -> Xsux
- {0x81600000u, 46u}, // ala -> Latn
- {0xA1600000u, 46u}, // ali -> Latn
- {0xB5600000u, 46u}, // aln -> Latn
+ {0x99200000u, 45u}, // ajg -> Latn
+ {0x616B0000u, 45u}, // ak -> Latn
+ {0xA9400000u, 100u}, // akk -> Xsux
+ {0x81600000u, 45u}, // ala -> Latn
+ {0xA1600000u, 45u}, // ali -> Latn
+ {0xB5600000u, 45u}, // aln -> Latn
{0xCD600000u, 18u}, // alt -> Cyrl
{0x616D0000u, 21u}, // am -> Ethi
- {0xB1800000u, 46u}, // amm -> Latn
- {0xB5800000u, 46u}, // amn -> Latn
- {0xB9800000u, 46u}, // amo -> Latn
- {0xBD800000u, 46u}, // amp -> Latn
- {0x616E0000u, 46u}, // an -> Latn
- {0x89A00000u, 46u}, // anc -> Latn
- {0xA9A00000u, 46u}, // ank -> Latn
- {0xB5A00000u, 46u}, // ann -> Latn
- {0xE1A00000u, 46u}, // any -> Latn
- {0xA5C00000u, 46u}, // aoj -> Latn
- {0xB1C00000u, 46u}, // aom -> Latn
- {0xE5C00000u, 46u}, // aoz -> Latn
+ {0xB1800000u, 45u}, // amm -> Latn
+ {0xB5800000u, 45u}, // amn -> Latn
+ {0xB9800000u, 45u}, // amo -> Latn
+ {0xBD800000u, 45u}, // amp -> Latn
+ {0x616E0000u, 45u}, // an -> Latn
+ {0x89A00000u, 45u}, // anc -> Latn
+ {0xA9A00000u, 45u}, // ank -> Latn
+ {0xB5A00000u, 45u}, // ann -> Latn
+ {0xE1A00000u, 45u}, // any -> Latn
+ {0xA5C00000u, 45u}, // aoj -> Latn
+ {0xB1C00000u, 45u}, // aom -> Latn
+ {0xE5C00000u, 45u}, // aoz -> Latn
{0x89E00000u, 2u}, // apc -> Arab
{0x8DE00000u, 2u}, // apd -> Arab
- {0x91E00000u, 46u}, // ape -> Latn
- {0xC5E00000u, 46u}, // apr -> Latn
- {0xC9E00000u, 46u}, // aps -> Latn
- {0xE5E00000u, 46u}, // apz -> Latn
+ {0x91E00000u, 45u}, // ape -> Latn
+ {0xC5E00000u, 45u}, // apr -> Latn
+ {0xC9E00000u, 45u}, // aps -> Latn
+ {0xE5E00000u, 45u}, // apz -> Latn
{0x61720000u, 2u}, // ar -> Arab
- {0x61725842u, 100u}, // ar-XB -> ~~~B
+ {0x61725842u, 103u}, // ar-XB -> ~~~B
{0x8A200000u, 3u}, // arc -> Armi
- {0x9E200000u, 46u}, // arh -> Latn
- {0xB6200000u, 46u}, // arn -> Latn
- {0xBA200000u, 46u}, // aro -> Latn
+ {0x9E200000u, 45u}, // arh -> Latn
+ {0xB6200000u, 45u}, // arn -> Latn
+ {0xBA200000u, 45u}, // aro -> Latn
{0xC2200000u, 2u}, // arq -> Arab
{0xCA200000u, 2u}, // ars -> Arab
{0xE2200000u, 2u}, // ary -> Arab
{0xE6200000u, 2u}, // arz -> Arab
{0x61730000u, 8u}, // as -> Beng
- {0x82400000u, 46u}, // asa -> Latn
- {0x92400000u, 77u}, // ase -> Sgnw
- {0x9A400000u, 46u}, // asg -> Latn
- {0xBA400000u, 46u}, // aso -> Latn
- {0xCE400000u, 46u}, // ast -> Latn
- {0x82600000u, 46u}, // ata -> Latn
- {0x9A600000u, 46u}, // atg -> Latn
- {0xA6600000u, 46u}, // atj -> Latn
- {0xE2800000u, 46u}, // auy -> Latn
+ {0x82400000u, 45u}, // asa -> Latn
+ {0x92400000u, 78u}, // ase -> Sgnw
+ {0x9A400000u, 45u}, // asg -> Latn
+ {0xBA400000u, 45u}, // aso -> Latn
+ {0xCE400000u, 45u}, // ast -> Latn
+ {0x82600000u, 45u}, // ata -> Latn
+ {0x9A600000u, 45u}, // atg -> Latn
+ {0xA6600000u, 45u}, // atj -> Latn
+ {0xE2800000u, 45u}, // auy -> Latn
{0x61760000u, 18u}, // av -> Cyrl
{0xAEA00000u, 2u}, // avl -> Arab
- {0xB6A00000u, 46u}, // avn -> Latn
- {0xCEA00000u, 46u}, // avt -> Latn
- {0xD2A00000u, 46u}, // avu -> Latn
+ {0xB6A00000u, 45u}, // avn -> Latn
+ {0xCEA00000u, 45u}, // avt -> Latn
+ {0xD2A00000u, 45u}, // avu -> Latn
{0x82C00000u, 19u}, // awa -> Deva
- {0x86C00000u, 46u}, // awb -> Latn
- {0xBAC00000u, 46u}, // awo -> Latn
- {0xDEC00000u, 46u}, // awx -> Latn
- {0x61790000u, 46u}, // ay -> Latn
- {0x87000000u, 46u}, // ayb -> Latn
- {0x617A0000u, 46u}, // az -> Latn
+ {0x86C00000u, 45u}, // awb -> Latn
+ {0xBAC00000u, 45u}, // awo -> Latn
+ {0xDEC00000u, 45u}, // awx -> Latn
+ {0x61790000u, 45u}, // ay -> Latn
+ {0x87000000u, 45u}, // ayb -> Latn
+ {0x617A0000u, 45u}, // az -> Latn
{0x617A4951u, 2u}, // az-IQ -> Arab
{0x617A4952u, 2u}, // az-IR -> Arab
{0x617A5255u, 18u}, // az-RU -> Cyrl
{0x62610000u, 18u}, // ba -> Cyrl
{0xAC010000u, 2u}, // bal -> Arab
- {0xB4010000u, 46u}, // ban -> Latn
+ {0xB4010000u, 45u}, // ban -> Latn
{0xBC010000u, 19u}, // bap -> Deva
- {0xC4010000u, 46u}, // bar -> Latn
- {0xC8010000u, 46u}, // bas -> Latn
- {0xD4010000u, 46u}, // bav -> Latn
+ {0xC4010000u, 45u}, // bar -> Latn
+ {0xC8010000u, 45u}, // bas -> Latn
+ {0xD4010000u, 45u}, // bav -> Latn
{0xDC010000u, 6u}, // bax -> Bamu
- {0x80210000u, 46u}, // bba -> Latn
- {0x84210000u, 46u}, // bbb -> Latn
- {0x88210000u, 46u}, // bbc -> Latn
- {0x8C210000u, 46u}, // bbd -> Latn
- {0xA4210000u, 46u}, // bbj -> Latn
- {0xBC210000u, 46u}, // bbp -> Latn
- {0xC4210000u, 46u}, // bbr -> Latn
- {0x94410000u, 46u}, // bcf -> Latn
- {0x9C410000u, 46u}, // bch -> Latn
- {0xA0410000u, 46u}, // bci -> Latn
- {0xB0410000u, 46u}, // bcm -> Latn
- {0xB4410000u, 46u}, // bcn -> Latn
- {0xB8410000u, 46u}, // bco -> Latn
+ {0x80210000u, 45u}, // bba -> Latn
+ {0x84210000u, 45u}, // bbb -> Latn
+ {0x88210000u, 45u}, // bbc -> Latn
+ {0x8C210000u, 45u}, // bbd -> Latn
+ {0xA4210000u, 45u}, // bbj -> Latn
+ {0xBC210000u, 45u}, // bbp -> Latn
+ {0xC4210000u, 45u}, // bbr -> Latn
+ {0x94410000u, 45u}, // bcf -> Latn
+ {0x9C410000u, 45u}, // bch -> Latn
+ {0xA0410000u, 45u}, // bci -> Latn
+ {0xB0410000u, 45u}, // bcm -> Latn
+ {0xB4410000u, 45u}, // bcn -> Latn
+ {0xB8410000u, 45u}, // bco -> Latn
{0xC0410000u, 21u}, // bcq -> Ethi
- {0xD0410000u, 46u}, // bcu -> Latn
- {0x8C610000u, 46u}, // bdd -> Latn
+ {0xD0410000u, 45u}, // bcu -> Latn
+ {0x8C610000u, 45u}, // bdd -> Latn
{0x62650000u, 18u}, // be -> Cyrl
- {0x94810000u, 46u}, // bef -> Latn
- {0x9C810000u, 46u}, // beh -> Latn
+ {0x94810000u, 45u}, // bef -> Latn
+ {0x9C810000u, 45u}, // beh -> Latn
{0xA4810000u, 2u}, // bej -> Arab
- {0xB0810000u, 46u}, // bem -> Latn
- {0xCC810000u, 46u}, // bet -> Latn
- {0xD8810000u, 46u}, // bew -> Latn
- {0xDC810000u, 46u}, // bex -> Latn
- {0xE4810000u, 46u}, // bez -> Latn
- {0x8CA10000u, 46u}, // bfd -> Latn
- {0xC0A10000u, 85u}, // bfq -> Taml
+ {0xB0810000u, 45u}, // bem -> Latn
+ {0xCC810000u, 45u}, // bet -> Latn
+ {0xD8810000u, 45u}, // bew -> Latn
+ {0xDC810000u, 45u}, // bex -> Latn
+ {0xE4810000u, 45u}, // bez -> Latn
+ {0x8CA10000u, 45u}, // bfd -> Latn
+ {0xC0A10000u, 86u}, // bfq -> Taml
{0xCCA10000u, 2u}, // bft -> Arab
{0xE0A10000u, 19u}, // bfy -> Deva
{0x62670000u, 18u}, // bg -> Cyrl
@@ -241,1235 +244,1239 @@
{0xB4C10000u, 2u}, // bgn -> Arab
{0xDCC10000u, 26u}, // bgx -> Grek
{0x84E10000u, 19u}, // bhb -> Deva
- {0x98E10000u, 46u}, // bhg -> Latn
+ {0x98E10000u, 45u}, // bhg -> Latn
{0xA0E10000u, 19u}, // bhi -> Deva
- {0xACE10000u, 46u}, // bhl -> Latn
+ {0xACE10000u, 45u}, // bhl -> Latn
{0xB8E10000u, 19u}, // bho -> Deva
- {0xE0E10000u, 46u}, // bhy -> Latn
- {0x62690000u, 46u}, // bi -> Latn
- {0x85010000u, 46u}, // bib -> Latn
- {0x99010000u, 46u}, // big -> Latn
- {0xA9010000u, 46u}, // bik -> Latn
- {0xB1010000u, 46u}, // bim -> Latn
- {0xB5010000u, 46u}, // bin -> Latn
- {0xB9010000u, 46u}, // bio -> Latn
- {0xC1010000u, 46u}, // biq -> Latn
- {0x9D210000u, 46u}, // bjh -> Latn
+ {0xE0E10000u, 45u}, // bhy -> Latn
+ {0x62690000u, 45u}, // bi -> Latn
+ {0x85010000u, 45u}, // bib -> Latn
+ {0x99010000u, 45u}, // big -> Latn
+ {0xA9010000u, 45u}, // bik -> Latn
+ {0xB1010000u, 45u}, // bim -> Latn
+ {0xB5010000u, 45u}, // bin -> Latn
+ {0xB9010000u, 45u}, // bio -> Latn
+ {0xC1010000u, 45u}, // biq -> Latn
+ {0x9D210000u, 45u}, // bjh -> Latn
{0xA1210000u, 21u}, // bji -> Ethi
{0xA5210000u, 19u}, // bjj -> Deva
- {0xB5210000u, 46u}, // bjn -> Latn
- {0xB9210000u, 46u}, // bjo -> Latn
- {0xC5210000u, 46u}, // bjr -> Latn
- {0xCD210000u, 46u}, // bjt -> Latn
- {0xE5210000u, 46u}, // bjz -> Latn
- {0x89410000u, 46u}, // bkc -> Latn
- {0xB1410000u, 46u}, // bkm -> Latn
- {0xC1410000u, 46u}, // bkq -> Latn
- {0xD1410000u, 46u}, // bku -> Latn
- {0xD5410000u, 46u}, // bkv -> Latn
- {0xCD610000u, 87u}, // blt -> Tavt
- {0x626D0000u, 46u}, // bm -> Latn
- {0x9D810000u, 46u}, // bmh -> Latn
- {0xA9810000u, 46u}, // bmk -> Latn
- {0xC1810000u, 46u}, // bmq -> Latn
- {0xD1810000u, 46u}, // bmu -> Latn
+ {0xB5210000u, 45u}, // bjn -> Latn
+ {0xB9210000u, 45u}, // bjo -> Latn
+ {0xC5210000u, 45u}, // bjr -> Latn
+ {0xCD210000u, 45u}, // bjt -> Latn
+ {0xE5210000u, 45u}, // bjz -> Latn
+ {0x89410000u, 45u}, // bkc -> Latn
+ {0xB1410000u, 45u}, // bkm -> Latn
+ {0xC1410000u, 45u}, // bkq -> Latn
+ {0xD1410000u, 45u}, // bku -> Latn
+ {0xD5410000u, 45u}, // bkv -> Latn
+ {0x99610000u, 45u}, // blg -> Latn
+ {0xCD610000u, 88u}, // blt -> Tavt
+ {0x626D0000u, 45u}, // bm -> Latn
+ {0x9D810000u, 45u}, // bmh -> Latn
+ {0xA9810000u, 45u}, // bmk -> Latn
+ {0xC1810000u, 45u}, // bmq -> Latn
+ {0xD1810000u, 45u}, // bmu -> Latn
{0x626E0000u, 8u}, // bn -> Beng
- {0x99A10000u, 46u}, // bng -> Latn
- {0xB1A10000u, 46u}, // bnm -> Latn
- {0xBDA10000u, 46u}, // bnp -> Latn
- {0x626F0000u, 92u}, // bo -> Tibt
- {0xA5C10000u, 46u}, // boj -> Latn
- {0xB1C10000u, 46u}, // bom -> Latn
- {0xB5C10000u, 46u}, // bon -> Latn
+ {0x99A10000u, 45u}, // bng -> Latn
+ {0xB1A10000u, 45u}, // bnm -> Latn
+ {0xBDA10000u, 45u}, // bnp -> Latn
+ {0x626F0000u, 93u}, // bo -> Tibt
+ {0xA5C10000u, 45u}, // boj -> Latn
+ {0xB1C10000u, 45u}, // bom -> Latn
+ {0xB5C10000u, 45u}, // bon -> Latn
{0xE1E10000u, 8u}, // bpy -> Beng
- {0x8A010000u, 46u}, // bqc -> Latn
+ {0x8A010000u, 45u}, // bqc -> Latn
{0xA2010000u, 2u}, // bqi -> Arab
- {0xBE010000u, 46u}, // bqp -> Latn
- {0xD6010000u, 46u}, // bqv -> Latn
- {0x62720000u, 46u}, // br -> Latn
+ {0xBE010000u, 45u}, // bqp -> Latn
+ {0xD6010000u, 45u}, // bqv -> Latn
+ {0x62720000u, 45u}, // br -> Latn
{0x82210000u, 19u}, // bra -> Deva
{0x9E210000u, 2u}, // brh -> Arab
{0xDE210000u, 19u}, // brx -> Deva
- {0xE6210000u, 46u}, // brz -> Latn
- {0x62730000u, 46u}, // bs -> Latn
- {0xA6410000u, 46u}, // bsj -> Latn
+ {0xE6210000u, 45u}, // brz -> Latn
+ {0x62730000u, 45u}, // bs -> Latn
+ {0xA6410000u, 45u}, // bsj -> Latn
{0xC2410000u, 7u}, // bsq -> Bass
- {0xCA410000u, 46u}, // bss -> Latn
+ {0xCA410000u, 45u}, // bss -> Latn
{0xCE410000u, 21u}, // bst -> Ethi
- {0xBA610000u, 46u}, // bto -> Latn
- {0xCE610000u, 46u}, // btt -> Latn
+ {0xBA610000u, 45u}, // bto -> Latn
+ {0xCE610000u, 45u}, // btt -> Latn
{0xD6610000u, 19u}, // btv -> Deva
{0x82810000u, 18u}, // bua -> Cyrl
- {0x8A810000u, 46u}, // buc -> Latn
- {0x8E810000u, 46u}, // bud -> Latn
- {0x9A810000u, 46u}, // bug -> Latn
- {0xAA810000u, 46u}, // buk -> Latn
- {0xB2810000u, 46u}, // bum -> Latn
- {0xBA810000u, 46u}, // buo -> Latn
- {0xCA810000u, 46u}, // bus -> Latn
- {0xD2810000u, 46u}, // buu -> Latn
- {0x86A10000u, 46u}, // bvb -> Latn
- {0x8EC10000u, 46u}, // bwd -> Latn
- {0xC6C10000u, 46u}, // bwr -> Latn
- {0x9EE10000u, 46u}, // bxh -> Latn
- {0x93010000u, 46u}, // bye -> Latn
+ {0x8A810000u, 45u}, // buc -> Latn
+ {0x8E810000u, 45u}, // bud -> Latn
+ {0x9A810000u, 45u}, // bug -> Latn
+ {0xAA810000u, 45u}, // buk -> Latn
+ {0xB2810000u, 45u}, // bum -> Latn
+ {0xBA810000u, 45u}, // buo -> Latn
+ {0xCA810000u, 45u}, // bus -> Latn
+ {0xD2810000u, 45u}, // buu -> Latn
+ {0x86A10000u, 45u}, // bvb -> Latn
+ {0x8EC10000u, 45u}, // bwd -> Latn
+ {0xC6C10000u, 45u}, // bwr -> Latn
+ {0x9EE10000u, 45u}, // bxh -> Latn
+ {0x93010000u, 45u}, // bye -> Latn
{0xB7010000u, 21u}, // byn -> Ethi
- {0xC7010000u, 46u}, // byr -> Latn
- {0xCB010000u, 46u}, // bys -> Latn
- {0xD7010000u, 46u}, // byv -> Latn
- {0xDF010000u, 46u}, // byx -> Latn
- {0x83210000u, 46u}, // bza -> Latn
- {0x93210000u, 46u}, // bze -> Latn
- {0x97210000u, 46u}, // bzf -> Latn
- {0x9F210000u, 46u}, // bzh -> Latn
- {0xDB210000u, 46u}, // bzw -> Latn
- {0x63610000u, 46u}, // ca -> Latn
- {0x8C020000u, 46u}, // cad -> Latn
- {0xB4020000u, 46u}, // can -> Latn
- {0xA4220000u, 46u}, // cbj -> Latn
- {0x9C420000u, 46u}, // cch -> Latn
+ {0xC7010000u, 45u}, // byr -> Latn
+ {0xCB010000u, 45u}, // bys -> Latn
+ {0xD7010000u, 45u}, // byv -> Latn
+ {0xDF010000u, 45u}, // byx -> Latn
+ {0x83210000u, 45u}, // bza -> Latn
+ {0x93210000u, 45u}, // bze -> Latn
+ {0x97210000u, 45u}, // bzf -> Latn
+ {0x9F210000u, 45u}, // bzh -> Latn
+ {0xDB210000u, 45u}, // bzw -> Latn
+ {0x63610000u, 45u}, // ca -> Latn
+ {0x8C020000u, 45u}, // cad -> Latn
+ {0xB4020000u, 45u}, // can -> Latn
+ {0xA4220000u, 45u}, // cbj -> Latn
+ {0x9C420000u, 45u}, // cch -> Latn
{0xBC420000u, 10u}, // ccp -> Cakm
{0x63650000u, 18u}, // ce -> Cyrl
- {0x84820000u, 46u}, // ceb -> Latn
- {0x80A20000u, 46u}, // cfa -> Latn
- {0x98C20000u, 46u}, // cgg -> Latn
- {0x63680000u, 46u}, // ch -> Latn
- {0xA8E20000u, 46u}, // chk -> Latn
+ {0x84820000u, 45u}, // ceb -> Latn
+ {0x80A20000u, 45u}, // cfa -> Latn
+ {0x98C20000u, 45u}, // cgg -> Latn
+ {0x63680000u, 45u}, // ch -> Latn
+ {0xA8E20000u, 45u}, // chk -> Latn
{0xB0E20000u, 18u}, // chm -> Cyrl
- {0xB8E20000u, 46u}, // cho -> Latn
- {0xBCE20000u, 46u}, // chp -> Latn
+ {0xB8E20000u, 45u}, // cho -> Latn
+ {0xBCE20000u, 45u}, // chp -> Latn
{0xC4E20000u, 14u}, // chr -> Cher
- {0x89020000u, 46u}, // cic -> Latn
+ {0x89020000u, 45u}, // cic -> Latn
{0x81220000u, 2u}, // cja -> Arab
{0xB1220000u, 13u}, // cjm -> Cham
- {0xD5220000u, 46u}, // cjv -> Latn
+ {0xD5220000u, 45u}, // cjv -> Latn
{0x85420000u, 2u}, // ckb -> Arab
- {0xAD420000u, 46u}, // ckl -> Latn
- {0xB9420000u, 46u}, // cko -> Latn
- {0xE1420000u, 46u}, // cky -> Latn
- {0x81620000u, 46u}, // cla -> Latn
- {0x91820000u, 46u}, // cme -> Latn
- {0x99820000u, 81u}, // cmg -> Soyo
- {0x636F0000u, 46u}, // co -> Latn
+ {0xAD420000u, 45u}, // ckl -> Latn
+ {0xB9420000u, 45u}, // cko -> Latn
+ {0xE1420000u, 45u}, // cky -> Latn
+ {0x81620000u, 45u}, // cla -> Latn
+ {0x91820000u, 45u}, // cme -> Latn
+ {0x99820000u, 82u}, // cmg -> Soyo
+ {0x636F0000u, 45u}, // co -> Latn
{0xBDC20000u, 16u}, // cop -> Copt
- {0xC9E20000u, 46u}, // cps -> Latn
+ {0xC9E20000u, 45u}, // cps -> Latn
{0x63720000u, 11u}, // cr -> Cans
{0x9E220000u, 18u}, // crh -> Cyrl
{0xA6220000u, 11u}, // crj -> Cans
{0xAA220000u, 11u}, // crk -> Cans
{0xAE220000u, 11u}, // crl -> Cans
{0xB2220000u, 11u}, // crm -> Cans
- {0xCA220000u, 46u}, // crs -> Latn
- {0x63730000u, 46u}, // cs -> Latn
- {0x86420000u, 46u}, // csb -> Latn
+ {0xCA220000u, 45u}, // crs -> Latn
+ {0x63730000u, 45u}, // cs -> Latn
+ {0x86420000u, 45u}, // csb -> Latn
{0xDA420000u, 11u}, // csw -> Cans
{0x8E620000u, 68u}, // ctd -> Pauc
{0x63750000u, 18u}, // cu -> Cyrl
{0x63760000u, 18u}, // cv -> Cyrl
- {0x63790000u, 46u}, // cy -> Latn
- {0x64610000u, 46u}, // da -> Latn
- {0x8C030000u, 46u}, // dad -> Latn
- {0x94030000u, 46u}, // daf -> Latn
- {0x98030000u, 46u}, // dag -> Latn
- {0x9C030000u, 46u}, // dah -> Latn
- {0xA8030000u, 46u}, // dak -> Latn
+ {0x63790000u, 45u}, // cy -> Latn
+ {0x64610000u, 45u}, // da -> Latn
+ {0x8C030000u, 45u}, // dad -> Latn
+ {0x94030000u, 45u}, // daf -> Latn
+ {0x98030000u, 45u}, // dag -> Latn
+ {0x9C030000u, 45u}, // dah -> Latn
+ {0xA8030000u, 45u}, // dak -> Latn
{0xC4030000u, 18u}, // dar -> Cyrl
- {0xD4030000u, 46u}, // dav -> Latn
- {0x8C230000u, 46u}, // dbd -> Latn
- {0xC0230000u, 46u}, // dbq -> Latn
+ {0xD4030000u, 45u}, // dav -> Latn
+ {0x8C230000u, 45u}, // dbd -> Latn
+ {0xC0230000u, 45u}, // dbq -> Latn
{0x88430000u, 2u}, // dcc -> Arab
- {0xB4630000u, 46u}, // ddn -> Latn
- {0x64650000u, 46u}, // de -> Latn
- {0x8C830000u, 46u}, // ded -> Latn
- {0xB4830000u, 46u}, // den -> Latn
- {0x80C30000u, 46u}, // dga -> Latn
- {0x9CC30000u, 46u}, // dgh -> Latn
- {0xA0C30000u, 46u}, // dgi -> Latn
+ {0xB4630000u, 45u}, // ddn -> Latn
+ {0x64650000u, 45u}, // de -> Latn
+ {0x8C830000u, 45u}, // ded -> Latn
+ {0xB4830000u, 45u}, // den -> Latn
+ {0x80C30000u, 45u}, // dga -> Latn
+ {0x9CC30000u, 45u}, // dgh -> Latn
+ {0xA0C30000u, 45u}, // dgi -> Latn
{0xACC30000u, 2u}, // dgl -> Arab
- {0xC4C30000u, 46u}, // dgr -> Latn
- {0xE4C30000u, 46u}, // dgz -> Latn
- {0x81030000u, 46u}, // dia -> Latn
- {0x91230000u, 46u}, // dje -> Latn
- {0x95830000u, 54u}, // dmf -> Medf
- {0xA5A30000u, 46u}, // dnj -> Latn
- {0x85C30000u, 46u}, // dob -> Latn
+ {0xC4C30000u, 45u}, // dgr -> Latn
+ {0xE4C30000u, 45u}, // dgz -> Latn
+ {0x81030000u, 45u}, // dia -> Latn
+ {0x91230000u, 45u}, // dje -> Latn
+ {0x95830000u, 53u}, // dmf -> Medf
+ {0xA5A30000u, 45u}, // dnj -> Latn
+ {0x85C30000u, 45u}, // dob -> Latn
{0xA1C30000u, 19u}, // doi -> Deva
- {0xBDC30000u, 46u}, // dop -> Latn
- {0xD9C30000u, 46u}, // dow -> Latn
- {0x9E230000u, 57u}, // drh -> Mong
- {0xA2230000u, 46u}, // dri -> Latn
+ {0xBDC30000u, 45u}, // dop -> Latn
+ {0xD9C30000u, 45u}, // dow -> Latn
+ {0x9E230000u, 56u}, // drh -> Mong
+ {0xA2230000u, 45u}, // dri -> Latn
{0xCA230000u, 21u}, // drs -> Ethi
- {0x86430000u, 46u}, // dsb -> Latn
- {0xB2630000u, 46u}, // dtm -> Latn
- {0xBE630000u, 46u}, // dtp -> Latn
- {0xCA630000u, 46u}, // dts -> Latn
+ {0x86430000u, 45u}, // dsb -> Latn
+ {0xB2630000u, 45u}, // dtm -> Latn
+ {0xBE630000u, 45u}, // dtp -> Latn
+ {0xCA630000u, 45u}, // dts -> Latn
{0xE2630000u, 19u}, // dty -> Deva
- {0x82830000u, 46u}, // dua -> Latn
- {0x8A830000u, 46u}, // duc -> Latn
- {0x8E830000u, 46u}, // dud -> Latn
- {0x9A830000u, 46u}, // dug -> Latn
- {0x64760000u, 90u}, // dv -> Thaa
- {0x82A30000u, 46u}, // dva -> Latn
- {0xDAC30000u, 46u}, // dww -> Latn
- {0xBB030000u, 46u}, // dyo -> Latn
- {0xD3030000u, 46u}, // dyu -> Latn
- {0x647A0000u, 92u}, // dz -> Tibt
- {0x9B230000u, 46u}, // dzg -> Latn
- {0xD0240000u, 46u}, // ebu -> Latn
- {0x65650000u, 46u}, // ee -> Latn
- {0xA0A40000u, 46u}, // efi -> Latn
- {0xACC40000u, 46u}, // egl -> Latn
+ {0x82830000u, 45u}, // dua -> Latn
+ {0x8A830000u, 45u}, // duc -> Latn
+ {0x8E830000u, 45u}, // dud -> Latn
+ {0x9A830000u, 45u}, // dug -> Latn
+ {0x64760000u, 91u}, // dv -> Thaa
+ {0x82A30000u, 45u}, // dva -> Latn
+ {0xDAC30000u, 45u}, // dww -> Latn
+ {0xBB030000u, 45u}, // dyo -> Latn
+ {0xD3030000u, 45u}, // dyu -> Latn
+ {0x647A0000u, 93u}, // dz -> Tibt
+ {0x9B230000u, 45u}, // dzg -> Latn
+ {0xD0240000u, 45u}, // ebu -> Latn
+ {0x65650000u, 45u}, // ee -> Latn
+ {0xA0A40000u, 45u}, // efi -> Latn
+ {0xACC40000u, 45u}, // egl -> Latn
{0xE0C40000u, 20u}, // egy -> Egyp
- {0x81440000u, 46u}, // eka -> Latn
- {0xE1440000u, 37u}, // eky -> Kali
+ {0x81440000u, 45u}, // eka -> Latn
+ {0xE1440000u, 36u}, // eky -> Kali
{0x656C0000u, 26u}, // el -> Grek
- {0x81840000u, 46u}, // ema -> Latn
- {0xA1840000u, 46u}, // emi -> Latn
- {0x656E0000u, 46u}, // en -> Latn
- {0x656E5841u, 99u}, // en-XA -> ~~~A
- {0xB5A40000u, 46u}, // enn -> Latn
- {0xC1A40000u, 46u}, // enq -> Latn
- {0x656F0000u, 46u}, // eo -> Latn
- {0xA2240000u, 46u}, // eri -> Latn
- {0x65730000u, 46u}, // es -> Latn
+ {0x81840000u, 45u}, // ema -> Latn
+ {0xA1840000u, 45u}, // emi -> Latn
+ {0x656E0000u, 45u}, // en -> Latn
+ {0x656E5841u, 102u}, // en-XA -> ~~~A
+ {0xB5A40000u, 45u}, // enn -> Latn
+ {0xC1A40000u, 45u}, // enq -> Latn
+ {0x656F0000u, 45u}, // eo -> Latn
+ {0xA2240000u, 45u}, // eri -> Latn
+ {0x65730000u, 45u}, // es -> Latn
{0x9A440000u, 24u}, // esg -> Gonm
- {0xD2440000u, 46u}, // esu -> Latn
- {0x65740000u, 46u}, // et -> Latn
- {0xC6640000u, 46u}, // etr -> Latn
- {0xCE640000u, 35u}, // ett -> Ital
- {0xD2640000u, 46u}, // etu -> Latn
- {0xDE640000u, 46u}, // etx -> Latn
- {0x65750000u, 46u}, // eu -> Latn
- {0xBAC40000u, 46u}, // ewo -> Latn
- {0xCEE40000u, 46u}, // ext -> Latn
- {0x83240000u, 46u}, // eza -> Latn
+ {0xD2440000u, 45u}, // esu -> Latn
+ {0x65740000u, 45u}, // et -> Latn
+ {0xC6640000u, 45u}, // etr -> Latn
+ {0xCE640000u, 34u}, // ett -> Ital
+ {0xD2640000u, 45u}, // etu -> Latn
+ {0xDE640000u, 45u}, // etx -> Latn
+ {0x65750000u, 45u}, // eu -> Latn
+ {0xBAC40000u, 45u}, // ewo -> Latn
+ {0xCEE40000u, 45u}, // ext -> Latn
+ {0x83240000u, 45u}, // eza -> Latn
{0x66610000u, 2u}, // fa -> Arab
- {0x80050000u, 46u}, // faa -> Latn
- {0x84050000u, 46u}, // fab -> Latn
- {0x98050000u, 46u}, // fag -> Latn
- {0xA0050000u, 46u}, // fai -> Latn
- {0xB4050000u, 46u}, // fan -> Latn
- {0x66660000u, 46u}, // ff -> Latn
- {0xA0A50000u, 46u}, // ffi -> Latn
- {0xB0A50000u, 46u}, // ffm -> Latn
- {0x66690000u, 46u}, // fi -> Latn
+ {0x80050000u, 45u}, // faa -> Latn
+ {0x84050000u, 45u}, // fab -> Latn
+ {0x98050000u, 45u}, // fag -> Latn
+ {0xA0050000u, 45u}, // fai -> Latn
+ {0xB4050000u, 45u}, // fan -> Latn
+ {0x66660000u, 45u}, // ff -> Latn
+ {0xA0A50000u, 45u}, // ffi -> Latn
+ {0xB0A50000u, 45u}, // ffm -> Latn
+ {0x66690000u, 45u}, // fi -> Latn
{0x81050000u, 2u}, // fia -> Arab
- {0xAD050000u, 46u}, // fil -> Latn
- {0xCD050000u, 46u}, // fit -> Latn
- {0x666A0000u, 46u}, // fj -> Latn
- {0xC5650000u, 46u}, // flr -> Latn
- {0xBD850000u, 46u}, // fmp -> Latn
- {0x666F0000u, 46u}, // fo -> Latn
- {0x8DC50000u, 46u}, // fod -> Latn
- {0xB5C50000u, 46u}, // fon -> Latn
- {0xC5C50000u, 46u}, // for -> Latn
- {0x91E50000u, 46u}, // fpe -> Latn
- {0xCA050000u, 46u}, // fqs -> Latn
- {0x66720000u, 46u}, // fr -> Latn
- {0x8A250000u, 46u}, // frc -> Latn
- {0xBE250000u, 46u}, // frp -> Latn
- {0xC6250000u, 46u}, // frr -> Latn
- {0xCA250000u, 46u}, // frs -> Latn
+ {0xAD050000u, 45u}, // fil -> Latn
+ {0xCD050000u, 45u}, // fit -> Latn
+ {0x666A0000u, 45u}, // fj -> Latn
+ {0xC5650000u, 45u}, // flr -> Latn
+ {0xBD850000u, 45u}, // fmp -> Latn
+ {0x666F0000u, 45u}, // fo -> Latn
+ {0x8DC50000u, 45u}, // fod -> Latn
+ {0xB5C50000u, 45u}, // fon -> Latn
+ {0xC5C50000u, 45u}, // for -> Latn
+ {0x91E50000u, 45u}, // fpe -> Latn
+ {0xCA050000u, 45u}, // fqs -> Latn
+ {0x66720000u, 45u}, // fr -> Latn
+ {0x8A250000u, 45u}, // frc -> Latn
+ {0xBE250000u, 45u}, // frp -> Latn
+ {0xC6250000u, 45u}, // frr -> Latn
+ {0xCA250000u, 45u}, // frs -> Latn
{0x86850000u, 2u}, // fub -> Arab
- {0x8E850000u, 46u}, // fud -> Latn
- {0x92850000u, 46u}, // fue -> Latn
- {0x96850000u, 46u}, // fuf -> Latn
- {0x9E850000u, 46u}, // fuh -> Latn
- {0xC2850000u, 46u}, // fuq -> Latn
- {0xC6850000u, 46u}, // fur -> Latn
- {0xD6850000u, 46u}, // fuv -> Latn
- {0xE2850000u, 46u}, // fuy -> Latn
- {0xC6A50000u, 46u}, // fvr -> Latn
- {0x66790000u, 46u}, // fy -> Latn
- {0x67610000u, 46u}, // ga -> Latn
- {0x80060000u, 46u}, // gaa -> Latn
- {0x94060000u, 46u}, // gaf -> Latn
- {0x98060000u, 46u}, // gag -> Latn
- {0x9C060000u, 46u}, // gah -> Latn
- {0xA4060000u, 46u}, // gaj -> Latn
- {0xB0060000u, 46u}, // gam -> Latn
+ {0x8E850000u, 45u}, // fud -> Latn
+ {0x92850000u, 45u}, // fue -> Latn
+ {0x96850000u, 45u}, // fuf -> Latn
+ {0x9E850000u, 45u}, // fuh -> Latn
+ {0xC2850000u, 45u}, // fuq -> Latn
+ {0xC6850000u, 45u}, // fur -> Latn
+ {0xD6850000u, 45u}, // fuv -> Latn
+ {0xE2850000u, 45u}, // fuy -> Latn
+ {0xC6A50000u, 45u}, // fvr -> Latn
+ {0x66790000u, 45u}, // fy -> Latn
+ {0x67610000u, 45u}, // ga -> Latn
+ {0x80060000u, 45u}, // gaa -> Latn
+ {0x94060000u, 45u}, // gaf -> Latn
+ {0x98060000u, 45u}, // gag -> Latn
+ {0x9C060000u, 45u}, // gah -> Latn
+ {0xA4060000u, 45u}, // gaj -> Latn
+ {0xB0060000u, 45u}, // gam -> Latn
{0xB4060000u, 29u}, // gan -> Hans
- {0xD8060000u, 46u}, // gaw -> Latn
- {0xE0060000u, 46u}, // gay -> Latn
- {0x80260000u, 46u}, // gba -> Latn
- {0x94260000u, 46u}, // gbf -> Latn
+ {0xD8060000u, 45u}, // gaw -> Latn
+ {0xE0060000u, 45u}, // gay -> Latn
+ {0x80260000u, 45u}, // gba -> Latn
+ {0x94260000u, 45u}, // gbf -> Latn
{0xB0260000u, 19u}, // gbm -> Deva
- {0xE0260000u, 46u}, // gby -> Latn
+ {0xE0260000u, 45u}, // gby -> Latn
{0xE4260000u, 2u}, // gbz -> Arab
- {0xC4460000u, 46u}, // gcr -> Latn
- {0x67640000u, 46u}, // gd -> Latn
- {0x90660000u, 46u}, // gde -> Latn
- {0xB4660000u, 46u}, // gdn -> Latn
- {0xC4660000u, 46u}, // gdr -> Latn
- {0x84860000u, 46u}, // geb -> Latn
- {0xA4860000u, 46u}, // gej -> Latn
- {0xAC860000u, 46u}, // gel -> Latn
+ {0xC4460000u, 45u}, // gcr -> Latn
+ {0x67640000u, 45u}, // gd -> Latn
+ {0x90660000u, 45u}, // gde -> Latn
+ {0xB4660000u, 45u}, // gdn -> Latn
+ {0xC4660000u, 45u}, // gdr -> Latn
+ {0x84860000u, 45u}, // geb -> Latn
+ {0xA4860000u, 45u}, // gej -> Latn
+ {0xAC860000u, 45u}, // gel -> Latn
{0xE4860000u, 21u}, // gez -> Ethi
- {0xA8A60000u, 46u}, // gfk -> Latn
+ {0xA8A60000u, 45u}, // gfk -> Latn
{0xB4C60000u, 19u}, // ggn -> Deva
- {0xC8E60000u, 46u}, // ghs -> Latn
- {0xAD060000u, 46u}, // gil -> Latn
- {0xB1060000u, 46u}, // gim -> Latn
+ {0xC8E60000u, 45u}, // ghs -> Latn
+ {0xAD060000u, 45u}, // gil -> Latn
+ {0xB1060000u, 45u}, // gim -> Latn
{0xA9260000u, 2u}, // gjk -> Arab
- {0xB5260000u, 46u}, // gjn -> Latn
+ {0xB5260000u, 45u}, // gjn -> Latn
{0xD1260000u, 2u}, // gju -> Arab
- {0xB5460000u, 46u}, // gkn -> Latn
- {0xBD460000u, 46u}, // gkp -> Latn
- {0x676C0000u, 46u}, // gl -> Latn
+ {0xB5460000u, 45u}, // gkn -> Latn
+ {0xBD460000u, 45u}, // gkp -> Latn
+ {0x676C0000u, 45u}, // gl -> Latn
{0xA9660000u, 2u}, // glk -> Arab
- {0xB1860000u, 46u}, // gmm -> Latn
+ {0xB1860000u, 45u}, // gmm -> Latn
{0xD5860000u, 21u}, // gmv -> Ethi
- {0x676E0000u, 46u}, // gn -> Latn
- {0x8DA60000u, 46u}, // gnd -> Latn
- {0x99A60000u, 46u}, // gng -> Latn
- {0x8DC60000u, 46u}, // god -> Latn
+ {0x676E0000u, 45u}, // gn -> Latn
+ {0x8DA60000u, 45u}, // gnd -> Latn
+ {0x99A60000u, 45u}, // gng -> Latn
+ {0x8DC60000u, 45u}, // god -> Latn
{0x95C60000u, 21u}, // gof -> Ethi
- {0xA1C60000u, 46u}, // goi -> Latn
+ {0xA1C60000u, 45u}, // goi -> Latn
{0xB1C60000u, 19u}, // gom -> Deva
- {0xB5C60000u, 88u}, // gon -> Telu
- {0xC5C60000u, 46u}, // gor -> Latn
- {0xC9C60000u, 46u}, // gos -> Latn
+ {0xB5C60000u, 89u}, // gon -> Telu
+ {0xC5C60000u, 45u}, // gor -> Latn
+ {0xC9C60000u, 45u}, // gos -> Latn
{0xCDC60000u, 25u}, // got -> Goth
- {0x86260000u, 46u}, // grb -> Latn
+ {0x86260000u, 45u}, // grb -> Latn
{0x8A260000u, 17u}, // grc -> Cprt
{0xCE260000u, 8u}, // grt -> Beng
- {0xDA260000u, 46u}, // grw -> Latn
- {0xDA460000u, 46u}, // gsw -> Latn
+ {0xDA260000u, 45u}, // grw -> Latn
+ {0xDA460000u, 45u}, // gsw -> Latn
{0x67750000u, 27u}, // gu -> Gujr
- {0x86860000u, 46u}, // gub -> Latn
- {0x8A860000u, 46u}, // guc -> Latn
- {0x8E860000u, 46u}, // gud -> Latn
- {0xC6860000u, 46u}, // gur -> Latn
- {0xDA860000u, 46u}, // guw -> Latn
- {0xDE860000u, 46u}, // gux -> Latn
- {0xE6860000u, 46u}, // guz -> Latn
- {0x67760000u, 46u}, // gv -> Latn
- {0x96A60000u, 46u}, // gvf -> Latn
+ {0x86860000u, 45u}, // gub -> Latn
+ {0x8A860000u, 45u}, // guc -> Latn
+ {0x8E860000u, 45u}, // gud -> Latn
+ {0xC6860000u, 45u}, // gur -> Latn
+ {0xDA860000u, 45u}, // guw -> Latn
+ {0xDE860000u, 45u}, // gux -> Latn
+ {0xE6860000u, 45u}, // guz -> Latn
+ {0x67760000u, 45u}, // gv -> Latn
+ {0x96A60000u, 45u}, // gvf -> Latn
{0xC6A60000u, 19u}, // gvr -> Deva
- {0xCAA60000u, 46u}, // gvs -> Latn
+ {0xCAA60000u, 45u}, // gvs -> Latn
{0x8AC60000u, 2u}, // gwc -> Arab
- {0xA2C60000u, 46u}, // gwi -> Latn
+ {0xA2C60000u, 45u}, // gwi -> Latn
{0xCEC60000u, 2u}, // gwt -> Arab
- {0xA3060000u, 46u}, // gyi -> Latn
- {0x68610000u, 46u}, // ha -> Latn
+ {0xA3060000u, 45u}, // gyi -> Latn
+ {0x68610000u, 45u}, // ha -> Latn
{0x6861434Du, 2u}, // ha-CM -> Arab
{0x68615344u, 2u}, // ha-SD -> Arab
- {0x98070000u, 46u}, // hag -> Latn
+ {0x98070000u, 45u}, // hag -> Latn
{0xA8070000u, 29u}, // hak -> Hans
- {0xB0070000u, 46u}, // ham -> Latn
- {0xD8070000u, 46u}, // haw -> Latn
+ {0xB0070000u, 45u}, // ham -> Latn
+ {0xD8070000u, 45u}, // haw -> Latn
{0xE4070000u, 2u}, // haz -> Arab
- {0x84270000u, 46u}, // hbb -> Latn
+ {0x84270000u, 45u}, // hbb -> Latn
{0xE0670000u, 21u}, // hdy -> Ethi
{0x68650000u, 31u}, // he -> Hebr
- {0xE0E70000u, 46u}, // hhy -> Latn
+ {0xE0E70000u, 45u}, // hhy -> Latn
{0x68690000u, 19u}, // hi -> Deva
- {0x81070000u, 46u}, // hia -> Latn
- {0x95070000u, 46u}, // hif -> Latn
- {0x99070000u, 46u}, // hig -> Latn
- {0x9D070000u, 46u}, // hih -> Latn
- {0xAD070000u, 46u}, // hil -> Latn
- {0x81670000u, 46u}, // hla -> Latn
+ {0x81070000u, 45u}, // hia -> Latn
+ {0x95070000u, 45u}, // hif -> Latn
+ {0x99070000u, 45u}, // hig -> Latn
+ {0x9D070000u, 45u}, // hih -> Latn
+ {0xAD070000u, 45u}, // hil -> Latn
+ {0x81670000u, 45u}, // hla -> Latn
{0xD1670000u, 32u}, // hlu -> Hluw
{0x8D870000u, 71u}, // hmd -> Plrd
- {0xCD870000u, 46u}, // hmt -> Latn
+ {0xCD870000u, 45u}, // hmt -> Latn
{0x8DA70000u, 2u}, // hnd -> Arab
{0x91A70000u, 19u}, // hne -> Deva
- {0xA5A70000u, 33u}, // hnj -> Hmng
- {0xB5A70000u, 46u}, // hnn -> Latn
+ {0xA5A70000u, 33u}, // hnj -> Hmnp
+ {0xB5A70000u, 45u}, // hnn -> Latn
{0xB9A70000u, 2u}, // hno -> Arab
- {0x686F0000u, 46u}, // ho -> Latn
+ {0x686F0000u, 45u}, // ho -> Latn
{0x89C70000u, 19u}, // hoc -> Deva
{0xA5C70000u, 19u}, // hoj -> Deva
- {0xCDC70000u, 46u}, // hot -> Latn
- {0x68720000u, 46u}, // hr -> Latn
- {0x86470000u, 46u}, // hsb -> Latn
+ {0xCDC70000u, 45u}, // hot -> Latn
+ {0x68720000u, 45u}, // hr -> Latn
+ {0x86470000u, 45u}, // hsb -> Latn
{0xB6470000u, 29u}, // hsn -> Hans
- {0x68740000u, 46u}, // ht -> Latn
- {0x68750000u, 46u}, // hu -> Latn
- {0xA2870000u, 46u}, // hui -> Latn
+ {0x68740000u, 45u}, // ht -> Latn
+ {0x68750000u, 45u}, // hu -> Latn
+ {0xA2870000u, 45u}, // hui -> Latn
{0x68790000u, 4u}, // hy -> Armn
- {0x687A0000u, 46u}, // hz -> Latn
- {0x69610000u, 46u}, // ia -> Latn
- {0xB4080000u, 46u}, // ian -> Latn
- {0xC4080000u, 46u}, // iar -> Latn
- {0x80280000u, 46u}, // iba -> Latn
- {0x84280000u, 46u}, // ibb -> Latn
- {0xE0280000u, 46u}, // iby -> Latn
- {0x80480000u, 46u}, // ica -> Latn
- {0x9C480000u, 46u}, // ich -> Latn
- {0x69640000u, 46u}, // id -> Latn
- {0x8C680000u, 46u}, // idd -> Latn
- {0xA0680000u, 46u}, // idi -> Latn
- {0xD0680000u, 46u}, // idu -> Latn
- {0x90A80000u, 46u}, // ife -> Latn
- {0x69670000u, 46u}, // ig -> Latn
- {0x84C80000u, 46u}, // igb -> Latn
- {0x90C80000u, 46u}, // ige -> Latn
- {0x69690000u, 98u}, // ii -> Yiii
- {0xA5280000u, 46u}, // ijj -> Latn
- {0x696B0000u, 46u}, // ik -> Latn
- {0xA9480000u, 46u}, // ikk -> Latn
- {0xCD480000u, 46u}, // ikt -> Latn
- {0xD9480000u, 46u}, // ikw -> Latn
- {0xDD480000u, 46u}, // ikx -> Latn
- {0xB9680000u, 46u}, // ilo -> Latn
- {0xB9880000u, 46u}, // imo -> Latn
- {0x696E0000u, 46u}, // in -> Latn
+ {0x687A0000u, 45u}, // hz -> Latn
+ {0x69610000u, 45u}, // ia -> Latn
+ {0xB4080000u, 45u}, // ian -> Latn
+ {0xC4080000u, 45u}, // iar -> Latn
+ {0x80280000u, 45u}, // iba -> Latn
+ {0x84280000u, 45u}, // ibb -> Latn
+ {0xE0280000u, 45u}, // iby -> Latn
+ {0x80480000u, 45u}, // ica -> Latn
+ {0x9C480000u, 45u}, // ich -> Latn
+ {0x69640000u, 45u}, // id -> Latn
+ {0x8C680000u, 45u}, // idd -> Latn
+ {0xA0680000u, 45u}, // idi -> Latn
+ {0xD0680000u, 45u}, // idu -> Latn
+ {0x90A80000u, 45u}, // ife -> Latn
+ {0x69670000u, 45u}, // ig -> Latn
+ {0x84C80000u, 45u}, // igb -> Latn
+ {0x90C80000u, 45u}, // ige -> Latn
+ {0x69690000u, 101u}, // ii -> Yiii
+ {0xA5280000u, 45u}, // ijj -> Latn
+ {0x696B0000u, 45u}, // ik -> Latn
+ {0xA9480000u, 45u}, // ikk -> Latn
+ {0xCD480000u, 45u}, // ikt -> Latn
+ {0xD9480000u, 45u}, // ikw -> Latn
+ {0xDD480000u, 45u}, // ikx -> Latn
+ {0xB9680000u, 45u}, // ilo -> Latn
+ {0xB9880000u, 45u}, // imo -> Latn
+ {0x696E0000u, 45u}, // in -> Latn
{0x9DA80000u, 18u}, // inh -> Cyrl
- {0x696F0000u, 46u}, // io -> Latn
- {0xD1C80000u, 46u}, // iou -> Latn
- {0xA2280000u, 46u}, // iri -> Latn
- {0x69730000u, 46u}, // is -> Latn
- {0x69740000u, 46u}, // it -> Latn
+ {0x696F0000u, 45u}, // io -> Latn
+ {0xD1C80000u, 45u}, // iou -> Latn
+ {0xA2280000u, 45u}, // iri -> Latn
+ {0x69730000u, 45u}, // is -> Latn
+ {0x69740000u, 45u}, // it -> Latn
{0x69750000u, 11u}, // iu -> Cans
{0x69770000u, 31u}, // iw -> Hebr
- {0xB2C80000u, 46u}, // iwm -> Latn
- {0xCAC80000u, 46u}, // iws -> Latn
- {0x9F280000u, 46u}, // izh -> Latn
- {0xA3280000u, 46u}, // izi -> Latn
- {0x6A610000u, 36u}, // ja -> Jpan
- {0x84090000u, 46u}, // jab -> Latn
- {0xB0090000u, 46u}, // jam -> Latn
- {0xC4090000u, 46u}, // jar -> Latn
- {0xB8290000u, 46u}, // jbo -> Latn
- {0xD0290000u, 46u}, // jbu -> Latn
- {0xB4890000u, 46u}, // jen -> Latn
- {0xA8C90000u, 46u}, // jgk -> Latn
- {0xB8C90000u, 46u}, // jgo -> Latn
+ {0xB2C80000u, 45u}, // iwm -> Latn
+ {0xCAC80000u, 45u}, // iws -> Latn
+ {0x9F280000u, 45u}, // izh -> Latn
+ {0xA3280000u, 45u}, // izi -> Latn
+ {0x6A610000u, 35u}, // ja -> Jpan
+ {0x84090000u, 45u}, // jab -> Latn
+ {0xB0090000u, 45u}, // jam -> Latn
+ {0xC4090000u, 45u}, // jar -> Latn
+ {0xB8290000u, 45u}, // jbo -> Latn
+ {0xD0290000u, 45u}, // jbu -> Latn
+ {0xB4890000u, 45u}, // jen -> Latn
+ {0xA8C90000u, 45u}, // jgk -> Latn
+ {0xB8C90000u, 45u}, // jgo -> Latn
{0x6A690000u, 31u}, // ji -> Hebr
- {0x85090000u, 46u}, // jib -> Latn
- {0x89890000u, 46u}, // jmc -> Latn
+ {0x85090000u, 45u}, // jib -> Latn
+ {0x89890000u, 45u}, // jmc -> Latn
{0xAD890000u, 19u}, // jml -> Deva
- {0x82290000u, 46u}, // jra -> Latn
- {0xCE890000u, 46u}, // jut -> Latn
- {0x6A760000u, 46u}, // jv -> Latn
- {0x6A770000u, 46u}, // jw -> Latn
+ {0x82290000u, 45u}, // jra -> Latn
+ {0xCE890000u, 45u}, // jut -> Latn
+ {0x6A760000u, 45u}, // jv -> Latn
+ {0x6A770000u, 45u}, // jw -> Latn
{0x6B610000u, 22u}, // ka -> Geor
{0x800A0000u, 18u}, // kaa -> Cyrl
- {0x840A0000u, 46u}, // kab -> Latn
- {0x880A0000u, 46u}, // kac -> Latn
- {0x8C0A0000u, 46u}, // kad -> Latn
- {0xA00A0000u, 46u}, // kai -> Latn
- {0xA40A0000u, 46u}, // kaj -> Latn
- {0xB00A0000u, 46u}, // kam -> Latn
- {0xB80A0000u, 46u}, // kao -> Latn
+ {0x840A0000u, 45u}, // kab -> Latn
+ {0x880A0000u, 45u}, // kac -> Latn
+ {0x8C0A0000u, 45u}, // kad -> Latn
+ {0xA00A0000u, 45u}, // kai -> Latn
+ {0xA40A0000u, 45u}, // kaj -> Latn
+ {0xB00A0000u, 45u}, // kam -> Latn
+ {0xB80A0000u, 45u}, // kao -> Latn
{0x8C2A0000u, 18u}, // kbd -> Cyrl
- {0xB02A0000u, 46u}, // kbm -> Latn
- {0xBC2A0000u, 46u}, // kbp -> Latn
- {0xC02A0000u, 46u}, // kbq -> Latn
- {0xDC2A0000u, 46u}, // kbx -> Latn
+ {0xB02A0000u, 45u}, // kbm -> Latn
+ {0xBC2A0000u, 45u}, // kbp -> Latn
+ {0xC02A0000u, 45u}, // kbq -> Latn
+ {0xDC2A0000u, 45u}, // kbx -> Latn
{0xE02A0000u, 2u}, // kby -> Arab
- {0x984A0000u, 46u}, // kcg -> Latn
- {0xA84A0000u, 46u}, // kck -> Latn
- {0xAC4A0000u, 46u}, // kcl -> Latn
- {0xCC4A0000u, 46u}, // kct -> Latn
- {0x906A0000u, 46u}, // kde -> Latn
- {0x9C6A0000u, 2u}, // kdh -> Arab
- {0xAC6A0000u, 46u}, // kdl -> Latn
- {0xCC6A0000u, 91u}, // kdt -> Thai
- {0x808A0000u, 46u}, // kea -> Latn
- {0xB48A0000u, 46u}, // ken -> Latn
- {0xE48A0000u, 46u}, // kez -> Latn
- {0xB8AA0000u, 46u}, // kfo -> Latn
+ {0x984A0000u, 45u}, // kcg -> Latn
+ {0xA84A0000u, 45u}, // kck -> Latn
+ {0xAC4A0000u, 45u}, // kcl -> Latn
+ {0xCC4A0000u, 45u}, // kct -> Latn
+ {0x906A0000u, 45u}, // kde -> Latn
+ {0x9C6A0000u, 45u}, // kdh -> Latn
+ {0xAC6A0000u, 45u}, // kdl -> Latn
+ {0xCC6A0000u, 92u}, // kdt -> Thai
+ {0x808A0000u, 45u}, // kea -> Latn
+ {0xB48A0000u, 45u}, // ken -> Latn
+ {0xE48A0000u, 45u}, // kez -> Latn
+ {0xB8AA0000u, 45u}, // kfo -> Latn
{0xC4AA0000u, 19u}, // kfr -> Deva
{0xE0AA0000u, 19u}, // kfy -> Deva
- {0x6B670000u, 46u}, // kg -> Latn
- {0x90CA0000u, 46u}, // kge -> Latn
- {0x94CA0000u, 46u}, // kgf -> Latn
- {0xBCCA0000u, 46u}, // kgp -> Latn
- {0x80EA0000u, 46u}, // kha -> Latn
- {0x84EA0000u, 84u}, // khb -> Talu
+ {0x6B670000u, 45u}, // kg -> Latn
+ {0x90CA0000u, 45u}, // kge -> Latn
+ {0x94CA0000u, 45u}, // kgf -> Latn
+ {0xBCCA0000u, 45u}, // kgp -> Latn
+ {0x80EA0000u, 45u}, // kha -> Latn
+ {0x84EA0000u, 85u}, // khb -> Talu
{0xB4EA0000u, 19u}, // khn -> Deva
- {0xC0EA0000u, 46u}, // khq -> Latn
- {0xC8EA0000u, 46u}, // khs -> Latn
- {0xCCEA0000u, 59u}, // kht -> Mymr
+ {0xC0EA0000u, 45u}, // khq -> Latn
+ {0xC8EA0000u, 45u}, // khs -> Latn
+ {0xCCEA0000u, 58u}, // kht -> Mymr
{0xD8EA0000u, 2u}, // khw -> Arab
- {0xE4EA0000u, 46u}, // khz -> Latn
- {0x6B690000u, 46u}, // ki -> Latn
- {0xA50A0000u, 46u}, // kij -> Latn
- {0xD10A0000u, 46u}, // kiu -> Latn
- {0xD90A0000u, 46u}, // kiw -> Latn
- {0x6B6A0000u, 46u}, // kj -> Latn
- {0x8D2A0000u, 46u}, // kjd -> Latn
- {0x992A0000u, 45u}, // kjg -> Laoo
- {0xC92A0000u, 46u}, // kjs -> Latn
- {0xE12A0000u, 46u}, // kjy -> Latn
+ {0xE4EA0000u, 45u}, // khz -> Latn
+ {0x6B690000u, 45u}, // ki -> Latn
+ {0xA50A0000u, 45u}, // kij -> Latn
+ {0xD10A0000u, 45u}, // kiu -> Latn
+ {0xD90A0000u, 45u}, // kiw -> Latn
+ {0x6B6A0000u, 45u}, // kj -> Latn
+ {0x8D2A0000u, 45u}, // kjd -> Latn
+ {0x992A0000u, 44u}, // kjg -> Laoo
+ {0xC92A0000u, 45u}, // kjs -> Latn
+ {0xE12A0000u, 45u}, // kjy -> Latn
{0x6B6B0000u, 18u}, // kk -> Cyrl
{0x6B6B4146u, 2u}, // kk-AF -> Arab
{0x6B6B434Eu, 2u}, // kk-CN -> Arab
{0x6B6B4952u, 2u}, // kk-IR -> Arab
{0x6B6B4D4Eu, 2u}, // kk-MN -> Arab
- {0x894A0000u, 46u}, // kkc -> Latn
- {0xA54A0000u, 46u}, // kkj -> Latn
- {0x6B6C0000u, 46u}, // kl -> Latn
- {0xB56A0000u, 46u}, // kln -> Latn
- {0xC16A0000u, 46u}, // klq -> Latn
- {0xCD6A0000u, 46u}, // klt -> Latn
- {0xDD6A0000u, 46u}, // klx -> Latn
- {0x6B6D0000u, 40u}, // km -> Khmr
- {0x858A0000u, 46u}, // kmb -> Latn
- {0x9D8A0000u, 46u}, // kmh -> Latn
- {0xB98A0000u, 46u}, // kmo -> Latn
- {0xC98A0000u, 46u}, // kms -> Latn
- {0xD18A0000u, 46u}, // kmu -> Latn
- {0xD98A0000u, 46u}, // kmw -> Latn
- {0x6B6E0000u, 42u}, // kn -> Knda
- {0x95AA0000u, 46u}, // knf -> Latn
- {0xBDAA0000u, 46u}, // knp -> Latn
- {0x6B6F0000u, 43u}, // ko -> Kore
+ {0x894A0000u, 45u}, // kkc -> Latn
+ {0xA54A0000u, 45u}, // kkj -> Latn
+ {0x6B6C0000u, 45u}, // kl -> Latn
+ {0xB56A0000u, 45u}, // kln -> Latn
+ {0xC16A0000u, 45u}, // klq -> Latn
+ {0xCD6A0000u, 45u}, // klt -> Latn
+ {0xDD6A0000u, 45u}, // klx -> Latn
+ {0x6B6D0000u, 39u}, // km -> Khmr
+ {0x858A0000u, 45u}, // kmb -> Latn
+ {0x9D8A0000u, 45u}, // kmh -> Latn
+ {0xB98A0000u, 45u}, // kmo -> Latn
+ {0xC98A0000u, 45u}, // kms -> Latn
+ {0xD18A0000u, 45u}, // kmu -> Latn
+ {0xD98A0000u, 45u}, // kmw -> Latn
+ {0x6B6E0000u, 41u}, // kn -> Knda
+ {0x95AA0000u, 45u}, // knf -> Latn
+ {0xBDAA0000u, 45u}, // knp -> Latn
+ {0x6B6F0000u, 42u}, // ko -> Kore
{0xA1CA0000u, 18u}, // koi -> Cyrl
{0xA9CA0000u, 19u}, // kok -> Deva
- {0xADCA0000u, 46u}, // kol -> Latn
- {0xC9CA0000u, 46u}, // kos -> Latn
- {0xE5CA0000u, 46u}, // koz -> Latn
- {0x91EA0000u, 46u}, // kpe -> Latn
- {0x95EA0000u, 46u}, // kpf -> Latn
- {0xB9EA0000u, 46u}, // kpo -> Latn
- {0xC5EA0000u, 46u}, // kpr -> Latn
- {0xDDEA0000u, 46u}, // kpx -> Latn
- {0x860A0000u, 46u}, // kqb -> Latn
- {0x960A0000u, 46u}, // kqf -> Latn
- {0xCA0A0000u, 46u}, // kqs -> Latn
+ {0xADCA0000u, 45u}, // kol -> Latn
+ {0xC9CA0000u, 45u}, // kos -> Latn
+ {0xE5CA0000u, 45u}, // koz -> Latn
+ {0x91EA0000u, 45u}, // kpe -> Latn
+ {0x95EA0000u, 45u}, // kpf -> Latn
+ {0xB9EA0000u, 45u}, // kpo -> Latn
+ {0xC5EA0000u, 45u}, // kpr -> Latn
+ {0xDDEA0000u, 45u}, // kpx -> Latn
+ {0x860A0000u, 45u}, // kqb -> Latn
+ {0x960A0000u, 45u}, // kqf -> Latn
+ {0xCA0A0000u, 45u}, // kqs -> Latn
{0xE20A0000u, 21u}, // kqy -> Ethi
- {0x6B720000u, 46u}, // kr -> Latn
+ {0x6B720000u, 45u}, // kr -> Latn
{0x8A2A0000u, 18u}, // krc -> Cyrl
- {0xA22A0000u, 46u}, // kri -> Latn
- {0xA62A0000u, 46u}, // krj -> Latn
- {0xAE2A0000u, 46u}, // krl -> Latn
- {0xCA2A0000u, 46u}, // krs -> Latn
+ {0xA22A0000u, 45u}, // kri -> Latn
+ {0xA62A0000u, 45u}, // krj -> Latn
+ {0xAE2A0000u, 45u}, // krl -> Latn
+ {0xCA2A0000u, 45u}, // krs -> Latn
{0xD22A0000u, 19u}, // kru -> Deva
{0x6B730000u, 2u}, // ks -> Arab
- {0x864A0000u, 46u}, // ksb -> Latn
- {0x8E4A0000u, 46u}, // ksd -> Latn
- {0x964A0000u, 46u}, // ksf -> Latn
- {0x9E4A0000u, 46u}, // ksh -> Latn
- {0xA64A0000u, 46u}, // ksj -> Latn
- {0xC64A0000u, 46u}, // ksr -> Latn
+ {0x864A0000u, 45u}, // ksb -> Latn
+ {0x8E4A0000u, 45u}, // ksd -> Latn
+ {0x964A0000u, 45u}, // ksf -> Latn
+ {0x9E4A0000u, 45u}, // ksh -> Latn
+ {0xA64A0000u, 45u}, // ksj -> Latn
+ {0xC64A0000u, 45u}, // ksr -> Latn
{0x866A0000u, 21u}, // ktb -> Ethi
- {0xB26A0000u, 46u}, // ktm -> Latn
- {0xBA6A0000u, 46u}, // kto -> Latn
- {0xC66A0000u, 46u}, // ktr -> Latn
- {0x6B750000u, 46u}, // ku -> Latn
+ {0xB26A0000u, 45u}, // ktm -> Latn
+ {0xBA6A0000u, 45u}, // kto -> Latn
+ {0xC66A0000u, 45u}, // ktr -> Latn
+ {0x6B750000u, 45u}, // ku -> Latn
{0x6B754952u, 2u}, // ku-IR -> Arab
{0x6B754C42u, 2u}, // ku-LB -> Arab
- {0x868A0000u, 46u}, // kub -> Latn
- {0x8E8A0000u, 46u}, // kud -> Latn
- {0x928A0000u, 46u}, // kue -> Latn
- {0xA68A0000u, 46u}, // kuj -> Latn
+ {0x868A0000u, 45u}, // kub -> Latn
+ {0x8E8A0000u, 45u}, // kud -> Latn
+ {0x928A0000u, 45u}, // kue -> Latn
+ {0xA68A0000u, 45u}, // kuj -> Latn
{0xB28A0000u, 18u}, // kum -> Cyrl
- {0xB68A0000u, 46u}, // kun -> Latn
- {0xBE8A0000u, 46u}, // kup -> Latn
- {0xCA8A0000u, 46u}, // kus -> Latn
+ {0xB68A0000u, 45u}, // kun -> Latn
+ {0xBE8A0000u, 45u}, // kup -> Latn
+ {0xCA8A0000u, 45u}, // kus -> Latn
{0x6B760000u, 18u}, // kv -> Cyrl
- {0x9AAA0000u, 46u}, // kvg -> Latn
- {0xC6AA0000u, 46u}, // kvr -> Latn
+ {0x9AAA0000u, 45u}, // kvg -> Latn
+ {0xC6AA0000u, 45u}, // kvr -> Latn
{0xDEAA0000u, 2u}, // kvx -> Arab
- {0x6B770000u, 46u}, // kw -> Latn
- {0xA6CA0000u, 46u}, // kwj -> Latn
- {0xBACA0000u, 46u}, // kwo -> Latn
- {0xC2CA0000u, 46u}, // kwq -> Latn
- {0x82EA0000u, 46u}, // kxa -> Latn
+ {0x6B770000u, 45u}, // kw -> Latn
+ {0xA6CA0000u, 45u}, // kwj -> Latn
+ {0xBACA0000u, 45u}, // kwo -> Latn
+ {0xC2CA0000u, 45u}, // kwq -> Latn
+ {0x82EA0000u, 45u}, // kxa -> Latn
{0x8AEA0000u, 21u}, // kxc -> Ethi
- {0x92EA0000u, 46u}, // kxe -> Latn
+ {0x92EA0000u, 45u}, // kxe -> Latn
{0xAEEA0000u, 19u}, // kxl -> Deva
- {0xB2EA0000u, 91u}, // kxm -> Thai
+ {0xB2EA0000u, 92u}, // kxm -> Thai
{0xBEEA0000u, 2u}, // kxp -> Arab
- {0xDAEA0000u, 46u}, // kxw -> Latn
- {0xE6EA0000u, 46u}, // kxz -> Latn
+ {0xDAEA0000u, 45u}, // kxw -> Latn
+ {0xE6EA0000u, 45u}, // kxz -> Latn
{0x6B790000u, 18u}, // ky -> Cyrl
{0x6B79434Eu, 2u}, // ky-CN -> Arab
- {0x6B795452u, 46u}, // ky-TR -> Latn
- {0x930A0000u, 46u}, // kye -> Latn
- {0xDF0A0000u, 46u}, // kyx -> Latn
+ {0x6B795452u, 45u}, // ky-TR -> Latn
+ {0x930A0000u, 45u}, // kye -> Latn
+ {0xDF0A0000u, 45u}, // kyx -> Latn
{0x9F2A0000u, 2u}, // kzh -> Arab
- {0xA72A0000u, 46u}, // kzj -> Latn
- {0xC72A0000u, 46u}, // kzr -> Latn
- {0xCF2A0000u, 46u}, // kzt -> Latn
- {0x6C610000u, 46u}, // la -> Latn
- {0x840B0000u, 48u}, // lab -> Lina
+ {0xA72A0000u, 45u}, // kzj -> Latn
+ {0xC72A0000u, 45u}, // kzr -> Latn
+ {0xCF2A0000u, 45u}, // kzt -> Latn
+ {0x6C610000u, 45u}, // la -> Latn
+ {0x840B0000u, 47u}, // lab -> Lina
{0x8C0B0000u, 31u}, // lad -> Hebr
- {0x980B0000u, 46u}, // lag -> Latn
+ {0x980B0000u, 45u}, // lag -> Latn
{0x9C0B0000u, 2u}, // lah -> Arab
- {0xA40B0000u, 46u}, // laj -> Latn
- {0xC80B0000u, 46u}, // las -> Latn
- {0x6C620000u, 46u}, // lb -> Latn
+ {0xA40B0000u, 45u}, // laj -> Latn
+ {0xC80B0000u, 45u}, // las -> Latn
+ {0x6C620000u, 45u}, // lb -> Latn
{0x902B0000u, 18u}, // lbe -> Cyrl
- {0xD02B0000u, 46u}, // lbu -> Latn
- {0xD82B0000u, 46u}, // lbw -> Latn
- {0xB04B0000u, 46u}, // lcm -> Latn
- {0xBC4B0000u, 91u}, // lcp -> Thai
- {0x846B0000u, 46u}, // ldb -> Latn
- {0x8C8B0000u, 46u}, // led -> Latn
- {0x908B0000u, 46u}, // lee -> Latn
- {0xB08B0000u, 46u}, // lem -> Latn
- {0xBC8B0000u, 47u}, // lep -> Lepc
- {0xC08B0000u, 46u}, // leq -> Latn
- {0xD08B0000u, 46u}, // leu -> Latn
+ {0xD02B0000u, 45u}, // lbu -> Latn
+ {0xD82B0000u, 45u}, // lbw -> Latn
+ {0xB04B0000u, 45u}, // lcm -> Latn
+ {0xBC4B0000u, 92u}, // lcp -> Thai
+ {0x846B0000u, 45u}, // ldb -> Latn
+ {0x8C8B0000u, 45u}, // led -> Latn
+ {0x908B0000u, 45u}, // lee -> Latn
+ {0xB08B0000u, 45u}, // lem -> Latn
+ {0xBC8B0000u, 46u}, // lep -> Lepc
+ {0xC08B0000u, 45u}, // leq -> Latn
+ {0xD08B0000u, 45u}, // leu -> Latn
{0xE48B0000u, 18u}, // lez -> Cyrl
- {0x6C670000u, 46u}, // lg -> Latn
- {0x98CB0000u, 46u}, // lgg -> Latn
- {0x6C690000u, 46u}, // li -> Latn
- {0x810B0000u, 46u}, // lia -> Latn
- {0x8D0B0000u, 46u}, // lid -> Latn
+ {0x6C670000u, 45u}, // lg -> Latn
+ {0x98CB0000u, 45u}, // lgg -> Latn
+ {0x6C690000u, 45u}, // li -> Latn
+ {0x810B0000u, 45u}, // lia -> Latn
+ {0x8D0B0000u, 45u}, // lid -> Latn
{0x950B0000u, 19u}, // lif -> Deva
- {0x990B0000u, 46u}, // lig -> Latn
- {0x9D0B0000u, 46u}, // lih -> Latn
- {0xA50B0000u, 46u}, // lij -> Latn
- {0xC90B0000u, 49u}, // lis -> Lisu
- {0xBD2B0000u, 46u}, // ljp -> Latn
+ {0x990B0000u, 45u}, // lig -> Latn
+ {0x9D0B0000u, 45u}, // lih -> Latn
+ {0xA50B0000u, 45u}, // lij -> Latn
+ {0xC90B0000u, 48u}, // lis -> Lisu
+ {0xBD2B0000u, 45u}, // ljp -> Latn
{0xA14B0000u, 2u}, // lki -> Arab
- {0xCD4B0000u, 46u}, // lkt -> Latn
- {0x916B0000u, 46u}, // lle -> Latn
- {0xB56B0000u, 46u}, // lln -> Latn
- {0xB58B0000u, 88u}, // lmn -> Telu
- {0xB98B0000u, 46u}, // lmo -> Latn
- {0xBD8B0000u, 46u}, // lmp -> Latn
- {0x6C6E0000u, 46u}, // ln -> Latn
- {0xC9AB0000u, 46u}, // lns -> Latn
- {0xD1AB0000u, 46u}, // lnu -> Latn
- {0x6C6F0000u, 45u}, // lo -> Laoo
- {0xA5CB0000u, 46u}, // loj -> Latn
- {0xA9CB0000u, 46u}, // lok -> Latn
- {0xADCB0000u, 46u}, // lol -> Latn
- {0xC5CB0000u, 46u}, // lor -> Latn
- {0xC9CB0000u, 46u}, // los -> Latn
- {0xE5CB0000u, 46u}, // loz -> Latn
+ {0xCD4B0000u, 45u}, // lkt -> Latn
+ {0x916B0000u, 45u}, // lle -> Latn
+ {0xB56B0000u, 45u}, // lln -> Latn
+ {0xB58B0000u, 89u}, // lmn -> Telu
+ {0xB98B0000u, 45u}, // lmo -> Latn
+ {0xBD8B0000u, 45u}, // lmp -> Latn
+ {0x6C6E0000u, 45u}, // ln -> Latn
+ {0xC9AB0000u, 45u}, // lns -> Latn
+ {0xD1AB0000u, 45u}, // lnu -> Latn
+ {0x6C6F0000u, 44u}, // lo -> Laoo
+ {0xA5CB0000u, 45u}, // loj -> Latn
+ {0xA9CB0000u, 45u}, // lok -> Latn
+ {0xADCB0000u, 45u}, // lol -> Latn
+ {0xC5CB0000u, 45u}, // lor -> Latn
+ {0xC9CB0000u, 45u}, // los -> Latn
+ {0xE5CB0000u, 45u}, // loz -> Latn
{0x8A2B0000u, 2u}, // lrc -> Arab
- {0x6C740000u, 46u}, // lt -> Latn
- {0x9A6B0000u, 46u}, // ltg -> Latn
- {0x6C750000u, 46u}, // lu -> Latn
- {0x828B0000u, 46u}, // lua -> Latn
- {0xBA8B0000u, 46u}, // luo -> Latn
- {0xE28B0000u, 46u}, // luy -> Latn
+ {0x6C740000u, 45u}, // lt -> Latn
+ {0x9A6B0000u, 45u}, // ltg -> Latn
+ {0x6C750000u, 45u}, // lu -> Latn
+ {0x828B0000u, 45u}, // lua -> Latn
+ {0xBA8B0000u, 45u}, // luo -> Latn
+ {0xE28B0000u, 45u}, // luy -> Latn
{0xE68B0000u, 2u}, // luz -> Arab
- {0x6C760000u, 46u}, // lv -> Latn
- {0xAECB0000u, 91u}, // lwl -> Thai
+ {0x6C760000u, 45u}, // lv -> Latn
+ {0xAECB0000u, 92u}, // lwl -> Thai
{0x9F2B0000u, 29u}, // lzh -> Hans
- {0xE72B0000u, 46u}, // lzz -> Latn
- {0x8C0C0000u, 46u}, // mad -> Latn
- {0x940C0000u, 46u}, // maf -> Latn
+ {0xE72B0000u, 45u}, // lzz -> Latn
+ {0x8C0C0000u, 45u}, // mad -> Latn
+ {0x940C0000u, 45u}, // maf -> Latn
{0x980C0000u, 19u}, // mag -> Deva
{0xA00C0000u, 19u}, // mai -> Deva
- {0xA80C0000u, 46u}, // mak -> Latn
- {0xB40C0000u, 46u}, // man -> Latn
- {0xB40C474Eu, 61u}, // man-GN -> Nkoo
- {0xC80C0000u, 46u}, // mas -> Latn
- {0xD80C0000u, 46u}, // maw -> Latn
- {0xE40C0000u, 46u}, // maz -> Latn
- {0x9C2C0000u, 46u}, // mbh -> Latn
- {0xB82C0000u, 46u}, // mbo -> Latn
- {0xC02C0000u, 46u}, // mbq -> Latn
- {0xD02C0000u, 46u}, // mbu -> Latn
- {0xD82C0000u, 46u}, // mbw -> Latn
- {0xA04C0000u, 46u}, // mci -> Latn
- {0xBC4C0000u, 46u}, // mcp -> Latn
- {0xC04C0000u, 46u}, // mcq -> Latn
- {0xC44C0000u, 46u}, // mcr -> Latn
- {0xD04C0000u, 46u}, // mcu -> Latn
- {0x806C0000u, 46u}, // mda -> Latn
+ {0xA80C0000u, 45u}, // mak -> Latn
+ {0xB40C0000u, 45u}, // man -> Latn
+ {0xB40C474Eu, 60u}, // man-GN -> Nkoo
+ {0xC80C0000u, 45u}, // mas -> Latn
+ {0xD80C0000u, 45u}, // maw -> Latn
+ {0xE40C0000u, 45u}, // maz -> Latn
+ {0x9C2C0000u, 45u}, // mbh -> Latn
+ {0xB82C0000u, 45u}, // mbo -> Latn
+ {0xC02C0000u, 45u}, // mbq -> Latn
+ {0xD02C0000u, 45u}, // mbu -> Latn
+ {0xD82C0000u, 45u}, // mbw -> Latn
+ {0xA04C0000u, 45u}, // mci -> Latn
+ {0xBC4C0000u, 45u}, // mcp -> Latn
+ {0xC04C0000u, 45u}, // mcq -> Latn
+ {0xC44C0000u, 45u}, // mcr -> Latn
+ {0xD04C0000u, 45u}, // mcu -> Latn
+ {0x806C0000u, 45u}, // mda -> Latn
{0x906C0000u, 2u}, // mde -> Arab
{0x946C0000u, 18u}, // mdf -> Cyrl
- {0x9C6C0000u, 46u}, // mdh -> Latn
- {0xA46C0000u, 46u}, // mdj -> Latn
- {0xC46C0000u, 46u}, // mdr -> Latn
+ {0x9C6C0000u, 45u}, // mdh -> Latn
+ {0xA46C0000u, 45u}, // mdj -> Latn
+ {0xC46C0000u, 45u}, // mdr -> Latn
{0xDC6C0000u, 21u}, // mdx -> Ethi
- {0x8C8C0000u, 46u}, // med -> Latn
- {0x908C0000u, 46u}, // mee -> Latn
- {0xA88C0000u, 46u}, // mek -> Latn
- {0xB48C0000u, 46u}, // men -> Latn
- {0xC48C0000u, 46u}, // mer -> Latn
- {0xCC8C0000u, 46u}, // met -> Latn
- {0xD08C0000u, 46u}, // meu -> Latn
+ {0x8C8C0000u, 45u}, // med -> Latn
+ {0x908C0000u, 45u}, // mee -> Latn
+ {0xA88C0000u, 45u}, // mek -> Latn
+ {0xB48C0000u, 45u}, // men -> Latn
+ {0xC48C0000u, 45u}, // mer -> Latn
+ {0xCC8C0000u, 45u}, // met -> Latn
+ {0xD08C0000u, 45u}, // meu -> Latn
{0x80AC0000u, 2u}, // mfa -> Arab
- {0x90AC0000u, 46u}, // mfe -> Latn
- {0xB4AC0000u, 46u}, // mfn -> Latn
- {0xB8AC0000u, 46u}, // mfo -> Latn
- {0xC0AC0000u, 46u}, // mfq -> Latn
- {0x6D670000u, 46u}, // mg -> Latn
- {0x9CCC0000u, 46u}, // mgh -> Latn
- {0xACCC0000u, 46u}, // mgl -> Latn
- {0xB8CC0000u, 46u}, // mgo -> Latn
+ {0x90AC0000u, 45u}, // mfe -> Latn
+ {0xB4AC0000u, 45u}, // mfn -> Latn
+ {0xB8AC0000u, 45u}, // mfo -> Latn
+ {0xC0AC0000u, 45u}, // mfq -> Latn
+ {0x6D670000u, 45u}, // mg -> Latn
+ {0x9CCC0000u, 45u}, // mgh -> Latn
+ {0xACCC0000u, 45u}, // mgl -> Latn
+ {0xB8CC0000u, 45u}, // mgo -> Latn
{0xBCCC0000u, 19u}, // mgp -> Deva
- {0xE0CC0000u, 46u}, // mgy -> Latn
- {0x6D680000u, 46u}, // mh -> Latn
- {0xA0EC0000u, 46u}, // mhi -> Latn
- {0xACEC0000u, 46u}, // mhl -> Latn
- {0x6D690000u, 46u}, // mi -> Latn
- {0x950C0000u, 46u}, // mif -> Latn
- {0xB50C0000u, 46u}, // min -> Latn
- {0xD90C0000u, 46u}, // miw -> Latn
+ {0xE0CC0000u, 45u}, // mgy -> Latn
+ {0x6D680000u, 45u}, // mh -> Latn
+ {0xA0EC0000u, 45u}, // mhi -> Latn
+ {0xACEC0000u, 45u}, // mhl -> Latn
+ {0x6D690000u, 45u}, // mi -> Latn
+ {0x950C0000u, 45u}, // mif -> Latn
+ {0xB50C0000u, 45u}, // min -> Latn
+ {0xD90C0000u, 45u}, // miw -> Latn
{0x6D6B0000u, 18u}, // mk -> Cyrl
{0xA14C0000u, 2u}, // mki -> Arab
- {0xAD4C0000u, 46u}, // mkl -> Latn
- {0xBD4C0000u, 46u}, // mkp -> Latn
- {0xD94C0000u, 46u}, // mkw -> Latn
- {0x6D6C0000u, 56u}, // ml -> Mlym
- {0x916C0000u, 46u}, // mle -> Latn
- {0xBD6C0000u, 46u}, // mlp -> Latn
- {0xC96C0000u, 46u}, // mls -> Latn
- {0xB98C0000u, 46u}, // mmo -> Latn
- {0xD18C0000u, 46u}, // mmu -> Latn
- {0xDD8C0000u, 46u}, // mmx -> Latn
+ {0xAD4C0000u, 45u}, // mkl -> Latn
+ {0xBD4C0000u, 45u}, // mkp -> Latn
+ {0xD94C0000u, 45u}, // mkw -> Latn
+ {0x6D6C0000u, 55u}, // ml -> Mlym
+ {0x916C0000u, 45u}, // mle -> Latn
+ {0xBD6C0000u, 45u}, // mlp -> Latn
+ {0xC96C0000u, 45u}, // mls -> Latn
+ {0xB98C0000u, 45u}, // mmo -> Latn
+ {0xD18C0000u, 45u}, // mmu -> Latn
+ {0xDD8C0000u, 45u}, // mmx -> Latn
{0x6D6E0000u, 18u}, // mn -> Cyrl
- {0x6D6E434Eu, 57u}, // mn-CN -> Mong
- {0x81AC0000u, 46u}, // mna -> Latn
- {0x95AC0000u, 46u}, // mnf -> Latn
+ {0x6D6E434Eu, 56u}, // mn-CN -> Mong
+ {0x81AC0000u, 45u}, // mna -> Latn
+ {0x95AC0000u, 45u}, // mnf -> Latn
{0xA1AC0000u, 8u}, // mni -> Beng
- {0xD9AC0000u, 59u}, // mnw -> Mymr
- {0x6D6F0000u, 46u}, // mo -> Latn
- {0x81CC0000u, 46u}, // moa -> Latn
- {0x91CC0000u, 46u}, // moe -> Latn
- {0x9DCC0000u, 46u}, // moh -> Latn
- {0xC9CC0000u, 46u}, // mos -> Latn
- {0xDDCC0000u, 46u}, // mox -> Latn
- {0xBDEC0000u, 46u}, // mpp -> Latn
- {0xC9EC0000u, 46u}, // mps -> Latn
- {0xCDEC0000u, 46u}, // mpt -> Latn
- {0xDDEC0000u, 46u}, // mpx -> Latn
- {0xAE0C0000u, 46u}, // mql -> Latn
+ {0xD9AC0000u, 58u}, // mnw -> Mymr
+ {0x6D6F0000u, 45u}, // mo -> Latn
+ {0x81CC0000u, 45u}, // moa -> Latn
+ {0x91CC0000u, 45u}, // moe -> Latn
+ {0x9DCC0000u, 45u}, // moh -> Latn
+ {0xC9CC0000u, 45u}, // mos -> Latn
+ {0xDDCC0000u, 45u}, // mox -> Latn
+ {0xBDEC0000u, 45u}, // mpp -> Latn
+ {0xC9EC0000u, 45u}, // mps -> Latn
+ {0xCDEC0000u, 45u}, // mpt -> Latn
+ {0xDDEC0000u, 45u}, // mpx -> Latn
+ {0xAE0C0000u, 45u}, // mql -> Latn
{0x6D720000u, 19u}, // mr -> Deva
{0x8E2C0000u, 19u}, // mrd -> Deva
{0xA62C0000u, 18u}, // mrj -> Cyrl
- {0xBA2C0000u, 58u}, // mro -> Mroo
- {0x6D730000u, 46u}, // ms -> Latn
+ {0xBA2C0000u, 57u}, // mro -> Mroo
+ {0x6D730000u, 45u}, // ms -> Latn
{0x6D734343u, 2u}, // ms-CC -> Arab
- {0x6D740000u, 46u}, // mt -> Latn
- {0x8A6C0000u, 46u}, // mtc -> Latn
- {0x966C0000u, 46u}, // mtf -> Latn
- {0xA26C0000u, 46u}, // mti -> Latn
+ {0x6D740000u, 45u}, // mt -> Latn
+ {0x8A6C0000u, 45u}, // mtc -> Latn
+ {0x966C0000u, 45u}, // mtf -> Latn
+ {0xA26C0000u, 45u}, // mti -> Latn
{0xC66C0000u, 19u}, // mtr -> Deva
- {0x828C0000u, 46u}, // mua -> Latn
- {0xC68C0000u, 46u}, // mur -> Latn
- {0xCA8C0000u, 46u}, // mus -> Latn
- {0x82AC0000u, 46u}, // mva -> Latn
- {0xB6AC0000u, 46u}, // mvn -> Latn
+ {0x828C0000u, 45u}, // mua -> Latn
+ {0xC68C0000u, 45u}, // mur -> Latn
+ {0xCA8C0000u, 45u}, // mus -> Latn
+ {0x82AC0000u, 45u}, // mva -> Latn
+ {0xB6AC0000u, 45u}, // mvn -> Latn
{0xE2AC0000u, 2u}, // mvy -> Arab
- {0xAACC0000u, 46u}, // mwk -> Latn
+ {0xAACC0000u, 45u}, // mwk -> Latn
{0xC6CC0000u, 19u}, // mwr -> Deva
- {0xD6CC0000u, 46u}, // mwv -> Latn
- {0xDACC0000u, 34u}, // mww -> Hmnp
- {0x8AEC0000u, 46u}, // mxc -> Latn
- {0xB2EC0000u, 46u}, // mxm -> Latn
- {0x6D790000u, 59u}, // my -> Mymr
- {0xAB0C0000u, 46u}, // myk -> Latn
+ {0xD6CC0000u, 45u}, // mwv -> Latn
+ {0xDACC0000u, 33u}, // mww -> Hmnp
+ {0x8AEC0000u, 45u}, // mxc -> Latn
+ {0xB2EC0000u, 45u}, // mxm -> Latn
+ {0x6D790000u, 58u}, // my -> Mymr
+ {0xAB0C0000u, 45u}, // myk -> Latn
{0xB30C0000u, 21u}, // mym -> Ethi
{0xD70C0000u, 18u}, // myv -> Cyrl
- {0xDB0C0000u, 46u}, // myw -> Latn
- {0xDF0C0000u, 46u}, // myx -> Latn
- {0xE70C0000u, 52u}, // myz -> Mand
- {0xAB2C0000u, 46u}, // mzk -> Latn
- {0xB32C0000u, 46u}, // mzm -> Latn
+ {0xDB0C0000u, 45u}, // myw -> Latn
+ {0xDF0C0000u, 45u}, // myx -> Latn
+ {0xE70C0000u, 51u}, // myz -> Mand
+ {0xAB2C0000u, 45u}, // mzk -> Latn
+ {0xB32C0000u, 45u}, // mzm -> Latn
{0xB72C0000u, 2u}, // mzn -> Arab
- {0xBF2C0000u, 46u}, // mzp -> Latn
- {0xDB2C0000u, 46u}, // mzw -> Latn
- {0xE72C0000u, 46u}, // mzz -> Latn
- {0x6E610000u, 46u}, // na -> Latn
- {0x880D0000u, 46u}, // nac -> Latn
- {0x940D0000u, 46u}, // naf -> Latn
- {0xA80D0000u, 46u}, // nak -> Latn
+ {0xBF2C0000u, 45u}, // mzp -> Latn
+ {0xDB2C0000u, 45u}, // mzw -> Latn
+ {0xE72C0000u, 45u}, // mzz -> Latn
+ {0x6E610000u, 45u}, // na -> Latn
+ {0x880D0000u, 45u}, // nac -> Latn
+ {0x940D0000u, 45u}, // naf -> Latn
+ {0xA80D0000u, 45u}, // nak -> Latn
{0xB40D0000u, 29u}, // nan -> Hans
- {0xBC0D0000u, 46u}, // nap -> Latn
- {0xC00D0000u, 46u}, // naq -> Latn
- {0xC80D0000u, 46u}, // nas -> Latn
- {0x6E620000u, 46u}, // nb -> Latn
- {0x804D0000u, 46u}, // nca -> Latn
- {0x904D0000u, 46u}, // nce -> Latn
- {0x944D0000u, 46u}, // ncf -> Latn
- {0x9C4D0000u, 46u}, // nch -> Latn
- {0xB84D0000u, 46u}, // nco -> Latn
- {0xD04D0000u, 46u}, // ncu -> Latn
- {0x6E640000u, 46u}, // nd -> Latn
- {0x886D0000u, 46u}, // ndc -> Latn
- {0xC86D0000u, 46u}, // nds -> Latn
+ {0xBC0D0000u, 45u}, // nap -> Latn
+ {0xC00D0000u, 45u}, // naq -> Latn
+ {0xC80D0000u, 45u}, // nas -> Latn
+ {0x6E620000u, 45u}, // nb -> Latn
+ {0x804D0000u, 45u}, // nca -> Latn
+ {0x904D0000u, 45u}, // nce -> Latn
+ {0x944D0000u, 45u}, // ncf -> Latn
+ {0x9C4D0000u, 45u}, // nch -> Latn
+ {0xB84D0000u, 45u}, // nco -> Latn
+ {0xD04D0000u, 45u}, // ncu -> Latn
+ {0x6E640000u, 45u}, // nd -> Latn
+ {0x886D0000u, 45u}, // ndc -> Latn
+ {0xC86D0000u, 45u}, // nds -> Latn
{0x6E650000u, 19u}, // ne -> Deva
- {0x848D0000u, 46u}, // neb -> Latn
+ {0x848D0000u, 45u}, // neb -> Latn
{0xD88D0000u, 19u}, // new -> Deva
- {0xDC8D0000u, 46u}, // nex -> Latn
- {0xC4AD0000u, 46u}, // nfr -> Latn
- {0x6E670000u, 46u}, // ng -> Latn
- {0x80CD0000u, 46u}, // nga -> Latn
- {0x84CD0000u, 46u}, // ngb -> Latn
- {0xACCD0000u, 46u}, // ngl -> Latn
- {0x84ED0000u, 46u}, // nhb -> Latn
- {0x90ED0000u, 46u}, // nhe -> Latn
- {0xD8ED0000u, 46u}, // nhw -> Latn
- {0x950D0000u, 46u}, // nif -> Latn
- {0xA10D0000u, 46u}, // nii -> Latn
- {0xA50D0000u, 46u}, // nij -> Latn
- {0xB50D0000u, 46u}, // nin -> Latn
- {0xD10D0000u, 46u}, // niu -> Latn
- {0xE10D0000u, 46u}, // niy -> Latn
- {0xE50D0000u, 46u}, // niz -> Latn
- {0xB92D0000u, 46u}, // njo -> Latn
- {0x994D0000u, 46u}, // nkg -> Latn
- {0xB94D0000u, 46u}, // nko -> Latn
- {0x6E6C0000u, 46u}, // nl -> Latn
- {0x998D0000u, 46u}, // nmg -> Latn
- {0xE58D0000u, 46u}, // nmz -> Latn
- {0x6E6E0000u, 46u}, // nn -> Latn
- {0x95AD0000u, 46u}, // nnf -> Latn
- {0x9DAD0000u, 46u}, // nnh -> Latn
- {0xA9AD0000u, 46u}, // nnk -> Latn
- {0xB1AD0000u, 46u}, // nnm -> Latn
- {0xBDAD0000u, 95u}, // nnp -> Wcho
- {0x6E6F0000u, 46u}, // no -> Latn
- {0x8DCD0000u, 44u}, // nod -> Lana
+ {0xDC8D0000u, 45u}, // nex -> Latn
+ {0xC4AD0000u, 45u}, // nfr -> Latn
+ {0x6E670000u, 45u}, // ng -> Latn
+ {0x80CD0000u, 45u}, // nga -> Latn
+ {0x84CD0000u, 45u}, // ngb -> Latn
+ {0xACCD0000u, 45u}, // ngl -> Latn
+ {0x84ED0000u, 45u}, // nhb -> Latn
+ {0x90ED0000u, 45u}, // nhe -> Latn
+ {0xD8ED0000u, 45u}, // nhw -> Latn
+ {0x950D0000u, 45u}, // nif -> Latn
+ {0xA10D0000u, 45u}, // nii -> Latn
+ {0xA50D0000u, 45u}, // nij -> Latn
+ {0xB50D0000u, 45u}, // nin -> Latn
+ {0xD10D0000u, 45u}, // niu -> Latn
+ {0xE10D0000u, 45u}, // niy -> Latn
+ {0xE50D0000u, 45u}, // niz -> Latn
+ {0xB92D0000u, 45u}, // njo -> Latn
+ {0x994D0000u, 45u}, // nkg -> Latn
+ {0xB94D0000u, 45u}, // nko -> Latn
+ {0x6E6C0000u, 45u}, // nl -> Latn
+ {0x998D0000u, 45u}, // nmg -> Latn
+ {0xE58D0000u, 45u}, // nmz -> Latn
+ {0x6E6E0000u, 45u}, // nn -> Latn
+ {0x95AD0000u, 45u}, // nnf -> Latn
+ {0x9DAD0000u, 45u}, // nnh -> Latn
+ {0xA9AD0000u, 45u}, // nnk -> Latn
+ {0xB1AD0000u, 45u}, // nnm -> Latn
+ {0xBDAD0000u, 98u}, // nnp -> Wcho
+ {0x6E6F0000u, 45u}, // no -> Latn
+ {0x8DCD0000u, 43u}, // nod -> Lana
{0x91CD0000u, 19u}, // noe -> Deva
- {0xB5CD0000u, 73u}, // non -> Runr
- {0xBDCD0000u, 46u}, // nop -> Latn
- {0xD1CD0000u, 46u}, // nou -> Latn
- {0xBA0D0000u, 61u}, // nqo -> Nkoo
- {0x6E720000u, 46u}, // nr -> Latn
- {0x862D0000u, 46u}, // nrb -> Latn
+ {0xB5CD0000u, 74u}, // non -> Runr
+ {0xBDCD0000u, 45u}, // nop -> Latn
+ {0xD1CD0000u, 45u}, // nou -> Latn
+ {0xBA0D0000u, 60u}, // nqo -> Nkoo
+ {0x6E720000u, 45u}, // nr -> Latn
+ {0x862D0000u, 45u}, // nrb -> Latn
{0xAA4D0000u, 11u}, // nsk -> Cans
- {0xB64D0000u, 46u}, // nsn -> Latn
- {0xBA4D0000u, 46u}, // nso -> Latn
- {0xCA4D0000u, 46u}, // nss -> Latn
- {0xB26D0000u, 46u}, // ntm -> Latn
- {0xC66D0000u, 46u}, // ntr -> Latn
- {0xA28D0000u, 46u}, // nui -> Latn
- {0xBE8D0000u, 46u}, // nup -> Latn
- {0xCA8D0000u, 46u}, // nus -> Latn
- {0xD68D0000u, 46u}, // nuv -> Latn
- {0xDE8D0000u, 46u}, // nux -> Latn
- {0x6E760000u, 46u}, // nv -> Latn
- {0x86CD0000u, 46u}, // nwb -> Latn
- {0xC2ED0000u, 46u}, // nxq -> Latn
- {0xC6ED0000u, 46u}, // nxr -> Latn
- {0x6E790000u, 46u}, // ny -> Latn
- {0xB30D0000u, 46u}, // nym -> Latn
- {0xB70D0000u, 46u}, // nyn -> Latn
- {0xA32D0000u, 46u}, // nzi -> Latn
- {0x6F630000u, 46u}, // oc -> Latn
- {0x88CE0000u, 46u}, // ogc -> Latn
- {0xC54E0000u, 46u}, // okr -> Latn
- {0xD54E0000u, 46u}, // okv -> Latn
- {0x6F6D0000u, 46u}, // om -> Latn
- {0x99AE0000u, 46u}, // ong -> Latn
- {0xB5AE0000u, 46u}, // onn -> Latn
- {0xC9AE0000u, 46u}, // ons -> Latn
- {0xB1EE0000u, 46u}, // opm -> Latn
- {0x6F720000u, 66u}, // or -> Orya
- {0xBA2E0000u, 46u}, // oro -> Latn
+ {0xB64D0000u, 45u}, // nsn -> Latn
+ {0xBA4D0000u, 45u}, // nso -> Latn
+ {0xCA4D0000u, 45u}, // nss -> Latn
+ {0xCE4D0000u, 94u}, // nst -> Tnsa
+ {0xB26D0000u, 45u}, // ntm -> Latn
+ {0xC66D0000u, 45u}, // ntr -> Latn
+ {0xA28D0000u, 45u}, // nui -> Latn
+ {0xBE8D0000u, 45u}, // nup -> Latn
+ {0xCA8D0000u, 45u}, // nus -> Latn
+ {0xD68D0000u, 45u}, // nuv -> Latn
+ {0xDE8D0000u, 45u}, // nux -> Latn
+ {0x6E760000u, 45u}, // nv -> Latn
+ {0x86CD0000u, 45u}, // nwb -> Latn
+ {0xC2ED0000u, 45u}, // nxq -> Latn
+ {0xC6ED0000u, 45u}, // nxr -> Latn
+ {0x6E790000u, 45u}, // ny -> Latn
+ {0xB30D0000u, 45u}, // nym -> Latn
+ {0xB70D0000u, 45u}, // nyn -> Latn
+ {0xA32D0000u, 45u}, // nzi -> Latn
+ {0x6F630000u, 45u}, // oc -> Latn
+ {0x88CE0000u, 45u}, // ogc -> Latn
+ {0xC54E0000u, 45u}, // okr -> Latn
+ {0xD54E0000u, 45u}, // okv -> Latn
+ {0x6F6D0000u, 45u}, // om -> Latn
+ {0x99AE0000u, 45u}, // ong -> Latn
+ {0xB5AE0000u, 45u}, // onn -> Latn
+ {0xC9AE0000u, 45u}, // ons -> Latn
+ {0xB1EE0000u, 45u}, // opm -> Latn
+ {0x6F720000u, 65u}, // or -> Orya
+ {0xBA2E0000u, 45u}, // oro -> Latn
{0xD22E0000u, 2u}, // oru -> Arab
{0x6F730000u, 18u}, // os -> Cyrl
- {0x824E0000u, 67u}, // osa -> Osge
+ {0x824E0000u, 66u}, // osa -> Osge
{0x826E0000u, 2u}, // ota -> Arab
- {0xAA6E0000u, 65u}, // otk -> Orkh
- {0xB32E0000u, 46u}, // ozm -> Latn
+ {0xAA6E0000u, 64u}, // otk -> Orkh
+ {0xA28E0000u, 67u}, // oui -> Ougr
+ {0xB32E0000u, 45u}, // ozm -> Latn
{0x70610000u, 28u}, // pa -> Guru
{0x7061504Bu, 2u}, // pa-PK -> Arab
- {0x980F0000u, 46u}, // pag -> Latn
+ {0x980F0000u, 45u}, // pag -> Latn
{0xAC0F0000u, 69u}, // pal -> Phli
- {0xB00F0000u, 46u}, // pam -> Latn
- {0xBC0F0000u, 46u}, // pap -> Latn
- {0xD00F0000u, 46u}, // pau -> Latn
- {0xA02F0000u, 46u}, // pbi -> Latn
- {0x8C4F0000u, 46u}, // pcd -> Latn
- {0xB04F0000u, 46u}, // pcm -> Latn
- {0x886F0000u, 46u}, // pdc -> Latn
- {0xCC6F0000u, 46u}, // pdt -> Latn
- {0x8C8F0000u, 46u}, // ped -> Latn
- {0xB88F0000u, 96u}, // peo -> Xpeo
- {0xDC8F0000u, 46u}, // pex -> Latn
- {0xACAF0000u, 46u}, // pfl -> Latn
+ {0xB00F0000u, 45u}, // pam -> Latn
+ {0xBC0F0000u, 45u}, // pap -> Latn
+ {0xD00F0000u, 45u}, // pau -> Latn
+ {0xA02F0000u, 45u}, // pbi -> Latn
+ {0x8C4F0000u, 45u}, // pcd -> Latn
+ {0xB04F0000u, 45u}, // pcm -> Latn
+ {0x886F0000u, 45u}, // pdc -> Latn
+ {0xCC6F0000u, 45u}, // pdt -> Latn
+ {0x8C8F0000u, 45u}, // ped -> Latn
+ {0xB88F0000u, 99u}, // peo -> Xpeo
+ {0xDC8F0000u, 45u}, // pex -> Latn
+ {0xACAF0000u, 45u}, // pfl -> Latn
{0xACEF0000u, 2u}, // phl -> Arab
{0xB4EF0000u, 70u}, // phn -> Phnx
- {0xAD0F0000u, 46u}, // pil -> Latn
- {0xBD0F0000u, 46u}, // pip -> Latn
+ {0xAD0F0000u, 45u}, // pil -> Latn
+ {0xBD0F0000u, 45u}, // pip -> Latn
{0x814F0000u, 9u}, // pka -> Brah
- {0xB94F0000u, 46u}, // pko -> Latn
- {0x706C0000u, 46u}, // pl -> Latn
- {0x816F0000u, 46u}, // pla -> Latn
- {0xC98F0000u, 46u}, // pms -> Latn
- {0x99AF0000u, 46u}, // png -> Latn
- {0xB5AF0000u, 46u}, // pnn -> Latn
+ {0xB94F0000u, 45u}, // pko -> Latn
+ {0x706C0000u, 45u}, // pl -> Latn
+ {0x816F0000u, 45u}, // pla -> Latn
+ {0xC98F0000u, 45u}, // pms -> Latn
+ {0x99AF0000u, 45u}, // png -> Latn
+ {0xB5AF0000u, 45u}, // pnn -> Latn
{0xCDAF0000u, 26u}, // pnt -> Grek
- {0xB5CF0000u, 46u}, // pon -> Latn
+ {0xB5CF0000u, 45u}, // pon -> Latn
{0x81EF0000u, 19u}, // ppa -> Deva
- {0xB9EF0000u, 46u}, // ppo -> Latn
- {0x822F0000u, 39u}, // pra -> Khar
+ {0xB9EF0000u, 45u}, // ppo -> Latn
+ {0x822F0000u, 38u}, // pra -> Khar
{0x8E2F0000u, 2u}, // prd -> Arab
- {0x9A2F0000u, 46u}, // prg -> Latn
+ {0x9A2F0000u, 45u}, // prg -> Latn
{0x70730000u, 2u}, // ps -> Arab
- {0xCA4F0000u, 46u}, // pss -> Latn
- {0x70740000u, 46u}, // pt -> Latn
- {0xBE6F0000u, 46u}, // ptp -> Latn
- {0xD28F0000u, 46u}, // puu -> Latn
- {0x82CF0000u, 46u}, // pwa -> Latn
- {0x71750000u, 46u}, // qu -> Latn
- {0x8A900000u, 46u}, // quc -> Latn
- {0x9A900000u, 46u}, // qug -> Latn
- {0xA0110000u, 46u}, // rai -> Latn
+ {0xCA4F0000u, 45u}, // pss -> Latn
+ {0x70740000u, 45u}, // pt -> Latn
+ {0xBE6F0000u, 45u}, // ptp -> Latn
+ {0xD28F0000u, 45u}, // puu -> Latn
+ {0x82CF0000u, 45u}, // pwa -> Latn
+ {0x71750000u, 45u}, // qu -> Latn
+ {0x8A900000u, 45u}, // quc -> Latn
+ {0x9A900000u, 45u}, // qug -> Latn
+ {0xA0110000u, 45u}, // rai -> Latn
{0xA4110000u, 19u}, // raj -> Deva
- {0xB8110000u, 46u}, // rao -> Latn
- {0x94510000u, 46u}, // rcf -> Latn
- {0xA4910000u, 46u}, // rej -> Latn
- {0xAC910000u, 46u}, // rel -> Latn
- {0xC8910000u, 46u}, // res -> Latn
- {0xB4D10000u, 46u}, // rgn -> Latn
- {0x98F10000u, 2u}, // rhg -> Arab
- {0x81110000u, 46u}, // ria -> Latn
- {0x95110000u, 89u}, // rif -> Tfng
- {0x95114E4Cu, 46u}, // rif-NL -> Latn
+ {0xB8110000u, 45u}, // rao -> Latn
+ {0x94510000u, 45u}, // rcf -> Latn
+ {0xA4910000u, 45u}, // rej -> Latn
+ {0xAC910000u, 45u}, // rel -> Latn
+ {0xC8910000u, 45u}, // res -> Latn
+ {0xB4D10000u, 45u}, // rgn -> Latn
+ {0x98F10000u, 73u}, // rhg -> Rohg
+ {0x81110000u, 45u}, // ria -> Latn
+ {0x95110000u, 90u}, // rif -> Tfng
+ {0x95114E4Cu, 45u}, // rif-NL -> Latn
{0xC9310000u, 19u}, // rjs -> Deva
{0xCD510000u, 8u}, // rkt -> Beng
- {0x726D0000u, 46u}, // rm -> Latn
- {0x95910000u, 46u}, // rmf -> Latn
- {0xB9910000u, 46u}, // rmo -> Latn
+ {0x726D0000u, 45u}, // rm -> Latn
+ {0x95910000u, 45u}, // rmf -> Latn
+ {0xB9910000u, 45u}, // rmo -> Latn
{0xCD910000u, 2u}, // rmt -> Arab
- {0xD1910000u, 46u}, // rmu -> Latn
- {0x726E0000u, 46u}, // rn -> Latn
- {0x81B10000u, 46u}, // rna -> Latn
- {0x99B10000u, 46u}, // rng -> Latn
- {0x726F0000u, 46u}, // ro -> Latn
- {0x85D10000u, 46u}, // rob -> Latn
- {0x95D10000u, 46u}, // rof -> Latn
- {0xB9D10000u, 46u}, // roo -> Latn
- {0xBA310000u, 46u}, // rro -> Latn
- {0xB2710000u, 46u}, // rtm -> Latn
+ {0xD1910000u, 45u}, // rmu -> Latn
+ {0x726E0000u, 45u}, // rn -> Latn
+ {0x81B10000u, 45u}, // rna -> Latn
+ {0x99B10000u, 45u}, // rng -> Latn
+ {0x726F0000u, 45u}, // ro -> Latn
+ {0x85D10000u, 45u}, // rob -> Latn
+ {0x95D10000u, 45u}, // rof -> Latn
+ {0xB9D10000u, 45u}, // roo -> Latn
+ {0xBA310000u, 45u}, // rro -> Latn
+ {0xB2710000u, 45u}, // rtm -> Latn
{0x72750000u, 18u}, // ru -> Cyrl
{0x92910000u, 18u}, // rue -> Cyrl
- {0x9A910000u, 46u}, // rug -> Latn
- {0x72770000u, 46u}, // rw -> Latn
- {0xAAD10000u, 46u}, // rwk -> Latn
- {0xBAD10000u, 46u}, // rwo -> Latn
- {0xD3110000u, 38u}, // ryu -> Kana
+ {0x9A910000u, 45u}, // rug -> Latn
+ {0x72770000u, 45u}, // rw -> Latn
+ {0xAAD10000u, 45u}, // rwk -> Latn
+ {0xBAD10000u, 45u}, // rwo -> Latn
+ {0xD3110000u, 37u}, // ryu -> Kana
{0x73610000u, 19u}, // sa -> Deva
- {0x94120000u, 46u}, // saf -> Latn
+ {0x94120000u, 45u}, // saf -> Latn
{0x9C120000u, 18u}, // sah -> Cyrl
- {0xC0120000u, 46u}, // saq -> Latn
- {0xC8120000u, 46u}, // sas -> Latn
- {0xCC120000u, 64u}, // sat -> Olck
- {0xD4120000u, 46u}, // sav -> Latn
- {0xE4120000u, 76u}, // saz -> Saur
- {0x80320000u, 46u}, // sba -> Latn
- {0x90320000u, 46u}, // sbe -> Latn
- {0xBC320000u, 46u}, // sbp -> Latn
- {0x73630000u, 46u}, // sc -> Latn
+ {0xC0120000u, 45u}, // saq -> Latn
+ {0xC8120000u, 45u}, // sas -> Latn
+ {0xCC120000u, 63u}, // sat -> Olck
+ {0xD4120000u, 45u}, // sav -> Latn
+ {0xE4120000u, 77u}, // saz -> Saur
+ {0x80320000u, 45u}, // sba -> Latn
+ {0x90320000u, 45u}, // sbe -> Latn
+ {0xBC320000u, 45u}, // sbp -> Latn
+ {0x73630000u, 45u}, // sc -> Latn
{0xA8520000u, 19u}, // sck -> Deva
{0xAC520000u, 2u}, // scl -> Arab
- {0xB4520000u, 46u}, // scn -> Latn
- {0xB8520000u, 46u}, // sco -> Latn
- {0xC8520000u, 46u}, // scs -> Latn
+ {0xB4520000u, 45u}, // scn -> Latn
+ {0xB8520000u, 45u}, // sco -> Latn
+ {0xC8520000u, 45u}, // scs -> Latn
{0x73640000u, 2u}, // sd -> Arab
- {0x88720000u, 46u}, // sdc -> Latn
+ {0x88720000u, 45u}, // sdc -> Latn
{0x9C720000u, 2u}, // sdh -> Arab
- {0x73650000u, 46u}, // se -> Latn
- {0x94920000u, 46u}, // sef -> Latn
- {0x9C920000u, 46u}, // seh -> Latn
- {0xA0920000u, 46u}, // sei -> Latn
- {0xC8920000u, 46u}, // ses -> Latn
- {0x73670000u, 46u}, // sg -> Latn
- {0x80D20000u, 63u}, // sga -> Ogam
- {0xC8D20000u, 46u}, // sgs -> Latn
+ {0x73650000u, 45u}, // se -> Latn
+ {0x94920000u, 45u}, // sef -> Latn
+ {0x9C920000u, 45u}, // seh -> Latn
+ {0xA0920000u, 45u}, // sei -> Latn
+ {0xC8920000u, 45u}, // ses -> Latn
+ {0x73670000u, 45u}, // sg -> Latn
+ {0x80D20000u, 62u}, // sga -> Ogam
+ {0xC8D20000u, 45u}, // sgs -> Latn
{0xD8D20000u, 21u}, // sgw -> Ethi
- {0xE4D20000u, 46u}, // sgz -> Latn
- {0x73680000u, 46u}, // sh -> Latn
- {0xA0F20000u, 89u}, // shi -> Tfng
- {0xA8F20000u, 46u}, // shk -> Latn
- {0xB4F20000u, 59u}, // shn -> Mymr
+ {0xE4D20000u, 45u}, // sgz -> Latn
+ {0x73680000u, 45u}, // sh -> Latn
+ {0xA0F20000u, 90u}, // shi -> Tfng
+ {0xA8F20000u, 45u}, // shk -> Latn
+ {0xB4F20000u, 58u}, // shn -> Mymr
{0xD0F20000u, 2u}, // shu -> Arab
- {0x73690000u, 78u}, // si -> Sinh
- {0x8D120000u, 46u}, // sid -> Latn
- {0x99120000u, 46u}, // sig -> Latn
- {0xAD120000u, 46u}, // sil -> Latn
- {0xB1120000u, 46u}, // sim -> Latn
- {0xC5320000u, 46u}, // sjr -> Latn
- {0x736B0000u, 46u}, // sk -> Latn
- {0x89520000u, 46u}, // skc -> Latn
+ {0x73690000u, 79u}, // si -> Sinh
+ {0x8D120000u, 45u}, // sid -> Latn
+ {0x99120000u, 45u}, // sig -> Latn
+ {0xAD120000u, 45u}, // sil -> Latn
+ {0xB1120000u, 45u}, // sim -> Latn
+ {0xC5320000u, 45u}, // sjr -> Latn
+ {0x736B0000u, 45u}, // sk -> Latn
+ {0x89520000u, 45u}, // skc -> Latn
{0xC5520000u, 2u}, // skr -> Arab
- {0xC9520000u, 46u}, // sks -> Latn
- {0x736C0000u, 46u}, // sl -> Latn
- {0x8D720000u, 46u}, // sld -> Latn
- {0xA1720000u, 46u}, // sli -> Latn
- {0xAD720000u, 46u}, // sll -> Latn
- {0xE1720000u, 46u}, // sly -> Latn
- {0x736D0000u, 46u}, // sm -> Latn
- {0x81920000u, 46u}, // sma -> Latn
- {0xA5920000u, 46u}, // smj -> Latn
- {0xB5920000u, 46u}, // smn -> Latn
- {0xBD920000u, 74u}, // smp -> Samr
- {0xC1920000u, 46u}, // smq -> Latn
- {0xC9920000u, 46u}, // sms -> Latn
- {0x736E0000u, 46u}, // sn -> Latn
- {0x89B20000u, 46u}, // snc -> Latn
- {0xA9B20000u, 46u}, // snk -> Latn
- {0xBDB20000u, 46u}, // snp -> Latn
- {0xDDB20000u, 46u}, // snx -> Latn
- {0xE1B20000u, 46u}, // sny -> Latn
- {0x736F0000u, 46u}, // so -> Latn
- {0x99D20000u, 79u}, // sog -> Sogd
- {0xA9D20000u, 46u}, // sok -> Latn
- {0xC1D20000u, 46u}, // soq -> Latn
- {0xD1D20000u, 91u}, // sou -> Thai
- {0xE1D20000u, 46u}, // soy -> Latn
- {0x8DF20000u, 46u}, // spd -> Latn
- {0xADF20000u, 46u}, // spl -> Latn
- {0xC9F20000u, 46u}, // sps -> Latn
- {0x73710000u, 46u}, // sq -> Latn
+ {0xC9520000u, 45u}, // sks -> Latn
+ {0x736C0000u, 45u}, // sl -> Latn
+ {0x8D720000u, 45u}, // sld -> Latn
+ {0xA1720000u, 45u}, // sli -> Latn
+ {0xAD720000u, 45u}, // sll -> Latn
+ {0xE1720000u, 45u}, // sly -> Latn
+ {0x736D0000u, 45u}, // sm -> Latn
+ {0x81920000u, 45u}, // sma -> Latn
+ {0xA5920000u, 45u}, // smj -> Latn
+ {0xB5920000u, 45u}, // smn -> Latn
+ {0xBD920000u, 75u}, // smp -> Samr
+ {0xC1920000u, 45u}, // smq -> Latn
+ {0xC9920000u, 45u}, // sms -> Latn
+ {0x736E0000u, 45u}, // sn -> Latn
+ {0x89B20000u, 45u}, // snc -> Latn
+ {0xA9B20000u, 45u}, // snk -> Latn
+ {0xBDB20000u, 45u}, // snp -> Latn
+ {0xDDB20000u, 45u}, // snx -> Latn
+ {0xE1B20000u, 45u}, // sny -> Latn
+ {0x736F0000u, 45u}, // so -> Latn
+ {0x99D20000u, 80u}, // sog -> Sogd
+ {0xA9D20000u, 45u}, // sok -> Latn
+ {0xC1D20000u, 45u}, // soq -> Latn
+ {0xD1D20000u, 92u}, // sou -> Thai
+ {0xE1D20000u, 45u}, // soy -> Latn
+ {0x8DF20000u, 45u}, // spd -> Latn
+ {0xADF20000u, 45u}, // spl -> Latn
+ {0xC9F20000u, 45u}, // sps -> Latn
+ {0x73710000u, 45u}, // sq -> Latn
{0x73720000u, 18u}, // sr -> Cyrl
- {0x73724D45u, 46u}, // sr-ME -> Latn
- {0x7372524Fu, 46u}, // sr-RO -> Latn
- {0x73725255u, 46u}, // sr-RU -> Latn
- {0x73725452u, 46u}, // sr-TR -> Latn
- {0x86320000u, 80u}, // srb -> Sora
- {0xB6320000u, 46u}, // srn -> Latn
- {0xC6320000u, 46u}, // srr -> Latn
+ {0x73724D45u, 45u}, // sr-ME -> Latn
+ {0x7372524Fu, 45u}, // sr-RO -> Latn
+ {0x73725255u, 45u}, // sr-RU -> Latn
+ {0x73725452u, 45u}, // sr-TR -> Latn
+ {0x86320000u, 81u}, // srb -> Sora
+ {0xB6320000u, 45u}, // srn -> Latn
+ {0xC6320000u, 45u}, // srr -> Latn
{0xDE320000u, 19u}, // srx -> Deva
- {0x73730000u, 46u}, // ss -> Latn
- {0x8E520000u, 46u}, // ssd -> Latn
- {0x9A520000u, 46u}, // ssg -> Latn
- {0xE2520000u, 46u}, // ssy -> Latn
- {0x73740000u, 46u}, // st -> Latn
- {0xAA720000u, 46u}, // stk -> Latn
- {0xC2720000u, 46u}, // stq -> Latn
- {0x73750000u, 46u}, // su -> Latn
- {0x82920000u, 46u}, // sua -> Latn
- {0x92920000u, 46u}, // sue -> Latn
- {0xAA920000u, 46u}, // suk -> Latn
- {0xC6920000u, 46u}, // sur -> Latn
- {0xCA920000u, 46u}, // sus -> Latn
- {0x73760000u, 46u}, // sv -> Latn
- {0x73770000u, 46u}, // sw -> Latn
+ {0x73730000u, 45u}, // ss -> Latn
+ {0x8E520000u, 45u}, // ssd -> Latn
+ {0x9A520000u, 45u}, // ssg -> Latn
+ {0xE2520000u, 45u}, // ssy -> Latn
+ {0x73740000u, 45u}, // st -> Latn
+ {0xAA720000u, 45u}, // stk -> Latn
+ {0xC2720000u, 45u}, // stq -> Latn
+ {0x73750000u, 45u}, // su -> Latn
+ {0x82920000u, 45u}, // sua -> Latn
+ {0x92920000u, 45u}, // sue -> Latn
+ {0xAA920000u, 45u}, // suk -> Latn
+ {0xC6920000u, 45u}, // sur -> Latn
+ {0xCA920000u, 45u}, // sus -> Latn
+ {0x73760000u, 45u}, // sv -> Latn
+ {0x73770000u, 45u}, // sw -> Latn
{0x86D20000u, 2u}, // swb -> Arab
- {0x8AD20000u, 46u}, // swc -> Latn
- {0x9AD20000u, 46u}, // swg -> Latn
- {0xBED20000u, 46u}, // swp -> Latn
+ {0x8AD20000u, 45u}, // swc -> Latn
+ {0x9AD20000u, 45u}, // swg -> Latn
+ {0xBED20000u, 45u}, // swp -> Latn
{0xD6D20000u, 19u}, // swv -> Deva
- {0xB6F20000u, 46u}, // sxn -> Latn
- {0xDAF20000u, 46u}, // sxw -> Latn
+ {0xB6F20000u, 45u}, // sxn -> Latn
+ {0xDAF20000u, 45u}, // sxw -> Latn
{0xAF120000u, 8u}, // syl -> Beng
- {0xC7120000u, 82u}, // syr -> Syrc
- {0xAF320000u, 46u}, // szl -> Latn
- {0x74610000u, 85u}, // ta -> Taml
+ {0xC7120000u, 83u}, // syr -> Syrc
+ {0xAF320000u, 45u}, // szl -> Latn
+ {0x74610000u, 86u}, // ta -> Taml
{0xA4130000u, 19u}, // taj -> Deva
- {0xAC130000u, 46u}, // tal -> Latn
- {0xB4130000u, 46u}, // tan -> Latn
- {0xC0130000u, 46u}, // taq -> Latn
- {0x88330000u, 46u}, // tbc -> Latn
- {0x8C330000u, 46u}, // tbd -> Latn
- {0x94330000u, 46u}, // tbf -> Latn
- {0x98330000u, 46u}, // tbg -> Latn
- {0xB8330000u, 46u}, // tbo -> Latn
- {0xD8330000u, 46u}, // tbw -> Latn
- {0xE4330000u, 46u}, // tbz -> Latn
- {0xA0530000u, 46u}, // tci -> Latn
- {0xE0530000u, 42u}, // tcy -> Knda
- {0x8C730000u, 83u}, // tdd -> Tale
+ {0xAC130000u, 45u}, // tal -> Latn
+ {0xB4130000u, 45u}, // tan -> Latn
+ {0xC0130000u, 45u}, // taq -> Latn
+ {0x88330000u, 45u}, // tbc -> Latn
+ {0x8C330000u, 45u}, // tbd -> Latn
+ {0x94330000u, 45u}, // tbf -> Latn
+ {0x98330000u, 45u}, // tbg -> Latn
+ {0xB8330000u, 45u}, // tbo -> Latn
+ {0xD8330000u, 45u}, // tbw -> Latn
+ {0xE4330000u, 45u}, // tbz -> Latn
+ {0xA0530000u, 45u}, // tci -> Latn
+ {0xE0530000u, 41u}, // tcy -> Knda
+ {0x8C730000u, 84u}, // tdd -> Tale
{0x98730000u, 19u}, // tdg -> Deva
{0x9C730000u, 19u}, // tdh -> Deva
- {0xD0730000u, 46u}, // tdu -> Latn
- {0x74650000u, 88u}, // te -> Telu
- {0x8C930000u, 46u}, // ted -> Latn
- {0xB0930000u, 46u}, // tem -> Latn
- {0xB8930000u, 46u}, // teo -> Latn
- {0xCC930000u, 46u}, // tet -> Latn
- {0xA0B30000u, 46u}, // tfi -> Latn
+ {0xD0730000u, 45u}, // tdu -> Latn
+ {0x74650000u, 89u}, // te -> Telu
+ {0x8C930000u, 45u}, // ted -> Latn
+ {0xB0930000u, 45u}, // tem -> Latn
+ {0xB8930000u, 45u}, // teo -> Latn
+ {0xCC930000u, 45u}, // tet -> Latn
+ {0xA0B30000u, 45u}, // tfi -> Latn
{0x74670000u, 18u}, // tg -> Cyrl
{0x7467504Bu, 2u}, // tg-PK -> Arab
- {0x88D30000u, 46u}, // tgc -> Latn
- {0xB8D30000u, 46u}, // tgo -> Latn
- {0xD0D30000u, 46u}, // tgu -> Latn
- {0x74680000u, 91u}, // th -> Thai
+ {0x88D30000u, 45u}, // tgc -> Latn
+ {0xB8D30000u, 45u}, // tgo -> Latn
+ {0xD0D30000u, 45u}, // tgu -> Latn
+ {0x74680000u, 92u}, // th -> Thai
{0xACF30000u, 19u}, // thl -> Deva
{0xC0F30000u, 19u}, // thq -> Deva
{0xC4F30000u, 19u}, // thr -> Deva
{0x74690000u, 21u}, // ti -> Ethi
- {0x95130000u, 46u}, // tif -> Latn
+ {0x95130000u, 45u}, // tif -> Latn
{0x99130000u, 21u}, // tig -> Ethi
- {0xA9130000u, 46u}, // tik -> Latn
- {0xB1130000u, 46u}, // tim -> Latn
- {0xB9130000u, 46u}, // tio -> Latn
- {0xD5130000u, 46u}, // tiv -> Latn
- {0x746B0000u, 46u}, // tk -> Latn
- {0xAD530000u, 46u}, // tkl -> Latn
- {0xC5530000u, 46u}, // tkr -> Latn
+ {0xA9130000u, 45u}, // tik -> Latn
+ {0xB1130000u, 45u}, // tim -> Latn
+ {0xB9130000u, 45u}, // tio -> Latn
+ {0xD5130000u, 45u}, // tiv -> Latn
+ {0x746B0000u, 45u}, // tk -> Latn
+ {0xAD530000u, 45u}, // tkl -> Latn
+ {0xC5530000u, 45u}, // tkr -> Latn
{0xCD530000u, 19u}, // tkt -> Deva
- {0x746C0000u, 46u}, // tl -> Latn
- {0x95730000u, 46u}, // tlf -> Latn
- {0xDD730000u, 46u}, // tlx -> Latn
- {0xE1730000u, 46u}, // tly -> Latn
- {0x9D930000u, 46u}, // tmh -> Latn
- {0xE1930000u, 46u}, // tmy -> Latn
- {0x746E0000u, 46u}, // tn -> Latn
- {0x9DB30000u, 46u}, // tnh -> Latn
- {0x746F0000u, 46u}, // to -> Latn
- {0x95D30000u, 46u}, // tof -> Latn
- {0x99D30000u, 46u}, // tog -> Latn
- {0xC1D30000u, 46u}, // toq -> Latn
- {0xA1F30000u, 46u}, // tpi -> Latn
- {0xB1F30000u, 46u}, // tpm -> Latn
- {0xE5F30000u, 46u}, // tpz -> Latn
- {0xBA130000u, 46u}, // tqo -> Latn
- {0x74720000u, 46u}, // tr -> Latn
- {0xD2330000u, 46u}, // tru -> Latn
- {0xD6330000u, 46u}, // trv -> Latn
+ {0x746C0000u, 45u}, // tl -> Latn
+ {0x95730000u, 45u}, // tlf -> Latn
+ {0xDD730000u, 45u}, // tlx -> Latn
+ {0xE1730000u, 45u}, // tly -> Latn
+ {0x9D930000u, 45u}, // tmh -> Latn
+ {0xE1930000u, 45u}, // tmy -> Latn
+ {0x746E0000u, 45u}, // tn -> Latn
+ {0x9DB30000u, 45u}, // tnh -> Latn
+ {0x746F0000u, 45u}, // to -> Latn
+ {0x95D30000u, 45u}, // tof -> Latn
+ {0x99D30000u, 45u}, // tog -> Latn
+ {0xC1D30000u, 45u}, // toq -> Latn
+ {0xA1F30000u, 45u}, // tpi -> Latn
+ {0xB1F30000u, 45u}, // tpm -> Latn
+ {0xE5F30000u, 45u}, // tpz -> Latn
+ {0xBA130000u, 45u}, // tqo -> Latn
+ {0x74720000u, 45u}, // tr -> Latn
+ {0xD2330000u, 45u}, // tru -> Latn
+ {0xD6330000u, 45u}, // trv -> Latn
{0xDA330000u, 2u}, // trw -> Arab
- {0x74730000u, 46u}, // ts -> Latn
+ {0x74730000u, 45u}, // ts -> Latn
{0x8E530000u, 26u}, // tsd -> Grek
{0x96530000u, 19u}, // tsf -> Deva
- {0x9A530000u, 46u}, // tsg -> Latn
- {0xA6530000u, 92u}, // tsj -> Tibt
- {0xDA530000u, 46u}, // tsw -> Latn
+ {0x9A530000u, 45u}, // tsg -> Latn
+ {0xA6530000u, 93u}, // tsj -> Tibt
+ {0xDA530000u, 45u}, // tsw -> Latn
{0x74740000u, 18u}, // tt -> Cyrl
- {0x8E730000u, 46u}, // ttd -> Latn
- {0x92730000u, 46u}, // tte -> Latn
- {0xA6730000u, 46u}, // ttj -> Latn
- {0xC6730000u, 46u}, // ttr -> Latn
- {0xCA730000u, 91u}, // tts -> Thai
- {0xCE730000u, 46u}, // ttt -> Latn
- {0x9E930000u, 46u}, // tuh -> Latn
- {0xAE930000u, 46u}, // tul -> Latn
- {0xB2930000u, 46u}, // tum -> Latn
- {0xC2930000u, 46u}, // tuq -> Latn
- {0x8EB30000u, 46u}, // tvd -> Latn
- {0xAEB30000u, 46u}, // tvl -> Latn
- {0xD2B30000u, 46u}, // tvu -> Latn
- {0x9ED30000u, 46u}, // twh -> Latn
- {0xC2D30000u, 46u}, // twq -> Latn
- {0x9AF30000u, 86u}, // txg -> Tang
- {0x74790000u, 46u}, // ty -> Latn
- {0x83130000u, 46u}, // tya -> Latn
+ {0x8E730000u, 45u}, // ttd -> Latn
+ {0x92730000u, 45u}, // tte -> Latn
+ {0xA6730000u, 45u}, // ttj -> Latn
+ {0xC6730000u, 45u}, // ttr -> Latn
+ {0xCA730000u, 92u}, // tts -> Thai
+ {0xCE730000u, 45u}, // ttt -> Latn
+ {0x9E930000u, 45u}, // tuh -> Latn
+ {0xAE930000u, 45u}, // tul -> Latn
+ {0xB2930000u, 45u}, // tum -> Latn
+ {0xC2930000u, 45u}, // tuq -> Latn
+ {0x8EB30000u, 45u}, // tvd -> Latn
+ {0xAEB30000u, 45u}, // tvl -> Latn
+ {0xD2B30000u, 45u}, // tvu -> Latn
+ {0x9ED30000u, 45u}, // twh -> Latn
+ {0xC2D30000u, 45u}, // twq -> Latn
+ {0x9AF30000u, 87u}, // txg -> Tang
+ {0xBAF30000u, 95u}, // txo -> Toto
+ {0x74790000u, 45u}, // ty -> Latn
+ {0x83130000u, 45u}, // tya -> Latn
{0xD7130000u, 18u}, // tyv -> Cyrl
- {0xB3330000u, 46u}, // tzm -> Latn
- {0xD0340000u, 46u}, // ubu -> Latn
+ {0xB3330000u, 45u}, // tzm -> Latn
+ {0xD0340000u, 45u}, // ubu -> Latn
{0xA0740000u, 0u}, // udi -> Aghb
{0xB0740000u, 18u}, // udm -> Cyrl
{0x75670000u, 2u}, // ug -> Arab
{0x75674B5Au, 18u}, // ug-KZ -> Cyrl
{0x75674D4Eu, 18u}, // ug-MN -> Cyrl
- {0x80D40000u, 93u}, // uga -> Ugar
+ {0x80D40000u, 96u}, // uga -> Ugar
{0x756B0000u, 18u}, // uk -> Cyrl
- {0xA1740000u, 46u}, // uli -> Latn
- {0x85940000u, 46u}, // umb -> Latn
+ {0xA1740000u, 45u}, // uli -> Latn
+ {0x85940000u, 45u}, // umb -> Latn
{0xC5B40000u, 8u}, // unr -> Beng
{0xC5B44E50u, 19u}, // unr-NP -> Deva
{0xDDB40000u, 8u}, // unx -> Beng
- {0xA9D40000u, 46u}, // uok -> Latn
+ {0xA9D40000u, 45u}, // uok -> Latn
{0x75720000u, 2u}, // ur -> Arab
- {0xA2340000u, 46u}, // uri -> Latn
- {0xCE340000u, 46u}, // urt -> Latn
- {0xDA340000u, 46u}, // urw -> Latn
- {0x82540000u, 46u}, // usa -> Latn
- {0x9E740000u, 46u}, // uth -> Latn
- {0xC6740000u, 46u}, // utr -> Latn
- {0x9EB40000u, 46u}, // uvh -> Latn
- {0xAEB40000u, 46u}, // uvl -> Latn
- {0x757A0000u, 46u}, // uz -> Latn
+ {0xA2340000u, 45u}, // uri -> Latn
+ {0xCE340000u, 45u}, // urt -> Latn
+ {0xDA340000u, 45u}, // urw -> Latn
+ {0x82540000u, 45u}, // usa -> Latn
+ {0x9E740000u, 45u}, // uth -> Latn
+ {0xC6740000u, 45u}, // utr -> Latn
+ {0x9EB40000u, 45u}, // uvh -> Latn
+ {0xAEB40000u, 45u}, // uvl -> Latn
+ {0x757A0000u, 45u}, // uz -> Latn
{0x757A4146u, 2u}, // uz-AF -> Arab
{0x757A434Eu, 18u}, // uz-CN -> Cyrl
- {0x98150000u, 46u}, // vag -> Latn
- {0xA0150000u, 94u}, // vai -> Vaii
- {0xB4150000u, 46u}, // van -> Latn
- {0x76650000u, 46u}, // ve -> Latn
- {0x88950000u, 46u}, // vec -> Latn
- {0xBC950000u, 46u}, // vep -> Latn
- {0x76690000u, 46u}, // vi -> Latn
- {0x89150000u, 46u}, // vic -> Latn
- {0xD5150000u, 46u}, // viv -> Latn
- {0xC9750000u, 46u}, // vls -> Latn
- {0x95950000u, 46u}, // vmf -> Latn
- {0xD9950000u, 46u}, // vmw -> Latn
- {0x766F0000u, 46u}, // vo -> Latn
- {0xCDD50000u, 46u}, // vot -> Latn
- {0xBA350000u, 46u}, // vro -> Latn
- {0xB6950000u, 46u}, // vun -> Latn
- {0xCE950000u, 46u}, // vut -> Latn
- {0x77610000u, 46u}, // wa -> Latn
- {0x90160000u, 46u}, // wae -> Latn
- {0xA4160000u, 46u}, // waj -> Latn
+ {0x98150000u, 45u}, // vag -> Latn
+ {0xA0150000u, 97u}, // vai -> Vaii
+ {0xB4150000u, 45u}, // van -> Latn
+ {0x76650000u, 45u}, // ve -> Latn
+ {0x88950000u, 45u}, // vec -> Latn
+ {0xBC950000u, 45u}, // vep -> Latn
+ {0x76690000u, 45u}, // vi -> Latn
+ {0x89150000u, 45u}, // vic -> Latn
+ {0xD5150000u, 45u}, // viv -> Latn
+ {0xC9750000u, 45u}, // vls -> Latn
+ {0x95950000u, 45u}, // vmf -> Latn
+ {0xD9950000u, 45u}, // vmw -> Latn
+ {0x766F0000u, 45u}, // vo -> Latn
+ {0xCDD50000u, 45u}, // vot -> Latn
+ {0xBA350000u, 45u}, // vro -> Latn
+ {0xB6950000u, 45u}, // vun -> Latn
+ {0xCE950000u, 45u}, // vut -> Latn
+ {0x77610000u, 45u}, // wa -> Latn
+ {0x90160000u, 45u}, // wae -> Latn
+ {0xA4160000u, 45u}, // waj -> Latn
{0xAC160000u, 21u}, // wal -> Ethi
- {0xB4160000u, 46u}, // wan -> Latn
- {0xC4160000u, 46u}, // war -> Latn
- {0xBC360000u, 46u}, // wbp -> Latn
- {0xC0360000u, 88u}, // wbq -> Telu
+ {0xB4160000u, 45u}, // wan -> Latn
+ {0xC4160000u, 45u}, // war -> Latn
+ {0xBC360000u, 45u}, // wbp -> Latn
+ {0xC0360000u, 89u}, // wbq -> Telu
{0xC4360000u, 19u}, // wbr -> Deva
- {0xA0560000u, 46u}, // wci -> Latn
- {0xC4960000u, 46u}, // wer -> Latn
- {0xA0D60000u, 46u}, // wgi -> Latn
- {0x98F60000u, 46u}, // whg -> Latn
- {0x85160000u, 46u}, // wib -> Latn
- {0xD1160000u, 46u}, // wiu -> Latn
- {0xD5160000u, 46u}, // wiv -> Latn
- {0x81360000u, 46u}, // wja -> Latn
- {0xA1360000u, 46u}, // wji -> Latn
- {0xC9760000u, 46u}, // wls -> Latn
- {0xB9960000u, 46u}, // wmo -> Latn
- {0x89B60000u, 46u}, // wnc -> Latn
+ {0xA0560000u, 45u}, // wci -> Latn
+ {0xC4960000u, 45u}, // wer -> Latn
+ {0xA0D60000u, 45u}, // wgi -> Latn
+ {0x98F60000u, 45u}, // whg -> Latn
+ {0x85160000u, 45u}, // wib -> Latn
+ {0xD1160000u, 45u}, // wiu -> Latn
+ {0xD5160000u, 45u}, // wiv -> Latn
+ {0x81360000u, 45u}, // wja -> Latn
+ {0xA1360000u, 45u}, // wji -> Latn
+ {0xC9760000u, 45u}, // wls -> Latn
+ {0xB9960000u, 45u}, // wmo -> Latn
+ {0x89B60000u, 45u}, // wnc -> Latn
{0xA1B60000u, 2u}, // wni -> Arab
- {0xD1B60000u, 46u}, // wnu -> Latn
- {0x776F0000u, 46u}, // wo -> Latn
- {0x85D60000u, 46u}, // wob -> Latn
- {0xC9D60000u, 46u}, // wos -> Latn
- {0xCA360000u, 46u}, // wrs -> Latn
+ {0xD1B60000u, 45u}, // wnu -> Latn
+ {0x776F0000u, 45u}, // wo -> Latn
+ {0x85D60000u, 45u}, // wob -> Latn
+ {0xC9D60000u, 45u}, // wos -> Latn
+ {0xCA360000u, 45u}, // wrs -> Latn
{0x9A560000u, 23u}, // wsg -> Gong
- {0xAA560000u, 46u}, // wsk -> Latn
+ {0xAA560000u, 45u}, // wsk -> Latn
{0xB2760000u, 19u}, // wtm -> Deva
{0xD2960000u, 29u}, // wuu -> Hans
- {0xD6960000u, 46u}, // wuv -> Latn
- {0x82D60000u, 46u}, // wwa -> Latn
- {0xD4170000u, 46u}, // xav -> Latn
- {0xA0370000u, 46u}, // xbi -> Latn
+ {0xD6960000u, 45u}, // wuv -> Latn
+ {0x82D60000u, 45u}, // wwa -> Latn
+ {0xD4170000u, 45u}, // xav -> Latn
+ {0xA0370000u, 45u}, // xbi -> Latn
{0xB8570000u, 15u}, // xco -> Chrs
{0xC4570000u, 12u}, // xcr -> Cari
- {0xC8970000u, 46u}, // xes -> Latn
- {0x78680000u, 46u}, // xh -> Latn
- {0x81770000u, 46u}, // xla -> Latn
- {0x89770000u, 50u}, // xlc -> Lyci
- {0x8D770000u, 51u}, // xld -> Lydi
+ {0xC8970000u, 45u}, // xes -> Latn
+ {0x78680000u, 45u}, // xh -> Latn
+ {0x81770000u, 45u}, // xla -> Latn
+ {0x89770000u, 49u}, // xlc -> Lyci
+ {0x8D770000u, 50u}, // xld -> Lydi
{0x95970000u, 22u}, // xmf -> Geor
- {0xB5970000u, 53u}, // xmn -> Mani
- {0xC5970000u, 55u}, // xmr -> Merc
- {0x81B70000u, 60u}, // xna -> Narb
+ {0xB5970000u, 52u}, // xmn -> Mani
+ {0xC5970000u, 54u}, // xmr -> Merc
+ {0x81B70000u, 59u}, // xna -> Narb
{0xC5B70000u, 19u}, // xnr -> Deva
- {0x99D70000u, 46u}, // xog -> Latn
- {0xB5D70000u, 46u}, // xon -> Latn
+ {0x99D70000u, 45u}, // xog -> Latn
+ {0xB5D70000u, 45u}, // xon -> Latn
{0xC5F70000u, 72u}, // xpr -> Prti
- {0x86370000u, 46u}, // xrb -> Latn
- {0x82570000u, 75u}, // xsa -> Sarb
- {0xA2570000u, 46u}, // xsi -> Latn
- {0xB2570000u, 46u}, // xsm -> Latn
+ {0x86370000u, 45u}, // xrb -> Latn
+ {0x82570000u, 76u}, // xsa -> Sarb
+ {0xA2570000u, 45u}, // xsi -> Latn
+ {0xB2570000u, 45u}, // xsm -> Latn
{0xC6570000u, 19u}, // xsr -> Deva
- {0x92D70000u, 46u}, // xwe -> Latn
- {0xB0180000u, 46u}, // yam -> Latn
- {0xB8180000u, 46u}, // yao -> Latn
- {0xBC180000u, 46u}, // yap -> Latn
- {0xC8180000u, 46u}, // yas -> Latn
- {0xCC180000u, 46u}, // yat -> Latn
- {0xD4180000u, 46u}, // yav -> Latn
- {0xE0180000u, 46u}, // yay -> Latn
- {0xE4180000u, 46u}, // yaz -> Latn
- {0x80380000u, 46u}, // yba -> Latn
- {0x84380000u, 46u}, // ybb -> Latn
- {0xE0380000u, 46u}, // yby -> Latn
- {0xC4980000u, 46u}, // yer -> Latn
- {0xC4D80000u, 46u}, // ygr -> Latn
- {0xD8D80000u, 46u}, // ygw -> Latn
+ {0x92D70000u, 45u}, // xwe -> Latn
+ {0xB0180000u, 45u}, // yam -> Latn
+ {0xB8180000u, 45u}, // yao -> Latn
+ {0xBC180000u, 45u}, // yap -> Latn
+ {0xC8180000u, 45u}, // yas -> Latn
+ {0xCC180000u, 45u}, // yat -> Latn
+ {0xD4180000u, 45u}, // yav -> Latn
+ {0xE0180000u, 45u}, // yay -> Latn
+ {0xE4180000u, 45u}, // yaz -> Latn
+ {0x80380000u, 45u}, // yba -> Latn
+ {0x84380000u, 45u}, // ybb -> Latn
+ {0xE0380000u, 45u}, // yby -> Latn
+ {0xC4980000u, 45u}, // yer -> Latn
+ {0xC4D80000u, 45u}, // ygr -> Latn
+ {0xD8D80000u, 45u}, // ygw -> Latn
{0x79690000u, 31u}, // yi -> Hebr
- {0xB9580000u, 46u}, // yko -> Latn
- {0x91780000u, 46u}, // yle -> Latn
- {0x99780000u, 46u}, // ylg -> Latn
- {0xAD780000u, 46u}, // yll -> Latn
- {0xAD980000u, 46u}, // yml -> Latn
- {0x796F0000u, 46u}, // yo -> Latn
- {0xB5D80000u, 46u}, // yon -> Latn
- {0x86380000u, 46u}, // yrb -> Latn
- {0x92380000u, 46u}, // yre -> Latn
- {0xAE380000u, 46u}, // yrl -> Latn
- {0xCA580000u, 46u}, // yss -> Latn
- {0x82980000u, 46u}, // yua -> Latn
+ {0xB9580000u, 45u}, // yko -> Latn
+ {0x91780000u, 45u}, // yle -> Latn
+ {0x99780000u, 45u}, // ylg -> Latn
+ {0xAD780000u, 45u}, // yll -> Latn
+ {0xAD980000u, 45u}, // yml -> Latn
+ {0x796F0000u, 45u}, // yo -> Latn
+ {0xB5D80000u, 45u}, // yon -> Latn
+ {0x86380000u, 45u}, // yrb -> Latn
+ {0x92380000u, 45u}, // yre -> Latn
+ {0xAE380000u, 45u}, // yrl -> Latn
+ {0xCA580000u, 45u}, // yss -> Latn
+ {0x82980000u, 45u}, // yua -> Latn
{0x92980000u, 30u}, // yue -> Hant
{0x9298434Eu, 29u}, // yue-CN -> Hans
- {0xA6980000u, 46u}, // yuj -> Latn
- {0xCE980000u, 46u}, // yut -> Latn
- {0xDA980000u, 46u}, // yuw -> Latn
- {0x7A610000u, 46u}, // za -> Latn
- {0x98190000u, 46u}, // zag -> Latn
+ {0xA6980000u, 45u}, // yuj -> Latn
+ {0xCE980000u, 45u}, // yut -> Latn
+ {0xDA980000u, 45u}, // yuw -> Latn
+ {0x7A610000u, 45u}, // za -> Latn
+ {0x98190000u, 45u}, // zag -> Latn
{0xA4790000u, 2u}, // zdj -> Arab
- {0x80990000u, 46u}, // zea -> Latn
- {0x9CD90000u, 89u}, // zgh -> Tfng
+ {0x80990000u, 45u}, // zea -> Latn
+ {0x9CD90000u, 90u}, // zgh -> Tfng
{0x7A680000u, 29u}, // zh -> Hans
{0x7A684155u, 30u}, // zh-AU -> Hant
{0x7A68424Eu, 30u}, // zh-BN -> Hant
@@ -1486,14 +1493,14 @@
{0x7A685457u, 30u}, // zh-TW -> Hant
{0x7A685553u, 30u}, // zh-US -> Hant
{0x7A68564Eu, 30u}, // zh-VN -> Hant
- {0xDCF90000u, 62u}, // zhx -> Nshu
- {0x81190000u, 46u}, // zia -> Latn
- {0xCD590000u, 41u}, // zkt -> Kits
- {0xB1790000u, 46u}, // zlm -> Latn
- {0xA1990000u, 46u}, // zmi -> Latn
- {0x91B90000u, 46u}, // zne -> Latn
- {0x7A750000u, 46u}, // zu -> Latn
- {0x83390000u, 46u}, // zza -> Latn
+ {0xDCF90000u, 61u}, // zhx -> Nshu
+ {0x81190000u, 45u}, // zia -> Latn
+ {0xCD590000u, 40u}, // zkt -> Kits
+ {0xB1790000u, 45u}, // zlm -> Latn
+ {0xA1990000u, 45u}, // zmi -> Latn
+ {0x91B90000u, 45u}, // zne -> Latn
+ {0x7A750000u, 45u}, // zu -> Latn
+ {0x83390000u, 45u}, // zza -> Latn
});
std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({
@@ -1573,6 +1580,7 @@
0xCD21534E4C61746ELLU, // bjt_Latn_SN
0xB141434D4C61746ELLU, // bkm_Latn_CM
0xD14150484C61746ELLU, // bku_Latn_PH
+ 0x99614D594C61746ELLU, // blg_Latn_MY
0xCD61564E54617674LLU, // blt_Tavt_VN
0x626D4D4C4C61746ELLU, // bm_Latn_ML
0xC1814D4C4C61746ELLU, // bmq_Latn_ML
@@ -1748,7 +1756,7 @@
0x8D87434E506C7264LLU, // hmd_Plrd_CN
0x8DA7504B41726162LLU, // hnd_Arab_PK
0x91A7494E44657661LLU, // hne_Deva_IN
- 0xA5A74C41486D6E67LLU, // hnj_Hmng_LA
+ 0xA5A75553486D6E70LLU, // hnj_Hmnp_US
0xB5A750484C61746ELLU, // hnn_Latn_PH
0xB9A7504B41726162LLU, // hno_Arab_PK
0x686F50474C61746ELLU, // ho_Latn_PG
@@ -1797,7 +1805,7 @@
0x984A4E474C61746ELLU, // kcg_Latn_NG
0xA84A5A574C61746ELLU, // kck_Latn_ZW
0x906A545A4C61746ELLU, // kde_Latn_TZ
- 0x9C6A544741726162LLU, // kdh_Arab_TG
+ 0x9C6A54474C61746ELLU, // kdh_Latn_TG
0xCC6A544854686169LLU, // kdt_Thai_TH
0x808A43564C61746ELLU, // kea_Latn_CV
0xB48A434D4C61746ELLU, // ken_Latn_CM
@@ -1982,6 +1990,7 @@
0x6E725A414C61746ELLU, // nr_Latn_ZA
0xAA4D434143616E73LLU, // nsk_Cans_CA
0xBA4D5A414C61746ELLU, // nso_Latn_ZA
+ 0xCE4D494E546E7361LLU, // nst_Tnsa_IN
0xCA8D53534C61746ELLU, // nus_Latn_SS
0x6E7655534C61746ELLU, // nv_Latn_US
0xC2ED434E4C61746ELLU, // nxq_Latn_CN
@@ -1995,6 +2004,7 @@
0x6F7347454379726CLLU, // os_Cyrl_GE
0x824E55534F736765LLU, // osa_Osge_US
0xAA6E4D4E4F726B68LLU, // otk_Orkh_MN
+ 0xA28E8C814F756772LLU, // oui_Ougr_143
0x7061504B41726162LLU, // pa_Arab_PK
0x7061494E47757275LLU, // pa_Guru_IN
0x980F50484C61746ELLU, // pag_Latn_PH
@@ -2029,7 +2039,7 @@
0x945152454C61746ELLU, // rcf_Latn_RE
0xA49149444C61746ELLU, // rej_Latn_ID
0xB4D149544C61746ELLU, // rgn_Latn_IT
- 0x98F14D4D41726162LLU, // rhg_Arab_MM
+ 0x98F14D4D526F6867LLU, // rhg_Rohg_MM
0x8111494E4C61746ELLU, // ria_Latn_IN
0x95114D4154666E67LLU, // rif_Tfng_MA
0xC9314E5044657661LLU, // rjs_Deva_NP
@@ -2172,6 +2182,7 @@
0xAEB354564C61746ELLU, // tvl_Latn_TV
0xC2D34E454C61746ELLU, // twq_Latn_NE
0x9AF3434E54616E67LLU, // txg_Tang_CN
+ 0xBAF3494E546F746FLLU, // txo_Toto_IN
0x747950464C61746ELLU, // ty_Latn_PF
0xD71352554379726CLLU, // tyv_Cyrl_RU
0xB3334D414C61746ELLU, // tzm_Latn_MA
@@ -2256,6 +2267,7 @@
});
const std::unordered_map<uint32_t, uint32_t> ARAB_PARENTS({
+ {0x61724145u, 0x61729420u}, // ar-AE -> ar-015
{0x6172445Au, 0x61729420u}, // ar-DZ -> ar-015
{0x61724548u, 0x61729420u}, // ar-EH -> ar-015
{0x61724C59u, 0x61729420u}, // ar-LY -> ar-015
@@ -2279,7 +2291,6 @@
{0x656E4253u, 0x656E8400u}, // en-BS -> en-001
{0x656E4257u, 0x656E8400u}, // en-BW -> en-001
{0x656E425Au, 0x656E8400u}, // en-BZ -> en-001
- {0x656E4341u, 0x656E8400u}, // en-CA -> en-001
{0x656E4343u, 0x656E8400u}, // en-CC -> en-001
{0x656E4348u, 0x656E80A1u}, // en-CH -> en-150
{0x656E434Bu, 0x656E8400u}, // en-CK -> en-001
@@ -2332,7 +2343,6 @@
{0x656E4E55u, 0x656E8400u}, // en-NU -> en-001
{0x656E4E5Au, 0x656E8400u}, // en-NZ -> en-001
{0x656E5047u, 0x656E8400u}, // en-PG -> en-001
- {0x656E5048u, 0x656E8400u}, // en-PH -> en-001
{0x656E504Bu, 0x656E8400u}, // en-PK -> en-001
{0x656E504Eu, 0x656E8400u}, // en-PN -> en-001
{0x656E5057u, 0x656E8400u}, // en-PW -> en-001
diff --git a/media/java/android/media/tv/BroadcastInfoRequest.java b/media/java/android/media/tv/BroadcastInfoRequest.java
index 4d8eda1..c439356 100644
--- a/media/java/android/media/tv/BroadcastInfoRequest.java
+++ b/media/java/android/media/tv/BroadcastInfoRequest.java
@@ -23,19 +23,35 @@
/** @hide */
public abstract class BroadcastInfoRequest implements Parcelable {
- protected static final int PARCEL_TOKEN_TS_REQUEST = 1;
+
+ // todo: change const declaration to intdef
+ public static final int REQUEST_OPTION_REPEAT = 11;
+ public static final int REQUEST_OPTION_AUTO_UPDATE = 12;
public static final @NonNull Parcelable.Creator<BroadcastInfoRequest> CREATOR =
new Parcelable.Creator<BroadcastInfoRequest>() {
@Override
public BroadcastInfoRequest createFromParcel(Parcel source) {
- int token = source.readInt();
- switch (token) {
- case PARCEL_TOKEN_TS_REQUEST:
+ int type = source.readInt();
+ switch (type) {
+ case BroadcastInfoType.TS:
return TsRequest.createFromParcelBody(source);
+ case BroadcastInfoType.TABLE:
+ return TableRequest.createFromParcelBody(source);
+ case BroadcastInfoType.SECTION:
+ return SectionRequest.createFromParcelBody(source);
+ case BroadcastInfoType.PES:
+ return PesRequest.createFromParcelBody(source);
+ case BroadcastInfoType.STREAM_EVENT:
+ return StreamEventRequest.createFromParcelBody(source);
+ case BroadcastInfoType.DSMCC:
+ return DsmccRequest.createFromParcelBody(source);
+ case BroadcastInfoType.TV_PROPRIETARY_FUNCTION:
+ return TvProprietaryFunctionRequest.createFromParcelBody(source);
default:
throw new IllegalStateException(
- "Unexpected broadcast info request type token in parcel.");
+ "Unexpected broadcast info request type (value "
+ + type + ") in parcel.");
}
}
@@ -45,18 +61,32 @@
}
};
- int requestId;
+ protected final int mType;
+ protected final int mRequestId;
+ protected final int mOption;
- public BroadcastInfoRequest(int requestId) {
- this.requestId = requestId;
+ protected BroadcastInfoRequest(int type, int requestId, int option) {
+ mType = type;
+ mRequestId = requestId;
+ mOption = option;
}
- protected BroadcastInfoRequest(Parcel source) {
- requestId = source.readInt();
+ protected BroadcastInfoRequest(int type, Parcel source) {
+ mType = type;
+ mRequestId = source.readInt();
+ mOption = source.readInt();
+ }
+
+ public int getType() {
+ return mType;
}
public int getRequestId() {
- return requestId;
+ return mRequestId;
+ }
+
+ public int getOption() {
+ return mOption;
}
@Override
@@ -66,6 +96,8 @@
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeInt(requestId);
+ dest.writeInt(mType);
+ dest.writeInt(mRequestId);
+ dest.writeInt(mOption);
}
}
diff --git a/media/java/android/media/tv/BroadcastInfoResponse.java b/media/java/android/media/tv/BroadcastInfoResponse.java
index fe4e8b7..288f2f9 100644
--- a/media/java/android/media/tv/BroadcastInfoResponse.java
+++ b/media/java/android/media/tv/BroadcastInfoResponse.java
@@ -22,12 +22,37 @@
import android.annotation.NonNull;
/** @hide */
-public final class BroadcastInfoResponse implements Parcelable {
+public abstract class BroadcastInfoResponse implements Parcelable {
+ // todo: change const declaration to intdef
+ public static final int ERROR = 1;
+ public static final int OK = 2;
+ public static final int CANCEL = 3;
+
public static final @NonNull Parcelable.Creator<BroadcastInfoResponse> CREATOR =
new Parcelable.Creator<BroadcastInfoResponse>() {
@Override
public BroadcastInfoResponse createFromParcel(Parcel source) {
- return new BroadcastInfoResponse(source);
+ int type = source.readInt();
+ switch (type) {
+ case BroadcastInfoType.TS:
+ return TsResponse.createFromParcelBody(source);
+ case BroadcastInfoType.TABLE:
+ return TableResponse.createFromParcelBody(source);
+ case BroadcastInfoType.SECTION:
+ return SectionResponse.createFromParcelBody(source);
+ case BroadcastInfoType.PES:
+ return PesResponse.createFromParcelBody(source);
+ case BroadcastInfoType.STREAM_EVENT:
+ return StreamEventResponse.createFromParcelBody(source);
+ case BroadcastInfoType.DSMCC:
+ return DsmccResponse.createFromParcelBody(source);
+ case BroadcastInfoType.TV_PROPRIETARY_FUNCTION:
+ return TvProprietaryFunctionResponse.createFromParcelBody(source);
+ default:
+ throw new IllegalStateException(
+ "Unexpected broadcast info response type (value "
+ + type + ") in parcel.");
+ }
}
@Override
@@ -36,18 +61,39 @@
}
};
- int requestId;
+ protected final int mType;
+ protected final int mRequestId;
+ protected final int mSequence;
+ protected final int mResponseResult;
- public BroadcastInfoResponse(int requestId) {
- this.requestId = requestId;
+ protected BroadcastInfoResponse(int type, int requestId, int sequence, int responseResult) {
+ mType = type;
+ mRequestId = requestId;
+ mSequence = sequence;
+ mResponseResult = responseResult;
}
- private BroadcastInfoResponse(Parcel source) {
- requestId = source.readInt();
+ protected BroadcastInfoResponse(int type, Parcel source) {
+ mType = type;
+ mRequestId = source.readInt();
+ mSequence = source.readInt();
+ mResponseResult = source.readInt();
+ }
+
+ public int getType() {
+ return mType;
}
public int getRequestId() {
- return requestId;
+ return mRequestId;
+ }
+
+ public int getSequence() {
+ return mSequence;
+ }
+
+ public int getResponseResult() {
+ return mResponseResult;
}
@Override
@@ -57,6 +103,9 @@
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeInt(requestId);
+ dest.writeInt(mType);
+ dest.writeInt(mRequestId);
+ dest.writeInt(mSequence);
+ dest.writeInt(mResponseResult);
}
}
diff --git a/media/java/android/media/tv/BroadcastInfoType.java b/media/java/android/media/tv/BroadcastInfoType.java
new file mode 100644
index 0000000..e7a0595
--- /dev/null
+++ b/media/java/android/media/tv/BroadcastInfoType.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+/** @hide */
+public final class BroadcastInfoType {
+ // todo: change const declaration to intdef in TvInputManager
+ public static final int TS = 1;
+ public static final int TABLE = 2;
+ public static final int SECTION = 3;
+ public static final int PES = 4;
+ public static final int STREAM_EVENT = 5;
+ public static final int DSMCC = 6;
+ public static final int TV_PROPRIETARY_FUNCTION = 7;
+
+ private BroadcastInfoType() {
+ }
+}
diff --git a/media/java/android/media/tv/DsmccRequest.java b/media/java/android/media/tv/DsmccRequest.java
new file mode 100644
index 0000000..f2e4750
--- /dev/null
+++ b/media/java/android/media/tv/DsmccRequest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.annotation.NonNull;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/** @hide */
+public class DsmccRequest extends BroadcastInfoRequest implements Parcelable {
+ public static final int requestType = BroadcastInfoType.DSMCC;
+
+ public static final @NonNull Parcelable.Creator<DsmccRequest> CREATOR =
+ new Parcelable.Creator<DsmccRequest>() {
+ @Override
+ public DsmccRequest createFromParcel(Parcel source) {
+ source.readInt();
+ return createFromParcelBody(source);
+ }
+
+ @Override
+ public DsmccRequest[] newArray(int size) {
+ return new DsmccRequest[size];
+ }
+ };
+
+ private final Uri mUri;
+
+ public static DsmccRequest createFromParcelBody(Parcel in) {
+ return new DsmccRequest(in);
+ }
+
+ public DsmccRequest(int requestId, int option, Uri uri) {
+ super(requestType, requestId, option);
+ mUri = uri;
+ }
+
+ protected DsmccRequest(Parcel source) {
+ super(requestType, source);
+ String uriString = source.readString();
+ mUri = uriString == null ? null : Uri.parse(uriString);
+ }
+
+ public Uri getUri() {
+ return mUri;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ String uriString = mUri == null ? null : mUri.toString();
+ dest.writeString(uriString);
+ }
+}
diff --git a/media/java/android/media/tv/DsmccResponse.java b/media/java/android/media/tv/DsmccResponse.java
new file mode 100644
index 0000000..3bdfb95
--- /dev/null
+++ b/media/java/android/media/tv/DsmccResponse.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
+
+/** @hide */
+public class DsmccResponse extends BroadcastInfoResponse implements Parcelable {
+ public static final int responseType = BroadcastInfoType.DSMCC;
+
+ public static final @NonNull Parcelable.Creator<DsmccResponse> CREATOR =
+ new Parcelable.Creator<DsmccResponse>() {
+ @Override
+ public DsmccResponse createFromParcel(Parcel source) {
+ source.readInt();
+ return createFromParcelBody(source);
+ }
+
+ @Override
+ public DsmccResponse[] newArray(int size) {
+ return new DsmccResponse[size];
+ }
+ };
+
+ private final ParcelFileDescriptor mFile;
+
+ public static DsmccResponse createFromParcelBody(Parcel in) {
+ return new DsmccResponse(in);
+ }
+
+ public DsmccResponse(int requestId, int sequence, int responseResult,
+ ParcelFileDescriptor file) {
+ super(responseType, requestId, sequence, responseResult);
+ mFile = file;
+ }
+
+ protected DsmccResponse(Parcel source) {
+ super(responseType, source);
+ mFile = source.readFileDescriptor();
+ }
+
+ public ParcelFileDescriptor getFile() {
+ return mFile;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ mFile.writeToParcel(dest, flags);
+ }
+}
diff --git a/media/java/android/media/tv/PesRequest.java b/media/java/android/media/tv/PesRequest.java
new file mode 100644
index 0000000..0e444b8
--- /dev/null
+++ b/media/java/android/media/tv/PesRequest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/** @hide */
+public class PesRequest extends BroadcastInfoRequest implements Parcelable {
+ public static final int requestType = BroadcastInfoType.PES;
+
+ public static final @NonNull Parcelable.Creator<PesRequest> CREATOR =
+ new Parcelable.Creator<PesRequest>() {
+ @Override
+ public PesRequest createFromParcel(Parcel source) {
+ source.readInt();
+ return createFromParcelBody(source);
+ }
+
+ @Override
+ public PesRequest[] newArray(int size) {
+ return new PesRequest[size];
+ }
+ };
+
+ private final int mTsPid;
+ private final int mStreamId;
+
+ public static PesRequest createFromParcelBody(Parcel in) {
+ return new PesRequest(in);
+ }
+
+ public PesRequest(int requestId, int option, int tsPid, int streamId) {
+ super(requestType, requestId, option);
+ mTsPid = tsPid;
+ mStreamId = streamId;
+ }
+
+ protected PesRequest(Parcel source) {
+ super(requestType, source);
+ mTsPid = source.readInt();
+ mStreamId = source.readInt();
+ }
+
+ public int getTsPid() {
+ return mTsPid;
+ }
+
+ public int getStreamId() {
+ return mStreamId;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeInt(mTsPid);
+ dest.writeInt(mStreamId);
+ }
+}
diff --git a/media/java/android/media/tv/PesResponse.java b/media/java/android/media/tv/PesResponse.java
new file mode 100644
index 0000000..d46e6fc
--- /dev/null
+++ b/media/java/android/media/tv/PesResponse.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/** @hide */
+public class PesResponse extends BroadcastInfoResponse implements Parcelable {
+ public static final int responseType = BroadcastInfoType.PES;
+
+ public static final @NonNull Parcelable.Creator<PesResponse> CREATOR =
+ new Parcelable.Creator<PesResponse>() {
+ @Override
+ public PesResponse createFromParcel(Parcel source) {
+ source.readInt();
+ return createFromParcelBody(source);
+ }
+
+ @Override
+ public PesResponse[] newArray(int size) {
+ return new PesResponse[size];
+ }
+ };
+
+ private final String mSharedFilterToken;
+
+ public static PesResponse createFromParcelBody(Parcel in) {
+ return new PesResponse(in);
+ }
+
+ public PesResponse(int requestId, int sequence, int responseResult, String sharedFilterToken) {
+ super(responseType, requestId, sequence, responseResult);
+ mSharedFilterToken = sharedFilterToken;
+ }
+
+ protected PesResponse(Parcel source) {
+ super(responseType, source);
+ mSharedFilterToken = source.readString();
+ }
+
+ public String getSharedFilterToken() {
+ return mSharedFilterToken;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeString(mSharedFilterToken);
+ }
+}
diff --git a/media/java/android/media/tv/SectionRequest.java b/media/java/android/media/tv/SectionRequest.java
new file mode 100644
index 0000000..3e8e909
--- /dev/null
+++ b/media/java/android/media/tv/SectionRequest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/** @hide */
+public class SectionRequest extends BroadcastInfoRequest implements Parcelable {
+ public static final int requestType = BroadcastInfoType.SECTION;
+
+ public static final @NonNull Parcelable.Creator<SectionRequest> CREATOR =
+ new Parcelable.Creator<SectionRequest>() {
+ @Override
+ public SectionRequest createFromParcel(Parcel source) {
+ source.readInt();
+ return createFromParcelBody(source);
+ }
+
+ @Override
+ public SectionRequest[] newArray(int size) {
+ return new SectionRequest[size];
+ }
+ };
+
+ private final int mTsPid;
+ private final int mTableId;
+ private final int mVersion;
+
+ public static SectionRequest createFromParcelBody(Parcel in) {
+ return new SectionRequest(in);
+ }
+
+ public SectionRequest(int requestId, int option, int tsPid, int tableId, int version) {
+ super(requestType, requestId, option);
+ mTsPid = tsPid;
+ mTableId = tableId;
+ mVersion = version;
+ }
+
+ protected SectionRequest(Parcel source) {
+ super(requestType, source);
+ mTsPid = source.readInt();
+ mTableId = source.readInt();
+ mVersion = source.readInt();
+ }
+
+ public int getTsPid() {
+ return mTsPid;
+ }
+
+ public int getTableId() {
+ return mTableId;
+ }
+
+ public int getVersion() {
+ return mVersion;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeInt(mTsPid);
+ dest.writeInt(mTableId);
+ dest.writeInt(mVersion);
+ }
+}
diff --git a/media/java/android/media/tv/SectionResponse.java b/media/java/android/media/tv/SectionResponse.java
new file mode 100644
index 0000000..1c8f965
--- /dev/null
+++ b/media/java/android/media/tv/SectionResponse.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.annotation.NonNull;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/** @hide */
+public class SectionResponse extends BroadcastInfoResponse implements Parcelable {
+ public static final int responseType = BroadcastInfoType.SECTION;
+
+ public static final @NonNull Parcelable.Creator<SectionResponse> CREATOR =
+ new Parcelable.Creator<SectionResponse>() {
+ @Override
+ public SectionResponse createFromParcel(Parcel source) {
+ source.readInt();
+ return createFromParcelBody(source);
+ }
+
+ @Override
+ public SectionResponse[] newArray(int size) {
+ return new SectionResponse[size];
+ }
+ };
+
+ private final int mSessionId;
+ private final int mVersion;
+ private final Bundle mSessionData;
+
+ public static SectionResponse createFromParcelBody(Parcel in) {
+ return new SectionResponse(in);
+ }
+
+ public SectionResponse(int requestId, int sequence, int responseResult, int sessionId,
+ int version, Bundle sessionData) {
+ super(responseType, requestId, sequence, responseResult);
+ mSessionId = sessionId;
+ mVersion = version;
+ mSessionData = sessionData;
+ }
+
+ protected SectionResponse(Parcel source) {
+ super(responseType, source);
+ mSessionId = source.readInt();
+ mVersion = source.readInt();
+ mSessionData = source.readBundle();
+ }
+
+ public int getSessionId() {
+ return mSessionId;
+ }
+
+ public int getVersion() {
+ return mVersion;
+ }
+
+ public Bundle getSessionData() {
+ return mSessionData;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeInt(mSessionId);
+ dest.writeInt(mVersion);
+ dest.writeBundle(mSessionData);
+ }
+}
diff --git a/media/java/android/media/tv/StreamEventRequest.java b/media/java/android/media/tv/StreamEventRequest.java
new file mode 100644
index 0000000..09399c2
--- /dev/null
+++ b/media/java/android/media/tv/StreamEventRequest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.annotation.NonNull;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/** @hide */
+public class StreamEventRequest extends BroadcastInfoRequest implements Parcelable {
+ public static final int requestType = BroadcastInfoType.STREAM_EVENT;
+
+ public static final @NonNull Parcelable.Creator<StreamEventRequest> CREATOR =
+ new Parcelable.Creator<StreamEventRequest>() {
+ @Override
+ public StreamEventRequest createFromParcel(Parcel source) {
+ source.readInt();
+ return createFromParcelBody(source);
+ }
+
+ @Override
+ public StreamEventRequest[] newArray(int size) {
+ return new StreamEventRequest[size];
+ }
+ };
+
+ private final Uri mTargetUri;
+ private final String mEventName;
+
+ public static StreamEventRequest createFromParcelBody(Parcel in) {
+ return new StreamEventRequest(in);
+ }
+
+ public StreamEventRequest(int requestId, int option, Uri targetUri, String eventName) {
+ super(requestType, requestId, option);
+ this.mTargetUri = targetUri;
+ this.mEventName = eventName;
+ }
+
+ protected StreamEventRequest(Parcel source) {
+ super(requestType, source);
+ String uriString = source.readString();
+ mTargetUri = uriString == null ? null : Uri.parse(uriString);
+ mEventName = source.readString();
+ }
+
+ public Uri getTargetUri() {
+ return mTargetUri;
+ }
+
+ public String getEventName() {
+ return mEventName;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ String uriString = mTargetUri == null ? null : mTargetUri.toString();
+ dest.writeString(uriString);
+ dest.writeString(mEventName);
+ }
+}
diff --git a/media/java/android/media/tv/StreamEventResponse.java b/media/java/android/media/tv/StreamEventResponse.java
new file mode 100644
index 0000000..027b735
--- /dev/null
+++ b/media/java/android/media/tv/StreamEventResponse.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/** @hide */
+public class StreamEventResponse extends BroadcastInfoResponse implements Parcelable {
+ public static final int responseType = BroadcastInfoType.STREAM_EVENT;
+
+ public static final @NonNull Parcelable.Creator<StreamEventResponse> CREATOR =
+ new Parcelable.Creator<StreamEventResponse>() {
+ @Override
+ public StreamEventResponse createFromParcel(Parcel source) {
+ source.readInt();
+ return createFromParcelBody(source);
+ }
+
+ @Override
+ public StreamEventResponse[] newArray(int size) {
+ return new StreamEventResponse[size];
+ }
+ };
+
+ private final String mName;
+ private final String mText;
+ private final String mData;
+ private final String mStatus;
+
+ public static StreamEventResponse createFromParcelBody(Parcel in) {
+ return new StreamEventResponse(in);
+ }
+
+ public StreamEventResponse(int requestId, int sequence, int responseResult, String name,
+ String text, String data, String status) {
+ super(responseType, requestId, sequence, responseResult);
+ mName = name;
+ mText = text;
+ mData = data;
+ mStatus = status;
+ }
+
+ protected StreamEventResponse(Parcel source) {
+ super(responseType, source);
+ mName = source.readString();
+ mText = source.readString();
+ mData = source.readString();
+ mStatus = source.readString();
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public String getText() {
+ return mText;
+ }
+
+ public String getData() {
+ return mData;
+ }
+
+ public String getStatus() {
+ return mStatus;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeString(mName);
+ dest.writeString(mText);
+ dest.writeString(mData);
+ dest.writeString(mStatus);
+ }
+}
diff --git a/media/java/android/media/tv/TableRequest.java b/media/java/android/media/tv/TableRequest.java
new file mode 100644
index 0000000..5432215
--- /dev/null
+++ b/media/java/android/media/tv/TableRequest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/** @hide */
+public class TableRequest extends BroadcastInfoRequest implements Parcelable {
+ public static final int requestType = BroadcastInfoType.TABLE;
+
+ // todo: change const declaration to intdef
+ public static final int PAT = 1;
+ public static final int PMT = 2;
+
+ public static final @NonNull Parcelable.Creator<TableRequest> CREATOR =
+ new Parcelable.Creator<TableRequest>() {
+ @Override
+ public TableRequest createFromParcel(Parcel source) {
+ source.readInt();
+ return createFromParcelBody(source);
+ }
+
+ @Override
+ public TableRequest[] newArray(int size) {
+ return new TableRequest[size];
+ }
+ };
+
+ private final int mTableId;
+ private final int mTableName;
+ private final int mVersion;
+
+ public static TableRequest createFromParcelBody(Parcel in) {
+ return new TableRequest(in);
+ }
+
+ public TableRequest(int requestId, int option, int tableId, int tableName, int version) {
+ super(requestType, requestId, option);
+ mTableId = tableId;
+ mTableName = tableName;
+ mVersion = version;
+ }
+
+ protected TableRequest(Parcel source) {
+ super(requestType, source);
+ mTableId = source.readInt();
+ mTableName = source.readInt();
+ mVersion = source.readInt();
+ }
+
+ public int getTableId() {
+ return mTableId;
+ }
+
+ public int getTableName() {
+ return mTableName;
+ }
+
+ public int getVersion() {
+ return mVersion;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeInt(mTableId);
+ dest.writeInt(mTableName);
+ dest.writeInt(mVersion);
+ }
+}
diff --git a/media/java/android/media/tv/TableResponse.java b/media/java/android/media/tv/TableResponse.java
new file mode 100644
index 0000000..a6d3e39
--- /dev/null
+++ b/media/java/android/media/tv/TableResponse.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.net.Uri;
+
+/** @hide */
+public class TableResponse extends BroadcastInfoResponse implements Parcelable {
+ public static final int responseType = BroadcastInfoType.TABLE;
+
+ public static final @NonNull Parcelable.Creator<TableResponse> CREATOR =
+ new Parcelable.Creator<TableResponse>() {
+ @Override
+ public TableResponse createFromParcel(Parcel source) {
+ source.readInt();
+ return createFromParcelBody(source);
+ }
+
+ @Override
+ public TableResponse[] newArray(int size) {
+ return new TableResponse[size];
+ }
+ };
+
+ private final Uri mTableUri;
+ private final int mVersion;
+ private final int mSize;
+
+ public static TableResponse createFromParcelBody(Parcel in) {
+ return new TableResponse(in);
+ }
+
+ public TableResponse(int requestId, int sequence, int responseResult, Uri tableUri,
+ int version, int size) {
+ super(responseType, requestId, sequence, responseResult);
+ mTableUri = tableUri;
+ mVersion = version;
+ mSize = size;
+ }
+
+ protected TableResponse(Parcel source) {
+ super(responseType, source);
+ String uriString = source.readString();
+ mTableUri = uriString == null ? null : Uri.parse(uriString);
+ mVersion = source.readInt();
+ mSize = source.readInt();
+ }
+
+ public Uri getTableUri() {
+ return mTableUri;
+ }
+
+ public int getVersion() {
+ return mVersion;
+ }
+
+ public int getSize() {
+ return mSize;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ String uriString = mTableUri == null ? null : mTableUri.toString();
+ dest.writeString(uriString);
+ dest.writeInt(mVersion);
+ dest.writeInt(mSize);
+ }
+}
diff --git a/media/java/android/media/tv/TsRequest.aidl b/media/java/android/media/tv/TsRequest.aidl
deleted file mode 100644
index 0abc7ec..0000000
--- a/media/java/android/media/tv/TsRequest.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.tv;
-
-parcelable TsRequest;
diff --git a/media/java/android/media/tv/TsRequest.java b/media/java/android/media/tv/TsRequest.java
index 3690d05..141f3ac 100644
--- a/media/java/android/media/tv/TsRequest.java
+++ b/media/java/android/media/tv/TsRequest.java
@@ -22,6 +22,8 @@
/** @hide */
public class TsRequest extends BroadcastInfoRequest implements Parcelable {
+ public static final int requestType = BroadcastInfoType.TS;
+
public static final @NonNull Parcelable.Creator<TsRequest> CREATOR =
new Parcelable.Creator<TsRequest>() {
@Override
@@ -36,30 +38,29 @@
}
};
- int tsPid;
+ private final int mTsPid;
public static TsRequest createFromParcelBody(Parcel in) {
return new TsRequest(in);
}
- public TsRequest(int requestId, int tsPid) {
- super(requestId);
- this.tsPid = tsPid;
+ public TsRequest(int requestId, int option, int tsPid) {
+ super(requestType, requestId, option);
+ mTsPid = tsPid;
}
protected TsRequest(Parcel source) {
- super(source);
- tsPid = source.readInt();
+ super(requestType, source);
+ mTsPid = source.readInt();
}
public int getTsPid() {
- return tsPid;
+ return mTsPid;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeInt(PARCEL_TOKEN_TS_REQUEST);
super.writeToParcel(dest, flags);
- dest.writeInt(tsPid);
+ dest.writeInt(mTsPid);
}
}
diff --git a/media/java/android/media/tv/TsResponse.java b/media/java/android/media/tv/TsResponse.java
new file mode 100644
index 0000000..e30ff54
--- /dev/null
+++ b/media/java/android/media/tv/TsResponse.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/** @hide */
+public class TsResponse extends BroadcastInfoResponse implements Parcelable {
+ public static final int responseType = BroadcastInfoType.TS;
+
+ public static final @NonNull Parcelable.Creator<TsResponse> CREATOR =
+ new Parcelable.Creator<TsResponse>() {
+ @Override
+ public TsResponse createFromParcel(Parcel source) {
+ source.readInt();
+ return createFromParcelBody(source);
+ }
+
+ @Override
+ public TsResponse[] newArray(int size) {
+ return new TsResponse[size];
+ }
+ };
+
+ private final String mSharedFilterToken;
+
+ public static TsResponse createFromParcelBody(Parcel in) {
+ return new TsResponse(in);
+ }
+
+ public TsResponse(int requestId, int sequence, int responseResult, String sharedFilterToken) {
+ super(responseType, requestId, sequence, responseResult);
+ this.mSharedFilterToken = sharedFilterToken;
+ }
+
+ protected TsResponse(Parcel source) {
+ super(responseType, source);
+ mSharedFilterToken = source.readString();
+ }
+
+ public String getSharedFilterToken() {
+ return mSharedFilterToken;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeString(mSharedFilterToken);
+ }
+}
diff --git a/media/java/android/media/tv/TvProprietaryFunctionRequest.java b/media/java/android/media/tv/TvProprietaryFunctionRequest.java
new file mode 100644
index 0000000..845641d
--- /dev/null
+++ b/media/java/android/media/tv/TvProprietaryFunctionRequest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/** @hide */
+public class TvProprietaryFunctionRequest extends BroadcastInfoRequest implements Parcelable {
+ public static final int requestType = BroadcastInfoType.TV_PROPRIETARY_FUNCTION;
+
+ public static final @NonNull Parcelable.Creator<TvProprietaryFunctionRequest> CREATOR =
+ new Parcelable.Creator<TvProprietaryFunctionRequest>() {
+ @Override
+ public TvProprietaryFunctionRequest createFromParcel(Parcel source) {
+ source.readInt();
+ return createFromParcelBody(source);
+ }
+
+ @Override
+ public TvProprietaryFunctionRequest[] newArray(int size) {
+ return new TvProprietaryFunctionRequest[size];
+ }
+ };
+
+ private final String mNameSpace;
+ private final String mName;
+ private final String mArguments;
+
+ public static TvProprietaryFunctionRequest createFromParcelBody(Parcel in) {
+ return new TvProprietaryFunctionRequest(in);
+ }
+
+ public TvProprietaryFunctionRequest(int requestId, int option, String nameSpace,
+ String name, String arguments) {
+ super(requestType, requestId, option);
+ mNameSpace = nameSpace;
+ mName = name;
+ mArguments = arguments;
+ }
+
+ protected TvProprietaryFunctionRequest(Parcel source) {
+ super(requestType, source);
+ mNameSpace = source.readString();
+ mName = source.readString();
+ mArguments = source.readString();
+ }
+
+ public String getNameSpace() {
+ return mNameSpace;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public String getArguments() {
+ return mArguments;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeString(mNameSpace);
+ dest.writeString(mName);
+ dest.writeString(mArguments);
+ }
+}
diff --git a/media/java/android/media/tv/TvProprietaryFunctionResponse.java b/media/java/android/media/tv/TvProprietaryFunctionResponse.java
new file mode 100644
index 0000000..3181b08
--- /dev/null
+++ b/media/java/android/media/tv/TvProprietaryFunctionResponse.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/** @hide */
+public class TvProprietaryFunctionResponse extends BroadcastInfoResponse implements Parcelable {
+ public static final int responseType = BroadcastInfoType.TV_PROPRIETARY_FUNCTION;
+
+ public static final @NonNull Parcelable.Creator<TvProprietaryFunctionResponse> CREATOR =
+ new Parcelable.Creator<TvProprietaryFunctionResponse>() {
+ @Override
+ public TvProprietaryFunctionResponse createFromParcel(Parcel source) {
+ source.readInt();
+ return createFromParcelBody(source);
+ }
+
+ @Override
+ public TvProprietaryFunctionResponse[] newArray(int size) {
+ return new TvProprietaryFunctionResponse[size];
+ }
+ };
+
+ private final String mResponse;
+
+ public static TvProprietaryFunctionResponse createFromParcelBody(Parcel in) {
+ return new TvProprietaryFunctionResponse(in);
+ }
+
+ public TvProprietaryFunctionResponse(int requestId, int sequence, int responseResult,
+ String response) {
+ super(responseType, requestId, sequence, responseResult);
+ mResponse = response;
+ }
+
+ protected TvProprietaryFunctionResponse(Parcel source) {
+ super(responseType, source);
+ mResponse = source.readString();
+ }
+
+ public String getResponse() {
+ return mResponse;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeString(mResponse);
+ }
+}
diff --git a/packages/ConnectivityT/OWNERS b/packages/ConnectivityT/OWNERS
new file mode 100644
index 0000000..4862377
--- /dev/null
+++ b/packages/ConnectivityT/OWNERS
@@ -0,0 +1 @@
+file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking
\ No newline at end of file
diff --git a/packages/DynamicSystemInstallationService/AndroidManifest.xml b/packages/DynamicSystemInstallationService/AndroidManifest.xml
index 1bc4983..1765348 100644
--- a/packages/DynamicSystemInstallationService/AndroidManifest.xml
+++ b/packages/DynamicSystemInstallationService/AndroidManifest.xml
@@ -8,6 +8,7 @@
<uses-permission android:name="android.permission.REBOOT" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.READ_OEM_UNLOCK_STATE" />
+ <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application
android:allowBackup="false"
diff --git a/packages/PackageInstaller/res/values/strings.xml b/packages/PackageInstaller/res/values/strings.xml
index ca3ec3c..688d116 100644
--- a/packages/PackageInstaller/res/values/strings.xml
+++ b/packages/PackageInstaller/res/values/strings.xml
@@ -112,6 +112,8 @@
<!-- [CHAR LIMIT=none] -->
<string name="uninstall_application_text_user">Do you want to uninstall this app for the user <xliff:g id="username">%1$s</xliff:g>?</string>
<!-- [CHAR LIMIT=none] -->
+ <string name="uninstall_application_text_current_user_work_profile">Do you want to uninstall this app from your work profile?</string>
+ <!-- [CHAR LIMIT=none] -->
<string name="uninstall_update_text">Replace this app with the factory version? All data will be removed.</string>
<!-- [CHAR LIMIT=none] -->
<string name="uninstall_update_text_multiuser">Replace this app with the factory version? All data will be removed. This affects all users of this device, including those with work profiles.</string>
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
index 99f6a92..36294ac 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
@@ -31,6 +31,7 @@
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.os.Bundle;
+import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
@@ -125,6 +126,7 @@
final boolean isUpdate =
((dialogInfo.appInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
+ final UserHandle myUserHandle = Process.myUserHandle();
UserManager userManager = UserManager.get(getActivity());
if (isUpdate) {
if (isSingleUser(userManager)) {
@@ -135,10 +137,17 @@
} else {
if (dialogInfo.allUsers && !isSingleUser(userManager)) {
messageBuilder.append(getString(R.string.uninstall_application_text_all_users));
- } else if (!dialogInfo.user.equals(android.os.Process.myUserHandle())) {
+ } else if (!dialogInfo.user.equals(myUserHandle)) {
UserInfo userInfo = userManager.getUserInfo(dialogInfo.user.getIdentifier());
- messageBuilder.append(
- getString(R.string.uninstall_application_text_user, userInfo.name));
+ if (userInfo.isManagedProfile()
+ && userInfo.profileGroupId == myUserHandle.getIdentifier()) {
+ messageBuilder.append(
+ getString(R.string.uninstall_application_text_current_user_work_profile,
+ userInfo.name));
+ } else {
+ messageBuilder.append(
+ getString(R.string.uninstall_application_text_user, userInfo.name));
+ }
} else {
messageBuilder.append(getString(R.string.uninstall_application_text));
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAlertFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAlertFragment.java
index 21d25f5..2d241ca 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAlertFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAlertFragment.java
@@ -21,7 +21,10 @@
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.os.Bundle;
+import android.os.Process;
+import android.os.UserHandle;
import android.os.UserManager;
+
import androidx.leanback.app.GuidedStepFragment;
import androidx.leanback.widget.GuidanceStylist;
import androidx.leanback.widget.GuidedAction;
@@ -59,6 +62,7 @@
final boolean isUpdate =
((dialogInfo.appInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
+ final UserHandle myUserHandle = Process.myUserHandle();
UserManager userManager = UserManager.get(getActivity());
if (isUpdate) {
if (isSingleUser(userManager)) {
@@ -69,10 +73,17 @@
} else {
if (dialogInfo.allUsers && !isSingleUser(userManager)) {
messageBuilder.append(getString(R.string.uninstall_application_text_all_users));
- } else if (!dialogInfo.user.equals(android.os.Process.myUserHandle())) {
+ } else if (!dialogInfo.user.equals(myUserHandle)) {
UserInfo userInfo = userManager.getUserInfo(dialogInfo.user.getIdentifier());
- messageBuilder.append(
- getString(R.string.uninstall_application_text_user, userInfo.name));
+ if (userInfo.isManagedProfile()
+ && userInfo.profileGroupId == myUserHandle.getIdentifier()) {
+ messageBuilder.append(
+ getString(R.string.uninstall_application_text_current_user_work_profile,
+ userInfo.name));
+ } else {
+ messageBuilder.append(
+ getString(R.string.uninstall_application_text_user, userInfo.name));
+ }
} else {
messageBuilder.append(getString(R.string.uninstall_application_text));
}
diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
index cb858c8..6d5615d 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
@@ -109,7 +109,7 @@
a.recycle();
}
- setBackground(true);
+ setBackground(mSwitch.isChecked());
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
index 8b17be1..dee6894 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
@@ -85,6 +85,7 @@
@VisibleForTesting
protected Context mContext;
private final int mThemeResId;
+ private final boolean mCancelIsNeutral;
@VisibleForTesting
protected TextView mZenAlarmWarning;
@VisibleForTesting
@@ -101,8 +102,13 @@
}
public EnableZenModeDialog(Context context, int themeResId) {
+ this(context, themeResId, false /* cancelIsNeutral */);
+ }
+
+ public EnableZenModeDialog(Context context, int themeResId, boolean cancelIsNeutral) {
mContext = context;
mThemeResId = themeResId;
+ mCancelIsNeutral = cancelIsNeutral;
}
public AlertDialog createDialog() {
@@ -115,7 +121,6 @@
final AlertDialog.Builder builder = new AlertDialog.Builder(mContext, mThemeResId)
.setTitle(R.string.zen_mode_settings_turn_on_dialog_title)
- .setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.zen_mode_enable_dialog_turn_on,
new DialogInterface.OnClickListener() {
@Override
@@ -145,6 +150,12 @@
}
});
+ if (mCancelIsNeutral) {
+ builder.setNeutralButton(R.string.cancel, null);
+ } else {
+ builder.setNegativeButton(R.string.cancel, null);
+ }
+
View contentView = getContentView();
bindConditions(forever());
builder.setView(contentView);
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index dd1cb6b..e76e51d 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -276,6 +276,8 @@
VALIDATORS.put(Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.EMERGENCY_GESTURE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.EMERGENCY_GESTURE_SOUND_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS,
+ NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(Secure.ADAPTIVE_CONNECTIVITY_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(
Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS, NONE_NEGATIVE_LONG_VALIDATOR);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index f0f2c0f..fb8ffbd 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -655,7 +655,8 @@
Settings.Global.Wearable.BURN_IN_PROTECTION_ENABLED,
Settings.Global.Wearable.WRIST_ORIENTATION_MODE,
Settings.Global.Wearable.CLOCKWORK_SYSUI_PACKAGE,
- Settings.Global.Wearable.CLOCKWORK_SYSUI_MAIN_ACTIVITY);
+ Settings.Global.Wearable.CLOCKWORK_SYSUI_MAIN_ACTIVITY,
+ Settings.Global.Wearable.WEAR_ACTIVITY_AUTO_RESUME_TIMEOUT_SET_BY_USER);
private static final Set<String> BACKUP_DENY_LIST_SECURE_SETTINGS =
newHashSet(
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 262cf53..e5b5285 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -606,6 +606,10 @@
<!-- Permission required for CTS test - CtsSafetyCenterTestCases -->
<uses-permission android:name="android.permission.SEND_SAFETY_CENTER_UPDATE" />
+ <!-- Permission required for CTS test - CommunalManagerTest -->
+ <uses-permission android:name="android.permission.WRITE_COMMUNAL_STATE" />
+ <uses-permission android:name="android.permission.READ_COMMUNAL_STATE" />
+
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index bcf95d8..a1b3df8 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -121,6 +121,7 @@
<uses-permission android:name="android.permission.SET_ORIENTATION" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.MONITOR_INPUT" />
+ <uses-permission android:name="android.permission.ALLOW_SLIPPERY_TOUCHES" />
<uses-permission android:name="android.permission.INPUT_CONSUMER" />
<!-- DreamManager -->
@@ -219,6 +220,7 @@
<uses-permission android:name="android.permission.MANAGE_NOTIFICATIONS" />
<uses-permission android:name="android.permission.REVOKE_RUNTIME_PERMISSIONS" />
<uses-permission android:name="android.permission.GET_RUNTIME_PERMISSIONS" />
+ <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<!-- It's like, reality, but, you know, virtual -->
<uses-permission android:name="android.permission.ACCESS_VR_MANAGER" />
diff --git a/packages/SystemUI/res/drawable/internet_dialog_footer_background.xml b/packages/SystemUI/res/drawable/internet_dialog_footer_background.xml
deleted file mode 100644
index 50267fd..0000000
--- a/packages/SystemUI/res/drawable/internet_dialog_footer_background.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- android:shape="rectangle">
- <stroke
- android:color="?androidprv:attr/colorAccentPrimaryVariant"
- android:width="1dp"/>
- <corners android:radius="20dp"/>
- <padding
- android:left="8dp"
- android:right="8dp"
- android:top="4dp"
- android:bottom="4dp" />
- <solid android:color="@android:color/transparent" />
-</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/media_output_dialog_button_background.xml b/packages/SystemUI/res/drawable/media_output_dialog_button_background.xml
deleted file mode 100644
index d9ae777..0000000
--- a/packages/SystemUI/res/drawable/media_output_dialog_button_background.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
- android:insetBottom="6dp"
- android:insetTop="6dp">
- <shape android:shape="rectangle">
- <stroke
- android:color="@color/media_dialog_outlined_button"
- android:width="1dp"/>
- <corners android:radius="20dp"/>
- <padding
- android:left="16dp"
- android:right="16dp"
- android:top="8dp"
- android:bottom="8dp"/>
- <solid android:color="@android:color/transparent"/>
- </shape>
-</inset>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/media_output_dialog_solid_button_background.xml b/packages/SystemUI/res/drawable/media_output_dialog_solid_button_background.xml
deleted file mode 100644
index d49454f..0000000
--- a/packages/SystemUI/res/drawable/media_output_dialog_solid_button_background.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
- android:insetBottom="6dp"
- android:insetTop="6dp">
- <shape android:shape="rectangle">
- <stroke
- android:color="@android:color/transparent"
- android:width="1dp"/>
- <corners android:radius="20dp"/>
- <padding
- android:left="@dimen/media_output_dialog_button_padding_horizontal"
- android:right="@dimen/media_output_dialog_button_padding_horizontal"
- android:top="@dimen/media_output_dialog_button_padding_vertical"
- android:bottom="@dimen/media_output_dialog_button_padding_vertical"/>
- <solid android:color="@color/media_dialog_solid_button_background"/>
- </shape>
-</inset>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml b/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml
index 665b456..a47299d 100644
--- a/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml
+++ b/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml
@@ -29,7 +29,7 @@
<shape android:shape="rectangle">
<corners android:radius="?android:attr/buttonCornerRadius"/>
<solid android:color="@android:color/transparent"/>
- <stroke android:color="?androidprv:attr/colorAccentPrimary"
+ <stroke android:color="?androidprv:attr/colorAccentPrimaryVariant"
android:width="1dp"
/>
<padding android:left="@dimen/dialog_button_horizontal_padding"
diff --git a/packages/SystemUI/res/layout/auth_credential_password_view.xml b/packages/SystemUI/res/layout/auth_credential_password_view.xml
index 46cbc25..1e0ce00 100644
--- a/packages/SystemUI/res/layout/auth_credential_password_view.xml
+++ b/packages/SystemUI/res/layout/auth_credential_password_view.xml
@@ -18,71 +18,64 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:elevation="@dimen/biometric_dialog_elevation"
- android:orientation="vertical">
+ android:orientation="vertical"
+ android:gravity="center_horizontal"
+ android:elevation="@dimen/biometric_dialog_elevation">
- <RelativeLayout
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
+
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <TextView
+ android:id="@+id/title"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="wrap_content"
+ style="@style/TextAppearance.AuthCredential.Title"/>
- <LinearLayout
- android:id="@+id/auth_credential_header"
- style="@style/AuthCredentialHeaderStyle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentTop="true">
+ <TextView
+ android:id="@+id/subtitle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/TextAppearance.AuthCredential.Subtitle"/>
- <ImageView
- android:id="@+id/icon"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:contentDescription="@null" />
+ <TextView
+ android:id="@+id/description"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/TextAppearance.AuthCredential.Description"/>
- <TextView
- android:id="@+id/title"
- style="@style/TextAppearance.AuthCredential.Title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
- <TextView
- android:id="@+id/subtitle"
- style="@style/TextAppearance.AuthCredential.Subtitle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
+ <ImeAwareEditText
+ android:id="@+id/lockPassword"
+ android:layout_width="208dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:minHeight="48dp"
+ android:gravity="center"
+ android:inputType="textPassword"
+ android:maxLength="500"
+ android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii"
+ style="@style/TextAppearance.AuthCredential.PasswordEntry"/>
- <TextView
- android:id="@+id/description"
- style="@style/TextAppearance.AuthCredential.Description"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
+ <TextView
+ android:id="@+id/error"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/TextAppearance.AuthCredential.Error"/>
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:gravity="center"
- android:orientation="vertical"
- android:layout_alignParentBottom="true">
-
- <ImeAwareEditText
- android:id="@+id/lockPassword"
- style="@style/TextAppearance.AuthCredential.PasswordEntry"
- android:layout_width="208dp"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii"
- android:inputType="textPassword"
- android:minHeight="48dp" />
-
- <TextView
- android:id="@+id/error"
- style="@style/TextAppearance.AuthCredential.Error"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
- </LinearLayout>
-
- </RelativeLayout>
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="5"/>
</com.android.systemui.biometrics.AuthCredentialPasswordView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/auth_credential_pattern_view.xml b/packages/SystemUI/res/layout/auth_credential_pattern_view.xml
index 470298e..4939ea2 100644
--- a/packages/SystemUI/res/layout/auth_credential_pattern_view.xml
+++ b/packages/SystemUI/res/layout/auth_credential_pattern_view.xml
@@ -22,81 +22,76 @@
android:gravity="center_horizontal"
android:elevation="@dimen/biometric_dialog_elevation">
- <RelativeLayout
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
+
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <TextView
+ android:id="@+id/title"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
+ android:layout_height="wrap_content"
+ style="@style/TextAppearance.AuthCredential.Title"/>
- <LinearLayout
- android:id="@+id/auth_credential_header"
- style="@style/AuthCredentialHeaderStyle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
+ <TextView
+ android:id="@+id/subtitle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/TextAppearance.AuthCredential.Subtitle"/>
- <ImageView
- android:id="@+id/icon"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:contentDescription="@null" />
+ <TextView
+ android:id="@+id/description"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/TextAppearance.AuthCredential.Description"/>
- <TextView
- android:id="@+id/title"
- style="@style/TextAppearance.AuthCredential.Title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
- <TextView
- android:id="@+id/subtitle"
- style="@style/TextAppearance.AuthCredential.Subtitle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:gravity="center"
+ android:paddingLeft="0dp"
+ android:paddingRight="0dp"
+ android:paddingTop="0dp"
+ android:paddingBottom="16dp"
+ android:clipToPadding="false">
- <TextView
- android:id="@+id/description"
- style="@style/TextAppearance.AuthCredential.Description"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
- </LinearLayout>
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ style="@style/LockPatternContainerStyle">
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/auth_credential_header"
- android:gravity="center"
- android:orientation="vertical"
- android:paddingBottom="16dp"
- android:paddingTop="60dp">
-
- <FrameLayout
- style="@style/LockPatternContainerStyle"
- android:layout_width="wrap_content"
- android:layout_height="0dp"
- android:layout_weight="1">
-
- <com.android.internal.widget.LockPatternView
- android:id="@+id/lockPattern"
- style="@style/LockPatternStyle"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center" />
-
- </FrameLayout>
-
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true">
-
- <TextView
- android:id="@+id/error"
- style="@style/TextAppearance.AuthCredential.Error"
+ <com.android.internal.widget.LockPatternView
+ android:id="@+id/lockPattern"
android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ style="@style/LockPatternStyleBiometricPrompt"/>
- </LinearLayout>
+ </FrameLayout>
- </RelativeLayout>
+ <TextView
+ android:id="@+id/error"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/TextAppearance.AuthCredential.Error"/>
+
+ </LinearLayout>
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
</com.android.systemui.biometrics.AuthCredentialPatternView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
index c575855..275e0a5 100644
--- a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
+++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
@@ -37,7 +37,7 @@
android:ellipsize="end"
android:gravity="center_vertical|center_horizontal"
android:layout_width="wrap_content"
- android:layout_height="32dp"
+ android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.InternetDialog"
android:textSize="24sp"/>
@@ -45,7 +45,7 @@
android:id="@+id/internet_dialog_subtitle"
android:gravity="center_vertical|center_horizontal"
android:layout_width="wrap_content"
- android:layout_height="20dp"
+ android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:ellipsize="end"
android:maxLines="1"
@@ -150,6 +150,7 @@
android:gravity="start|center_vertical">
<TextView
android:id="@+id/mobile_title"
+ android:maxLines="1"
style="@style/InternetDialog.NetworkTitle"/>
<TextView
android:id="@+id/mobile_summary"
@@ -380,54 +381,44 @@
android:id="@+id/button_layout"
android:orientation="horizontal"
android:layout_width="match_parent"
- android:layout_height="48dp"
- android:layout_marginStart="24dp"
- android:layout_marginEnd="24dp"
+ android:layout_height="wrap_content"
android:layout_marginTop="8dp"
- android:layout_marginBottom="34dp"
+ android:layout_marginStart="@dimen/dialog_side_padding"
+ android:layout_marginEnd="@dimen/dialog_side_padding"
+ android:layout_marginBottom="@dimen/dialog_bottom_padding"
android:clickable="false"
android:focusable="false">
<FrameLayout
android:id="@+id/apm_layout"
android:layout_width="wrap_content"
- android:layout_height="48dp"
+ android:layout_height="wrap_content"
android:clickable="true"
android:focusable="true"
android:layout_gravity="start|center_vertical"
android:orientation="vertical">
<Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:text="@string/turn_off_airplane_mode"
android:ellipsize="end"
- style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
- android:layout_width="wrap_content"
- android:layout_height="36dp"
- android:layout_gravity="start|center_vertical"
- android:textAppearance="@style/TextAppearance.InternetDialog"
- android:textSize="14sp"
- android:background="@drawable/internet_dialog_footer_background"
+ style="@style/Widget.Dialog.Button.BorderButton"
android:clickable="false"/>
</FrameLayout>
<FrameLayout
android:id="@+id/done_layout"
android:layout_width="wrap_content"
- android:layout_height="48dp"
+ android:layout_height="wrap_content"
android:layout_marginStart="16dp"
- android:clickable="true"
- android:focusable="true"
android:layout_gravity="end|center_vertical"
- android:orientation="vertical">
+ android:clickable="true"
+ android:focusable="true">
<Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:text="@string/inline_done_button"
- android:ellipsize="end"
- style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
- android:layout_width="67dp"
- android:layout_height="36dp"
- android:layout_gravity="end|center_vertical"
- android:textAppearance="@style/TextAppearance.InternetDialog"
- android:textSize="14sp"
- android:background="@drawable/internet_dialog_footer_background"
+ style="@style/Widget.Dialog.Button"
android:clickable="false"/>
</FrameLayout>
</FrameLayout>
diff --git a/packages/SystemUI/res/layout/media_output_dialog.xml b/packages/SystemUI/res/layout/media_output_dialog.xml
index 8437702..b7265b9 100644
--- a/packages/SystemUI/res/layout/media_output_dialog.xml
+++ b/packages/SystemUI/res/layout/media_output_dialog.xml
@@ -41,7 +41,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="12dp"
- android:background="@color/media_dialog_background"
android:orientation="vertical">
<ImageView
android:id="@+id/app_source_icon"
@@ -76,7 +75,6 @@
android:id="@+id/device_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@color/media_dialog_background"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
@@ -91,18 +89,16 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
- android:layout_marginStart="16dp"
- android:layout_marginBottom="24dp"
- android:layout_marginEnd="16dp"
- android:background="@color/media_dialog_background"
+ android:layout_marginStart="@dimen/dialog_side_padding"
+ android:layout_marginEnd="@dimen/dialog_side_padding"
+ android:layout_marginBottom="@dimen/dialog_bottom_padding"
android:orientation="horizontal">
<Button
android:id="@+id/stop"
- style="@style/MediaOutputRoundedOutlinedButton"
+ style="@style/Widget.Dialog.Button.BorderButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:minWidth="0dp"
android:text="@string/keyboard_key_media_stop"
android:visibility="gone"/>
@@ -113,10 +109,9 @@
<Button
android:id="@+id/done"
- style="@style/MediaOutputRoundedButton"
+ style="@style/Widget.Dialog.Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:minWidth="0dp"
android:text="@string/inline_done_button"/>
</LinearLayout>
</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/screen_record_dialog.xml b/packages/SystemUI/res/layout/screen_record_dialog.xml
index e43a149..f57d65a 100644
--- a/packages/SystemUI/res/layout/screen_record_dialog.xml
+++ b/packages/SystemUI/res/layout/screen_record_dialog.xml
@@ -27,10 +27,10 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingStart="24dp"
- android:paddingEnd="24dp"
- android:paddingTop="26dp"
- android:paddingBottom="30dp"
+ android:paddingStart="@dimen/dialog_side_padding"
+ android:paddingEnd="@dimen/dialog_side_padding"
+ android:paddingTop="@dimen/dialog_top_padding"
+ android:paddingBottom="@dimen/dialog_bottom_padding"
android:orientation="vertical">
<!-- Header -->
@@ -143,10 +143,7 @@
android:layout_weight="0"
android:layout_gravity="start"
android:text="@string/cancel"
- android:textColor="?android:textColorPrimary"
- android:background="@drawable/screenrecord_button_background_outline"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textSize="14sp"/>
+ style="@style/Widget.Dialog.Button.BorderButton" />
<Space
android:layout_width="0dp"
android:layout_height="match_parent"
@@ -158,10 +155,7 @@
android:layout_weight="0"
android:layout_gravity="end"
android:text="@string/screenrecord_start"
- android:textColor="@android:color/system_neutral1_900"
- android:background="@drawable/screenrecord_button_background_solid"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textSize="14sp"/>
+ style="@style/Widget.Dialog.Button" />
</LinearLayout>
</LinearLayout>
</ScrollView>
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index c434285..9d4c2c3 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -67,10 +67,6 @@
<!-- media output dialog-->
<color name="media_dialog_background">@android:color/system_neutral1_900</color>
- <color name="media_dialog_solid_button_background">@android:color/system_accent1_100</color>
- <color name="media_dialog_solid_button_text">@android:color/system_accent2_800</color>
- <color name="media_dialog_outlined_button">@android:color/system_accent1_100</color>
- <color name="media_dialog_outlined_button_text">@android:color/system_neutral1_50</color>
<color name="media_dialog_active_item_main_content">@android:color/system_accent2_800</color>
<color name="media_dialog_inactive_item_main_content">@android:color/system_accent1_100</color>
<color name="media_dialog_item_status">@android:color/system_accent1_100</color>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 11865a5..8cad5b3 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -174,10 +174,6 @@
<!-- media output dialog-->
<color name="media_dialog_background" android:lstar="98">@android:color/system_neutral1_100</color>
- <color name="media_dialog_solid_button_background">@android:color/system_accent1_600</color>
- <color name="media_dialog_solid_button_text">@android:color/system_neutral1_50</color>
- <color name="media_dialog_outlined_button">@android:color/system_accent1_600</color>
- <color name="media_dialog_outlined_button_text">@android:color/system_neutral1_900</color>
<color name="media_dialog_active_item_main_content">@android:color/system_accent1_900</color>
<color name="media_dialog_inactive_item_main_content">@android:color/system_accent1_600</color>
<color name="media_dialog_item_status">@android:color/system_accent1_900</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index c09658b..c7350a1 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1098,8 +1098,6 @@
<dimen name="media_output_dialog_header_icon_padding">16dp</dimen>
<dimen name="media_output_dialog_icon_corner_radius">16dp</dimen>
<dimen name="media_output_dialog_title_anim_y_delta">12.5dp</dimen>
- <dimen name="media_output_dialog_button_padding_horizontal">16dp</dimen>
- <dimen name="media_output_dialog_button_padding_vertical">8dp</dimen>
<!-- Distance that the full shade transition takes in order for qs to fully transition to the
shade -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 69f9966..9358349 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -200,9 +200,9 @@
<style name="TextAppearance.DeviceManagementDialog.Title" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle"/>
- <style name="TextAppearance.AuthCredential"
- parent="@android:style/TextAppearance.DeviceDefault">
+ <style name="TextAppearance.AuthCredential">
<item name="android:accessibilityLiveRegion">polite</item>
+ <item name="android:gravity">center_horizontal</item>
<item name="android:textAlignment">gravity</item>
<item name="android:layout_gravity">top</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
@@ -210,57 +210,44 @@
<style name="TextAppearance.AuthCredential.Title">
<item name="android:fontFamily">google-sans</item>
- <item name="android:layout_marginTop">20dp</item>
- <item name="android:textSize">36sp</item>
+ <item name="android:paddingTop">12dp</item>
+ <item name="android:paddingHorizontal">24dp</item>
+ <item name="android:textSize">24sp</item>
</style>
<style name="TextAppearance.AuthCredential.Subtitle">
<item name="android:fontFamily">google-sans</item>
- <item name="android:layout_marginTop">20dp</item>
- <item name="android:textSize">18sp</item>
+ <item name="android:paddingTop">8dp</item>
+ <item name="android:paddingHorizontal">24dp</item>
+ <item name="android:textSize">16sp</item>
</style>
<style name="TextAppearance.AuthCredential.Description">
<item name="android:fontFamily">google-sans</item>
- <item name="android:layout_marginTop">20dp</item>
- <item name="android:textSize">16sp</item>
+ <item name="android:paddingTop">8dp</item>
+ <item name="android:paddingHorizontal">24dp</item>
+ <item name="android:textSize">14sp</item>
</style>
<style name="TextAppearance.AuthCredential.Error">
<item name="android:paddingTop">6dp</item>
- <item name="android:paddingBottom">18dp</item>
<item name="android:paddingHorizontal">24dp</item>
<item name="android:textSize">14sp</item>
<item name="android:textColor">?android:attr/colorError</item>
- <item name="android:gravity">center</item>
</style>
- <style name="TextAppearance.AuthCredential.PasswordEntry">
+ <style name="TextAppearance.AuthCredential.PasswordEntry" parent="@android:style/TextAppearance.DeviceDefault">
<item name="android:gravity">center</item>
<item name="android:singleLine">true</item>
<item name="android:textColor">?android:attr/colorForeground</item>
<item name="android:textSize">24sp</item>
</style>
- <style name="AuthCredentialHeaderStyle">
- <item name="android:paddingStart">48dp</item>
- <item name="android:paddingEnd">24dp</item>
- <item name="android:paddingTop">28dp</item>
- <item name="android:paddingBottom">20dp</item>
- <item name="android:orientation">vertical</item>
- <item name="android:layout_gravity">top</item>
- </style>
-
<style name="DeviceManagementDialogTitle">
<item name="android:gravity">center</item>
<item name="android:textAppearance">@style/TextAppearance.DeviceManagementDialog.Title</item>
</style>
- <style name="AuthCredentialPasswordTheme" parent="@style/Theme.MaterialComponents.DayNight">
- <item name="colorPrimary">?android:attr/colorPrimary</item>
- <item name="colorPrimaryDark">?android:attr/colorPrimary</item>
- </style>
-
<style name="TextAppearance.DeviceManagementDialog.Content" parent="@*android:style/TextAppearance.DeviceDefault.Subhead"/>
<style name="BaseBrightnessDialogContainer" parent="@style/Theme.SystemUI">
@@ -320,8 +307,9 @@
<item name="android:maxWidth">420dp</item>
<item name="android:minHeight">0dp</item>
<item name="android:minWidth">0dp</item>
- <item name="android:paddingHorizontal">60dp</item>
- <item name="android:paddingBottom">40dp</item>
+ <item name="android:paddingBottom">0dp</item>
+ <item name="android:paddingHorizontal">44dp</item>
+ <item name="android:paddingTop">0dp</item>
</style>
<style name="LockPatternStyle">
@@ -466,20 +454,6 @@
<item name="android:colorBackground">@color/media_dialog_background</item>
</style>
- <style name="MediaOutputRoundedOutlinedButton" parent="@android:style/Widget.Material.Button">
- <item name="android:background">@drawable/media_output_dialog_button_background</item>
- <item name="android:textColor">@color/media_dialog_outlined_button_text</item>
- <item name="android:textSize">14sp</item>
- <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
- </style>
-
- <style name="MediaOutputRoundedButton" parent="@android:style/Widget.Material.Button">
- <item name="android:background">@drawable/media_output_dialog_solid_button_background</item>
- <item name="android:textColor">@color/media_dialog_solid_button_text</item>
- <item name="android:textSize">14sp</item>
- <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
- </style>
-
<style name="MediaOutputItemInactiveTitle">
<item name="android:textSize">16sp</item>
<item name="android:textColor">@color/media_dialog_inactive_item_main_content</item>
@@ -912,13 +886,18 @@
<item name="android:textAlignment">center</item>
</style>
- <style name="Widget.Dialog.Button" parent = "Theme.SystemUI.Dialog">
+
+ <style name="Widget" />
+ <style name="Widget.Dialog" />
+ <style name="Widget.Dialog.Button">
+ <item name="android:buttonCornerRadius">28dp</item>
<item name="android:background">@drawable/qs_dialog_btn_filled</item>
<item name="android:textColor">?androidprv:attr/textColorOnAccent</item>
<item name="android:textSize">14sp</item>
<item name="android:lineHeight">20sp</item>
<item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
<item name="android:stateListAnimator">@null</item>
+ <item name="android:minWidth">0dp</item>
</style>
<style name="Widget.Dialog.Button.BorderButton">
diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagReader.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagListenable.kt
similarity index 61%
rename from packages/SystemUI/shared/src/com/android/systemui/flags/FlagReader.kt
rename to packages/SystemUI/shared/src/com/android/systemui/flags/FlagListenable.kt
index 26c6a4b..195ba465 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagReader.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagListenable.kt
@@ -18,28 +18,24 @@
/**
* Plugin for loading flag values
*/
-interface FlagReader {
- /** Returns a boolean value for the given flag. */
- fun isEnabled(flag: BooleanFlag): Boolean {
- return flag.default
- }
-
- fun isEnabled(flag: ResourceBooleanFlag): Boolean
-
- /** Returns a boolean value for the given flag. */
- fun isEnabled(id: Int, def: Boolean): Boolean {
- return def
- }
-
- /** Add a listener to be alerted when any flag changes. */
- fun addListener(listener: Listener) {}
+interface FlagListenable {
+ /** Add a listener to be alerted when the given flag changes. */
+ fun addListener(flag: Flag<*>, listener: Listener)
/** Remove a listener to be alerted when any flag changes. */
- fun removeListener(listener: Listener) {}
+ fun removeListener(listener: Listener)
/** A simple listener to be alerted when a flag changes. */
fun interface Listener {
- /** */
- fun onFlagChanged(id: Int)
+ /** Called when the flag changes */
+ fun onFlagChanged(event: FlagEvent)
+ }
+
+ /** An event representing the change */
+ interface FlagEvent {
+ /** the id of the flag which changed */
+ val flagId: Int
+ /** if all listeners alerted invoke this method, the restart will be skipped */
+ fun requestNoRestart()
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt
index b2ca2d7..ec619dd 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt
@@ -24,30 +24,39 @@
import android.net.Uri
import android.os.Bundle
import android.os.Handler
-import android.provider.Settings
import androidx.concurrent.futures.CallbackToFutureAdapter
import com.google.common.util.concurrent.ListenableFuture
-import org.json.JSONException
-import org.json.JSONObject
+import java.util.function.Consumer
class FlagManager constructor(
private val context: Context,
+ private val settings: FlagSettingsHelper,
private val handler: Handler
-) : FlagReader {
+) : FlagListenable {
companion object {
const val RECEIVING_PACKAGE = "com.android.systemui"
const val ACTION_SET_FLAG = "com.android.systemui.action.SET_FLAG"
const val ACTION_GET_FLAGS = "com.android.systemui.action.GET_FLAGS"
const val FLAGS_PERMISSION = "com.android.systemui.permission.FLAGS"
- const val FIELD_ID = "id"
- const val FIELD_VALUE = "value"
- const val FIELD_TYPE = "type"
- const val FIELD_FLAGS = "flags"
- const val TYPE_BOOLEAN = "boolean"
+ const val EXTRA_ID = "id"
+ const val EXTRA_VALUE = "value"
+ const val EXTRA_FLAGS = "flags"
private const val SETTINGS_PREFIX = "systemui/flags"
}
- private val listeners: MutableSet<FlagReader.Listener> = mutableSetOf()
+ constructor(context: Context, handler: Handler) : this(
+ context,
+ FlagSettingsHelper(context.contentResolver),
+ handler
+ )
+
+ /**
+ * An action called on restart which takes as an argument whether the listeners requested
+ * that the restart be suppressed
+ */
+ var restartAction: Consumer<Boolean>? = null
+ var clearCacheAction: Consumer<Int>? = null
+ private val listeners: MutableSet<PerFlagListener> = mutableSetOf()
private val settingsObserver: ContentObserver = SettingsObserver()
fun getFlagsFuture(): ListenableFuture<Collection<Flag<*>>> {
@@ -61,7 +70,7 @@
override fun onReceive(context: Context, intent: Intent) {
val extras: Bundle? = getResultExtras(false)
val listOfFlags: java.util.ArrayList<ParcelableFlag<*>>? =
- extras?.getParcelableArrayList(FIELD_FLAGS)
+ extras?.getParcelableArrayList(EXTRA_FLAGS)
if (listOfFlags != null) {
completer.set(listOfFlags)
} else {
@@ -73,9 +82,19 @@
} as ListenableFuture<Collection<Flag<*>>>
}
+ /**
+ * Returns the stored value or null if not set.
+ * This API is used by TheFlippinApp.
+ */
+ fun isEnabled(id: Int): Boolean? = readFlagValue(id, BooleanFlagSerializer)
+
+ /**
+ * Sets the value of a boolean flag.
+ * This API is used by TheFlippinApp.
+ */
fun setFlagValue(id: Int, enabled: Boolean) {
val intent = createIntent(id)
- intent.putExtra(FIELD_VALUE, enabled)
+ intent.putExtra(EXTRA_VALUE, enabled)
context.sendBroadcast(intent)
}
@@ -86,49 +105,30 @@
context.sendBroadcast(intent)
}
- override fun isEnabled(id: Int, def: Boolean): Boolean {
- return isEnabled(id) ?: def
- }
-
/** Returns the stored value or null if not set. */
- fun isEnabled(id: Int): Boolean? {
- val data: String? = Settings.Secure.getString(
- context.contentResolver, keyToSettingsPrefix(id))
- if (data == null || data?.isEmpty()) {
- return null
- }
- val json: JSONObject
- try {
- json = JSONObject(data)
- return if (!assertType(json, TYPE_BOOLEAN)) {
- null
- } else json.getBoolean(FIELD_VALUE)
- } catch (e: JSONException) {
- throw InvalidFlagStorageException()
- }
+ fun <T> readFlagValue(id: Int, serializer: FlagSerializer<T>): T? {
+ val data = settings.getString(idToSettingsKey(id))
+ return serializer.fromSettingsData(data)
}
- override fun isEnabled(flag: ResourceBooleanFlag): Boolean {
- throw RuntimeException("Not implemented in FlagManager")
- }
-
- override fun addListener(listener: FlagReader.Listener) {
+ override fun addListener(flag: Flag<*>, listener: FlagListenable.Listener) {
synchronized(listeners) {
val registerNeeded = listeners.isEmpty()
- listeners.add(listener)
+ listeners.add(PerFlagListener(flag.id, listener))
if (registerNeeded) {
- context.contentResolver.registerContentObserver(
- Settings.Secure.getUriFor(SETTINGS_PREFIX), true, settingsObserver)
+ settings.registerContentObserver(SETTINGS_PREFIX, true, settingsObserver)
}
}
}
- override fun removeListener(listener: FlagReader.Listener) {
+ override fun removeListener(listener: FlagListenable.Listener) {
synchronized(listeners) {
- val isRegistered = !listeners.isEmpty()
- listeners.remove(listener)
- if (isRegistered && listeners.isEmpty()) {
- context.contentResolver.unregisterContentObserver(settingsObserver)
+ if (listeners.isEmpty()) {
+ return
+ }
+ listeners.removeIf { it.listener == listener }
+ if (listeners.isEmpty()) {
+ settings.unregisterContentObserver(settingsObserver)
}
}
}
@@ -136,21 +136,13 @@
private fun createIntent(id: Int): Intent {
val intent = Intent(ACTION_SET_FLAG)
intent.setPackage(RECEIVING_PACKAGE)
- intent.putExtra(FIELD_ID, id)
+ intent.putExtra(EXTRA_ID, id)
return intent
}
- fun keyToSettingsPrefix(key: Int): String {
- return SETTINGS_PREFIX + "/" + key
- }
-
- private fun assertType(json: JSONObject, type: String): Boolean {
- return try {
- json.getString(FIELD_TYPE) == TYPE_BOOLEAN
- } catch (e: JSONException) {
- false
- }
+ fun idToSettingsKey(id: Int): String {
+ return "$SETTINGS_PREFIX/$id"
}
inner class SettingsObserver : ContentObserver(handler) {
@@ -160,17 +152,40 @@
}
val parts = uri.pathSegments
val idStr = parts[parts.size - 1]
- try {
- val id = idStr.toInt()
- listeners.forEach { l -> l.onFlagChanged(id) }
- } catch (e: NumberFormatException) {
- // no-op
- }
+ val id = try { idStr.toInt() } catch (e: NumberFormatException) { return }
+ clearCacheAction?.accept(id)
+ dispatchListenersAndMaybeRestart(id)
}
}
-}
-class InvalidFlagStorageException : Exception("Data found but is invalid")
+ fun dispatchListenersAndMaybeRestart(id: Int) {
+ val filteredListeners: List<FlagListenable.Listener> = synchronized(listeners) {
+ listeners.mapNotNull { if (it.id == id) it.listener else null }
+ }
+ // If there are no listeners, there's nothing to dispatch to, and nothing to suppress it.
+ if (filteredListeners.isEmpty()) {
+ restartAction?.accept(false)
+ return
+ }
+ // Dispatch to every listener and save whether each one called requestNoRestart.
+ val suppressRestartList: List<Boolean> = filteredListeners.map { listener ->
+ var didRequestNoRestart = false
+ val event = object : FlagListenable.FlagEvent {
+ override val flagId = id
+ override fun requestNoRestart() {
+ didRequestNoRestart = true
+ }
+ }
+ listener.onFlagChanged(event)
+ didRequestNoRestart
+ }
+ // Suppress restart only if ALL listeners request it.
+ val suppressRestart = suppressRestartList.all { it }
+ restartAction?.accept(suppressRestart)
+ }
+
+ private data class PerFlagListener(val id: Int, val listener: FlagListenable.Listener)
+}
class NoFlagResultsException : Exception(
"SystemUI failed to communicate its flags back successfully")
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagSerializer.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagSerializer.kt
new file mode 100644
index 0000000..e9ea19d
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagSerializer.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.flags
+
+import android.util.Log
+import org.json.JSONException
+import org.json.JSONObject
+
+private const val FIELD_VALUE = "value"
+private const val FIELD_TYPE = "type"
+private const val TYPE_BOOLEAN = "boolean"
+private const val TYPE_STRING = "string"
+
+private const val TAG = "FlagSerializer"
+
+abstract class FlagSerializer<T>(
+ private val type: String,
+ private val setter: (JSONObject, String, T) -> Unit,
+ private val getter: (JSONObject, String) -> T
+) {
+ fun toSettingsData(value: T): String? {
+ return try {
+ JSONObject()
+ .put(FIELD_TYPE, type)
+ .also { setter(it, FIELD_VALUE, value) }
+ .toString()
+ } catch (e: JSONException) {
+ Log.w(TAG, "write error", e)
+ null
+ }
+ }
+
+ /**
+ * @throws InvalidFlagStorageException
+ */
+ fun fromSettingsData(data: String?): T? {
+ if (data == null || data.isEmpty()) {
+ return null
+ }
+ try {
+ val json = JSONObject(data)
+ return if (json.getString(FIELD_TYPE) == type) {
+ getter(json, FIELD_VALUE)
+ } else {
+ null
+ }
+ } catch (e: JSONException) {
+ Log.w(TAG, "read error", e)
+ throw InvalidFlagStorageException()
+ }
+ }
+}
+
+object BooleanFlagSerializer : FlagSerializer<Boolean>(
+ TYPE_BOOLEAN,
+ JSONObject::put,
+ JSONObject::getBoolean
+)
+
+object StringFlagSerializer : FlagSerializer<String>(
+ TYPE_STRING,
+ JSONObject::put,
+ JSONObject::getString
+)
+
+class InvalidFlagStorageException : Exception("Data found but is invalid")
diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagSettingsHelper.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagSettingsHelper.kt
new file mode 100644
index 0000000..742bb0b
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagSettingsHelper.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.flags
+
+import android.content.ContentResolver
+import android.database.ContentObserver
+import android.provider.Settings
+
+class FlagSettingsHelper(private val contentResolver: ContentResolver) {
+
+ fun getString(key: String): String? = Settings.Secure.getString(contentResolver, key)
+
+ fun registerContentObserver(
+ name: String,
+ notifyForDescendants: Boolean,
+ observer: ContentObserver
+ ) {
+ contentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(name),
+ notifyForDescendants,
+ observer
+ )
+ }
+
+ fun unregisterContentObserver(observer: ContentObserver) {
+ contentResolver.unregisterContentObserver(observer)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
index e46b6f1..ea93a3b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
@@ -60,14 +60,12 @@
hingeAngleProvider,
screenStatusProvider,
deviceStateManager,
- mainExecutor
+ mainExecutor,
+ mainHandler
)
val unfoldTransitionProgressProvider = if (config.isHingeAngleEnabled) {
- PhysicsBasedUnfoldTransitionProgressProvider(
- mainHandler,
- foldStateProvider
- )
+ PhysicsBasedUnfoldTransitionProgressProvider(foldStateProvider)
} else {
FixedTimingTransitionProgressProvider(foldStateProvider)
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
index 90f5998..51eae57 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
@@ -15,7 +15,6 @@
*/
package com.android.systemui.unfold.progress
-import android.os.Handler
import android.util.Log
import android.util.MathUtils.saturate
import androidx.dynamicanimation.animation.DynamicAnimation
@@ -24,9 +23,10 @@
import androidx.dynamicanimation.animation.SpringForce
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import com.android.systemui.unfold.updates.FOLD_UPDATE_ABORTED
import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED
-import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN
import com.android.systemui.unfold.updates.FOLD_UPDATE_START_CLOSING
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN
import com.android.systemui.unfold.updates.FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE
import com.android.systemui.unfold.updates.FoldStateProvider
import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
@@ -39,7 +39,6 @@
* - doesn't handle postures
*/
internal class PhysicsBasedUnfoldTransitionProgressProvider(
- private val handler: Handler,
private val foldStateProvider: FoldStateProvider
) :
UnfoldTransitionProgressProvider,
@@ -51,8 +50,6 @@
addEndListener(this@PhysicsBasedUnfoldTransitionProgressProvider)
}
- private val timeoutRunnable = TimeoutRunnable()
-
private var isTransitionRunning = false
private var isAnimatedCancelRunning = false
@@ -92,7 +89,7 @@
cancelTransition(endValue = 1f, animate = true)
}
}
- FOLD_UPDATE_FINISH_FULL_OPEN -> {
+ FOLD_UPDATE_FINISH_FULL_OPEN, FOLD_UPDATE_ABORTED -> {
// Do not cancel if we haven't started the transition yet.
// This could happen when we fully unfolded the device before the screen
// became available. In this case we start and immediately cancel the animation
@@ -106,7 +103,11 @@
cancelTransition(endValue = 0f, animate = false)
}
FOLD_UPDATE_START_CLOSING -> {
- startTransition(startValue = 1f)
+ // The transition might be already running as the device might start closing several
+ // times before reaching an end state.
+ if (!isTransitionRunning) {
+ startTransition(startValue = 1f)
+ }
}
}
@@ -116,8 +117,6 @@
}
private fun cancelTransition(endValue: Float, animate: Boolean) {
- handler.removeCallbacks(timeoutRunnable)
-
if (isTransitionRunning && animate) {
isAnimatedCancelRunning = true
springAnimation.animateToFinalPosition(endValue)
@@ -175,8 +174,6 @@
}
springAnimation.start()
-
- handler.postDelayed(timeoutRunnable, TRANSITION_TIMEOUT_MILLIS)
}
override fun addCallback(listener: TransitionProgressListener) {
@@ -187,13 +184,6 @@
listeners.remove(listener)
}
- private inner class TimeoutRunnable : Runnable {
-
- override fun run() {
- cancelTransition(endValue = 1f, animate = true)
- }
- }
-
private object AnimationProgressProperty :
FloatPropertyCompat<PhysicsBasedUnfoldTransitionProgressProvider>("animation_progress") {
@@ -212,7 +202,6 @@
private const val TAG = "PhysicsBasedUnfoldTransitionProgressProvider"
private const val DEBUG = true
-private const val TRANSITION_TIMEOUT_MILLIS = 2000L
private const val SPRING_STIFFNESS = 200.0f
private const val MINIMAL_VISIBLE_CHANGE = 0.001f
private const val FINAL_HINGE_ANGLE_POSITION = 165f
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
index 35e2b30..6d9631c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
@@ -15,14 +15,19 @@
*/
package com.android.systemui.unfold.updates
+import android.annotation.FloatRange
import android.content.Context
import android.hardware.devicestate.DeviceStateManager
+import android.os.Handler
+import android.util.Log
+import androidx.annotation.VisibleForTesting
import androidx.core.util.Consumer
-import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener
+import com.android.systemui.unfold.updates.hinge.FULLY_CLOSED_DEGREES
import com.android.systemui.unfold.updates.hinge.FULLY_OPEN_DEGREES
import com.android.systemui.unfold.updates.hinge.HingeAngleProvider
+import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
import java.util.concurrent.Executor
class DeviceFoldStateProvider(
@@ -30,7 +35,8 @@
private val hingeAngleProvider: HingeAngleProvider,
private val screenStatusProvider: ScreenStatusProvider,
private val deviceStateManager: DeviceStateManager,
- private val mainExecutor: Executor
+ private val mainExecutor: Executor,
+ private val handler: Handler
) : FoldStateProvider {
private val outputListeners: MutableList<FoldUpdatesListener> = mutableListOf()
@@ -38,9 +44,13 @@
@FoldUpdate
private var lastFoldUpdate: Int? = null
+ @FloatRange(from = 0.0, to = 180.0)
+ private var lastHingeAngle: Float = 0f
+
private val hingeAngleListener = HingeAngleListener()
private val screenListener = ScreenStatusListener()
private val foldStateListener = FoldStateListener(context)
+ private val timeoutRunnable = TimeoutRunnable()
private var isFolded = false
private var isUnfoldHandled = true
@@ -72,47 +82,69 @@
override val isFullyOpened: Boolean
get() = !isFolded && lastFoldUpdate == FOLD_UPDATE_FINISH_FULL_OPEN
+ private val isTransitionInProgess: Boolean
+ get() = lastFoldUpdate == FOLD_UPDATE_START_OPENING ||
+ lastFoldUpdate == FOLD_UPDATE_START_CLOSING
+
private fun onHingeAngle(angle: Float) {
- when (lastFoldUpdate) {
- FOLD_UPDATE_FINISH_FULL_OPEN -> {
- if (FULLY_OPEN_DEGREES - angle > START_CLOSING_THRESHOLD_DEGREES) {
- lastFoldUpdate = FOLD_UPDATE_START_CLOSING
- outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_START_CLOSING) }
- }
- }
- FOLD_UPDATE_START_OPENING -> {
- if (FULLY_OPEN_DEGREES - angle < FULLY_OPEN_THRESHOLD_DEGREES) {
- lastFoldUpdate = FOLD_UPDATE_FINISH_FULL_OPEN
- outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN) }
- }
- }
- FOLD_UPDATE_START_CLOSING -> {
- if (FULLY_OPEN_DEGREES - angle < START_CLOSING_THRESHOLD_DEGREES) {
- lastFoldUpdate = FOLD_UPDATE_FINISH_FULL_OPEN
- outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN) }
- }
+ if (DEBUG) { Log.d(TAG, "Hinge angle: $angle, lastHingeAngle: $lastHingeAngle") }
+
+ val isClosing = angle < lastHingeAngle
+ val isFullyOpened = FULLY_OPEN_DEGREES - angle < FULLY_OPEN_THRESHOLD_DEGREES
+ val closingEventDispatched = lastFoldUpdate == FOLD_UPDATE_START_CLOSING
+
+ if (isClosing && !closingEventDispatched && !isFullyOpened) {
+ notifyFoldUpdate(FOLD_UPDATE_START_CLOSING)
+ }
+
+ if (isTransitionInProgess) {
+ if (isFullyOpened) {
+ notifyFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN)
+ cancelTimeout()
+ } else {
+ // The timeout will trigger some constant time after the last angle update.
+ rescheduleAbortAnimationTimeout()
}
}
+ lastHingeAngle = angle
outputListeners.forEach { it.onHingeAngleUpdate(angle) }
}
private inner class FoldStateListener(context: Context) :
DeviceStateManager.FoldStateListener(context, { folded: Boolean ->
isFolded = folded
+ lastHingeAngle = FULLY_CLOSED_DEGREES
if (folded) {
- lastFoldUpdate = FOLD_UPDATE_FINISH_CLOSED
- outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_FINISH_CLOSED) }
hingeAngleProvider.stop()
+ notifyFoldUpdate(FOLD_UPDATE_FINISH_CLOSED)
+ cancelTimeout()
isUnfoldHandled = false
} else {
- lastFoldUpdate = FOLD_UPDATE_START_OPENING
- outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_START_OPENING) }
+ notifyFoldUpdate(FOLD_UPDATE_START_OPENING)
+ rescheduleAbortAnimationTimeout()
hingeAngleProvider.start()
}
})
+ private fun notifyFoldUpdate(@FoldUpdate update: Int) {
+ if (DEBUG) { Log.d(TAG, stateToString(update)) }
+ outputListeners.forEach { it.onFoldUpdate(update) }
+ lastFoldUpdate = update
+ }
+
+ private fun rescheduleAbortAnimationTimeout() {
+ if (isTransitionInProgess) {
+ cancelTimeout()
+ }
+ handler.postDelayed(timeoutRunnable, ABORT_CLOSING_MILLIS)
+ }
+
+ private fun cancelTimeout() {
+ handler.removeCallbacks(timeoutRunnable)
+ }
+
private inner class ScreenStatusListener :
ScreenStatusProvider.ScreenListener {
@@ -136,7 +168,39 @@
onHingeAngle(angle)
}
}
+
+ private inner class TimeoutRunnable : Runnable {
+
+ override fun run() {
+ notifyFoldUpdate(FOLD_UPDATE_ABORTED)
+ }
+ }
}
-private const val START_CLOSING_THRESHOLD_DEGREES = 95f
-private const val FULLY_OPEN_THRESHOLD_DEGREES = 15f
\ No newline at end of file
+private fun stateToString(@FoldUpdate update: Int): String {
+ return when (update) {
+ FOLD_UPDATE_START_OPENING -> "START_OPENING"
+ FOLD_UPDATE_HALF_OPEN -> "HALF_OPEN"
+ FOLD_UPDATE_START_CLOSING -> "START_CLOSING"
+ FOLD_UPDATE_ABORTED -> "ABORTED"
+ FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE -> "UNFOLDED_SCREEN_AVAILABLE"
+ FOLD_UPDATE_FINISH_HALF_OPEN -> "FINISH_HALF_OPEN"
+ FOLD_UPDATE_FINISH_FULL_OPEN -> "FINISH_FULL_OPEN"
+ FOLD_UPDATE_FINISH_CLOSED -> "FINISH_CLOSED"
+ else -> "UNKNOWN"
+ }
+}
+
+private const val TAG = "DeviceFoldProvider"
+private const val DEBUG = false
+
+/**
+ * Time after which [FOLD_UPDATE_ABORTED] is emitted following a [FOLD_UPDATE_START_CLOSING] or
+ * [FOLD_UPDATE_START_OPENING] event, if an end state is not reached.
+ */
+@VisibleForTesting
+const val ABORT_CLOSING_MILLIS = 1000L
+
+/** Threshold after which we consider the device fully unfolded. */
+@VisibleForTesting
+const val FULLY_OPEN_THRESHOLD_DEGREES = 15f
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
index 643ece3..bffebcd 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
@@ -39,6 +39,7 @@
FOLD_UPDATE_START_OPENING,
FOLD_UPDATE_HALF_OPEN,
FOLD_UPDATE_START_CLOSING,
+ FOLD_UPDATE_ABORTED,
FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE,
FOLD_UPDATE_FINISH_HALF_OPEN,
FOLD_UPDATE_FINISH_FULL_OPEN,
@@ -51,7 +52,8 @@
const val FOLD_UPDATE_START_OPENING = 0
const val FOLD_UPDATE_HALF_OPEN = 1
const val FOLD_UPDATE_START_CLOSING = 2
-const val FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE = 3
-const val FOLD_UPDATE_FINISH_HALF_OPEN = 4
-const val FOLD_UPDATE_FINISH_FULL_OPEN = 5
-const val FOLD_UPDATE_FINISH_CLOSED = 6
+const val FOLD_UPDATE_ABORTED = 3
+const val FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE = 4
+const val FOLD_UPDATE_FINISH_HALF_OPEN = 5
+const val FOLD_UPDATE_FINISH_FULL_OPEN = 6
+const val FOLD_UPDATE_FINISH_CLOSED = 7
diff --git a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt
index 10ceee9..adfc872 100644
--- a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt
+++ b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt
@@ -23,6 +23,7 @@
import dagger.Binds
import dagger.Module
import dagger.Provides
+import java.util.function.Supplier
@Module(includes = [
SettingsUtilModule::class
@@ -38,5 +39,9 @@
fun provideFlagManager(context: Context, @Main handler: Handler): FlagManager {
return FlagManager(context, handler)
}
+
+ @JvmStatic
+ @Provides
+ fun providesFlagCollector(): Supplier<Map<Int, Flag<*>>>? = null
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt
new file mode 100644
index 0000000..96a90df
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.flags
+
+/**
+ * Class to manage simple DeviceConfig-based feature flags.
+ *
+ * See [Flags] for instructions on defining new flags.
+ */
+interface FeatureFlags : FlagListenable {
+ /** Returns a boolean value for the given flag. */
+ fun isEnabled(flag: BooleanFlag): Boolean
+
+ /** Returns a boolean value for the given flag. */
+ fun isEnabled(flag: ResourceBooleanFlag): Boolean
+
+ /** Returns a string value for the given flag. */
+ fun getString(flag: StringFlag): String
+
+ /** Returns a string value for the given flag. */
+ fun getString(flag: ResourceStringFlag): String
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
index 3f00b87..89623f4 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
@@ -18,11 +18,12 @@
import static com.android.systemui.flags.FlagManager.ACTION_GET_FLAGS;
import static com.android.systemui.flags.FlagManager.ACTION_SET_FLAG;
-import static com.android.systemui.flags.FlagManager.FIELD_FLAGS;
-import static com.android.systemui.flags.FlagManager.FIELD_ID;
-import static com.android.systemui.flags.FlagManager.FIELD_VALUE;
+import static com.android.systemui.flags.FlagManager.EXTRA_FLAGS;
+import static com.android.systemui.flags.FlagManager.EXTRA_ID;
+import static com.android.systemui.flags.FlagManager.EXTRA_VALUE;
-import android.annotation.Nullable;
+import static java.util.Objects.requireNonNull;
+
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -32,6 +33,7 @@
import android.util.Log;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
@@ -39,14 +41,13 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.util.settings.SecureSettings;
-import org.json.JSONException;
-import org.json.JSONObject;
-
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.Map;
+import java.util.Objects;
+import java.util.TreeMap;
+import java.util.function.Supplier;
import javax.inject.Inject;
@@ -66,7 +67,9 @@
private final FlagManager mFlagManager;
private final SecureSettings mSecureSettings;
private final Resources mResources;
- private final Map<Integer, Boolean> mBooleanFlagCache = new HashMap<>();
+ private final Supplier<Map<Integer, Flag<?>>> mFlagsCollector;
+ private final Map<Integer, Boolean> mBooleanFlagCache = new TreeMap<>();
+ private final Map<Integer, String> mStringFlagCache = new TreeMap<>();
@Inject
public FeatureFlagsDebug(
@@ -74,99 +77,135 @@
Context context,
SecureSettings secureSettings,
@Main Resources resources,
- DumpManager dumpManager) {
+ DumpManager dumpManager,
+ @Nullable Supplier<Map<Integer, Flag<?>>> flagsCollector) {
mFlagManager = flagManager;
mSecureSettings = secureSettings;
mResources = resources;
+ mFlagsCollector = flagsCollector != null ? flagsCollector : Flags::collectFlags;
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_SET_FLAG);
filter.addAction(ACTION_GET_FLAGS);
+ flagManager.setRestartAction(this::restartSystemUI);
+ flagManager.setClearCacheAction(this::removeFromCache);
context.registerReceiver(mReceiver, filter, null, null);
dumpManager.registerDumpable(TAG, this);
}
@Override
- public boolean isEnabled(BooleanFlag flag) {
+ public boolean isEnabled(@NonNull BooleanFlag flag) {
int id = flag.getId();
if (!mBooleanFlagCache.containsKey(id)) {
- mBooleanFlagCache.put(id, isEnabled(id, flag.getDefault()));
+ mBooleanFlagCache.put(id,
+ readFlagValue(id, flag.getDefault(), BooleanFlagSerializer.INSTANCE));
}
return mBooleanFlagCache.get(id);
}
@Override
- public boolean isEnabled(ResourceBooleanFlag flag) {
+ public boolean isEnabled(@NonNull ResourceBooleanFlag flag) {
int id = flag.getId();
if (!mBooleanFlagCache.containsKey(id)) {
- mBooleanFlagCache.put(
- id, isEnabled(id, mResources.getBoolean(flag.getResourceId())));
+ mBooleanFlagCache.put(id,
+ readFlagValue(id, mResources.getBoolean(flag.getResourceId()),
+ BooleanFlagSerializer.INSTANCE));
}
return mBooleanFlagCache.get(id);
}
- /** Return a {@link BooleanFlag}'s value. */
+ @NonNull
@Override
- public boolean isEnabled(int id, boolean defaultValue) {
- Boolean result = isEnabledInternal(id);
+ public String getString(@NonNull StringFlag flag) {
+ int id = flag.getId();
+ if (!mStringFlagCache.containsKey(id)) {
+ mStringFlagCache.put(id,
+ readFlagValue(id, flag.getDefault(), StringFlagSerializer.INSTANCE));
+ }
+
+ return mStringFlagCache.get(id);
+ }
+
+ @NonNull
+ @Override
+ public String getString(@NonNull ResourceStringFlag flag) {
+ int id = flag.getId();
+ if (!mStringFlagCache.containsKey(id)) {
+ mStringFlagCache.put(id,
+ readFlagValue(id, mResources.getString(flag.getResourceId()),
+ StringFlagSerializer.INSTANCE));
+ }
+
+ return mStringFlagCache.get(id);
+ }
+
+ @NonNull
+ private <T> T readFlagValue(int id, @NonNull T defaultValue, FlagSerializer<T> serializer) {
+ requireNonNull(defaultValue, "defaultValue");
+ T result = readFlagValueInternal(id, serializer);
return result == null ? defaultValue : result;
}
/** Returns the stored value or null if not set. */
- private Boolean isEnabledInternal(int id) {
+ @Nullable
+ private <T> T readFlagValueInternal(int id, FlagSerializer<T> serializer) {
try {
- return mFlagManager.isEnabled(id);
+ return mFlagManager.readFlagValue(id, serializer);
} catch (Exception e) {
eraseInternal(id);
}
return null;
}
- /** Set whether a given {@link BooleanFlag} is enabled or not. */
- public void setEnabled(int id, boolean value) {
- Boolean currentValue = isEnabledInternal(id);
- if (currentValue != null && currentValue == value) {
+ private <T> void setFlagValue(int id, @NonNull T value, FlagSerializer<T> serializer) {
+ requireNonNull(value, "Cannot set a null value");
+ T currentValue = readFlagValueInternal(id, serializer);
+ if (Objects.equals(currentValue, value)) {
+ Log.i(TAG, "Flag id " + id + " is already " + value);
return;
}
-
- JSONObject json = new JSONObject();
- try {
- json.put(FlagManager.FIELD_TYPE, FlagManager.TYPE_BOOLEAN);
- json.put(FIELD_VALUE, value);
- mSecureSettings.putString(mFlagManager.keyToSettingsPrefix(id), json.toString());
- Log.i(TAG, "Set id " + id + " to " + value);
- restartSystemUI();
- } catch (JSONException e) {
- // no-op
+ final String data = serializer.toSettingsData(value);
+ if (data == null) {
+ Log.w(TAG, "Failed to set id " + id + " to " + value);
+ return;
}
+ mSecureSettings.putString(mFlagManager.idToSettingsKey(id), data);
+ Log.i(TAG, "Set id " + id + " to " + value);
+ removeFromCache(id);
+ mFlagManager.dispatchListenersAndMaybeRestart(id);
}
/** Erase a flag's overridden value if there is one. */
public void eraseFlag(int id) {
eraseInternal(id);
- restartSystemUI();
+ removeFromCache(id);
+ mFlagManager.dispatchListenersAndMaybeRestart(id);
}
/** Works just like {@link #eraseFlag(int)} except that it doesn't restart SystemUI. */
private void eraseInternal(int id) {
// We can't actually "erase" things from sysprops, but we can set them to empty!
- mSecureSettings.putString(mFlagManager.keyToSettingsPrefix(id), "");
+ mSecureSettings.putString(mFlagManager.idToSettingsKey(id), "");
Log.i(TAG, "Erase id " + id);
}
@Override
- public void addListener(Listener run) {
- mFlagManager.addListener(run);
+ public void addListener(@NonNull Flag<?> flag, @NonNull Listener listener) {
+ mFlagManager.addListener(flag, listener);
}
@Override
- public void removeListener(Listener run) {
- mFlagManager.removeListener(run);
+ public void removeListener(@NonNull Listener listener) {
+ mFlagManager.removeListener(listener);
}
- private void restartSystemUI() {
+ private void restartSystemUI(boolean requestSuppress) {
+ if (requestSuppress) {
+ Log.i(TAG, "SystemUI Restart Suppressed");
+ return;
+ }
Log.i(TAG, "Restarting SystemUI");
// SysUI starts back when up exited. Is there a better way to do this?
System.exit(0);
@@ -175,14 +214,14 @@
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
+ String action = intent == null ? null : intent.getAction();
if (action == null) {
return;
}
if (ACTION_SET_FLAG.equals(action)) {
handleSetFlag(intent.getExtras());
} else if (ACTION_GET_FLAGS.equals(action)) {
- Map<Integer, Flag<?>> knownFlagMap = Flags.collectFlags();
+ Map<Integer, Flag<?>> knownFlagMap = mFlagsCollector.get();
ArrayList<Flag<?>> flags = new ArrayList<>(knownFlagMap.values());
// Convert all flags to parcelable flags.
@@ -196,32 +235,47 @@
Bundle extras = getResultExtras(true);
if (extras != null) {
- extras.putParcelableArrayList(FIELD_FLAGS, pFlags);
+ extras.putParcelableArrayList(EXTRA_FLAGS, pFlags);
}
}
}
private void handleSetFlag(Bundle extras) {
- int id = extras.getInt(FIELD_ID);
+ if (extras == null) {
+ Log.w(TAG, "No extras");
+ return;
+ }
+ int id = extras.getInt(EXTRA_ID);
if (id <= 0) {
Log.w(TAG, "ID not set or less than or equal to 0: " + id);
return;
}
- Map<Integer, Flag<?>> flagMap = Flags.collectFlags();
+ Map<Integer, Flag<?>> flagMap = mFlagsCollector.get();
if (!flagMap.containsKey(id)) {
Log.w(TAG, "Tried to set unknown id: " + id);
return;
}
Flag<?> flag = flagMap.get(id);
- if (!extras.containsKey(FIELD_VALUE)) {
+ if (!extras.containsKey(EXTRA_VALUE)) {
eraseFlag(id);
return;
}
- if (flag instanceof BooleanFlag) {
- setEnabled(id, extras.getBoolean(FIELD_VALUE));
+ Object value = extras.get(EXTRA_VALUE);
+ if (flag instanceof BooleanFlag && value instanceof Boolean) {
+ setFlagValue(id, (Boolean) value, BooleanFlagSerializer.INSTANCE);
+ } else if (flag instanceof ResourceBooleanFlag && value instanceof Boolean) {
+ setFlagValue(id, (Boolean) value, BooleanFlagSerializer.INSTANCE);
+ } else if (flag instanceof StringFlag && value instanceof String) {
+ setFlagValue(id, (String) value, StringFlagSerializer.INSTANCE);
+ } else if (flag instanceof ResourceStringFlag && value instanceof String) {
+ setFlagValue(id, (String) value, StringFlagSerializer.INSTANCE);
+ } else {
+ Log.w(TAG,
+ "Unable to set " + id + " of type " + flag.getClass() + " to value of type "
+ + (value == null ? null : value.getClass()));
}
}
@@ -245,16 +299,18 @@
}
};
+ private void removeFromCache(int id) {
+ mBooleanFlagCache.remove(id);
+ mStringFlagCache.remove(id);
+ }
+
@Override
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("can override: true");
- ArrayList<String> flagStrings = new ArrayList<>(mBooleanFlagCache.size());
- for (Map.Entry<Integer, Boolean> entry : mBooleanFlagCache.entrySet()) {
- flagStrings.add(" sysui_flag_" + entry.getKey() + ": " + entry.getValue());
- }
- flagStrings.sort(String.CASE_INSENSITIVE_ORDER);
- for (String flagString : flagStrings) {
- pw.println(flagString);
- }
+ pw.println("booleans: " + mBooleanFlagCache.size());
+ mBooleanFlagCache.forEach((key, value) -> pw.println(" sysui_flag_" + key + ": " + value));
+ pw.println("Strings: " + mStringFlagCache.size());
+ mStringFlagCache.forEach((key, value) -> pw.println(" sysui_flag_" + key
+ + ": [length=" + value.length() + "] \"" + value + "\""));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
index 5b6404f..348a8e2 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
@@ -16,7 +16,10 @@
package com.android.systemui.flags;
+import static java.util.Objects.requireNonNull;
+
import android.content.res.Resources;
+import android.util.SparseArray;
import android.util.SparseBooleanArray;
import androidx.annotation.NonNull;
@@ -40,7 +43,8 @@
@SysUISingleton
public class FeatureFlagsRelease implements FeatureFlags, Dumpable {
private final Resources mResources;
- SparseBooleanArray mFlagCache = new SparseBooleanArray();
+ SparseBooleanArray mBooleanCache = new SparseBooleanArray();
+ SparseArray<String> mStringCache = new SparseArray<>();
@Inject
public FeatureFlagsRelease(@Main Resources resources, DumpManager dumpManager) {
mResources = resources;
@@ -48,10 +52,10 @@
}
@Override
- public void addListener(Listener run) {}
+ public void addListener(@NonNull Flag<?> flag, @NonNull Listener listener) {}
@Override
- public void removeListener(Listener run) {}
+ public void removeListener(@NonNull Listener listener) {}
@Override
public boolean isEnabled(BooleanFlag flag) {
@@ -60,27 +64,57 @@
@Override
public boolean isEnabled(ResourceBooleanFlag flag) {
- int cacheIndex = mFlagCache.indexOfKey(flag.getId());
+ int cacheIndex = mBooleanCache.indexOfKey(flag.getId());
if (cacheIndex < 0) {
return isEnabled(flag.getId(), mResources.getBoolean(flag.getResourceId()));
}
- return mFlagCache.valueAt(cacheIndex);
+ return mBooleanCache.valueAt(cacheIndex);
}
+ private boolean isEnabled(int key, boolean defaultValue) {
+ mBooleanCache.append(key, defaultValue);
+ return defaultValue;
+ }
+
+ @NonNull
@Override
- public boolean isEnabled(int key, boolean defaultValue) {
- mFlagCache.append(key, defaultValue);
+ public String getString(@NonNull StringFlag flag) {
+ return getString(flag.getId(), flag.getDefault());
+ }
+
+ @NonNull
+ @Override
+ public String getString(@NonNull ResourceStringFlag flag) {
+ int cacheIndex = mStringCache.indexOfKey(flag.getId());
+ if (cacheIndex < 0) {
+ return getString(flag.getId(),
+ requireNonNull(mResources.getString(flag.getResourceId())));
+ }
+
+ return mStringCache.valueAt(cacheIndex);
+ }
+
+ private String getString(int key, String defaultValue) {
+ mStringCache.append(key, defaultValue);
return defaultValue;
}
@Override
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("can override: false");
- int size = mFlagCache.size();
- for (int i = 0; i < size; i++) {
- pw.println(" sysui_flag_" + mFlagCache.keyAt(i)
- + ": " + mFlagCache.valueAt(i));
+ int numBooleans = mBooleanCache.size();
+ pw.println("booleans: " + numBooleans);
+ for (int i = 0; i < numBooleans; i++) {
+ pw.println(" sysui_flag_" + mBooleanCache.keyAt(i) + ": " + mBooleanCache.valueAt(i));
+ }
+ int numStrings = mStringCache.size();
+ pw.println("Strings: " + numStrings);
+ for (int i = 0; i < numStrings; i++) {
+ final int id = mStringCache.keyAt(i);
+ final String value = mStringCache.valueAt(i);
+ final int length = value.length();
+ pw.println(" sysui_flag_" + id + ": [length=" + length + "] \"" + value + "\"");
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index f74fbf4..7f5744c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -85,7 +85,9 @@
public MediaOutputBaseDialog(Context context, MediaOutputController mediaOutputController) {
super(context, R.style.Theme_SystemUI_Dialog_Media);
- mContext = context;
+
+ // Save the context that is wrapped with our theme.
+ mContext = getContext();
mMediaOutputController = mediaOutputController;
mLayoutManager = new LinearLayoutManager(mContext);
mListMaxHeight = context.getResources().getDimensionPixelSize(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java
index dab0efe..e93c349 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java
@@ -259,6 +259,12 @@
}
public static class Item {
+ public Item(int iconResId, CharSequence line1, Object tag) {
+ this.iconResId = iconResId;
+ this.line1 = line1;
+ this.tag = tag;
+ }
+
public int iconResId;
public QSTile.Icon icon;
public Drawable overlay;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index 7e410d0..6c072f1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -146,6 +146,10 @@
}
private static class TilePair {
+ private TilePair(QSTile tile) {
+ mTile = tile;
+ }
+
QSTile mTile;
boolean mReady = false;
}
@@ -157,8 +161,7 @@
TileCollector(List<QSTile> tilesToAdd, QSTileHost host) {
for (QSTile tile: tilesToAdd) {
- TilePair pair = new TilePair();
- pair.mTile = tile;
+ TilePair pair = new TilePair(tile);
mQSTileList.add(pair);
}
mQSTileHost = host;
@@ -288,15 +291,11 @@
if (mSpecs.contains(spec)) {
return;
}
- TileInfo info = new TileInfo();
- info.state = state;
- info.state.dualTarget = false; // No dual targets in edit.
- info.state.expandedAccessibilityClassName =
- Button.class.getName();
- info.spec = spec;
- info.state.secondaryLabel = (isSystem || TextUtils.equals(state.label, appLabel))
+ state.dualTarget = false; // No dual targets in edit.
+ state.expandedAccessibilityClassName = Button.class.getName();
+ state.secondaryLabel = (isSystem || TextUtils.equals(state.label, appLabel))
? null : appLabel;
- info.isSystem = isSystem;
+ TileInfo info = new TileInfo(spec, state, isSystem);
mTiles.add(info);
mSpecs.add(spec);
}
@@ -312,6 +311,12 @@
}
public static class TileInfo {
+ public TileInfo(String spec, QSTile.State state, boolean isSystem) {
+ this.spec = spec;
+ this.state = state;
+ this.isSystem = isSystem;
+ }
+
public String spec;
public QSTile.State state;
public boolean isSystem;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 65b6617..6fcfc3a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -394,10 +394,11 @@
int count = 0;
for (CachedBluetoothDevice device : devices) {
if (mController.getBondState(device) == BluetoothDevice.BOND_NONE) continue;
- final Item item = new Item();
- item.iconResId = com.android.internal.R.drawable.ic_qs_bluetooth;
- item.line1 = device.getName();
- item.tag = device;
+ final Item item =
+ new Item(
+ com.android.internal.R.drawable.ic_qs_bluetooth,
+ device.getName(),
+ device);
int state = device.getMaxConnectionState();
if (state == BluetoothProfile.STATE_CONNECTED) {
item.iconResId = R.drawable.ic_bluetooth_connected;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index b83dc52..1fb608a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -402,11 +402,12 @@
// if we are connected, simply show that device
for (CastDevice device : devices) {
if (device.state == CastDevice.STATE_CONNECTED) {
- final Item item = new Item();
- item.iconResId = R.drawable.ic_cast_connected;
- item.line1 = getDeviceName(device);
+ final Item item =
+ new Item(
+ R.drawable.ic_cast_connected,
+ getDeviceName(device),
+ device);
item.line2 = mContext.getString(R.string.quick_settings_connected);
- item.tag = device;
item.canDisconnect = true;
items = new Item[] { item };
break;
@@ -422,13 +423,11 @@
for (String id : mVisibleOrder.keySet()) {
final CastDevice device = mVisibleOrder.get(id);
if (!devices.contains(device)) continue;
- final Item item = new Item();
- item.iconResId = R.drawable.ic_cast;
- item.line1 = getDeviceName(device);
+ final Item item =
+ new Item(R.drawable.ic_cast, getDeviceName(device), device);
if (device.state == CastDevice.STATE_CONNECTING) {
item.line2 = mContext.getString(R.string.quick_settings_connecting);
}
- item.tag = device;
items[i++] = item;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
index 7ba9cc2..a2577d6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -98,7 +98,7 @@
toggleDataSaver();
Prefs.putBoolean(mContext, Prefs.Key.QS_DATA_SAVER_DIALOG_SHOWN, true);
});
- dialog.setNegativeButton(com.android.internal.R.string.cancel, null);
+ dialog.setNeutralButton(com.android.internal.R.string.cancel, null);
dialog.setShowForAllUsers(true);
if (view != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 83506b2..bb27458 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -190,7 +190,6 @@
case Settings.Secure.ZEN_DURATION_PROMPT:
mUiHandler.post(() -> {
Dialog dialog = makeZenModeDialog();
- SystemUIDialog.registerDismissListener(dialog);
if (view != null) {
mDialogLaunchAnimator.showFromView(dialog, view, false);
} else {
@@ -211,10 +210,12 @@
}
private Dialog makeZenModeDialog() {
- AlertDialog dialog = new EnableZenModeDialog(mContext, R.style.Theme_SystemUI_Dialog)
- .createDialog();
+ AlertDialog dialog = new EnableZenModeDialog(mContext, R.style.Theme_SystemUI_Dialog,
+ true /* cancelIsNeutral */).createDialog();
SystemUIDialog.applyFlags(dialog);
SystemUIDialog.setShowForAllUsers(dialog, true);
+ SystemUIDialog.registerDismissListener(dialog);
+ SystemUIDialog.setDialogSize(dialog);
return dialog;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index e79ca0c..da0069f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -378,26 +378,26 @@
@Override
public void onAccessPointsChanged(final List<WifiEntry> accessPoints) {
- mAccessPoints = accessPoints.toArray(new WifiEntry[accessPoints.size()]);
- filterUnreachableAPs();
+ mAccessPoints = filterUnreachableAPs(accessPoints);
updateItems();
}
/** Filter unreachable APs from mAccessPoints */
- private void filterUnreachableAPs() {
+ private WifiEntry[] filterUnreachableAPs(List<WifiEntry> unfiltered) {
int numReachable = 0;
- for (WifiEntry ap : mAccessPoints) {
+ for (WifiEntry ap : unfiltered) {
if (isWifiEntryReachable(ap)) numReachable++;
}
- if (numReachable != mAccessPoints.length) {
- WifiEntry[] unfiltered = mAccessPoints;
- mAccessPoints = new WifiEntry[numReachable];
+ if (numReachable != unfiltered.size()) {
+ WifiEntry[] accessPoints = new WifiEntry[numReachable];
int i = 0;
for (WifiEntry ap : unfiltered) {
- if (isWifiEntryReachable(ap)) mAccessPoints[i++] = ap;
+ if (isWifiEntryReachable(ap)) accessPoints[i++] = ap;
}
+ return accessPoints;
}
+ return unfiltered.toArray(new WifiEntry[0]);
}
@Override
@@ -454,10 +454,7 @@
items = new Item[mAccessPoints.length];
for (int i = 0; i < mAccessPoints.length; i++) {
final WifiEntry ap = mAccessPoints[i];
- final Item item = new Item();
- item.tag = ap;
- item.iconResId = mWifiController.getIcon(ap);
- item.line1 = ap.getSsid();
+ final Item item = new Item(mWifiController.getIcon(ap), ap.getSsid(), ap);
item.line2 = ap.getSummary();
item.icon2 = ap.getSecurity() != AccessPoint.SECURITY_NONE
? R.drawable.qs_ic_wifi_lock
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index ba4257f..e3f085c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -159,7 +159,9 @@
if (DEBUG) {
Log.d(TAG, "Init InternetDialog");
}
- mContext = context;
+
+ // Save the context that is wrapped with our theme.
+ mContext = getContext();
mHandler = handler;
mBackgroundExecutor = executor;
mInternetDialogFactory = internetDialogFactory;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index 1fee1b4..7dd24b4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -316,15 +316,11 @@
}
CharSequence getSubtitleText(boolean isProgressBarVisible) {
- if (isAirplaneModeEnabled()) {
- return null;
- }
-
if (mCanConfigWifi && !mWifiManager.isWifiEnabled()) {
- // When the airplane mode is off and Wi-Fi is disabled.
+ // When Wi-Fi is disabled.
// Sub-Title: Wi-Fi is off
if (DEBUG) {
- Log.d(TAG, "Airplane mode off + Wi-Fi off.");
+ Log.d(TAG, "Wi-Fi off.");
}
return mContext.getText(SUBTITLE_TEXT_WIFI_IS_OFF);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/UserUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/UserUtil.java
index c4fadff..4551807 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/UserUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/UserUtil.java
@@ -40,7 +40,7 @@
super(context);
setTitle(R.string.user_remove_user_title);
setMessage(context.getString(R.string.user_remove_user_message));
- setButton(DialogInterface.BUTTON_NEGATIVE,
+ setButton(DialogInterface.BUTTON_NEUTRAL,
context.getString(android.R.string.cancel), this);
setButton(DialogInterface.BUTTON_POSITIVE,
context.getString(R.string.user_remove_user_remove), this);
@@ -51,7 +51,7 @@
@Override
public void onClick(DialogInterface dialog, int which) {
- if (which == BUTTON_NEGATIVE) {
+ if (which == BUTTON_NEUTRAL) {
cancel();
} else {
dismiss();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 0389a7b..f500d39 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -45,6 +45,7 @@
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationRemoveInterceptor;
import com.android.systemui.statusbar.NotificationUiAdjustment;
+import com.android.systemui.statusbar.notification.collection.NotifLiveDataStoreImpl;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.legacy.LegacyNotificationRanker;
@@ -107,6 +108,7 @@
private final LeakDetector mLeakDetector;
private final ForegroundServiceDismissalFeatureController mFgsFeatureController;
private final IStatusBarService mStatusBarService;
+ private final NotifLiveDataStoreImpl mNotifLiveDataStore;
private final DumpManager mDumpManager;
private final Set<NotificationEntry> mAllNotifications = new ArraySet<>();
@@ -154,6 +156,7 @@
LeakDetector leakDetector,
ForegroundServiceDismissalFeatureController fgsFeatureController,
IStatusBarService statusBarService,
+ NotifLiveDataStoreImpl notifLiveDataStore,
DumpManager dumpManager
) {
mLogger = logger;
@@ -164,6 +167,7 @@
mLeakDetector = leakDetector;
mFgsFeatureController = fgsFeatureController;
mStatusBarService = statusBarService;
+ mNotifLiveDataStore = notifLiveDataStore;
mDumpManager = dumpManager;
}
@@ -725,9 +729,10 @@
return;
}
reapplyFilterAndSort(reason);
- if (mPresenter != null && !mNotifPipelineFlags.isNewPipelineEnabled()) {
+ if (mPresenter != null) {
mPresenter.updateNotificationViews(reason);
}
+ mNotifLiveDataStore.setActiveNotifList(getVisibleNotifications());
}
public void updateNotificationRanking(RankingMap rankingMap) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStore.kt
new file mode 100644
index 0000000..ef00627
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStore.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection
+
+import androidx.lifecycle.Observer
+
+/**
+ * An object which provides pieces of information about the notification shade.
+ *
+ * Note that individual fields of this object are updated together before synchronous observers are
+ * notified, so synchronous observers of two fields can be assured that they will see consistent
+ * results: e.g. if [hasActiveNotifs] is false then [activeNotifList] will be empty, and vice versa.
+ *
+ * This interface is read-only.
+ */
+interface NotifLiveDataStore {
+ val hasActiveNotifs: NotifLiveData<Boolean>
+ val activeNotifCount: NotifLiveData<Int>
+ val activeNotifList: NotifLiveData<List<NotificationEntry>>
+}
+
+/**
+ * An individual value which can be accessed directly, or observed for changes either synchronously
+ * or asynchronously.
+ *
+ * This interface is read-only.
+ */
+interface NotifLiveData<T> {
+ /** Access the current value */
+ val value: T
+ /** Add an observer which will be invoked synchronously when the value is changed. */
+ fun addSyncObserver(observer: Observer<T>)
+ /** Add an observer which will be invoked asynchronously after the value has changed */
+ fun addAsyncObserver(observer: Observer<T>)
+ /** Remove an observer previously added with [addSyncObserver] or [addAsyncObserver]. */
+ fun removeObserver(observer: Observer<T>)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreImpl.kt
new file mode 100644
index 0000000..8aa6b81
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreImpl.kt
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection
+
+import androidx.lifecycle.Observer
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.util.Assert
+import com.android.systemui.util.ListenerSet
+import com.android.systemui.util.isNotEmpty
+import com.android.systemui.util.traceSection
+import java.util.Collections.unmodifiableList
+import java.util.concurrent.Executor
+import java.util.concurrent.atomic.AtomicReference
+import javax.inject.Inject
+
+/** Writeable implementation of [NotifLiveDataStore] */
+@SysUISingleton
+class NotifLiveDataStoreImpl @Inject constructor(
+ @Main private val mainExecutor: Executor
+) : NotifLiveDataStore {
+ private val hasActiveNotifsPrivate = NotifLiveDataImpl(
+ name = "hasActiveNotifs",
+ initialValue = false,
+ mainExecutor
+ )
+ private val activeNotifCountPrivate = NotifLiveDataImpl(
+ name = "activeNotifCount",
+ initialValue = 0,
+ mainExecutor
+ )
+ private val activeNotifListPrivate = NotifLiveDataImpl(
+ name = "activeNotifList",
+ initialValue = listOf<NotificationEntry>(),
+ mainExecutor
+ )
+
+ override val hasActiveNotifs: NotifLiveData<Boolean> = hasActiveNotifsPrivate
+ override val activeNotifCount: NotifLiveData<Int> = activeNotifCountPrivate
+ override val activeNotifList: NotifLiveData<List<NotificationEntry>> = activeNotifListPrivate
+
+ /** Set the latest flattened list of notification entries. */
+ fun setActiveNotifList(flatEntryList: List<NotificationEntry>) {
+ traceSection("NotifLiveDataStore.setActiveNotifList") {
+ Assert.isMainThread()
+ val unmodifiableCopy = unmodifiableList(flatEntryList.toList())
+ // This ensures we set all values before dispatching to any observers
+ listOf(
+ activeNotifListPrivate.setValueAndProvideDispatcher(unmodifiableCopy),
+ activeNotifCountPrivate.setValueAndProvideDispatcher(unmodifiableCopy.size),
+ hasActiveNotifsPrivate.setValueAndProvideDispatcher(unmodifiableCopy.isNotEmpty())
+ ).forEach { dispatcher -> dispatcher.invoke() }
+ }
+ }
+}
+
+/** Read-write implementation of [NotifLiveData] */
+class NotifLiveDataImpl<T>(
+ private val name: String,
+ initialValue: T,
+ @Main private val mainExecutor: Executor
+) : NotifLiveData<T> {
+ private val syncObservers = ListenerSet<Observer<T>>()
+ private val asyncObservers = ListenerSet<Observer<T>>()
+ private val atomicValue = AtomicReference(initialValue)
+ private var lastAsyncValue: T? = null
+
+ private fun dispatchToAsyncObservers() {
+ val value = atomicValue.get()
+ if (lastAsyncValue != value) {
+ lastAsyncValue = value
+ traceSection("NotifLiveData($name).dispatchToAsyncObservers") {
+ asyncObservers.forEach { it.onChanged(value) }
+ }
+ }
+ }
+
+ /**
+ * Access or set the current value.
+ *
+ * When setting, sync observers will be dispatched synchronously, and a task will be posted to
+ * dispatch the value to async observers.
+ */
+ override var value: T
+ get() = atomicValue.get()
+ set(value) = setValueAndProvideDispatcher(value).invoke()
+
+ /**
+ * Set the value, and return a function that when invoked will dispatch to the observers.
+ *
+ * This is intended to allow multiple instances with related data to be updated together and
+ * have their dispatchers invoked after all data has been updated.
+ */
+ fun setValueAndProvideDispatcher(value: T): () -> Unit {
+ val oldValue = atomicValue.getAndSet(value)
+ if (oldValue != value) {
+ return {
+ if (syncObservers.isNotEmpty()) {
+ traceSection("NotifLiveData($name).dispatchToSyncObservers") {
+ syncObservers.forEach { it.onChanged(value) }
+ }
+ }
+ if (asyncObservers.isNotEmpty()) {
+ mainExecutor.execute(::dispatchToAsyncObservers)
+ }
+ }
+ }
+ return {}
+ }
+
+ override fun addSyncObserver(observer: Observer<T>) {
+ syncObservers.addIfAbsent(observer)
+ }
+
+ override fun addAsyncObserver(observer: Observer<T>) {
+ asyncObservers.addIfAbsent(observer)
+ }
+
+ override fun removeObserver(observer: Observer<T>) {
+ syncObservers.remove(observer)
+ asyncObservers.remove(observer)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt
index 6fbed9a8..5ada7a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt
@@ -245,36 +245,4 @@
fun getInternalNotifUpdater(name: String?): InternalNotifUpdater {
return mNotifCollection.getInternalNotifUpdater(name)
}
-
- /**
- * Returns a read-only view in to the current shade list, i.e. the list of notifications that
- * are currently present in the shade.
- * @throws IllegalStateException if called during pipeline execution.
- */
- val shadeList: List<ListEntry>
- get() = mShadeListBuilder.shadeList
-
- /**
- * Constructs a flattened representation of the notification tree, where each group will have
- * the summary (if present) followed by the children.
- * @throws IllegalStateException if called during pipeline execution.
- */
- fun getFlatShadeList(): List<NotificationEntry> = shadeList.flatMap { entry ->
- when (entry) {
- is NotificationEntry -> sequenceOf(entry)
- is GroupEntry -> sequenceOf(entry.summary).filterNotNull() + entry.children
- else -> throw RuntimeException("Unexpected entry $entry")
- }
- }
-
- /**
- * Returns the number of notifications currently shown in the shade. This includes all
- * children and summary notifications.
- * @throws IllegalStateException if called during pipeline execution.
- */
- fun getShadeListCount(): Int = shadeList.sumOf { entry ->
- // include the summary in the count
- if (entry is GroupEntry) 1 + entry.children.size
- else 1
- }
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinator.kt
new file mode 100644
index 0000000..8e307ec
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinator.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator
+
+import com.android.systemui.statusbar.notification.collection.GroupEntry
+import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.NotifLiveDataStoreImpl
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
+import com.android.systemui.statusbar.notification.collection.render.requireSummary
+import javax.inject.Inject
+
+/**
+ * A small coordinator which updates the notif stack (the view layer which holds notifications)
+ * with high-level data after the stack is populated with the final entries.
+ */
+@CoordinatorScope
+class DataStoreCoordinator @Inject internal constructor(
+ private val notifLiveDataStoreImpl: NotifLiveDataStoreImpl
+) : Coordinator {
+
+ override fun attach(pipeline: NotifPipeline) {
+ pipeline.addOnAfterRenderListListener { entries, _ -> onAfterRenderList(entries) }
+ }
+
+ fun onAfterRenderList(entries: List<ListEntry>) {
+ val flatEntryList = flattenedEntryList(entries)
+ notifLiveDataStoreImpl.setActiveNotifList(flatEntryList)
+ }
+
+ private fun flattenedEntryList(entries: List<ListEntry>) =
+ mutableListOf<NotificationEntry>().also { list ->
+ entries.forEach { entry ->
+ when (entry) {
+ is NotificationEntry -> list.add(entry)
+ is GroupEntry -> {
+ list.add(entry.requireSummary)
+ list.addAll(entry.children)
+ }
+ else -> error("Unexpected entry $entry")
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
index a16b565..02649ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
@@ -35,6 +35,7 @@
class NotifCoordinatorsImpl @Inject constructor(
dumpManager: DumpManager,
notifPipelineFlags: NotifPipelineFlags,
+ dataStoreCoordinator: DataStoreCoordinator,
hideLocallyDismissedNotifsCoordinator: HideLocallyDismissedNotifsCoordinator,
hideNotifsForOtherUsersCoordinator: HideNotifsForOtherUsersCoordinator,
keyguardCoordinator: KeyguardCoordinator,
@@ -44,10 +45,11 @@
bubbleCoordinator: BubbleCoordinator,
headsUpCoordinator: HeadsUpCoordinator,
gutsCoordinator: GutsCoordinator,
+ communalCoordinator: CommunalCoordinator,
conversationCoordinator: ConversationCoordinator,
- preparationCoordinator: PreparationCoordinator,
groupCountCoordinator: GroupCountCoordinator,
mediaCoordinator: MediaCoordinator,
+ preparationCoordinator: PreparationCoordinator,
remoteInputCoordinator: RemoteInputCoordinator,
rowAppearanceCoordinator: RowAppearanceCoordinator,
stackCoordinator: StackCoordinator,
@@ -55,7 +57,6 @@
smartspaceDedupingCoordinator: SmartspaceDedupingCoordinator,
viewConfigCoordinator: ViewConfigCoordinator,
visualStabilityCoordinator: VisualStabilityCoordinator,
- communalCoordinator: CommunalCoordinator,
sensitiveContentCoordinator: SensitiveContentCoordinator
) : NotifCoordinators {
@@ -67,6 +68,16 @@
*/
init {
dumpManager.registerDumpable(TAG, this)
+
+ // TODO(b/208866714): formalize the system by which some coordinators may be required by the
+ // pipeline, such as this DataStoreCoordinator which cannot be removed, as it's a critical
+ // glue between the pipeline and parts of SystemUI which depend on pipeline output via the
+ // NotifLiveDataStore.
+ if (notifPipelineFlags.isNewPipelineEnabled()) {
+ mCoordinators.add(dataStoreCoordinator)
+ }
+
+ // Attach normal coordinators.
mCoordinators.add(hideLocallyDismissedNotifsCoordinator)
mCoordinators.add(hideNotifsForOtherUsersCoordinator)
mCoordinators.add(keyguardCoordinator)
@@ -74,6 +85,7 @@
mCoordinators.add(appOpsCoordinator)
mCoordinators.add(deviceProvisionedCoordinator)
mCoordinators.add(bubbleCoordinator)
+ mCoordinators.add(communalCoordinator)
mCoordinators.add(conversationCoordinator)
mCoordinators.add(groupCountCoordinator)
mCoordinators.add(mediaCoordinator)
@@ -83,7 +95,6 @@
mCoordinators.add(shadeEventCoordinator)
mCoordinators.add(viewConfigCoordinator)
mCoordinators.add(visualStabilityCoordinator)
- mCoordinators.add(communalCoordinator)
mCoordinators.add(sensitiveContentCoordinator)
if (notifPipelineFlags.isSmartspaceDedupingEnabled()) {
mCoordinators.add(smartspaceDedupingCoordinator)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
index 38f11fc..c6a8a69 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
@@ -19,8 +19,8 @@
import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
-import com.android.systemui.statusbar.notification.collection.render.NotifStats
import com.android.systemui.statusbar.notification.collection.render.NotifStackController
+import com.android.systemui.statusbar.notification.collection.render.NotifStats
import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT
import com.android.systemui.statusbar.phone.NotificationIconAreaController
import javax.inject.Inject
@@ -49,10 +49,12 @@
var hasNonClearableSilentNotifs = false
var hasClearableSilentNotifs = false
entries.forEach {
- val isSilent = it.section!!.bucket == BUCKET_SILENT
+ val section = checkNotNull(it.section) { "Null section for ${it.key}" }
+ val entry = checkNotNull(it.representativeEntry) { "Null notif entry for ${it.key}" }
+ val isSilent = section.bucket == BUCKET_SILENT
// NOTE: NotificationEntry.isClearable will internally check group children to ensure
// the group itself definitively clearable.
- val isClearable = it.representativeEntry!!.isClearable
+ val isClearable = entry.isClearable
when {
isSilent && isClearable -> hasClearableSilentNotifs = true
isSilent && !isClearable -> hasNonClearableSilentNotifs = true
@@ -60,13 +62,12 @@
!isSilent && !isClearable -> hasNonClearableAlertingNotifs = true
}
}
- val stats = NotifStats(
+ return NotifStats(
numActiveNotifs = entries.size,
hasNonClearableAlertingNotifs = hasNonClearableAlertingNotifs,
hasClearableAlertingNotifs = hasClearableAlertingNotifs,
hasNonClearableSilentNotifs = hasNonClearableSilentNotifs,
hasClearableSilentNotifs = hasClearableSilentNotifs
)
- return stats
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationVisibilityProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationVisibilityProvider.kt
deleted file mode 100644
index 5c70f32..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationVisibilityProvider.kt
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification.collection.legacy
-
-import com.android.internal.statusbar.NotificationVisibility
-import com.android.systemui.statusbar.notification.NotificationEntryManager
-import com.android.systemui.statusbar.notification.collection.NotificationEntry
-import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider
-import com.android.systemui.statusbar.notification.logging.NotificationLogger
-import javax.inject.Inject
-
-/** Legacy pipeline implementation for getting [NotificationVisibility]. */
-class LegacyNotificationVisibilityProvider @Inject constructor(
- private val notifEntryManager: NotificationEntryManager
-) : NotificationVisibilityProvider {
- override fun obtain(entry: NotificationEntry, visible: Boolean): NotificationVisibility {
- val count: Int = notifEntryManager.activeNotificationsCount
- val rank = entry.ranking.rank
- val hasRow = entry.row != null
- val location = NotificationLogger.getNotificationLocation(entry)
- return NotificationVisibility.obtain(entry.key, rank, count, visible && hasRow, location)
- }
-
- override fun obtain(key: String, visible: Boolean): NotificationVisibility {
- val entry: NotificationEntry? = notifEntryManager.getActiveNotificationUnfiltered(key)
- val count: Int = notifEntryManager.activeNotificationsCount
- val rank = entry?.ranking?.rank ?: -1
- val hasRow = entry?.row != null
- val location = NotificationLogger.getNotificationLocation(entry)
- return NotificationVisibility.obtain(key, rank, count, visible && hasRow, location)
- }
-
- override fun getLocation(key: String): NotificationVisibility.NotificationLocation =
- NotificationLogger.getNotificationLocation(
- notifEntryManager.getActiveNotificationUnfiltered(key))
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotificationVisibilityProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/NotificationVisibilityProviderImpl.kt
similarity index 65%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotificationVisibilityProviderImpl.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/NotificationVisibilityProviderImpl.kt
index 51de08d..6a1e36f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotificationVisibilityProviderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/NotificationVisibilityProviderImpl.kt
@@ -14,20 +14,24 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification.collection.render
+package com.android.systemui.statusbar.notification.collection.provider
import com.android.internal.statusbar.NotificationVisibility
-import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore
import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
+import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider
import com.android.systemui.statusbar.notification.logging.NotificationLogger
import javax.inject.Inject
-/** New pipeline implementation for getting [NotificationVisibility]. */
+/** pipeline-agnostic implementation for getting [NotificationVisibility]. */
class NotificationVisibilityProviderImpl @Inject constructor(
- private val notifPipeline: NotifPipeline
+ private val notifDataStore: NotifLiveDataStore,
+ private val notifCollection: CommonNotifCollection
) : NotificationVisibilityProvider {
+
override fun obtain(entry: NotificationEntry, visible: Boolean): NotificationVisibility {
- val count: Int = notifPipeline.getShadeListCount()
+ val count: Int = getCount()
val rank = entry.ranking.rank
val hasRow = entry.row != null
val location = NotificationLogger.getNotificationLocation(entry)
@@ -35,9 +39,11 @@
}
override fun obtain(key: String, visible: Boolean): NotificationVisibility =
- notifPipeline.getEntry(key)?.let { return obtain(it, visible) }
- ?: NotificationVisibility.obtain(key, -1, notifPipeline.getShadeListCount(), false)
+ notifCollection.getEntry(key)?.let { return obtain(it, visible) }
+ ?: NotificationVisibility.obtain(key, -1, getCount(), false)
override fun getLocation(key: String): NotificationVisibility.NotificationLocation =
- NotificationLogger.getNotificationLocation(notifPipeline.getEntry(key))
+ NotificationLogger.getNotificationLocation(notifCollection.getEntry(key))
+
+ private fun getCount() = notifDataStore.activeNotifCount.value
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index d25a2d3..f1cba34 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -44,6 +44,8 @@
import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
+import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
+import com.android.systemui.statusbar.notification.collection.NotifLiveDataStoreImpl;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.coordinator.ShadeEventCoordinator;
import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator;
@@ -52,12 +54,12 @@
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.inflation.OnUserInteractionCallbackImpl;
import com.android.systemui.statusbar.notification.collection.legacy.LegacyNotificationPresenterExtensions;
-import com.android.systemui.statusbar.notification.collection.legacy.LegacyNotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.legacy.OnUserInteractionCallbackImplLegacy;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
+import com.android.systemui.statusbar.notification.collection.provider.NotificationVisibilityProviderImpl;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManagerImpl;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
@@ -65,7 +67,6 @@
import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager;
import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
-import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProviderImpl;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.init.NotificationsControllerImpl;
import com.android.systemui.statusbar.notification.init.NotificationsControllerStub;
@@ -122,6 +123,7 @@
LeakDetector leakDetector,
ForegroundServiceDismissalFeatureController fgsFeatureController,
IStatusBarService statusBarService,
+ NotifLiveDataStoreImpl notifLiveDataStore,
DumpManager dumpManager) {
return new NotificationEntryManager(
logger,
@@ -132,6 +134,7 @@
leakDetector,
fgsFeatureController,
statusBarService,
+ notifLiveDataStore,
dumpManager);
}
@@ -212,6 +215,7 @@
NotificationListener notificationListener,
@UiBackground Executor uiBgExecutor,
NotifPipelineFlags notifPipelineFlags,
+ NotifLiveDataStore notifLiveDataStore,
NotificationVisibilityProvider visibilityProvider,
NotificationEntryManager entryManager,
NotifPipeline notifPipeline,
@@ -222,6 +226,7 @@
notificationListener,
uiBgExecutor,
notifPipelineFlags,
+ notifLiveDataStore,
visibilityProvider,
entryManager,
notifPipeline,
@@ -290,16 +295,10 @@
/**
* Provide the object which can be used to obtain NotificationVisibility objects.
*/
- @Provides
+ @Binds
@SysUISingleton
- static NotificationVisibilityProvider provideNotificationVisibilityProvider(
- NotifPipelineFlags notifPipelineFlags,
- Lazy<NotificationVisibilityProviderImpl> newProvider,
- Lazy<LegacyNotificationVisibilityProvider> legacyProvider) {
- return notifPipelineFlags.isNewPipelineEnabled()
- ? newProvider.get()
- : legacyProvider.get();
- }
+ NotificationVisibilityProvider provideNotificationVisibilityProvider(
+ NotificationVisibilityProviderImpl newProvider);
/**
* Provide the active implementation for presenting notifications.
@@ -356,4 +355,8 @@
/** */
@Binds
NotifInflater bindNotifInflater(NotifInflaterImpl notifInflaterImpl);
+
+ /** */
+ @Binds
+ NotifLiveDataStore bindNotifLiveDataStore(NotifLiveDataStoreImpl notifLiveDataStoreImpl);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
index 212c342e..84f0955 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -28,12 +28,14 @@
import com.android.systemui.statusbar.notification.NotificationClicker
import com.android.systemui.statusbar.notification.NotificationEntryManager
import com.android.systemui.statusbar.notification.NotificationListController
+import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager
import com.android.systemui.statusbar.notification.collection.TargetSdkResolver
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl
import com.android.systemui.statusbar.notification.collection.init.NotifPipelineInitializer
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
import com.android.systemui.statusbar.notification.collection.render.NotifStackController
import com.android.systemui.statusbar.notification.interruption.HeadsUpController
import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder
@@ -64,9 +66,11 @@
private val notificationListener: NotificationListener,
private val entryManager: NotificationEntryManager,
private val legacyRanker: NotificationRankingManager,
+ private val commonNotifCollection: Lazy<CommonNotifCollection>,
private val notifPipeline: Lazy<NotifPipeline>,
+ private val notifLiveDataStore: NotifLiveDataStore,
private val targetSdkResolver: TargetSdkResolver,
- private val newNotifPipeline: Lazy<NotifPipelineInitializer>,
+ private val newNotifPipelineInitializer: Lazy<NotifPipelineInitializer>,
private val notifBindPipelineInitializer: NotifBindPipelineInitializer,
private val deviceProvisionedController: DeviceProvisionedController,
private val notificationRowBinder: NotificationRowBinderImpl,
@@ -111,7 +115,7 @@
animatedImageNotificationManager.bind()
if (INITIALIZE_NEW_PIPELINE) {
- newNotifPipeline.get().initialize(
+ newNotifPipelineInitializer.get().initialize(
notificationListener,
notificationRowBinder,
listContainer,
@@ -155,14 +159,10 @@
}
override fun resetUserExpandedStates() {
- if (notifPipelineFlags.isNewPipelineEnabled()) {
- for (entry in notifPipeline.get().allNotifs) {
- entry.resetUserExpansion()
- }
- } else {
- for (entry in entryManager.visibleNotifications) {
- entry.resetUserExpansion()
- }
+ // TODO: this is a view thing that should be done through the views, but that means doing it
+ // both when this event is fired and any time a row is attached.
+ for (entry in commonNotifCollection.get().allNotifs) {
+ entry.resetUserExpansion()
}
}
@@ -177,11 +177,7 @@
}
override fun getActiveNotificationsCount(): Int =
- if (notifPipelineFlags.isNewPipelineEnabled()) {
- notifPipeline.get().getShadeListCount()
- } else {
- entryManager.activeNotificationsCount
- }
+ notifLiveDataStore.activeNotifCount.value
companion object {
// NOTE: The new pipeline is always active, even if the old pipeline is *rendering*.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index 52488da..9e8200b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -42,6 +42,7 @@
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
@@ -76,7 +77,7 @@
// Dependencies:
private final NotificationListenerService mNotificationListener;
private final Executor mUiBgExecutor;
- private final NotifPipelineFlags mNotifPipelineFlags;
+ private final NotifLiveDataStore mNotifLiveDataStore;
private final NotificationVisibilityProvider mVisibilityProvider;
private final NotificationEntryManager mEntryManager;
private final NotifPipeline mNotifPipeline;
@@ -179,11 +180,7 @@
};
private List<NotificationEntry> getVisibleNotifications() {
- if (mNotifPipelineFlags.isNewPipelineEnabled()) {
- return mNotifPipeline.getFlatShadeList();
- } else {
- return mEntryManager.getVisibleNotifications();
- }
+ return mNotifLiveDataStore.getActiveNotifList().getValue();
}
/**
@@ -223,6 +220,7 @@
public NotificationLogger(NotificationListener notificationListener,
@UiBackground Executor uiBgExecutor,
NotifPipelineFlags notifPipelineFlags,
+ NotifLiveDataStore notifLiveDataStore,
NotificationVisibilityProvider visibilityProvider,
NotificationEntryManager entryManager,
NotifPipeline notifPipeline,
@@ -231,7 +229,7 @@
NotificationPanelLogger notificationPanelLogger) {
mNotificationListener = notificationListener;
mUiBgExecutor = uiBgExecutor;
- mNotifPipelineFlags = notifPipelineFlags;
+ mNotifLiveDataStore = notifLiveDataStore;
mVisibilityProvider = visibilityProvider;
mEntryManager = entryManager;
mNotifPipeline = notifPipeline;
@@ -242,7 +240,7 @@
// Not expected to be destroyed, don't need to unsubscribe
statusBarStateController.addCallback(this);
- if (mNotifPipelineFlags.isNewPipelineEnabled()) {
+ if (notifPipelineFlags.isNewPipelineEnabled()) {
registerNewPipelineListener();
} else {
registerLegacyListener();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index aa3b3e1..ad1c232 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -74,7 +74,7 @@
private static final long BIOMETRIC_WAKELOCK_TIMEOUT_MS = 15 * 1000;
private static final String BIOMETRIC_WAKE_LOCK_NAME = "wake-and-unlock:wakelock";
private static final UiEventLogger UI_EVENT_LOGGER = new UiEventLoggerImpl();
- private static final int FP_ATTEMPTS_BEFORE_SHOW_BOUNCER = 3;
+ private static final int FP_ATTEMPTS_BEFORE_SHOW_BOUNCER = 2;
@IntDef(prefix = { "MODE_" }, value = {
MODE_NONE,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
index c09cca1..c61510c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
@@ -22,7 +22,6 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.annotation.Nullable;
import android.view.InsetsVisibilities;
import android.view.View;
import android.view.WindowInsetsController.Appearance;
@@ -30,13 +29,12 @@
import android.view.WindowManager;
import android.view.animation.AccelerateInterpolator;
+import androidx.lifecycle.Observer;
+
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.view.AppearanceRegion;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.notification.NotificationEntryListener;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentScope;
import com.android.systemui.util.ViewController;
@@ -44,20 +42,21 @@
import javax.inject.Named;
/**
- * Apps can request a low profile mode {@link View.SYSTEM_UI_FLAG_LOW_PROFILE}
+ * Apps can request a low profile mode {@link View#SYSTEM_UI_FLAG_LOW_PROFILE}
* where status bar and navigation icons dim. In this mode, a notification dot appears
* where the notification icons would appear if they would be shown outside of this mode.
*
* This controller shows and hides the notification dot in the status bar to indicate
- * whether there are notifications when the device is in {@link View.SYSTEM_UI_FLAG_LOW_PROFILE}.
+ * whether there are notifications when the device is in {@link View#SYSTEM_UI_FLAG_LOW_PROFILE}.
*/
@StatusBarFragmentScope
public class LightsOutNotifController extends ViewController<View> {
private final CommandQueue mCommandQueue;
- private final NotificationEntryManager mEntryManager;
+ private final NotifLiveDataStore mNotifDataStore;
private final WindowManager mWindowManager;
+ private final Observer<Boolean> mObserver = hasNotifs -> updateLightsOutView();
- /** @see android.view.WindowInsetsController#setSystemBarsAppearance(int) */
+ /** @see android.view.WindowInsetsController#setSystemBarsAppearance(int, int) */
@VisibleForTesting @Appearance int mAppearance;
private int mDisplayId;
@@ -66,18 +65,18 @@
LightsOutNotifController(
@Named(LIGHTS_OUT_NOTIF_VIEW) View lightsOutNotifView,
WindowManager windowManager,
- NotificationEntryManager entryManager,
+ NotifLiveDataStore notifDataStore,
CommandQueue commandQueue) {
super(lightsOutNotifView);
mWindowManager = windowManager;
- mEntryManager = entryManager;
+ mNotifDataStore = notifDataStore;
mCommandQueue = commandQueue;
}
@Override
protected void onViewDetached() {
- mEntryManager.removeNotificationEntryListener(mEntryListener);
+ mNotifDataStore.getHasActiveNotifs().removeObserver(mObserver);
mCommandQueue.removeCallback(mCallback);
}
@@ -87,14 +86,14 @@
mView.setAlpha(0f);
mDisplayId = mWindowManager.getDefaultDisplay().getDisplayId();
- mEntryManager.addNotificationEntryListener(mEntryListener);
+ mNotifDataStore.getHasActiveNotifs().addSyncObserver(mObserver);
mCommandQueue.addCallback(mCallback);
updateLightsOutView();
}
private boolean hasActiveNotifications() {
- return mEntryManager.hasActiveNotifications();
+ return mNotifDataStore.getHasActiveNotifs().getValue();
}
@VisibleForTesting
@@ -153,23 +152,4 @@
updateLightsOutView();
}
};
-
- private final NotificationEntryListener mEntryListener = new NotificationEntryListener() {
- // Cares about notifications post-filtering
- @Override
- public void onNotificationAdded(NotificationEntry entry) {
- updateLightsOutView();
- }
-
- @Override
- public void onPostEntryUpdated(NotificationEntry entry) {
- updateLightsOutView();
- }
-
- @Override
- public void onEntryRemoved(@Nullable NotificationEntry entry,
- NotificationVisibility visibility, boolean removedByUser, int reason) {
- updateLightsOutView();
- }
- };
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index a64e579..4e68b19 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -3419,12 +3419,6 @@
mStatusBarStateController.setState(KEYGUARD);
}
return true;
- case StatusBarState.SHADE:
-
- // This gets called in the middle of the touch handling, where the state is still
- // that we are tracking the panel. Collapse the panel after this is done.
- mView.post(mPostCollapseRunnable);
- return false;
default:
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index 9af79a9..53bfd77 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -933,7 +933,6 @@
private void abortAnimations() {
cancelHeightAnimator();
- mView.removeCallbacks(mPostCollapseRunnable);
mView.removeCallbacks(mFlingCollapseRunnable);
}
@@ -1110,13 +1109,6 @@
return onMiddleClicked();
}
- protected final Runnable mPostCollapseRunnable = new Runnable() {
- @Override
- public void run() {
- collapse(false /* delayed */, 1.0f /* speedUpFactor */);
- }
- };
-
protected abstract boolean onMiddleClicked();
protected abstract boolean isDozing();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index ec7e93b..b9386bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -15,12 +15,14 @@
*/
package com.android.systemui.statusbar.phone
+import android.content.res.Configuration
import android.graphics.Point
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
import com.android.systemui.R
import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator
+import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.unfold.SysUIUnfoldComponent
import com.android.systemui.unfold.UNFOLD_STATUS_BAR
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
@@ -35,9 +37,16 @@
view: PhoneStatusBarView,
@Named(UNFOLD_STATUS_BAR) private val progressProvider: ScopedUnfoldTransitionProgressProvider?,
private val moveFromCenterAnimationController: StatusBarMoveFromCenterAnimationController?,
- touchEventHandler: PhoneStatusBarView.TouchEventHandler
+ touchEventHandler: PhoneStatusBarView.TouchEventHandler,
+ private val configurationController: ConfigurationController
) : ViewController<PhoneStatusBarView>(view) {
+ private val configurationListener = object : ConfigurationController.ConfigurationListener {
+ override fun onConfigChanged(newConfig: Configuration?) {
+ mView.updateResources()
+ }
+ }
+
override fun onViewAttached() {
moveFromCenterAnimationController?.let { animationController ->
val statusBarLeftSide: View = mView.findViewById(R.id.status_bar_left_side)
@@ -66,11 +75,13 @@
}
progressProvider?.setReadyToHandleTransition(true)
+ configurationController.addCallback(configurationListener)
}
override fun onViewDetached() {
progressProvider?.setReadyToHandleTransition(false)
moveFromCenterAnimationController?.onViewDetached()
+ configurationController.removeCallback(configurationListener)
}
init {
@@ -116,7 +127,8 @@
class Factory @Inject constructor(
private val unfoldComponent: Optional<SysUIUnfoldComponent>,
@Named(UNFOLD_STATUS_BAR)
- private val progressProvider: Optional<ScopedUnfoldTransitionProgressProvider>
+ private val progressProvider: Optional<ScopedUnfoldTransitionProgressProvider>,
+ private val configurationController: ConfigurationController
) {
fun create(
view: PhoneStatusBarView,
@@ -128,7 +140,8 @@
unfoldComponent.map {
it.getStatusBarMoveFromCenterAnimationController()
}.getOrNull(),
- touchEventHandler
+ touchEventHandler,
+ configurationController
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
index a54251a..b4fed2b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
@@ -218,10 +218,6 @@
return getStatusBar().getNotificationShadeWindowView();
}
- protected PhoneStatusBarView getStatusBarView() {
- return (PhoneStatusBarView) getStatusBar().getStatusBarView();
- }
-
private NotificationPanelViewController getNotificationPanelViewController() {
return getStatusBar().getPanelController();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
index 1ad9fa6..f6e19bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
@@ -18,6 +18,7 @@
import android.view.View
import androidx.constraintlayout.motion.widget.MotionLayout
+import com.android.settingslib.Utils
import com.android.systemui.R
import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.battery.BatteryMeterView
@@ -49,7 +50,7 @@
private val combinedHeaders = featureFlags.isEnabled(Flags.COMBINED_QS_HEADERS)
// TODO(b/194178072) Handle RSSI hiding when multi carrier
- private val iconManager: StatusBarIconController.IconManager
+ private val iconManager: StatusBarIconController.TintedIconManager
private val qsCarrierGroupController: QSCarrierGroupController
private var visible = false
set(value) {
@@ -117,7 +118,9 @@
batteryIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE)
val iconContainer: StatusIconContainer = statusBar.findViewById(R.id.statusIcons)
- iconManager = StatusBarIconController.IconManager(iconContainer, featureFlags)
+ iconManager = StatusBarIconController.TintedIconManager(iconContainer, featureFlags)
+ iconManager.setTint(Utils.getColorAttrDefaultColor(statusBar.context,
+ android.R.attr.textColorPrimary))
qsCarrierGroupController = qsCarrierGroupControllerBuilder
.setQSCarrierGroup(statusBar.findViewById(R.id.carrier_group))
.build()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 0f0a2f0..6c0b717 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -2715,9 +2715,6 @@
mStatusBarWindowController.refreshStatusBarHeight();
}
- if (mStatusBarView != null) {
- mStatusBarView.updateResources();
- }
if (mNotificationPanelViewController != null) {
mNotificationPanelViewController.updateResources();
}
@@ -4192,7 +4189,7 @@
if (userSetup != mUserSetup) {
mUserSetup = userSetup;
- if (!mUserSetup && mStatusBarView != null) {
+ if (!mUserSetup) {
animateCollapseQuickSettings();
}
if (mNotificationPanelViewController != null) {
@@ -4307,7 +4304,7 @@
updateTheme();
mNavigationBarController.touchAutoDim(mDisplayId);
Trace.beginSection("StatusBar#updateKeyguardState");
- if (mState == StatusBarState.KEYGUARD && mStatusBarView != null) {
+ if (mState == StatusBarState.KEYGUARD) {
mNotificationPanelViewController.cancelPendingPanelCollapse();
}
updateDozingState();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
index abb7449..b391de3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
@@ -529,9 +529,7 @@
if (StatusBar.DEBUG_WINDOW_STATE) {
Log.d(StatusBar.TAG, "Status bar " + windowStateToString(state));
}
- if (mStatusBar.getStatusBarView() != null
- && !showing
- && mStatusBarStateController.getState() == StatusBarState.SHADE) {
+ if (!showing && mStatusBarStateController.getState() == StatusBarState.SHADE) {
mNotificationPanelViewController.collapsePanel(
false /* animate */, false /* delayed */, 1.0f /* speedUpFactor */);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 863ce57..ff86d74 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -660,14 +660,6 @@
// --------------------- NotificationEntryManager/NotifPipeline methods ------------------------
- private int getVisibleNotificationsCount() {
- if (mNotifPipelineFlags.isNewPipelineEnabled()) {
- return mNotifPipeline.getShadeListCount();
- } else {
- return mEntryManager.getActiveNotificationsCount();
- }
- }
-
/**
* Public builder for {@link StatusBarNotificationActivityStarter}.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index 43264b6..8df7b45 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -126,30 +126,7 @@
* the device configuration changes, and the result will be used to resize this dialog window.
*/
protected int getWidth() {
- boolean isOnTablet =
- mContext.getResources().getConfiguration().smallestScreenWidthDp >= 600;
- if (!isOnTablet) {
- return ViewGroup.LayoutParams.MATCH_PARENT;
- }
-
- int flagValue = SystemProperties.getInt(FLAG_TABLET_DIALOG_WIDTH, 0);
- if (flagValue == -1) {
- // The width of bottom sheets (624dp).
- return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 624,
- mContext.getResources().getDisplayMetrics()));
- } else if (flagValue == -2) {
- // The suggested small width for all dialogs (348dp)
- return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 348,
- mContext.getResources().getDisplayMetrics()));
- } else if (flagValue > 0) {
- // Any given width.
- return Math.round(
- TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, flagValue,
- mContext.getResources().getDisplayMetrics()));
- } else {
- // By default we use the same width as the notification shade in portrait mode (504dp).
- return mContext.getResources().getDimensionPixelSize(R.dimen.large_dialog_width);
- }
+ return getDefaultDialogWidth(mContext);
}
/**
@@ -157,7 +134,7 @@
* the device configuration changes, and the result will be used to resize this dialog window.
*/
protected int getHeight() {
- return ViewGroup.LayoutParams.WRAP_CONTENT;
+ return getDefaultDialogHeight();
}
@Override
@@ -267,6 +244,45 @@
dismissReceiver.register();
}
+ /** Set an appropriate size to {@code dialog} depending on the current configuration. */
+ public static void setDialogSize(Dialog dialog) {
+ // We need to create the dialog first, otherwise the size will be overridden when it is
+ // created.
+ dialog.create();
+ dialog.getWindow().setLayout(getDefaultDialogWidth(dialog.getContext()),
+ getDefaultDialogHeight());
+ }
+
+ private static int getDefaultDialogWidth(Context context) {
+ boolean isOnTablet = context.getResources().getConfiguration().smallestScreenWidthDp >= 600;
+ if (!isOnTablet) {
+ return ViewGroup.LayoutParams.MATCH_PARENT;
+ }
+
+ int flagValue = SystemProperties.getInt(FLAG_TABLET_DIALOG_WIDTH, 0);
+ if (flagValue == -1) {
+ // The width of bottom sheets (624dp).
+ return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 624,
+ context.getResources().getDisplayMetrics()));
+ } else if (flagValue == -2) {
+ // The suggested small width for all dialogs (348dp)
+ return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 348,
+ context.getResources().getDisplayMetrics()));
+ } else if (flagValue > 0) {
+ // Any given width.
+ return Math.round(
+ TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, flagValue,
+ context.getResources().getDisplayMetrics()));
+ } else {
+ // By default we use the same width as the notification shade in portrait mode (504dp).
+ return context.getResources().getDimensionPixelSize(R.dimen.large_dialog_width);
+ }
+ }
+
+ private static int getDefaultDialogHeight() {
+ return ViewGroup.LayoutParams.WRAP_CONTENT;
+ }
+
private static class DismissReceiver extends BroadcastReceiver {
private static final IntentFilter INTENT_FILTER = new IntentFilter();
static {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index cac19aa..79ee746 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -1166,7 +1166,7 @@
? com.android.settingslib.R.string.guest_reset_guest_dialog_title
: R.string.guest_exit_guest_dialog_title);
setMessage(context.getString(R.string.guest_exit_guest_dialog_message));
- setButton(DialogInterface.BUTTON_NEGATIVE,
+ setButton(DialogInterface.BUTTON_NEUTRAL,
context.getString(android.R.string.cancel), this);
setButton(DialogInterface.BUTTON_POSITIVE,
context.getString(mGuestUserAutoCreated
@@ -1185,7 +1185,7 @@
if (mFalsingManager.isFalseTap(penalty)) {
return;
}
- if (which == BUTTON_NEGATIVE) {
+ if (which == BUTTON_NEUTRAL) {
cancel();
} else {
mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE);
@@ -1203,7 +1203,7 @@
super(context);
setTitle(R.string.user_add_user_title);
setMessage(context.getString(R.string.user_add_user_message_short));
- setButton(DialogInterface.BUTTON_NEGATIVE,
+ setButton(DialogInterface.BUTTON_NEUTRAL,
context.getString(android.R.string.cancel), this);
setButton(DialogInterface.BUTTON_POSITIVE,
context.getString(android.R.string.ok), this);
@@ -1217,7 +1217,7 @@
if (mFalsingManager.isFalseTap(penalty)) {
return;
}
- if (which == BUTTON_NEGATIVE) {
+ if (which == BUTTON_NEUTRAL) {
cancel();
} else {
mDialogLaunchAnimator.dismissStack(this);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 8ea7d62..fb6861d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.tv;
import android.content.Context;
+import android.content.Intent;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -38,6 +39,10 @@
@SysUISingleton
public class TvStatusBar extends CoreStartable implements CommandQueue.Callbacks {
+ private static final String ACTION_SHOW_PIP_MENU =
+ "com.android.wm.shell.pip.tv.notification.action.SHOW_PIP_MENU";
+ private static final String SYSTEMUI_PERMISSION = "com.android.systemui.permission.SELF";
+
private final CommandQueue mCommandQueue;
private final Lazy<AssistManager> mAssistManagerLazy;
@@ -65,4 +70,9 @@
public void startAssist(Bundle args) {
mAssistManagerLazy.get().startAssist(args);
}
+
+ @Override
+ public void showPictureInPictureMenu() {
+ mContext.sendBroadcast(new Intent(ACTION_SHOW_PIP_MENU), SYSTEMUI_PERMISSION);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/ListenerSet.kt b/packages/SystemUI/src/com/android/systemui/util/ListenerSet.kt
index 0f4193e9..4f20067 100644
--- a/packages/SystemUI/src/com/android/systemui/util/ListenerSet.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/ListenerSet.kt
@@ -39,9 +39,17 @@
fun remove(element: E): Boolean = listeners.remove(element)
/**
+ * Determine if the listener set is empty
+ */
+ fun isEmpty(): Boolean = listeners.isEmpty()
+
+ /**
* Returns an iterator over the listeners currently in the set. Note that to ensure
* [ConcurrentModificationException] is never thrown, this iterator will not reflect changes
* made to the set after the iterator is constructed.
*/
override fun iterator(): Iterator<E> = listeners.iterator()
}
+
+/** Extension to match Collection which is implemented to only be (easily) accessible in kotlin */
+fun <T> ListenerSet<T>.isNotEmpty(): Boolean = !isEmpty()
diff --git a/packages/SystemUI/tests/AndroidTest.xml b/packages/SystemUI/tests/AndroidTest.xml
index fc353a1..5ffd300 100644
--- a/packages/SystemUI/tests/AndroidTest.xml
+++ b/packages/SystemUI/tests/AndroidTest.xml
@@ -18,12 +18,17 @@
<option name="test-file-name" value="SystemUITests.apk" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+ <option name="force-root" value="true" />
+ </target_preparer>
+
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="framework-base-presubmit" />
<option name="test-tag" value="SystemUITests" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.systemui.tests" />
<option name="runner" value="android.testing.TestableInstrumentation" />
+ <option name="test-filter-dir" value="/data/data/com.android.systemui.tests" />
<option name="hidden-api-checks" value="false"/>
</test>
</configuration>
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
new file mode 100644
index 0000000..cb16bec
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.flags
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.NameNotFoundException
+import android.content.res.Resources
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.nullable
+import com.android.systemui.util.mockito.withArgCaptor
+import com.android.systemui.util.settings.SecureSettings
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.Mockito.inOrder
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.MockitoAnnotations
+import java.io.PrintWriter
+import java.io.Serializable
+import java.io.StringWriter
+import java.util.function.Consumer
+import org.mockito.Mockito.`when` as whenever
+
+/**
+ * NOTE: This test is for the version of FeatureFlagManager in src-release, which should not allow
+ * overriding, and should never return any value other than the one provided as the default.
+ */
+@SmallTest
+class FeatureFlagsDebugTest : SysuiTestCase() {
+ private lateinit var mFeatureFlagsDebug: FeatureFlagsDebug
+
+ @Mock private lateinit var mFlagManager: FlagManager
+ @Mock private lateinit var mMockContext: Context
+ @Mock private lateinit var mSecureSettings: SecureSettings
+ @Mock private lateinit var mResources: Resources
+ @Mock private lateinit var mDumpManager: DumpManager
+ private val mFlagMap = mutableMapOf<Int, Flag<*>>()
+ private lateinit var mBroadcastReceiver: BroadcastReceiver
+ private lateinit var mClearCacheAction: Consumer<Int>
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ mFeatureFlagsDebug = FeatureFlagsDebug(
+ mFlagManager,
+ mMockContext,
+ mSecureSettings,
+ mResources,
+ mDumpManager,
+ { mFlagMap }
+ )
+ verify(mFlagManager).restartAction = any()
+ mBroadcastReceiver = withArgCaptor {
+ verify(mMockContext).registerReceiver(capture(), any(), nullable(), nullable())
+ }
+ mClearCacheAction = withArgCaptor {
+ verify(mFlagManager).clearCacheAction = capture()
+ }
+ whenever(mFlagManager.idToSettingsKey(any())).thenAnswer { "key-${it.arguments[0]}" }
+ }
+
+ @Test
+ fun testReadBooleanFlag() {
+ whenever(mFlagManager.readFlagValue<Boolean>(eq(3), any())).thenReturn(true)
+ whenever(mFlagManager.readFlagValue<Boolean>(eq(4), any())).thenReturn(false)
+ assertThat(mFeatureFlagsDebug.isEnabled(BooleanFlag(1, false))).isFalse()
+ assertThat(mFeatureFlagsDebug.isEnabled(BooleanFlag(2, true))).isTrue()
+ assertThat(mFeatureFlagsDebug.isEnabled(BooleanFlag(3, false))).isTrue()
+ assertThat(mFeatureFlagsDebug.isEnabled(BooleanFlag(4, true))).isFalse()
+ }
+
+ @Test
+ fun testReadResourceBooleanFlag() {
+ whenever(mResources.getBoolean(1001)).thenReturn(false)
+ whenever(mResources.getBoolean(1002)).thenReturn(true)
+ whenever(mResources.getBoolean(1003)).thenReturn(false)
+ whenever(mResources.getBoolean(1004)).thenAnswer { throw NameNotFoundException() }
+ whenever(mResources.getBoolean(1005)).thenAnswer { throw NameNotFoundException() }
+
+ whenever(mFlagManager.readFlagValue<Boolean>(eq(3), any())).thenReturn(true)
+ whenever(mFlagManager.readFlagValue<Boolean>(eq(5), any())).thenReturn(false)
+
+ assertThat(mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(1, 1001))).isFalse()
+ assertThat(mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(2, 1002))).isTrue()
+ assertThat(mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(3, 1003))).isTrue()
+
+ Assert.assertThrows(NameNotFoundException::class.java) {
+ mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(4, 1004))
+ }
+ // Test that resource is loaded (and validated) even when the setting is set.
+ // This prevents developers from not noticing when they reference an invalid resource.
+ Assert.assertThrows(NameNotFoundException::class.java) {
+ mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(5, 1005))
+ }
+ }
+
+ @Test
+ fun testReadStringFlag() {
+ whenever(mFlagManager.readFlagValue<String>(eq(3), any())).thenReturn("foo")
+ whenever(mFlagManager.readFlagValue<String>(eq(4), any())).thenReturn("bar")
+ assertThat(mFeatureFlagsDebug.getString(StringFlag(1, "biz"))).isEqualTo("biz")
+ assertThat(mFeatureFlagsDebug.getString(StringFlag(2, "baz"))).isEqualTo("baz")
+ assertThat(mFeatureFlagsDebug.getString(StringFlag(3, "buz"))).isEqualTo("foo")
+ assertThat(mFeatureFlagsDebug.getString(StringFlag(4, "buz"))).isEqualTo("bar")
+ }
+
+ @Test
+ fun testReadResourceStringFlag() {
+ whenever(mResources.getString(1001)).thenReturn("")
+ whenever(mResources.getString(1002)).thenReturn("resource2")
+ whenever(mResources.getString(1003)).thenReturn("resource3")
+ whenever(mResources.getString(1004)).thenReturn(null)
+ whenever(mResources.getString(1005)).thenAnswer { throw NameNotFoundException() }
+ whenever(mResources.getString(1006)).thenAnswer { throw NameNotFoundException() }
+
+ whenever(mFlagManager.readFlagValue<String>(eq(3), any())).thenReturn("override3")
+ whenever(mFlagManager.readFlagValue<String>(eq(4), any())).thenReturn("override4")
+ whenever(mFlagManager.readFlagValue<String>(eq(6), any())).thenReturn("override6")
+
+ assertThat(mFeatureFlagsDebug.getString(ResourceStringFlag(1, 1001))).isEqualTo("")
+ assertThat(mFeatureFlagsDebug.getString(ResourceStringFlag(2, 1002))).isEqualTo("resource2")
+ assertThat(mFeatureFlagsDebug.getString(ResourceStringFlag(3, 1003))).isEqualTo("override3")
+
+ Assert.assertThrows(NullPointerException::class.java) {
+ mFeatureFlagsDebug.getString(ResourceStringFlag(4, 1004))
+ }
+ Assert.assertThrows(NameNotFoundException::class.java) {
+ mFeatureFlagsDebug.getString(ResourceStringFlag(5, 1005))
+ }
+ // Test that resource is loaded (and validated) even when the setting is set.
+ // This prevents developers from not noticing when they reference an invalid resource.
+ Assert.assertThrows(NameNotFoundException::class.java) {
+ mFeatureFlagsDebug.getString(ResourceStringFlag(6, 1005))
+ }
+ }
+
+ @Test
+ fun testBroadcastReceiverIgnoresInvalidData() {
+ addFlag(BooleanFlag(1, false))
+ addFlag(ResourceBooleanFlag(2, 1002))
+ addFlag(StringFlag(3, "flag3"))
+ addFlag(ResourceStringFlag(4, 1004))
+
+ mBroadcastReceiver.onReceive(mMockContext, null)
+ mBroadcastReceiver.onReceive(mMockContext, Intent())
+ mBroadcastReceiver.onReceive(mMockContext, Intent("invalid action"))
+ mBroadcastReceiver.onReceive(mMockContext, Intent(FlagManager.ACTION_SET_FLAG))
+ setByBroadcast(0, false) // unknown id does nothing
+ setByBroadcast(1, "string") // wrong type does nothing
+ setByBroadcast(2, 123) // wrong type does nothing
+ setByBroadcast(3, false) // wrong type does nothing
+ setByBroadcast(4, 123) // wrong type does nothing
+ verifyNoMoreInteractions(mFlagManager, mSecureSettings)
+ }
+
+ @Test
+ fun testIntentWithIdButNoValueKeyClears() {
+ addFlag(BooleanFlag(1, false))
+
+ // trying to erase an id not in the map does noting
+ mBroadcastReceiver.onReceive(
+ mMockContext,
+ Intent(FlagManager.ACTION_SET_FLAG).putExtra(FlagManager.EXTRA_ID, 0)
+ )
+ verifyNoMoreInteractions(mFlagManager, mSecureSettings)
+
+ // valid id with no value puts empty string in the setting
+ mBroadcastReceiver.onReceive(
+ mMockContext,
+ Intent(FlagManager.ACTION_SET_FLAG).putExtra(FlagManager.EXTRA_ID, 1)
+ )
+ verifyPutData(1, "", numReads = 0)
+ }
+
+ @Test
+ fun testSetBooleanFlag() {
+ addFlag(BooleanFlag(1, false))
+ addFlag(BooleanFlag(2, false))
+ addFlag(ResourceBooleanFlag(3, 1003))
+ addFlag(ResourceBooleanFlag(4, 1004))
+
+ setByBroadcast(1, false)
+ verifyPutData(1, "{\"type\":\"boolean\",\"value\":false}")
+
+ setByBroadcast(2, true)
+ verifyPutData(2, "{\"type\":\"boolean\",\"value\":true}")
+
+ setByBroadcast(3, false)
+ verifyPutData(3, "{\"type\":\"boolean\",\"value\":false}")
+
+ setByBroadcast(4, true)
+ verifyPutData(4, "{\"type\":\"boolean\",\"value\":true}")
+ }
+
+ @Test
+ fun testSetStringFlag() {
+ addFlag(StringFlag(1, "flag1"))
+ addFlag(ResourceStringFlag(2, 1002))
+
+ setByBroadcast(1, "override1")
+ verifyPutData(1, "{\"type\":\"string\",\"value\":\"override1\"}")
+
+ setByBroadcast(2, "override2")
+ verifyPutData(2, "{\"type\":\"string\",\"value\":\"override2\"}")
+ }
+
+ @Test
+ fun testSetFlagClearsCache() {
+ val flag1 = addFlag(StringFlag(1, "flag1"))
+ whenever(mFlagManager.readFlagValue<String>(eq(1), any())).thenReturn("original")
+
+ // gets the flag & cache it
+ assertThat(mFeatureFlagsDebug.getString(flag1)).isEqualTo("original")
+ verify(mFlagManager).readFlagValue(eq(1), eq(StringFlagSerializer))
+
+ // hit the cache
+ assertThat(mFeatureFlagsDebug.getString(flag1)).isEqualTo("original")
+ verifyNoMoreInteractions(mFlagManager)
+
+ // set the flag
+ setByBroadcast(1, "new")
+ verifyPutData(1, "{\"type\":\"string\",\"value\":\"new\"}", numReads = 2)
+ whenever(mFlagManager.readFlagValue<String>(eq(1), any())).thenReturn("new")
+
+ assertThat(mFeatureFlagsDebug.getString(flag1)).isEqualTo("new")
+ verify(mFlagManager, times(3)).readFlagValue(eq(1), eq(StringFlagSerializer))
+ }
+
+ private fun verifyPutData(id: Int, data: String, numReads: Int = 1) {
+ inOrder(mFlagManager, mSecureSettings).apply {
+ verify(mFlagManager, times(numReads)).readFlagValue(eq(id), any<FlagSerializer<*>>())
+ verify(mFlagManager).idToSettingsKey(eq(id))
+ verify(mSecureSettings).putString(eq("key-$id"), eq(data))
+ verify(mFlagManager).dispatchListenersAndMaybeRestart(eq(id))
+ }.verifyNoMoreInteractions()
+ verifyNoMoreInteractions(mFlagManager, mSecureSettings)
+ }
+
+ private fun setByBroadcast(id: Int, value: Serializable?) {
+ val intent = Intent(FlagManager.ACTION_SET_FLAG)
+ intent.putExtra(FlagManager.EXTRA_ID, id)
+ intent.putExtra(FlagManager.EXTRA_VALUE, value)
+ mBroadcastReceiver.onReceive(mMockContext, intent)
+ }
+
+ private fun <F : Flag<*>> addFlag(flag: F): F {
+ val old = mFlagMap.put(flag.id, flag)
+ check(old == null) { "Flag ${flag.id} already registered" }
+ return flag
+ }
+
+ @Test
+ fun testDump() {
+ val flag1 = BooleanFlag(1, true)
+ val flag2 = ResourceBooleanFlag(2, 1002)
+ val flag3 = BooleanFlag(3, false)
+ val flag4 = StringFlag(4, "")
+ val flag5 = StringFlag(5, "flag5default")
+ val flag6 = ResourceStringFlag(6, 1006)
+ val flag7 = ResourceStringFlag(7, 1007)
+
+ whenever(mResources.getBoolean(1002)).thenReturn(true)
+ whenever(mResources.getString(1006)).thenReturn("resource1006")
+ whenever(mResources.getString(1007)).thenReturn("resource1007")
+ whenever(mFlagManager.readFlagValue(eq(7), eq(StringFlagSerializer)))
+ .thenReturn("override7")
+
+ // WHEN the flags have been accessed
+ assertThat(mFeatureFlagsDebug.isEnabled(flag1)).isTrue()
+ assertThat(mFeatureFlagsDebug.isEnabled(flag2)).isTrue()
+ assertThat(mFeatureFlagsDebug.isEnabled(flag3)).isFalse()
+ assertThat(mFeatureFlagsDebug.getString(flag4)).isEmpty()
+ assertThat(mFeatureFlagsDebug.getString(flag5)).isEqualTo("flag5default")
+ assertThat(mFeatureFlagsDebug.getString(flag6)).isEqualTo("resource1006")
+ assertThat(mFeatureFlagsDebug.getString(flag7)).isEqualTo("override7")
+
+ // THEN the dump contains the flags and the default values
+ val dump = dumpToString()
+ assertThat(dump).contains(" sysui_flag_1: true\n")
+ assertThat(dump).contains(" sysui_flag_2: true\n")
+ assertThat(dump).contains(" sysui_flag_3: false\n")
+ assertThat(dump).contains(" sysui_flag_4: [length=0] \"\"\n")
+ assertThat(dump).contains(" sysui_flag_5: [length=12] \"flag5default\"\n")
+ assertThat(dump).contains(" sysui_flag_6: [length=12] \"resource1006\"\n")
+ assertThat(dump).contains(" sysui_flag_7: [length=9] \"override7\"\n")
+ }
+
+ private fun dumpToString(): String {
+ val sw = StringWriter()
+ val pw = PrintWriter(sw)
+ mFeatureFlagsDebug.dump(mock(), pw, emptyArray<String>())
+ pw.flush()
+ return sw.toString()
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.java
deleted file mode 100644
index dea42f9..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.flags;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-import android.content.res.Resources;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dump.DumpManager;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-
-/**
- * NOTE: This test is for the version of FeatureFlagManager in src-release, which should not allow
- * overriding, and should never return any value other than the one provided as the default.
- */
-@SmallTest
-public class FeatureFlagsReleaseTest extends SysuiTestCase {
- FeatureFlagsRelease mFeatureFlagsRelease;
-
- @Mock private Resources mResources;
- @Mock private DumpManager mDumpManager;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
-
- mFeatureFlagsRelease = new FeatureFlagsRelease(mResources, mDumpManager);
- }
-
- @After
- public void onFinished() {
- // The dump manager should be registered with even for the release version, but that's it.
- verify(mDumpManager).registerDumpable(anyString(), any());
- verifyNoMoreInteractions(mDumpManager);
- }
-
- @Test
- public void testBooleanResourceFlag() {
- int flagId = 213;
- int flagResourceId = 3;
- ResourceBooleanFlag flag = new ResourceBooleanFlag(flagId, flagResourceId);
- when(mResources.getBoolean(flagResourceId)).thenReturn(true);
-
- assertThat(mFeatureFlagsRelease.isEnabled(flag)).isTrue();
- }
-
- @Test
- public void testDump() {
- int flagIdA = 213;
- int flagIdB = 18;
- int flagResourceId = 3;
- BooleanFlag flagA = new BooleanFlag(flagIdA, true);
- ResourceBooleanFlag flagB = new ResourceBooleanFlag(flagIdB, flagResourceId);
- when(mResources.getBoolean(flagResourceId)).thenReturn(true);
-
- // WHEN the flags have been accessed
- assertThat(mFeatureFlagsRelease.isEnabled(1, false)).isFalse();
- assertThat(mFeatureFlagsRelease.isEnabled(flagA)).isTrue();
- assertThat(mFeatureFlagsRelease.isEnabled(flagB)).isTrue();
-
- // THEN the dump contains the flags and the default values
- String dump = dumpToString();
- assertThat(dump).contains(" sysui_flag_1: false\n");
- assertThat(dump).contains(" sysui_flag_" + flagIdA + ": true\n");
- assertThat(dump).contains(" sysui_flag_" + flagIdB + ": true\n");
- }
-
- private String dumpToString() {
- StringWriter sw = new StringWriter();
- PrintWriter pw = new PrintWriter(sw);
- mFeatureFlagsRelease.dump(mock(FileDescriptor.class), pw, new String[0]);
- pw.flush();
- String dump = sw.toString();
- return dump;
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt
new file mode 100644
index 0000000..b5e6602
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.flags
+
+import android.content.pm.PackageManager.NameNotFoundException
+import android.content.res.Resources
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Assert.assertThrows
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.MockitoAnnotations
+import java.io.PrintWriter
+import java.io.StringWriter
+import org.mockito.Mockito.`when` as whenever
+
+/**
+ * NOTE: This test is for the version of FeatureFlagManager in src-release, which should not allow
+ * overriding, and should never return any value other than the one provided as the default.
+ */
+@SmallTest
+class FeatureFlagsReleaseTest : SysuiTestCase() {
+ private lateinit var mFeatureFlagsRelease: FeatureFlagsRelease
+
+ @Mock private lateinit var mResources: Resources
+ @Mock private lateinit var mDumpManager: DumpManager
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ mFeatureFlagsRelease = FeatureFlagsRelease(mResources, mDumpManager)
+ }
+
+ @After
+ fun onFinished() {
+ // The dump manager should be registered with even for the release version, but that's it.
+ verify(mDumpManager).registerDumpable(any(), any())
+ verifyNoMoreInteractions(mDumpManager)
+ }
+
+ @Test
+ fun testBooleanResourceFlag() {
+ val flagId = 213
+ val flagResourceId = 3
+ val flag = ResourceBooleanFlag(flagId, flagResourceId)
+ whenever(mResources.getBoolean(flagResourceId)).thenReturn(true)
+ assertThat(mFeatureFlagsRelease.isEnabled(flag)).isTrue()
+ }
+
+ @Test
+ fun testReadResourceStringFlag() {
+ whenever(mResources.getString(1001)).thenReturn("")
+ whenever(mResources.getString(1002)).thenReturn("res2")
+ whenever(mResources.getString(1003)).thenReturn(null)
+ whenever(mResources.getString(1004)).thenAnswer { throw NameNotFoundException() }
+
+ assertThat(mFeatureFlagsRelease.getString(ResourceStringFlag(1, 1001))).isEqualTo("")
+ assertThat(mFeatureFlagsRelease.getString(ResourceStringFlag(2, 1002))).isEqualTo("res2")
+
+ assertThrows(NullPointerException::class.java) {
+ mFeatureFlagsRelease.getString(ResourceStringFlag(3, 1003))
+ }
+ assertThrows(NameNotFoundException::class.java) {
+ mFeatureFlagsRelease.getString(ResourceStringFlag(4, 1004))
+ }
+ }
+
+ @Test
+ fun testDump() {
+ val flag1 = BooleanFlag(1, true)
+ val flag2 = ResourceBooleanFlag(2, 1002)
+ val flag3 = BooleanFlag(3, false)
+ val flag4 = StringFlag(4, "")
+ val flag5 = StringFlag(5, "flag5default")
+ val flag6 = ResourceStringFlag(6, 1006)
+
+ whenever(mResources.getBoolean(1002)).thenReturn(true)
+ whenever(mResources.getString(1006)).thenReturn("resource1006")
+ whenever(mResources.getString(1007)).thenReturn("resource1007")
+
+ // WHEN the flags have been accessed
+ assertThat(mFeatureFlagsRelease.isEnabled(flag1)).isTrue()
+ assertThat(mFeatureFlagsRelease.isEnabled(flag2)).isTrue()
+ assertThat(mFeatureFlagsRelease.isEnabled(flag3)).isFalse()
+ assertThat(mFeatureFlagsRelease.getString(flag4)).isEmpty()
+ assertThat(mFeatureFlagsRelease.getString(flag5)).isEqualTo("flag5default")
+ assertThat(mFeatureFlagsRelease.getString(flag6)).isEqualTo("resource1006")
+
+ // THEN the dump contains the flags and the default values
+ val dump = dumpToString()
+ assertThat(dump).contains(" sysui_flag_1: true\n")
+ assertThat(dump).contains(" sysui_flag_2: true\n")
+ assertThat(dump).contains(" sysui_flag_3: false\n")
+ assertThat(dump).contains(" sysui_flag_4: [length=0] \"\"\n")
+ assertThat(dump).contains(" sysui_flag_5: [length=12] \"flag5default\"\n")
+ assertThat(dump).contains(" sysui_flag_6: [length=12] \"resource1006\"\n")
+ }
+
+ private fun dumpToString(): String {
+ val sw = StringWriter()
+ val pw = PrintWriter(sw)
+ mFeatureFlagsRelease.dump(mock(), pw, emptyArray())
+ pw.flush()
+ return sw.toString()
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt
new file mode 100644
index 0000000..644bd21
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.flags
+
+import android.content.Context
+import android.database.ContentObserver
+import android.net.Uri
+import android.os.Handler
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.withArgCaptor
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertThrows
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.MockitoAnnotations
+import java.util.function.Consumer
+import org.mockito.Mockito.`when` as whenever
+
+/**
+ * NOTE: This test is for the version of FeatureFlagManager in src-release, which should not allow
+ * overriding, and should never return any value other than the one provided as the default.
+ */
+@SmallTest
+class FlagManagerTest : SysuiTestCase() {
+ private lateinit var mFlagManager: FlagManager
+
+ @Mock private lateinit var mMockContext: Context
+ @Mock private lateinit var mFlagSettingsHelper: FlagSettingsHelper
+ @Mock private lateinit var mHandler: Handler
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ mFlagManager = FlagManager(mMockContext, mFlagSettingsHelper, mHandler)
+ }
+
+ @Test
+ fun testContentObserverAddedAndRemoved() {
+ val listener1 = mock<FlagListenable.Listener>()
+ val listener2 = mock<FlagListenable.Listener>()
+
+ // no interactions before adding listener
+ verifyNoMoreInteractions(mFlagSettingsHelper)
+
+ // adding the first listener registers the observer
+ mFlagManager.addListener(BooleanFlag(1, true), listener1)
+ val observer = withArgCaptor<ContentObserver> {
+ verify(mFlagSettingsHelper).registerContentObserver(any(), any(), capture())
+ }
+ verifyNoMoreInteractions(mFlagSettingsHelper)
+
+ // adding another listener does nothing
+ mFlagManager.addListener(BooleanFlag(2, true), listener2)
+ verifyNoMoreInteractions(mFlagSettingsHelper)
+
+ // removing the original listener does nothing with second one still present
+ mFlagManager.removeListener(listener1)
+ verifyNoMoreInteractions(mFlagSettingsHelper)
+
+ // removing the final listener unregisters the observer
+ mFlagManager.removeListener(listener2)
+ verify(mFlagSettingsHelper).unregisterContentObserver(eq(observer))
+ verifyNoMoreInteractions(mFlagSettingsHelper)
+ }
+
+ @Test
+ fun testObserverClearsCache() {
+ val listener = mock<FlagListenable.Listener>()
+ val clearCacheAction = mock<Consumer<Int>>()
+ mFlagManager.clearCacheAction = clearCacheAction
+ mFlagManager.addListener(BooleanFlag(1, true), listener)
+ val observer = withArgCaptor<ContentObserver> {
+ verify(mFlagSettingsHelper).registerContentObserver(any(), any(), capture())
+ }
+ observer.onChange(false, flagUri(1))
+ verify(clearCacheAction).accept(eq(1))
+ }
+
+ @Test
+ fun testObserverInvokesListeners() {
+ val listener1 = mock<FlagListenable.Listener>()
+ val listener10 = mock<FlagListenable.Listener>()
+ mFlagManager.addListener(BooleanFlag(1, true), listener1)
+ mFlagManager.addListener(BooleanFlag(10, true), listener10)
+ val observer = withArgCaptor<ContentObserver> {
+ verify(mFlagSettingsHelper).registerContentObserver(any(), any(), capture())
+ }
+ observer.onChange(false, flagUri(1))
+ val flagEvent1 = withArgCaptor<FlagListenable.FlagEvent> {
+ verify(listener1).onFlagChanged(capture())
+ }
+ assertThat(flagEvent1.flagId).isEqualTo(1)
+ verifyNoMoreInteractions(listener1, listener10)
+
+ observer.onChange(false, flagUri(10))
+ val flagEvent10 = withArgCaptor<FlagListenable.FlagEvent> {
+ verify(listener10).onFlagChanged(capture())
+ }
+ assertThat(flagEvent10.flagId).isEqualTo(10)
+ verifyNoMoreInteractions(listener1, listener10)
+ }
+
+ fun flagUri(id: Int): Uri = Uri.parse("content://settings/system/systemui/flags/$id")
+
+ @Test
+ fun testOnlySpecificFlagListenerIsInvoked() {
+ val listener1 = mock<FlagListenable.Listener>()
+ val listener10 = mock<FlagListenable.Listener>()
+ mFlagManager.addListener(BooleanFlag(1, true), listener1)
+ mFlagManager.addListener(BooleanFlag(10, true), listener10)
+
+ mFlagManager.dispatchListenersAndMaybeRestart(1)
+ val flagEvent1 = withArgCaptor<FlagListenable.FlagEvent> {
+ verify(listener1).onFlagChanged(capture())
+ }
+ assertThat(flagEvent1.flagId).isEqualTo(1)
+ verifyNoMoreInteractions(listener1, listener10)
+
+ mFlagManager.dispatchListenersAndMaybeRestart(10)
+ val flagEvent10 = withArgCaptor<FlagListenable.FlagEvent> {
+ verify(listener10).onFlagChanged(capture())
+ }
+ assertThat(flagEvent10.flagId).isEqualTo(10)
+ verifyNoMoreInteractions(listener1, listener10)
+ }
+
+ @Test
+ fun testSameListenerCanBeUsedForMultipleFlags() {
+ val listener = mock<FlagListenable.Listener>()
+ mFlagManager.addListener(BooleanFlag(1, true), listener)
+ mFlagManager.addListener(BooleanFlag(10, true), listener)
+
+ mFlagManager.dispatchListenersAndMaybeRestart(1)
+ val flagEvent1 = withArgCaptor<FlagListenable.FlagEvent> {
+ verify(listener).onFlagChanged(capture())
+ }
+ assertThat(flagEvent1.flagId).isEqualTo(1)
+ verifyNoMoreInteractions(listener)
+
+ mFlagManager.dispatchListenersAndMaybeRestart(10)
+ val flagEvent10 = withArgCaptor<FlagListenable.FlagEvent> {
+ verify(listener, times(2)).onFlagChanged(capture())
+ }
+ assertThat(flagEvent10.flagId).isEqualTo(10)
+ verifyNoMoreInteractions(listener)
+ }
+
+ @Test
+ fun testRestartWithNoListeners() {
+ val restartAction = mock<Consumer<Boolean>>()
+ mFlagManager.restartAction = restartAction
+ mFlagManager.dispatchListenersAndMaybeRestart(1)
+ verify(restartAction).accept(eq(false))
+ verifyNoMoreInteractions(restartAction)
+ }
+
+ @Test
+ fun testListenerCanSuppressRestart() {
+ val restartAction = mock<Consumer<Boolean>>()
+ mFlagManager.restartAction = restartAction
+ mFlagManager.addListener(BooleanFlag(1, true)) { event ->
+ event.requestNoRestart()
+ }
+ mFlagManager.dispatchListenersAndMaybeRestart(1)
+ verify(restartAction).accept(eq(true))
+ verifyNoMoreInteractions(restartAction)
+ }
+
+ @Test
+ fun testListenerOnlySuppressesRestartForOwnFlag() {
+ val restartAction = mock<Consumer<Boolean>>()
+ mFlagManager.restartAction = restartAction
+ mFlagManager.addListener(BooleanFlag(10, true)) { event ->
+ event.requestNoRestart()
+ }
+ mFlagManager.dispatchListenersAndMaybeRestart(1)
+ verify(restartAction).accept(eq(false))
+ verifyNoMoreInteractions(restartAction)
+ }
+
+ @Test
+ fun testRestartWhenNotAllListenersRequestSuppress() {
+ val restartAction = mock<Consumer<Boolean>>()
+ mFlagManager.restartAction = restartAction
+ mFlagManager.addListener(BooleanFlag(10, true)) { event ->
+ event.requestNoRestart()
+ }
+ mFlagManager.addListener(BooleanFlag(10, true)) {
+ // do not request
+ }
+ mFlagManager.dispatchListenersAndMaybeRestart(1)
+ verify(restartAction).accept(eq(false))
+ verifyNoMoreInteractions(restartAction)
+ }
+
+ @Test
+ fun testReadBooleanFlag() {
+ // test that null string returns null
+ whenever(mFlagSettingsHelper.getString(any())).thenReturn(null)
+ assertThat(mFlagManager.readFlagValue(1, BooleanFlagSerializer)).isNull()
+
+ // test that empty string returns null
+ whenever(mFlagSettingsHelper.getString(any())).thenReturn("")
+ assertThat(mFlagManager.readFlagValue(1, BooleanFlagSerializer)).isNull()
+
+ // test false
+ whenever(mFlagSettingsHelper.getString(any()))
+ .thenReturn("{\"type\":\"boolean\",\"value\":false}")
+ assertThat(mFlagManager.readFlagValue(1, BooleanFlagSerializer)).isFalse()
+
+ // test true
+ whenever(mFlagSettingsHelper.getString(any()))
+ .thenReturn("{\"type\":\"boolean\",\"value\":true}")
+ assertThat(mFlagManager.readFlagValue(1, BooleanFlagSerializer)).isTrue()
+
+ // Reading a value of a different type should just return null
+ whenever(mFlagSettingsHelper.getString(any()))
+ .thenReturn("{\"type\":\"string\",\"value\":\"foo\"}")
+ assertThat(mFlagManager.readFlagValue(1, BooleanFlagSerializer)).isNull()
+
+ // Reading a value that isn't json should throw an exception
+ assertThrows(InvalidFlagStorageException::class.java) {
+ whenever(mFlagSettingsHelper.getString(any())).thenReturn("1")
+ mFlagManager.readFlagValue(1, BooleanFlagSerializer)
+ }
+ }
+
+ @Test
+ fun testSerializeBooleanFlag() {
+ // test false
+ assertThat(BooleanFlagSerializer.toSettingsData(false))
+ .isEqualTo("{\"type\":\"boolean\",\"value\":false}")
+
+ // test true
+ assertThat(BooleanFlagSerializer.toSettingsData(true))
+ .isEqualTo("{\"type\":\"boolean\",\"value\":true}")
+ }
+
+ @Test
+ fun testReadStringFlag() {
+ // test that null string returns null
+ whenever(mFlagSettingsHelper.getString(any())).thenReturn(null)
+ assertThat(mFlagManager.readFlagValue(1, StringFlagSerializer)).isNull()
+
+ // test that empty string returns null
+ whenever(mFlagSettingsHelper.getString(any())).thenReturn("")
+ assertThat(mFlagManager.readFlagValue(1, StringFlagSerializer)).isNull()
+
+ // test json with the empty string value returns empty string
+ whenever(mFlagSettingsHelper.getString(any()))
+ .thenReturn("{\"type\":\"string\",\"value\":\"\"}")
+ assertThat(mFlagManager.readFlagValue(1, StringFlagSerializer)).isEqualTo("")
+
+ // test string with value is returned
+ whenever(mFlagSettingsHelper.getString(any()))
+ .thenReturn("{\"type\":\"string\",\"value\":\"foo\"}")
+ assertThat(mFlagManager.readFlagValue(1, StringFlagSerializer)).isEqualTo("foo")
+
+ // Reading a value of a different type should just return null
+ whenever(mFlagSettingsHelper.getString(any()))
+ .thenReturn("{\"type\":\"boolean\",\"value\":false}")
+ assertThat(mFlagManager.readFlagValue(1, StringFlagSerializer)).isNull()
+
+ // Reading a value that isn't json should throw an exception
+ assertThrows(InvalidFlagStorageException::class.java) {
+ whenever(mFlagSettingsHelper.getString(any())).thenReturn("1")
+ mFlagManager.readFlagValue(1, StringFlagSerializer)
+ }
+ }
+
+ @Test
+ fun testSerializeStringFlag() {
+ // test empty string
+ assertThat(StringFlagSerializer.toSettingsData(""))
+ .isEqualTo("{\"type\":\"string\",\"value\":\"\"}")
+
+ // test string "foo"
+ assertThat(StringFlagSerializer.toSettingsData("foo"))
+ .isEqualTo("{\"type\":\"string\",\"value\":\"foo\"}")
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
index 6dca2a7..47f6e5c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
@@ -235,10 +235,18 @@
}
@Test
- public void getSubtitleText_withAirplaneModeOn_returnNull() {
+ public void getSubtitleText_withApmOnAndWifiOff_returnWifiIsOff() {
fakeAirplaneModeEnabled(true);
+ when(mWifiManager.isWifiEnabled()).thenReturn(false);
- assertThat(mInternetDialogController.getSubtitleText(false)).isNull();
+ assertThat(mInternetDialogController.getSubtitleText(false))
+ .isEqualTo(getResourcesString("wifi_is_off"));
+
+ // if the Wi-Fi disallow config, then don't return Wi-Fi related string.
+ mInternetDialogController.mCanConfigWifi = false;
+
+ assertThat(mInternetDialogController.getSubtitleText(false))
+ .isNotEqualTo(getResourcesString("wifi_is_off"));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index f62de51..dc83c0d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -75,6 +75,7 @@
import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment;
+import com.android.systemui.statusbar.notification.collection.NotifLiveDataStoreMocksKt;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
@@ -201,6 +202,7 @@
mLeakDetector,
mock(ForegroundServiceDismissalFeatureController.class),
mock(IStatusBarService.class),
+ NotifLiveDataStoreMocksKt.createNotifLiveDataStoreImplMock(),
mock(DumpManager.class)
);
mEntryManager.initialize(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/GroupEntryBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/GroupEntryBuilder.java
index 2971c05..b45d78d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/GroupEntryBuilder.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/GroupEntryBuilder.java
@@ -18,6 +18,8 @@
import androidx.annotation.Nullable;
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection;
+
import java.util.ArrayList;
import java.util.List;
@@ -28,6 +30,7 @@
private String mKey = "test_group_key";
private long mCreationTime = 0;
@Nullable private GroupEntry mParent = GroupEntry.ROOT_ENTRY;
+ private NotifSection mNotifSection;
private NotificationEntry mSummary = null;
private List<NotificationEntry> mChildren = new ArrayList<>();
@@ -35,6 +38,7 @@
public GroupEntry build() {
GroupEntry ge = new GroupEntry(mKey, mCreationTime);
ge.setParent(mParent);
+ ge.getAttachState().setSection(mNotifSection);
ge.setSummary(mSummary);
mSummary.setParent(ge);
@@ -61,6 +65,11 @@
return this;
}
+ public GroupEntryBuilder setSection(@Nullable NotifSection section) {
+ mNotifSection = section;
+ return this;
+ }
+
public GroupEntryBuilder setSummary(
NotificationEntry summary) {
mSummary = summary;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataImplTest.kt
new file mode 100644
index 0000000..892575a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataImplTest.kt
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.lifecycle.Observer
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.inOrder
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class NotifLiveDataImplTest : SysuiTestCase() {
+
+ private val executor = FakeExecutor(FakeSystemClock())
+ private val liveDataImpl: NotifLiveDataImpl<Int> = NotifLiveDataImpl("tst", 9, executor)
+ private val syncObserver: Observer<Int> = mock()
+ private val asyncObserver: Observer<Int> = mock()
+
+ @Before
+ fun setup() {
+ allowTestableLooperAsMainThread()
+ liveDataImpl.addSyncObserver(syncObserver)
+ liveDataImpl.addAsyncObserver(asyncObserver)
+ }
+
+ @Test
+ fun testGetInitialValue() {
+ assertThat(liveDataImpl.value).isEqualTo(9)
+ }
+
+ @Test
+ fun testGetModifiedValue() {
+ liveDataImpl.value = 13
+ assertThat(liveDataImpl.value).isEqualTo(13)
+ }
+
+ @Test
+ fun testGetsModifiedValueFromWithinSyncObserver() {
+ liveDataImpl.addSyncObserver { intVal ->
+ assertThat(intVal).isEqualTo(13)
+ assertThat(liveDataImpl.value).isEqualTo(13)
+ }
+ liveDataImpl.value = 13
+ }
+
+ @Test
+ fun testDoesNotAlertsRemovedObservers() {
+ liveDataImpl.removeObserver(syncObserver)
+ liveDataImpl.removeObserver(asyncObserver)
+
+ liveDataImpl.value = 13
+
+ // There should be no runnables on the executor
+ assertThat(executor.runAllReady()).isEqualTo(0)
+
+ // And observers should not be called
+ verifyNoMoreInteractions(syncObserver, asyncObserver)
+ }
+
+ @Test
+ fun testDoesNotAsyncObserversRemovedSinceChange() {
+ liveDataImpl.value = 13
+ liveDataImpl.removeObserver(asyncObserver)
+
+ // There should be a runnable that will get executed...
+ assertThat(executor.runAllReady()).isEqualTo(1)
+
+ // ...but async observers should not be called
+ verifyNoMoreInteractions(asyncObserver)
+ }
+
+ @Test
+ fun testAlertsObservers() {
+ liveDataImpl.value = 13
+
+ // Verify that the synchronous observer is called immediately
+ verify(syncObserver).onChanged(eq(13))
+ verifyNoMoreInteractions(syncObserver, asyncObserver)
+
+ // Verify that the asynchronous observer is called when the executor runs
+ assertThat(executor.runAllReady()).isEqualTo(1)
+ verify(asyncObserver).onChanged(eq(13))
+ verifyNoMoreInteractions(syncObserver, asyncObserver)
+ }
+
+ @Test
+ fun testAlertsObserversFromDispatcher() {
+ // GIVEN that we use setValueAndProvideDispatcher()
+ val dispatcher = liveDataImpl.setValueAndProvideDispatcher(13)
+
+ // VERIFY that nothing is done before the dispatcher is called
+ assertThat(executor.numPending()).isEqualTo(0)
+ verifyNoMoreInteractions(syncObserver, asyncObserver)
+
+ // WHEN the dispatcher is invoked...
+ dispatcher.invoke()
+
+ // Verify that the synchronous observer is called immediately
+ verify(syncObserver).onChanged(eq(13))
+ verifyNoMoreInteractions(syncObserver, asyncObserver)
+
+ // Verify that the asynchronous observer is called when the executor runs
+ assertThat(executor.runAllReady()).isEqualTo(1)
+ verify(asyncObserver).onChanged(eq(13))
+ verifyNoMoreInteractions(syncObserver, asyncObserver)
+ }
+
+ @Test
+ fun testSkipsAllObserversIfValueDidNotChange() {
+ liveDataImpl.value = 9
+ // Does not add a runnable
+ assertThat(executor.runAllReady()).isEqualTo(0)
+ // Setting the current value does not call synchronous observers
+ verifyNoMoreInteractions(syncObserver, asyncObserver)
+ }
+
+ @Test
+ fun testSkipsAsyncObserversWhenValueTogglesBack() {
+ liveDataImpl.value = 13
+ liveDataImpl.value = 11
+ liveDataImpl.value = 9
+
+ // Synchronous observers will receive every change event immediately
+ inOrder(syncObserver).apply {
+ verify(syncObserver).onChanged(eq(13))
+ verify(syncObserver).onChanged(eq(11))
+ verify(syncObserver).onChanged(eq(9))
+ }
+ verifyNoMoreInteractions(syncObserver, asyncObserver)
+
+ // Running the first runnable on the queue will just emit the most recent value
+ assertThat(executor.runNextReady()).isTrue()
+ verify(asyncObserver).onChanged(eq(9))
+ verifyNoMoreInteractions(syncObserver, asyncObserver)
+
+ // Running the next 2 runnable will have no effect
+ assertThat(executor.runAllReady()).isEqualTo(2)
+ verifyNoMoreInteractions(syncObserver, asyncObserver)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreImplTest.kt
new file mode 100644
index 0000000..9c8ac5c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreImplTest.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.lang.UnsupportedOperationException
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class NotifLiveDataStoreImplTest : SysuiTestCase() {
+
+ private val executor = FakeExecutor(FakeSystemClock())
+ private val liveDataStoreImpl = NotifLiveDataStoreImpl(executor)
+
+ @Before
+ fun setup() {
+ allowTestableLooperAsMainThread()
+ }
+
+ @Test
+ fun testAllObserversSeeConsistentValues() {
+ val entry1 = NotificationEntryBuilder().setId(1).build()
+ val entry2 = NotificationEntryBuilder().setId(2).build()
+ val observer: (Any) -> Unit = {
+ assertThat(liveDataStoreImpl.hasActiveNotifs.value).isEqualTo(true)
+ assertThat(liveDataStoreImpl.activeNotifCount.value).isEqualTo(2)
+ assertThat(liveDataStoreImpl.activeNotifList.value).isEqualTo(listOf(entry1, entry2))
+ }
+ liveDataStoreImpl.hasActiveNotifs.addSyncObserver(observer)
+ liveDataStoreImpl.hasActiveNotifs.addAsyncObserver(observer)
+ liveDataStoreImpl.activeNotifCount.addSyncObserver(observer)
+ liveDataStoreImpl.activeNotifCount.addAsyncObserver(observer)
+ liveDataStoreImpl.activeNotifList.addSyncObserver(observer)
+ liveDataStoreImpl.activeNotifList.addAsyncObserver(observer)
+ liveDataStoreImpl.setActiveNotifList(listOf(entry1, entry2))
+ executor.runAllReady()
+ }
+
+ @Test
+ fun testOriginalListIsCopied() {
+ val entry1 = NotificationEntryBuilder().setId(1).build()
+ val entry2 = NotificationEntryBuilder().setId(2).build()
+ val mutableInputList = mutableListOf(entry1, entry2)
+ val observer: (Any) -> Unit = {
+ mutableInputList.clear()
+ assertThat(liveDataStoreImpl.hasActiveNotifs.value).isEqualTo(true)
+ assertThat(liveDataStoreImpl.activeNotifCount.value).isEqualTo(2)
+ assertThat(liveDataStoreImpl.activeNotifList.value).isEqualTo(listOf(entry1, entry2))
+ }
+ liveDataStoreImpl.hasActiveNotifs.addSyncObserver(observer)
+ liveDataStoreImpl.hasActiveNotifs.addAsyncObserver(observer)
+ liveDataStoreImpl.activeNotifCount.addSyncObserver(observer)
+ liveDataStoreImpl.activeNotifCount.addAsyncObserver(observer)
+ liveDataStoreImpl.activeNotifList.addSyncObserver(observer)
+ liveDataStoreImpl.activeNotifList.addAsyncObserver(observer)
+ liveDataStoreImpl.setActiveNotifList(mutableInputList)
+ executor.runAllReady()
+ }
+
+ @Test
+ fun testProvidedListIsUnmodifiable() {
+ val entry1 = NotificationEntryBuilder().setId(1).build()
+ val entry2 = NotificationEntryBuilder().setId(2).build()
+ val observer: (List<NotificationEntry>) -> Unit = { providedValue ->
+ val provided = providedValue as MutableList<NotificationEntry>
+ Assert.assertThrows(UnsupportedOperationException::class.java) {
+ provided.clear()
+ }
+ val current = liveDataStoreImpl.activeNotifList.value as MutableList<NotificationEntry>
+ Assert.assertThrows(UnsupportedOperationException::class.java) {
+ current.clear()
+ }
+ assertThat(liveDataStoreImpl.activeNotifList.value).isEqualTo(listOf(entry1, entry2))
+ }
+ liveDataStoreImpl.activeNotifList.addSyncObserver(observer)
+ liveDataStoreImpl.activeNotifList.addAsyncObserver(observer)
+ liveDataStoreImpl.setActiveNotifList(mutableListOf(entry1, entry2))
+ executor.runAllReady()
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreMocks.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreMocks.kt
new file mode 100644
index 0000000..6e81c69
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreMocks.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection
+
+import com.android.systemui.util.mockito.mock
+import org.mockito.Mockito.`when` as whenever
+
+/** Creates a mock which returns mocks for the NotifLiveDataImpl fields. */
+fun createNotifLiveDataStoreImplMock(): NotifLiveDataStoreImpl {
+ val dataStoreImpl: NotifLiveDataStoreImpl = mock()
+ whenever(dataStoreImpl.hasActiveNotifs).thenReturn(mock())
+ whenever(dataStoreImpl.activeNotifCount).thenReturn(mock())
+ whenever(dataStoreImpl.activeNotifList).thenReturn(mock())
+ return dataStoreImpl
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifPipelineTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifPipelineTest.kt
deleted file mode 100644
index 287f50c..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifPipelineTest.kt
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2019 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.statusbar.notification.collection
-
-import android.testing.AndroidTestingRunner
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.statusbar.notification.collection.render.RenderStageManager
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
-import org.mockito.Mockito.`when` as whenever
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-class NotifPipelineTest : SysuiTestCase() {
-
- @Mock private lateinit var notifCollection: NotifCollection
- @Mock private lateinit var shadeListBuilder: ShadeListBuilder
- @Mock private lateinit var renderStageManager: RenderStageManager
- private lateinit var notifPipeline: NotifPipeline
-
- @Before
- fun setup() {
- MockitoAnnotations.initMocks(this)
- notifPipeline = NotifPipeline(notifCollection, shadeListBuilder, renderStageManager)
- whenever(shadeListBuilder.shadeList).thenReturn(listOf(
- NotificationEntryBuilder().setPkg("foo").setId(1).build(),
- NotificationEntryBuilder().setPkg("foo").setId(2).build(),
- group(
- NotificationEntryBuilder().setPkg("bar").setId(1).build(),
- NotificationEntryBuilder().setPkg("bar").setId(2).build(),
- NotificationEntryBuilder().setPkg("bar").setId(3).build(),
- NotificationEntryBuilder().setPkg("bar").setId(4).build()
- ),
- NotificationEntryBuilder().setPkg("baz").setId(1).build()
- ))
- }
-
- private fun group(summary: NotificationEntry, vararg children: NotificationEntry): GroupEntry {
- return GroupEntry(summary.key, summary.creationTime).also { group ->
- group.summary = summary
- for (it in children) {
- group.addChild(it)
- }
- }
- }
-
- @Test
- fun testGetShadeListCount() {
- assertThat(notifPipeline.getShadeListCount()).isEqualTo(7)
- }
-
- @Test
- fun testGetFlatShadeList() {
- assertThat(notifPipeline.getFlatShadeList().map { it.key }).containsExactly(
- "0|foo|1|null|0",
- "0|foo|2|null|0",
- "0|bar|1|null|0",
- "0|bar|2|null|0",
- "0|bar|3|null|0",
- "0|bar|4|null|0",
- "0|baz|1|null|0"
- ).inOrder()
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinatorTest.kt
new file mode 100644
index 0000000..59fc591
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinatorTest.kt
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.notification.collection.coordinator
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
+import com.android.systemui.statusbar.notification.collection.NotifLiveDataStoreImpl
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderListListener
+import com.android.systemui.statusbar.notification.collection.render.NotifStackController
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.withArgCaptor
+import com.google.common.truth.Truth.assertThat
+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.Mockito.verifyNoMoreInteractions
+import org.mockito.MockitoAnnotations.initMocks
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class DataStoreCoordinatorTest : SysuiTestCase() {
+ private lateinit var coordinator: DataStoreCoordinator
+ private lateinit var afterRenderListListener: OnAfterRenderListListener
+
+ private lateinit var entry: NotificationEntry
+
+ @Mock private lateinit var pipeline: NotifPipeline
+ @Mock private lateinit var notifLiveDataStoreImpl: NotifLiveDataStoreImpl
+ @Mock private lateinit var stackController: NotifStackController
+ @Mock private lateinit var section: NotifSection
+
+ @Before
+ fun setUp() {
+ initMocks(this)
+ entry = NotificationEntryBuilder().setSection(section).build()
+ coordinator = DataStoreCoordinator(notifLiveDataStoreImpl)
+ coordinator.attach(pipeline)
+ afterRenderListListener = withArgCaptor {
+ verify(pipeline).addOnAfterRenderListListener(capture())
+ }
+ }
+
+ @Test
+ fun testUpdateDataStore_withOneEntry() {
+ afterRenderListListener.onAfterRenderList(listOf(entry), stackController)
+ verify(notifLiveDataStoreImpl).setActiveNotifList(eq(listOf(entry)))
+ verifyNoMoreInteractions(notifLiveDataStoreImpl)
+ }
+
+ @Test
+ fun testUpdateDataStore_withGroups() {
+ afterRenderListListener.onAfterRenderList(
+ listOf(
+ notificationEntry("foo", 1),
+ notificationEntry("foo", 2),
+ GroupEntryBuilder().setSummary(
+ notificationEntry("bar", 1)
+ ).setChildren(
+ listOf(
+ notificationEntry("bar", 2),
+ notificationEntry("bar", 3),
+ notificationEntry("bar", 4)
+ )
+ ).setSection(section).build(),
+ notificationEntry("baz", 1)
+ ),
+ stackController
+ )
+ val list: List<NotificationEntry> = withArgCaptor {
+ verify(notifLiveDataStoreImpl).setActiveNotifList(capture())
+ }
+ assertThat(list.map { it.key }).containsExactly(
+ "0|foo|1|null|0",
+ "0|foo|2|null|0",
+ "0|bar|1|null|0",
+ "0|bar|2|null|0",
+ "0|bar|3|null|0",
+ "0|bar|4|null|0",
+ "0|baz|1|null|0"
+ ).inOrder()
+ verifyNoMoreInteractions(notifLiveDataStoreImpl)
+ }
+
+ private fun notificationEntry(pkg: String, id: Int) =
+ NotificationEntryBuilder().setPkg(pkg).setId(id).setSection(section).build()
+
+ @Test
+ fun testUpdateDataStore_withZeroEntries_whenNewPipelineEnabled() {
+ afterRenderListListener.onAfterRenderList(listOf(), stackController)
+ verify(notifLiveDataStoreImpl).setActiveNotifList(eq(listOf()))
+ verifyNoMoreInteractions(notifLiveDataStoreImpl)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerLegacyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerLegacyTest.java
index 395aec3..2dfb9fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerLegacyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerLegacyTest.java
@@ -46,6 +46,8 @@
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotifLiveData;
+import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
@@ -65,6 +67,7 @@
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;
@@ -82,6 +85,8 @@
// Dependency mocks:
@Mock private NotifPipelineFlags mNotifPipelineFlags;
+ @Mock private NotifLiveDataStore mNotifLiveDataStore;
+ @Mock private NotifLiveData<List<NotificationEntry>> mActiveNotifList;
@Mock private NotificationVisibilityProvider mVisibilityProvider;
@Mock private NotificationEntryManager mEntryManager;
@Mock private NotifPipeline mNotifPipeline;
@@ -97,6 +102,7 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ when(mNotifLiveDataStore.getActiveNotifList()).thenReturn(mActiveNotifList);
mEntry = new NotificationEntryBuilder()
.setPkg(TEST_PACKAGE_NAME)
@@ -112,6 +118,7 @@
mListener,
mUiBgExecutor,
mNotifPipelineFlags,
+ mNotifLiveDataStore,
mVisibilityProvider,
mEntryManager,
mNotifPipeline,
@@ -145,7 +152,7 @@
any(NotificationVisibility[].class));
when(mListContainer.isInVisibleLocation(any())).thenReturn(true);
- when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(mEntry));
+ when(mActiveNotifList.getValue()).thenReturn(Lists.newArrayList(mEntry));
mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged();
TestableLooper.get(this).processAllMessages();
mUiBgExecutor.runAllReady();
@@ -167,7 +174,7 @@
public void testStoppingNotificationLoggingReportsCurrentNotifications()
throws Exception {
when(mListContainer.isInVisibleLocation(any())).thenReturn(true);
- when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(mEntry));
+ when(mActiveNotifList.getValue()).thenReturn(Lists.newArrayList(mEntry));
mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged();
TestableLooper.get(this).processAllMessages();
mUiBgExecutor.runAllReady();
@@ -196,7 +203,7 @@
@Test
public void testLogPanelShownOnWake() {
- when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(mEntry));
+ when(mActiveNotifList.getValue()).thenReturn(Lists.newArrayList(mEntry));
setStateAsleep();
mLogger.onDozingChanged(false); // Wake to lockscreen
assertEquals(1, mNotificationPanelLoggerFake.getCalls().size());
@@ -212,7 +219,7 @@
@Test
public void testLogPanelShownOnShadePull() {
- when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(mEntry));
+ when(mActiveNotifList.getValue()).thenReturn(Lists.newArrayList(mEntry));
setStateAwake();
// Now expand panel
mLogger.onPanelExpandedChanged(true);
@@ -240,7 +247,7 @@
.build();
entry.setRow(mRow);
- when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(entry));
+ when(mActiveNotifList.getValue()).thenReturn(Lists.newArrayList(entry));
setStateAsleep();
mLogger.onDozingChanged(false); // Wake to lockscreen
assertEquals(1, mNotificationPanelLoggerFake.getCalls().size());
@@ -254,6 +261,7 @@
TestableNotificationLogger(NotificationListener notificationListener,
Executor uiBgExecutor,
NotifPipelineFlags notifPipelineFlags,
+ NotifLiveDataStore notifLiveDataStore,
NotificationVisibilityProvider visibilityProvider,
NotificationEntryManager entryManager,
NotifPipeline notifPipeline,
@@ -264,6 +272,7 @@
notificationListener,
uiBgExecutor,
notifPipelineFlags,
+ notifLiveDataStore,
visibilityProvider,
entryManager,
notifPipeline,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
index 3a9b297..4d861f9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
@@ -46,6 +46,8 @@
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotifLiveData;
+import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
@@ -65,6 +67,7 @@
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;
@@ -82,6 +85,8 @@
// Dependency mocks:
@Mock private NotifPipelineFlags mNotifPipelineFlags;
+ @Mock private NotifLiveDataStore mNotifLiveDataStore;
+ @Mock private NotifLiveData<List<NotificationEntry>> mActiveNotifEntries;
@Mock private NotificationVisibilityProvider mVisibilityProvider;
@Mock private NotificationEntryManager mEntryManager;
@Mock private NotifPipeline mNotifPipeline;
@@ -98,6 +103,7 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mNotifPipelineFlags.isNewPipelineEnabled()).thenReturn(true);
+ when(mNotifLiveDataStore.getActiveNotifList()).thenReturn(mActiveNotifEntries);
mEntry = new NotificationEntryBuilder()
.setPkg(TEST_PACKAGE_NAME)
@@ -113,6 +119,7 @@
mListener,
mUiBgExecutor,
mNotifPipelineFlags,
+ mNotifLiveDataStore,
mVisibilityProvider,
mEntryManager,
mNotifPipeline,
@@ -146,7 +153,7 @@
any(NotificationVisibility[].class));
when(mListContainer.isInVisibleLocation(any())).thenReturn(true);
- when(mNotifPipeline.getFlatShadeList()).thenReturn(Lists.newArrayList(mEntry));
+ when(mActiveNotifEntries.getValue()).thenReturn(Lists.newArrayList(mEntry));
mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged();
TestableLooper.get(this).processAllMessages();
mUiBgExecutor.runAllReady();
@@ -168,7 +175,7 @@
public void testStoppingNotificationLoggingReportsCurrentNotifications()
throws Exception {
when(mListContainer.isInVisibleLocation(any())).thenReturn(true);
- when(mNotifPipeline.getFlatShadeList()).thenReturn(Lists.newArrayList(mEntry));
+ when(mActiveNotifEntries.getValue()).thenReturn(Lists.newArrayList(mEntry));
mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged();
TestableLooper.get(this).processAllMessages();
mUiBgExecutor.runAllReady();
@@ -197,7 +204,7 @@
@Test
public void testLogPanelShownOnWake() {
- when(mNotifPipeline.getFlatShadeList()).thenReturn(Lists.newArrayList(mEntry));
+ when(mActiveNotifEntries.getValue()).thenReturn(Lists.newArrayList(mEntry));
setStateAsleep();
mLogger.onDozingChanged(false); // Wake to lockscreen
assertEquals(1, mNotificationPanelLoggerFake.getCalls().size());
@@ -213,7 +220,7 @@
@Test
public void testLogPanelShownOnShadePull() {
- when(mNotifPipeline.getFlatShadeList()).thenReturn(Lists.newArrayList(mEntry));
+ when(mActiveNotifEntries.getValue()).thenReturn(Lists.newArrayList(mEntry));
setStateAwake();
// Now expand panel
mLogger.onPanelExpandedChanged(true);
@@ -241,7 +248,7 @@
.build();
entry.setRow(mRow);
- when(mNotifPipeline.getFlatShadeList()).thenReturn(Lists.newArrayList(entry));
+ when(mActiveNotifEntries.getValue()).thenReturn(Lists.newArrayList(entry));
setStateAsleep();
mLogger.onDozingChanged(false); // Wake to lockscreen
assertEquals(1, mNotificationPanelLoggerFake.getCalls().size());
@@ -255,6 +262,7 @@
TestableNotificationLogger(NotificationListener notificationListener,
Executor uiBgExecutor,
NotifPipelineFlags notifPipelineFlags,
+ NotifLiveDataStore notifLiveDataStore,
NotificationVisibilityProvider visibilityProvider,
NotificationEntryManager entryManager,
NotifPipeline notifPipeline,
@@ -265,6 +273,7 @@
notificationListener,
uiBgExecutor,
notifPipelineFlags,
+ notifLiveDataStore,
visibilityProvider,
entryManager,
notifPipeline,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
index eeda9dd..a890414 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
@@ -68,6 +68,7 @@
import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger;
import com.android.systemui.statusbar.notification.NotificationFilter;
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
+import com.android.systemui.statusbar.notification.collection.NotifLiveDataStoreMocksKt;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
@@ -191,6 +192,7 @@
mLeakDetector,
mock(ForegroundServiceDismissalFeatureController.class),
mock(IStatusBarService.class),
+ NotifLiveDataStoreMocksKt.createNotifLiveDataStoreImplMock(),
mock(DumpManager.class)
);
mEntryManager.initialize(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 07debe6..c3349f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -381,16 +381,15 @@
}
@Test
- public void onUdfpsConsecutivelyFailedThreeTimes_showBouncer() {
+ public void onUdfpsConsecutivelyFailedTwoTimes_showBouncer() {
// GIVEN UDFPS is supported
when(mUpdateMonitor.isUdfpsSupported()).thenReturn(true);
- // WHEN udfps fails twice - then don't show the bouncer
- mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
+ // WHEN udfps fails once - then don't show the bouncer
mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean());
- // WHEN udfps fails the third time
+ // WHEN udfps fails the second time
mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
// THEN show the bouncer
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java
index e386263..9664035 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java
@@ -16,14 +16,12 @@
package com.android.systemui.statusbar.phone;
-import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL;
import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
import static android.view.WindowInsetsController.BEHAVIOR_DEFAULT;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -34,13 +32,13 @@
import android.view.ViewPropertyAnimator;
import android.view.WindowManager;
+import androidx.lifecycle.Observer;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.notification.NotificationEntryListener;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotifLiveData;
+import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
import org.junit.Before;
import org.junit.Test;
@@ -59,18 +57,19 @@
private static final int LIGHTS_ON = 0;
private static final int LIGHTS_OUT = APPEARANCE_LOW_PROFILE_BARS;
- @Mock private NotificationEntryManager mEntryManager;
+ @Mock private NotifLiveData<Boolean> mHasActiveNotifs;
+ @Mock private NotifLiveDataStore mNotifLiveDataStore;
@Mock private CommandQueue mCommandQueue;
@Mock private WindowManager mWindowManager;
@Mock private Display mDisplay;
- @Captor private ArgumentCaptor<NotificationEntryListener> mListenerCaptor;
+ @Captor private ArgumentCaptor<Observer<Boolean>> mObserverCaptor;
@Captor private ArgumentCaptor<CommandQueue.Callbacks> mCallbacksCaptor;
private View mLightsOutView;
private LightsOutNotifController mLightsOutNotifController;
private int mDisplayId;
- private NotificationEntryListener mEntryListener;
+ private Observer<Boolean> mHaActiveNotifsObserver;
private CommandQueue.Callbacks mCallbacks;
@Before
@@ -80,15 +79,20 @@
mLightsOutView = new View(mContext);
when(mWindowManager.getDefaultDisplay()).thenReturn(mDisplay);
when(mDisplay.getDisplayId()).thenReturn(mDisplayId);
+ when(mNotifLiveDataStore.getHasActiveNotifs()).thenReturn(mHasActiveNotifs);
+ when(mHasActiveNotifs.getValue()).thenReturn(false);
mLightsOutNotifController = new LightsOutNotifController(
- mLightsOutView, mWindowManager, mEntryManager, mCommandQueue);
+ mLightsOutView,
+ mWindowManager,
+ mNotifLiveDataStore,
+ mCommandQueue);
mLightsOutNotifController.init();
mLightsOutNotifController.onViewAttached();
// Capture the entry listener object so we can simulate events in tests below
- verify(mEntryManager).addNotificationEntryListener(mListenerCaptor.capture());
- mEntryListener = Objects.requireNonNull(mListenerCaptor.getValue());
+ verify(mHasActiveNotifs).addSyncObserver(mObserverCaptor.capture());
+ mHaActiveNotifsObserver = Objects.requireNonNull(mObserverCaptor.getValue());
// Capture the callback object so we can simulate callback events in tests below
verify(mCommandQueue).addCallback(mCallbacksCaptor.capture());
@@ -138,7 +142,7 @@
@Test
public void testLightsOut_withNotifs_onSystemBarAttributesChanged() {
// GIVEN active visible notifications
- when(mEntryManager.hasActiveNotifications()).thenReturn(true);
+ when(mHasActiveNotifs.getValue()).thenReturn(true);
// WHEN lights out
mCallbacks.onSystemBarAttributesChanged(
@@ -158,7 +162,7 @@
@Test
public void testLightsOut_withoutNotifs_onSystemBarAttributesChanged() {
// GIVEN no active visible notifications
- when(mEntryManager.hasActiveNotifications()).thenReturn(false);
+ when(mHasActiveNotifs.getValue()).thenReturn(false);
// WHEN lights out
mCallbacks.onSystemBarAttributesChanged(
@@ -178,7 +182,7 @@
@Test
public void testLightsOn_afterLightsOut_onSystemBarAttributesChanged() {
// GIVEN active visible notifications
- when(mEntryManager.hasActiveNotifications()).thenReturn(true);
+ when(mHasActiveNotifs.getValue()).thenReturn(true);
// WHEN lights on
mCallbacks.onSystemBarAttributesChanged(
@@ -198,15 +202,15 @@
@Test
public void testEntryAdded() {
// GIVEN no visible notifications and lights out
- when(mEntryManager.hasActiveNotifications()).thenReturn(false);
+ when(mHasActiveNotifs.getValue()).thenReturn(false);
mLightsOutNotifController.mAppearance = LIGHTS_OUT;
mLightsOutNotifController.updateLightsOutView();
assertIsShowingDot(false);
// WHEN an active notification is added
- when(mEntryManager.hasActiveNotifications()).thenReturn(true);
+ when(mHasActiveNotifs.getValue()).thenReturn(true);
assertTrue(mLightsOutNotifController.shouldShowDot());
- mEntryListener.onNotificationAdded(mock(NotificationEntry.class));
+ mHaActiveNotifsObserver.onChanged(true);
// THEN we should see the dot view
assertIsShowingDot(true);
@@ -215,38 +219,20 @@
@Test
public void testEntryRemoved() {
// GIVEN a visible notification and lights out
- when(mEntryManager.hasActiveNotifications()).thenReturn(true);
+ when(mHasActiveNotifs.getValue()).thenReturn(true);
mLightsOutNotifController.mAppearance = LIGHTS_OUT;
mLightsOutNotifController.updateLightsOutView();
assertIsShowingDot(true);
// WHEN all active notifications are removed
- when(mEntryManager.hasActiveNotifications()).thenReturn(false);
+ when(mHasActiveNotifs.getValue()).thenReturn(false);
assertFalse(mLightsOutNotifController.shouldShowDot());
- mEntryListener.onEntryRemoved(
- mock(NotificationEntry.class), null, false, REASON_CANCEL_ALL);
+ mHaActiveNotifsObserver.onChanged(false);
// THEN we shouldn't see the dot view
assertIsShowingDot(false);
}
- @Test
- public void testEntryUpdated() {
- // GIVEN no visible notifications and lights out
- when(mEntryManager.hasActiveNotifications()).thenReturn(false);
- mLightsOutNotifController.mAppearance = LIGHTS_OUT;
- mLightsOutNotifController.updateLightsOutView();
- assertIsShowingDot(false);
-
- // WHEN an active notification is added
- when(mEntryManager.hasActiveNotifications()).thenReturn(true);
- assertTrue(mLightsOutNotifController.shouldShowDot());
- mEntryListener.onPostEntryUpdated(mock(NotificationEntry.class));
-
- // THEN we should see the dot view
- assertIsShowingDot(true);
- }
-
private void assertIsShowingDot(boolean isShowing) {
// cancel the animation so we can check the end state
final ViewPropertyAnimator animation = mLightsOutView.animate();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index 7d266e9..235de1e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -26,6 +26,7 @@
import androidx.test.platform.app.InstrumentationRegistry
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.unfold.SysUIUnfoldComponent
import com.android.systemui.unfold.config.UnfoldTransitionConfig
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
@@ -57,6 +58,8 @@
private lateinit var sysuiUnfoldComponent: SysUIUnfoldComponent
@Mock
private lateinit var progressProvider: ScopedUnfoldTransitionProgressProvider
+ @Mock
+ private lateinit var configurationController: ConfigurationController
private lateinit var view: PhoneStatusBarView
private lateinit var controller: PhoneStatusBarViewController
@@ -116,7 +119,8 @@
private fun createController(view: PhoneStatusBarView): PhoneStatusBarViewController {
return PhoneStatusBarViewController.Factory(
Optional.of(sysuiUnfoldComponent),
- Optional.of(progressProvider)
+ Optional.of(progressProvider),
+ configurationController
).create(view, touchEventHandler)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 0289b9a..9d5b17e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -119,6 +119,7 @@
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationFilter;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
+import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
@@ -274,6 +275,7 @@
@Mock private OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
@Mock private ActivityLaunchAnimator mActivityLaunchAnimator;
@Mock private NotifPipelineFlags mNotifPipelineFlags;
+ @Mock private NotifLiveDataStore mNotifLiveDataStore;
private ShadeController mShadeController;
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
private FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock);
@@ -306,6 +308,7 @@
mNotificationListener,
mUiBgExecutor,
mNotifPipelineFlags,
+ mNotifLiveDataStore,
mVisibilityProvider,
mock(NotificationEntryManager.class),
mock(NotifPipeline.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
index a1d9a7b..be1720d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
@@ -18,7 +18,9 @@
import android.hardware.devicestate.DeviceStateManager
import android.hardware.devicestate.DeviceStateManager.FoldStateListener
+import android.os.Handler
import android.testing.AndroidTestingRunner
+import androidx.core.util.Consumer
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.unfold.updates.hinge.HingeAngleProvider
@@ -31,9 +33,12 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
+import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
+import java.lang.Exception
@RunWith(AndroidTestingRunner::class)
@SmallTest
@@ -48,16 +53,28 @@
@Mock
private lateinit var deviceStateManager: DeviceStateManager
- private lateinit var foldStateProvider: FoldStateProvider
+ @Mock
+ private lateinit var handler: Handler
+
+ @Captor
+ private lateinit var foldStateListenerCaptor: ArgumentCaptor<FoldStateListener>
+
+ @Captor
+ private lateinit var screenOnListenerCaptor: ArgumentCaptor<ScreenListener>
+
+ @Captor
+ private lateinit var hingeAngleCaptor: ArgumentCaptor<Consumer<Float>>
+
+ private lateinit var foldStateProvider: DeviceFoldStateProvider
private val foldUpdates: MutableList<Int> = arrayListOf()
private val hingeAngleUpdates: MutableList<Float> = arrayListOf()
- private val foldStateListenerCaptor = ArgumentCaptor.forClass(FoldStateListener::class.java)
private var foldedDeviceState: Int = 0
private var unfoldedDeviceState: Int = 0
- private val screenOnListenerCaptor = ArgumentCaptor.forClass(ScreenListener::class.java)
+ private var scheduledRunnable: Runnable? = null
+ private var scheduledRunnableDelay: Long? = null
@Before
fun setUp() {
@@ -75,7 +92,8 @@
hingeAngleProvider,
screenStatusProvider,
deviceStateManager,
- context.mainExecutor
+ context.mainExecutor,
+ handler
)
foldStateProvider.addCallback(object : FoldStateProvider.FoldUpdatesListener {
@@ -91,6 +109,22 @@
verify(deviceStateManager).registerCallback(any(), foldStateListenerCaptor.capture())
verify(screenStatusProvider).addCallback(screenOnListenerCaptor.capture())
+ verify(hingeAngleProvider).addCallback(hingeAngleCaptor.capture())
+
+ whenever(handler.postDelayed(any<Runnable>(), any())).then { invocationOnMock ->
+ scheduledRunnable = invocationOnMock.getArgument<Runnable>(0)
+ scheduledRunnableDelay = invocationOnMock.getArgument<Long>(1)
+ null
+ }
+
+ whenever(handler.removeCallbacks(any<Runnable>())).then { invocationOnMock ->
+ val removedRunnable = invocationOnMock.getArgument<Runnable>(0)
+ if (removedRunnable == scheduledRunnable) {
+ scheduledRunnableDelay = null
+ scheduledRunnable = null
+ }
+ null
+ }
}
@Test
@@ -167,6 +201,86 @@
assertThat(foldUpdates).isEmpty()
}
+ @Test
+ fun startClosingEvent_afterTimeout_abortEmitted() {
+ sendHingeAngleEvent(90)
+ sendHingeAngleEvent(80)
+
+ simulateTimeout(ABORT_CLOSING_MILLIS)
+
+ assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING, FOLD_UPDATE_ABORTED)
+ }
+
+ @Test
+ fun startClosingEvent_beforeTimeout_abortNotEmitted() {
+ sendHingeAngleEvent(90)
+ sendHingeAngleEvent(80)
+
+ simulateTimeout(ABORT_CLOSING_MILLIS - 1)
+
+ assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING)
+ }
+
+ @Test
+ fun startClosingEvent_eventBeforeTimeout_oneEventEmitted() {
+ sendHingeAngleEvent(180)
+ sendHingeAngleEvent(90)
+
+ simulateTimeout(ABORT_CLOSING_MILLIS - 1)
+ sendHingeAngleEvent(80)
+
+ assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING)
+ }
+
+ @Test
+ fun startClosingEvent_timeoutAfterTimeoutRescheduled_abortEmitted() {
+ sendHingeAngleEvent(180)
+ sendHingeAngleEvent(90)
+
+ simulateTimeout(ABORT_CLOSING_MILLIS - 1) // The timeout should not trigger here.
+ sendHingeAngleEvent(80)
+ simulateTimeout(ABORT_CLOSING_MILLIS) // The timeout should trigger here.
+
+ assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING, FOLD_UPDATE_ABORTED)
+ }
+
+ @Test
+ fun startClosingEvent_shortTimeBetween_emitsOnlyOneEvents() {
+ sendHingeAngleEvent(180)
+
+ sendHingeAngleEvent(90)
+ sendHingeAngleEvent(80)
+
+ assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING)
+ }
+
+ @Test
+ fun startClosingEvent_whileClosing_emittedDespiteInitialAngle() {
+ val maxAngle = 180 - FULLY_OPEN_THRESHOLD_DEGREES.toInt()
+ for (i in 1..maxAngle) {
+ foldUpdates.clear()
+
+ simulateFolding(startAngle = i)
+
+ assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING)
+ simulateTimeout() // Timeout to set the state to aborted.
+ }
+ }
+
+ private fun simulateTimeout(waitTime: Long = ABORT_CLOSING_MILLIS) {
+ val runnableDelay = scheduledRunnableDelay ?: throw Exception("No runnable scheduled.")
+ if (waitTime >= runnableDelay) {
+ scheduledRunnable?.run()
+ scheduledRunnable = null
+ scheduledRunnableDelay = null
+ }
+ }
+
+ private fun simulateFolding(startAngle: Int) {
+ sendHingeAngleEvent(startAngle)
+ sendHingeAngleEvent(startAngle - 1)
+ }
+
private fun setFoldState(folded: Boolean) {
val state = if (folded) foldedDeviceState else unfoldedDeviceState
foldStateListenerCaptor.value.onStateChanged(state)
@@ -175,4 +289,8 @@
private fun fireScreenOnEvent() {
screenOnListenerCaptor.value.onScreenTurnedOn()
}
+
+ private fun sendHingeAngleEvent(angle: Int) {
+ hingeAngleCaptor.value.accept(angle.toFloat())
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt b/packages/SystemUI/tests/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
index eb54fe0..0f1b65c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
@@ -44,6 +44,11 @@
inline fun <reified T> any(): T = any(T::class.java)
/**
+ * Kotlin type-inferred version of Mockito.nullable()
+ */
+inline fun <reified T> nullable(): T? = Mockito.nullable(T::class.java)
+
+/**
* Returns ArgumentCaptor.capture() as nullable type to avoid java.lang.IllegalStateException
* when null is returned.
*
diff --git a/services/companion/Android.bp b/services/companion/Android.bp
index e3926b4..d3ef6dc 100644
--- a/services/companion/Android.bp
+++ b/services/companion/Android.bp
@@ -24,5 +24,8 @@
type: "stream",
},
srcs: [":services.companion-sources"],
- libs: ["services.core"],
+ libs: [
+ "app-compat-annotations",
+ "services.core",
+ ],
}
diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
new file mode 100644
index 0000000..a6a8793
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion.virtual;
+
+import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+
+import android.annotation.NonNull;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
+import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
+import android.os.Build;
+import android.os.UserHandle;
+import android.window.DisplayWindowPolicyController;
+
+import java.util.List;
+
+
+/**
+ * A controller to control the policies of the windows that can be displayed on the virtual display.
+ */
+class GenericWindowPolicyController extends DisplayWindowPolicyController {
+
+ /**
+ * If required, allow the secure activity to display on remote device since
+ * {@link android.os.Build.VERSION_CODES#TIRAMISU}.
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ public static final long ALLOW_SECURE_ACTIVITY_DISPLAY_ON_REMOTE_DEVICE = 201712607L;
+
+ GenericWindowPolicyController(int windowFlags, int systemWindowFlags) {
+ setInterestedWindowFlags(windowFlags, systemWindowFlags);
+ }
+
+ @Override
+ public boolean canContainActivities(@NonNull List<ActivityInfo> activities) {
+ // Can't display all the activities if any of them don't want to be displayed.
+ final int activityCount = activities.size();
+ for (int i = 0; i < activityCount; i++) {
+ final ActivityInfo aInfo = activities.get(i);
+ if ((aInfo.flags & ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES) == 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean keepActivityOnWindowFlagsChanged(ActivityInfo activityInfo, int windowFlags,
+ int systemWindowFlags) {
+ if ((activityInfo.flags & ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES) == 0) {
+ return false;
+ }
+ if (!CompatChanges.isChangeEnabled(ALLOW_SECURE_ACTIVITY_DISPLAY_ON_REMOTE_DEVICE,
+ activityInfo.packageName,
+ UserHandle.getUserHandleForUid(activityInfo.applicationInfo.uid))) {
+ // TODO(b/201712607): Add checks for the apps that use SurfaceView#setSecure.
+ if ((windowFlags & FLAG_SECURE) != 0) {
+ return false;
+ }
+ if ((systemWindowFlags & SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) != 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public void onTopActivityChanged(ComponentName topActivity, int uid) {
+
+ }
+
+ @Override
+ public void onRunningAppsChanged(int[] runningUids) {
+
+ }
+}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 58d0801..2742608 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -16,6 +16,9 @@
package com.android.server.companion.virtual;
+import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
@@ -31,6 +34,7 @@
import android.util.ExceptionUtils;
import android.util.Slog;
import android.util.SparseArray;
+import android.window.DisplayWindowPolicyController;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.DumpUtils;
@@ -38,11 +42,11 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
-/** @hide */
@SuppressLint("LongLogTag")
public class VirtualDeviceManagerService extends SystemService {
@@ -81,6 +85,16 @@
@Override
public void onStart() {
publishBinderService(Context.VIRTUAL_DEVICE_SERVICE, mImpl);
+ publishLocalService(VirtualDeviceManagerInternal.class, new LocalService());
+ }
+
+ @GuardedBy("mVirtualDeviceManagerLock")
+ private boolean isValidVirtualDeviceLocked(IVirtualDevice virtualDevice) {
+ try {
+ return mVirtualDevices.contains(virtualDevice.getAssociationId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
@Override
@@ -119,9 +133,15 @@
private class VirtualDeviceImpl extends IVirtualDevice.Stub implements IBinder.DeathRecipient {
private final AssociationInfo mAssociationInfo;
+ private final int mOwnerUid;
+ private final GenericWindowPolicyController mGenericWindowPolicyController;
+ private final ArrayList<Integer> mDisplayIds = new ArrayList<>();
- private VirtualDeviceImpl(IBinder token, AssociationInfo associationInfo) {
+ private VirtualDeviceImpl(int ownerUid, IBinder token, AssociationInfo associationInfo) {
+ mOwnerUid = ownerUid;
mAssociationInfo = associationInfo;
+ mGenericWindowPolicyController = new GenericWindowPolicyController(FLAG_SECURE,
+ SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
try {
token.linkToDeath(this, 0);
} catch (RemoteException e) {
@@ -146,6 +166,23 @@
public void binderDied() {
close();
}
+
+ DisplayWindowPolicyController onVirtualDisplayCreatedLocked(int displayId) {
+ if (mDisplayIds.contains(displayId)) {
+ throw new IllegalStateException(
+ "Virtual device already have a virtual display with ID " + displayId);
+ }
+ mDisplayIds.add(displayId);
+ return mGenericWindowPolicyController;
+ }
+
+ void onVirtualDisplayRemovedLocked(int displayId) {
+ if (!mDisplayIds.contains(displayId)) {
+ throw new IllegalStateException(
+ "Virtual device doesn't have a virtual display with ID " + displayId);
+ }
+ mDisplayIds.remove(displayId);
+ }
}
class VirtualDeviceManagerImpl extends IVirtualDeviceManager.Stub {
@@ -156,10 +193,11 @@
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.CREATE_VIRTUAL_DEVICE,
"createVirtualDevice");
- if (!PermissionUtils.validatePackageName(getContext(), packageName, getCallingUid())) {
+ final int callingUid = getCallingUid();
+ if (!PermissionUtils.validatePackageName(getContext(), packageName, callingUid)) {
throw new SecurityException(
"Package name " + packageName + " does not belong to calling uid "
- + getCallingUid());
+ + callingUid);
}
AssociationInfo associationInfo = getAssociationInfo(packageName, associationId);
if (associationInfo == null) {
@@ -171,7 +209,7 @@
"Virtual device for association ID " + associationId
+ " already exists");
}
- return new VirtualDeviceImpl(token, associationInfo);
+ return new VirtualDeviceImpl(callingUid, token, associationInfo);
}
}
@@ -222,4 +260,48 @@
}
}
}
+
+ private final class LocalService extends VirtualDeviceManagerInternal {
+
+ @Override
+ public boolean isValidVirtualDevice(IVirtualDevice virtualDevice) {
+ synchronized (mVirtualDeviceManagerLock) {
+ return isValidVirtualDeviceLocked(virtualDevice);
+ }
+ }
+
+ @Override
+ public DisplayWindowPolicyController onVirtualDisplayCreated(IVirtualDevice virtualDevice,
+ int displayId) {
+ synchronized (mVirtualDeviceManagerLock) {
+ return ((VirtualDeviceImpl) virtualDevice).onVirtualDisplayCreatedLocked(displayId);
+ }
+ }
+
+ @Override
+ public void onVirtualDisplayRemoved(IVirtualDevice virtualDevice, int displayId) {
+ synchronized (mVirtualDeviceManagerLock) {
+ ((VirtualDeviceImpl) virtualDevice).onVirtualDisplayRemovedLocked(displayId);
+ }
+ }
+
+ @Override
+ public boolean isAppOwnerOfAnyVirtualDevice(int uid) {
+ synchronized (mVirtualDeviceManagerLock) {
+ int size = mVirtualDevices.size();
+ for (int i = 0; i < size; i++) {
+ if (mVirtualDevices.valueAt(i).mOwnerUid == uid) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isAppRunningOnAnyVirtualDevice(int uid) {
+ // TODO(yukl): Implement this using DWPC.onRunningAppsChanged
+ return false;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index 25b36e8..34c21f2 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -88,6 +88,20 @@
private static final int EMERGENCY_GESTURE_POWER_TAP_COUNT_THRESHOLD = 5;
/**
+ * Default value of the power button "cooldown" period after the Emergency gesture is triggered.
+ * See {@link Settings.Secure#EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS}
+ */
+ private static final int EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_DEFAULT = 3000;
+
+ /**
+ * Maximum value of the power button "cooldown" period after the Emergency gesture is triggered.
+ * The value read from {@link Settings.Secure#EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS}
+ * is capped at this maximum.
+ */
+ @VisibleForTesting
+ static final int EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_MAX = 5000;
+
+ /**
* Number of taps required to launch camera shortcut.
*/
private static final int CAMERA_POWER_TAP_COUNT_THRESHOLD = 2;
@@ -163,7 +177,14 @@
*/
private boolean mEmergencyGestureEnabled;
+ /**
+ * Power button cooldown period in milliseconds, after emergency gesture is triggered. A zero
+ * value means the cooldown period is disabled.
+ */
+ private int mEmergencyGesturePowerButtonCooldownPeriodMs;
+
private long mLastPowerDown;
+ private long mLastEmergencyGestureTriggered;
private int mPowerButtonConsecutiveTaps;
private int mPowerButtonSlowConsecutiveTaps;
private final UiEventLogger mUiEventLogger;
@@ -231,6 +252,7 @@
updateCameraRegistered();
updateCameraDoubleTapPowerEnabled();
updateEmergencyGestureEnabled();
+ updateEmergencyGesturePowerButtonCooldownPeriodMs();
mUserId = ActivityManager.getCurrentUser();
mContext.registerReceiver(mUserReceiver, new IntentFilter(Intent.ACTION_USER_SWITCHED));
@@ -261,6 +283,10 @@
mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.EMERGENCY_GESTURE_ENABLED),
false, mSettingObserver, mUserId);
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(
+ Settings.Secure.EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS),
+ false, mSettingObserver, mUserId);
}
private void updateCameraRegistered() {
@@ -294,6 +320,14 @@
}
}
+ @VisibleForTesting
+ void updateEmergencyGesturePowerButtonCooldownPeriodMs() {
+ int cooldownPeriodMs = getEmergencyGesturePowerButtonCooldownPeriodMs(mContext, mUserId);
+ synchronized (this) {
+ mEmergencyGesturePowerButtonCooldownPeriodMs = cooldownPeriodMs;
+ }
+ }
+
private void unregisterCameraLaunchGesture() {
if (mCameraLaunchRegistered) {
mCameraLaunchRegistered = false;
@@ -428,6 +462,20 @@
}
/**
+ * Gets power button cooldown period in milliseconds after emergency gesture is triggered. The
+ * value is capped at a maximum
+ * {@link GestureLauncherService#EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_MAX}. If the
+ * value is zero, it means the cooldown period is disabled.
+ */
+ @VisibleForTesting
+ static int getEmergencyGesturePowerButtonCooldownPeriodMs(Context context, int userId) {
+ int cooldown = Settings.Secure.getIntForUser(context.getContentResolver(),
+ Settings.Secure.EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS,
+ EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_DEFAULT, userId);
+ return Math.min(cooldown, EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_MAX);
+ }
+
+ /**
* Whether to enable the camera launch gesture.
*/
private static boolean isCameraLaunchEnabled(Resources resources) {
@@ -475,10 +523,24 @@
*/
public boolean interceptPowerKeyDown(KeyEvent event, boolean interactive,
MutableBoolean outLaunched) {
+ if (mEmergencyGestureEnabled && mEmergencyGesturePowerButtonCooldownPeriodMs >= 0
+ && event.getEventTime() - mLastEmergencyGestureTriggered
+ < mEmergencyGesturePowerButtonCooldownPeriodMs) {
+ Slog.i(TAG, String.format(
+ "Suppressing power button: within %dms cooldown period after Emergency "
+ + "Gesture. Begin=%dms, end=%dms.",
+ mEmergencyGesturePowerButtonCooldownPeriodMs,
+ mLastEmergencyGestureTriggered,
+ mLastEmergencyGestureTriggered + mEmergencyGesturePowerButtonCooldownPeriodMs));
+ outLaunched.value = false;
+ return true;
+ }
+
if (event.isLongPress()) {
// Long presses are sent as a second key down. If the long press threshold is set lower
// than the double tap of sequence interval thresholds, this could cause false double
// taps or consecutive taps, so we want to ignore the long press event.
+ outLaunched.value = false;
return false;
}
boolean launchCamera = false;
@@ -542,6 +604,12 @@
Slog.i(TAG, "Emergency gesture detected, launching.");
launchEmergencyGesture = handleEmergencyGesture();
mUiEventLogger.log(GestureLauncherEvent.GESTURE_EMERGENCY_TAP_POWER);
+ // Record emergency trigger time if emergency UI was launched
+ if (launchEmergencyGesture) {
+ synchronized (this) {
+ mLastEmergencyGestureTriggered = event.getEventTime();
+ }
+ }
}
mMetricsLogger.histogram("power_consecutive_short_tap_count",
mPowerButtonSlowConsecutiveTaps);
@@ -670,6 +738,7 @@
updateCameraRegistered();
updateCameraDoubleTapPowerEnabled();
updateEmergencyGestureEnabled();
+ updateEmergencyGesturePowerButtonCooldownPeriodMs();
}
}
};
@@ -680,6 +749,7 @@
updateCameraRegistered();
updateCameraDoubleTapPowerEnabled();
updateEmergencyGestureEnabled();
+ updateEmergencyGesturePowerButtonCooldownPeriodMs();
}
}
};
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index b7b4870..9180ef8 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -45,7 +45,6 @@
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.power.MeasuredEnergyStats;
import com.android.internal.util.FrameworkStatsLog;
-import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
import libcore.util.EmptyArray;
@@ -260,43 +259,6 @@
}
@Override
- public Future<?> scheduleReadProcStateCpuTimes(
- boolean onBattery, boolean onBatteryScreenOff, long delayMillis) {
- synchronized (mStats) {
- if (!mStats.trackPerProcStateCpuTimes()) {
- return null;
- }
- }
- synchronized (BatteryExternalStatsWorker.this) {
- if (!mExecutorService.isShutdown()) {
- return mExecutorService.schedule(PooledLambda.obtainRunnable(
- BatteryStatsImpl::updateProcStateCpuTimes,
- mStats, onBattery, onBatteryScreenOff).recycleOnUse(),
- delayMillis, TimeUnit.MILLISECONDS);
- }
- }
- return null;
- }
-
- @Override
- public Future<?> scheduleCopyFromAllUidsCpuTimes(
- boolean onBattery, boolean onBatteryScreenOff) {
- synchronized (mStats) {
- if (!mStats.trackPerProcStateCpuTimes()) {
- return null;
- }
- }
- synchronized (BatteryExternalStatsWorker.this) {
- if (!mExecutorService.isShutdown()) {
- return mExecutorService.submit(PooledLambda.obtainRunnable(
- BatteryStatsImpl::copyFromAllUidsCpuTimes,
- mStats, onBattery, onBatteryScreenOff).recycleOnUse());
- }
- }
- return null;
- }
-
- @Override
public Future<?> scheduleSyncDueToScreenStateChange(int flags, boolean onBattery,
boolean onBatteryScreenOff, int screenState, int[] perDisplayScreenStates) {
synchronized (BatteryExternalStatsWorker.this) {
@@ -491,7 +453,7 @@
}
if ((updateFlags & UPDATE_CPU) != 0) {
- mStats.copyFromAllUidsCpuTimes();
+ mStats.updateCpuTimesForAllUids();
}
// Clean up any UIDs if necessary.
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index e79cba1..a7864b9 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -145,9 +145,11 @@
// giving up on them and unfreezing the screen.
static final int USER_SWITCH_TIMEOUT_MS = 3 * 1000;
- // Amount of time we wait for observers to handle a user switch before we log a warning.
- // Must be smaller than USER_SWITCH_TIMEOUT_MS.
- private static final int USER_SWITCH_WARNING_TIMEOUT_MS = 500;
+ /**
+ * Amount of time we wait for an observer to handle a user switch before we log a warning. This
+ * wait time is per observer.
+ */
+ private static final int LONG_USER_SWITCH_OBSERVER_WARNING_TIME_MS = 500;
// ActivityManager thread message constants
static final int REPORT_USER_SWITCH_MSG = 10;
@@ -1916,6 +1918,7 @@
final AtomicInteger waitingCallbacksCount = new AtomicInteger(observerCount);
final long dispatchStartedTime = SystemClock.elapsedRealtime();
for (int i = 0; i < observerCount; i++) {
+ final long dispatchStartedTimeForObserver = SystemClock.elapsedRealtime();
try {
// Prepend with unique prefix to guarantee that keys are unique
final String name = "#" + i + " " + mUserSwitchObservers.getBroadcastCookie(i);
@@ -1926,13 +1929,20 @@
@Override
public void sendResult(Bundle data) throws RemoteException {
synchronized (mLock) {
- long delay = SystemClock.elapsedRealtime() - dispatchStartedTime;
- if (delay > USER_SWITCH_TIMEOUT_MS) {
- Slogf.e(TAG, "User switch timeout: observer " + name
- + " sent result after " + delay + " ms");
- } else if (delay > USER_SWITCH_WARNING_TIMEOUT_MS) {
+ long delayForObserver = SystemClock.elapsedRealtime()
+ - dispatchStartedTimeForObserver;
+ if (delayForObserver > LONG_USER_SWITCH_OBSERVER_WARNING_TIME_MS) {
Slogf.w(TAG, "User switch slowed down by observer " + name
- + ": result sent after " + delay + " ms");
+ + ": result took " + delayForObserver
+ + " ms to process.");
+ }
+
+ long totalDelay = SystemClock.elapsedRealtime()
+ - dispatchStartedTime;
+ if (totalDelay > USER_SWITCH_TIMEOUT_MS) {
+ Slogf.e(TAG, "User switch timeout: observer " + name
+ + "'s result was received " + totalDelay
+ + " ms after dispatchUserSwitch.");
}
curWaitingUserSwitchCallbacks.remove(name);
diff --git a/services/core/java/com/android/server/biometrics/OWNERS b/services/core/java/com/android/server/biometrics/OWNERS
index 4eac972..f05f403 100644
--- a/services/core/java/com/android/server/biometrics/OWNERS
+++ b/services/core/java/com/android/server/biometrics/OWNERS
@@ -6,3 +6,4 @@
ilyamaty@google.com
joshmccloskey@google.com
jbolinger@google.com
+graciecheng@google.com
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index 7341e74..358263d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -503,10 +503,14 @@
protected int getShowOverlayReason() {
if (isKeyguard()) {
return BiometricOverlayConstants.REASON_AUTH_KEYGUARD;
- } else if (isSettings()) {
- return BiometricOverlayConstants.REASON_AUTH_SETTINGS;
} else if (isBiometricPrompt()) {
+ // BP reason always takes precedent over settings, since callers from within
+ // settings can always invoke BP.
return BiometricOverlayConstants.REASON_AUTH_BP;
+ } else if (isSettings()) {
+ // This is pretty much only for FingerprintManager#authenticate usage from
+ // FingerprintSettings.
+ return BiometricOverlayConstants.REASON_AUTH_SETTINGS;
} else {
return BiometricOverlayConstants.REASON_AUTH_OTHER;
}
diff --git a/services/core/java/com/android/server/communal/CommunalManagerService.java b/services/core/java/com/android/server/communal/CommunalManagerService.java
index 8d9b13e..3bf6ca2 100644
--- a/services/core/java/com/android/server/communal/CommunalManagerService.java
+++ b/services/core/java/com/android/server/communal/CommunalManagerService.java
@@ -30,6 +30,7 @@
import android.app.KeyguardManager;
import android.app.PendingIntent;
import android.app.communal.ICommunalManager;
+import android.app.communal.ICommunalModeListener;
import android.app.compat.CompatChanges;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -42,6 +43,8 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.dreams.DreamManagerInternal;
@@ -77,6 +80,8 @@
private final PackageReceiver mPackageReceiver;
private final PackageManager mPackageManager;
private final DreamManagerInternal mDreamManagerInternal;
+ private final RemoteCallbackList<ICommunalModeListener> mListeners =
+ new RemoteCallbackList<>();
private final ActivityInterceptorCallback mActivityInterceptorCallback =
new ActivityInterceptorCallback() {
@@ -129,7 +134,7 @@
@Override
public void onStart() {
- publishBinderService(Context.COMMUNAL_MANAGER_SERVICE, mBinderService);
+ publishBinderService(Context.COMMUNAL_SERVICE, mBinderService);
}
@Override
@@ -242,6 +247,27 @@
return !isAppAllowed(appInfo);
}
+ private void dispatchCommunalMode(boolean isShowing) {
+ synchronized (mListeners) {
+ int i = mListeners.beginBroadcast();
+ while (i > 0) {
+ i--;
+ try {
+ mListeners.getBroadcastItem(i).onCommunalModeChanged(isShowing);
+ } catch (RemoteException e) {
+ // Handled by the RemoteCallbackList.
+ }
+ }
+ mListeners.finishBroadcast();
+ }
+ }
+
+ private void enforceReadPermission() {
+ mContext.enforceCallingPermission(Manifest.permission.READ_COMMUNAL_STATE,
+ Manifest.permission.READ_COMMUNAL_STATE
+ + "permission required to read communal state.");
+ }
+
private final class BinderService extends ICommunalManager.Stub {
/**
* Sets whether or not we are in communal mode.
@@ -252,7 +278,43 @@
mContext.enforceCallingPermission(Manifest.permission.WRITE_COMMUNAL_STATE,
Manifest.permission.WRITE_COMMUNAL_STATE
+ "permission required to modify communal state.");
+ if (mCommunalViewIsShowing.get() == isShowing) {
+ return;
+ }
mCommunalViewIsShowing.set(isShowing);
+ dispatchCommunalMode(isShowing);
+ }
+
+ /**
+ * Checks whether or not we are in communal mode.
+ */
+ @RequiresPermission(Manifest.permission.READ_COMMUNAL_STATE)
+ @Override
+ public boolean isCommunalMode() {
+ enforceReadPermission();
+ return mCommunalViewIsShowing.get();
+ }
+
+ /**
+ * Adds a callback to execute when communal state changes.
+ */
+ @RequiresPermission(Manifest.permission.READ_COMMUNAL_STATE)
+ public void addCommunalModeListener(ICommunalModeListener listener) {
+ enforceReadPermission();
+ synchronized (mListeners) {
+ mListeners.register(listener);
+ }
+ }
+
+ /**
+ * Removes an added callback that execute when communal state changes.
+ */
+ @RequiresPermission(Manifest.permission.READ_COMMUNAL_STATE)
+ public void removeCommunalModeListener(ICommunalModeListener listener) {
+ enforceReadPermission();
+ synchronized (mListeners) {
+ mListeners.unregister(listener);
+ }
}
}
diff --git a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
new file mode 100644
index 0000000..39fa3f2
--- /dev/null
+++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion.virtual;
+
+import android.companion.virtual.IVirtualDevice;
+import android.window.DisplayWindowPolicyController;
+
+/**
+ * Virtual device manager local service interface.
+ * Only for use within system server.
+ */
+public abstract class VirtualDeviceManagerInternal {
+
+ /**
+ * Validate the virtual device.
+ */
+ public abstract boolean isValidVirtualDevice(IVirtualDevice virtualDevice);
+
+ /**
+ * Notify a virtual display is created.
+ *
+ * @param virtualDevice The virtual device where the virtual display located.
+ * @param displayId The display id of the created virtual display.
+ *
+ * @return The {@link DisplayWindowPolicyController} of the virtual device.
+ */
+ public abstract DisplayWindowPolicyController onVirtualDisplayCreated(
+ IVirtualDevice virtualDevice, int displayId);
+
+ /**
+ * Notify a virtual display is removed.
+ *
+ * @param virtualDevice The virtual device where the virtual display located.
+ * @param displayId The display id of the removed virtual display.
+ */
+ public abstract void onVirtualDisplayRemoved(IVirtualDevice virtualDevice, int displayId);
+
+ /**
+ * Returns true if the given {@code uid} is the owner of any virtual devices that are
+ * currently active.
+ */
+ public abstract boolean isAppOwnerOfAnyVirtualDevice(int uid);
+
+ /**
+ * Returns true if the given {@code uid} is currently running on any virtual devices. This is
+ * determined by whether the app has any activities in the task stack on a virtual-device-owned
+ * display.
+ */
+ public abstract boolean isAppRunningOnAnyVirtualDevice(int uid);
+}
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index e9af6011..c04032f 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -87,7 +87,10 @@
private final Sensor mLightSensor;
// The mapper to translate ambient lux to screen brightness in the range [0, 1.0].
- private final BrightnessMappingStrategy mBrightnessMapper;
+ @Nullable
+ private BrightnessMappingStrategy mCurrentBrightnessMapper;
+ private final BrightnessMappingStrategy mInteractiveModeBrightnessMapper;
+ private final BrightnessMappingStrategy mIdleModeBrightnessMapper;
// The minimum and maximum screen brightnesses.
private final float mScreenBrightnessRangeMinimum;
@@ -215,36 +218,41 @@
private final Injector mInjector;
AutomaticBrightnessController(Callbacks callbacks, Looper looper,
- SensorManager sensorManager, Sensor lightSensor, BrightnessMappingStrategy mapper,
+ SensorManager sensorManager, Sensor lightSensor,
+ BrightnessMappingStrategy interactiveModeBrightnessMapper,
int lightSensorWarmUpTime, float brightnessMin, float brightnessMax,
float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
HysteresisLevels screenBrightnessThresholds, Context context,
- HighBrightnessModeController hbmController) {
- this(new Injector(), callbacks, looper, sensorManager, lightSensor, mapper,
+ HighBrightnessModeController hbmController,
+ BrightnessMappingStrategy idleModeBrightnessMapper) {
+ this(new Injector(), callbacks, looper, sensorManager, lightSensor,
+ interactiveModeBrightnessMapper,
lightSensorWarmUpTime, brightnessMin, brightnessMax, dozeScaleFactor,
lightSensorRate, initialLightSensorRate, brighteningLightDebounceConfig,
darkeningLightDebounceConfig, resetAmbientLuxAfterWarmUpConfig,
ambientBrightnessThresholds, screenBrightnessThresholds, context,
- hbmController
+ hbmController, idleModeBrightnessMapper
);
}
@VisibleForTesting
AutomaticBrightnessController(Injector injector, Callbacks callbacks, Looper looper,
- SensorManager sensorManager, Sensor lightSensor, BrightnessMappingStrategy mapper,
+ SensorManager sensorManager, Sensor lightSensor,
+ BrightnessMappingStrategy interactiveModeBrightnessMapper,
int lightSensorWarmUpTime, float brightnessMin, float brightnessMax,
float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
HysteresisLevels screenBrightnessThresholds, Context context,
- HighBrightnessModeController hbmController) {
+ HighBrightnessModeController hbmController,
+ BrightnessMappingStrategy idleModeBrightnessMapper) {
mInjector = injector;
mContext = context;
mCallbacks = callbacks;
mSensorManager = sensorManager;
- mBrightnessMapper = mapper;
+ mCurrentBrightnessMapper = interactiveModeBrightnessMapper;
mScreenBrightnessRangeMinimum = brightnessMin;
mScreenBrightnessRangeMaximum = brightnessMax;
mLightSensorWarmUpTimeConfig = lightSensorWarmUpTime;
@@ -277,6 +285,10 @@
mForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
mHbmController = hbmController;
+ mInteractiveModeBrightnessMapper = interactiveModeBrightnessMapper;
+ mIdleModeBrightnessMapper = idleModeBrightnessMapper;
+ // Initialize to active (normal) screen brightness mode
+ switchToInteractiveScreenBrightnessMode();
}
/**
@@ -291,7 +303,12 @@
if (mLoggingEnabled == loggingEnabled) {
return false;
}
- mBrightnessMapper.setLoggingEnabled(loggingEnabled);
+ if (mInteractiveModeBrightnessMapper != null) {
+ mInteractiveModeBrightnessMapper.setLoggingEnabled(loggingEnabled);
+ }
+ if (mIdleModeBrightnessMapper != null) {
+ mIdleModeBrightnessMapper.setLoggingEnabled(loggingEnabled);
+ }
mLoggingEnabled = loggingEnabled;
return true;
}
@@ -311,7 +328,7 @@
}
public float getAutomaticScreenBrightnessAdjustment() {
- return mBrightnessMapper.getAutoBrightnessAdjustment();
+ return mCurrentBrightnessMapper.getAutoBrightnessAdjustment();
}
public void configure(boolean enable, @Nullable BrightnessConfiguration configuration,
@@ -350,15 +367,20 @@
}
public boolean hasUserDataPoints() {
- return mBrightnessMapper.hasUserDataPoints();
+ return mCurrentBrightnessMapper.hasUserDataPoints();
}
+ // Used internally to establish whether we have deviated from the default config.
public boolean isDefaultConfig() {
- return mBrightnessMapper.isDefaultConfig();
+ if (isInIdleMode()) {
+ return false;
+ }
+ return mInteractiveModeBrightnessMapper.isDefaultConfig();
}
+ // Called from APIs to get the configuration.
public BrightnessConfiguration getDefaultConfig() {
- return mBrightnessMapper.getDefaultConfig();
+ return mInteractiveModeBrightnessMapper.getDefaultConfig();
}
/**
@@ -379,7 +401,7 @@
}
if (!isInteractivePolicy(policy) && isInteractivePolicy(oldPolicy)) {
mHandler.sendEmptyMessageDelayed(MSG_INVALIDATE_SHORT_TERM_MODEL,
- mBrightnessMapper.getShortTermModelTimeout());
+ mCurrentBrightnessMapper.getShortTermModelTimeout());
} else if (isInteractivePolicy(policy) && !isInteractivePolicy(oldPolicy)) {
mHandler.removeMessages(MSG_INVALIDATE_SHORT_TERM_MODEL);
}
@@ -398,7 +420,7 @@
// and we can't use this data to add a new control point to the short-term model.
return false;
}
- mBrightnessMapper.addUserDataPoint(mAmbientLux, brightness);
+ mCurrentBrightnessMapper.addUserDataPoint(mAmbientLux, brightness);
mShortTermModelValid = true;
mShortTermModelAnchor = mAmbientLux;
if (mLoggingEnabled) {
@@ -408,7 +430,7 @@
}
public void resetShortTermModel() {
- mBrightnessMapper.clearUserDataPoints();
+ mCurrentBrightnessMapper.clearUserDataPoints();
mShortTermModelValid = true;
mShortTermModelAnchor = -1;
}
@@ -421,13 +443,19 @@
}
public boolean setBrightnessConfiguration(BrightnessConfiguration configuration) {
- if (mBrightnessMapper.setBrightnessConfiguration(configuration)) {
- resetShortTermModel();
+ if (mInteractiveModeBrightnessMapper.setBrightnessConfiguration(configuration)) {
+ if (!isInIdleMode()) {
+ resetShortTermModel();
+ }
return true;
}
return false;
}
+ public boolean isInIdleMode() {
+ return mCurrentBrightnessMapper.isForIdleMode();
+ }
+
public void dump(PrintWriter pw) {
pw.println();
pw.println("Automatic Brightness Controller Configuration:");
@@ -461,7 +489,12 @@
pw.println(" mAmbientLightRingBuffer=" + mAmbientLightRingBuffer);
pw.println(" mScreenAutoBrightness=" + mScreenAutoBrightness);
pw.println(" mDisplayPolicy=" + DisplayPowerRequest.policyToString(mDisplayPolicy));
- pw.println(" mShortTermModelTimeout=" + mBrightnessMapper.getShortTermModelTimeout());
+ pw.println(" mShortTermModelTimeout(active)="
+ + mInteractiveModeBrightnessMapper.getShortTermModelTimeout());
+ if (mIdleModeBrightnessMapper != null) {
+ pw.println(" mShortTermModelTimeout(idle)="
+ + mIdleModeBrightnessMapper.getShortTermModelTimeout());
+ }
pw.println(" mShortTermModelAnchor=" + mShortTermModelAnchor);
pw.println(" mShortTermModelValid=" + mShortTermModelValid);
pw.println(" mBrightnessAdjustmentSamplePending=" + mBrightnessAdjustmentSamplePending);
@@ -472,9 +505,15 @@
pw.println(" mPendingForegroundAppPackageName=" + mPendingForegroundAppPackageName);
pw.println(" mForegroundAppCategory=" + mForegroundAppCategory);
pw.println(" mPendingForegroundAppCategory=" + mPendingForegroundAppCategory);
+ pw.println(" Idle mode active=" + mCurrentBrightnessMapper.isForIdleMode());
pw.println();
- mBrightnessMapper.dump(pw);
+ pw.println(" mActiveMapper=");
+ mInteractiveModeBrightnessMapper.dump(pw);
+ if (mIdleModeBrightnessMapper != null) {
+ pw.println(" mIdleMapper=");
+ mIdleModeBrightnessMapper.dump(pw);
+ }
pw.println();
mAmbientBrightnessThresholds.dump(pw);
@@ -544,7 +583,7 @@
}
private boolean setAutoBrightnessAdjustment(float adjustment) {
- return mBrightnessMapper.setAutoBrightnessAdjustment(adjustment);
+ return mCurrentBrightnessMapper.setAutoBrightnessAdjustment(adjustment);
}
private void setAmbientLux(float lux) {
@@ -562,7 +601,8 @@
// If the short term model was invalidated and the change is drastic enough, reset it.
if (!mShortTermModelValid && mShortTermModelAnchor != -1) {
- if (mBrightnessMapper.shouldResetShortTermModel(mAmbientLux, mShortTermModelAnchor)) {
+ if (mCurrentBrightnessMapper.shouldResetShortTermModel(
+ mAmbientLux, mShortTermModelAnchor)) {
resetShortTermModel();
} else {
mShortTermModelValid = true;
@@ -743,7 +783,7 @@
return;
}
- float value = mBrightnessMapper.getBrightness(mAmbientLux, mForegroundAppPackageName,
+ float value = mCurrentBrightnessMapper.getBrightness(mAmbientLux, mForegroundAppPackageName,
mForegroundAppCategory);
float newScreenAutoBrightness = clampScreenBrightness(value);
@@ -909,6 +949,41 @@
updateAutoBrightness(true /* sendUpdate */, false /* isManuallySet */);
}
+ void switchToIdleMode() {
+ if (mIdleModeBrightnessMapper == null) {
+ return;
+ }
+ if (mCurrentBrightnessMapper.isForIdleMode()) {
+ return;
+ }
+ Slog.i(TAG, "Switching to Idle Screen Brightness Mode");
+ mCurrentBrightnessMapper = mIdleModeBrightnessMapper;
+ resetShortTermModel();
+ update();
+ }
+
+ void switchToInteractiveScreenBrightnessMode() {
+ if (!mCurrentBrightnessMapper.isForIdleMode()) {
+ return;
+ }
+ Slog.i(TAG, "Switching to Interactive Screen Brightness Mode");
+ mCurrentBrightnessMapper = mInteractiveModeBrightnessMapper;
+ resetShortTermModel();
+ update();
+ }
+
+ public float convertToNits(float brightness) {
+ if (mCurrentBrightnessMapper != null) {
+ return mCurrentBrightnessMapper.convertToNits(brightness);
+ } else {
+ return -1.0f;
+ }
+ }
+
+ public void recalculateSplines(boolean applyAdjustment, float[] adjustment) {
+ mCurrentBrightnessMapper.recalculateSplines(applyAdjustment, adjustment);
+ }
+
private final class AutomaticBrightnessHandler extends Handler {
public AutomaticBrightnessHandler(Looper looper) {
super(looper, null, true /*async*/);
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index beb4d5b..240168b 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -135,7 +135,7 @@
builder.setShortTermModelLowerLuxMultiplier(SHORT_TERM_MODEL_THRESHOLD_RATIO);
builder.setShortTermModelUpperLuxMultiplier(SHORT_TERM_MODEL_THRESHOLD_RATIO);
return new PhysicalMappingStrategy(builder.build(), nitsRange, brightnessRange,
- autoBrightnessAdjustmentMaxGamma);
+ autoBrightnessAdjustmentMaxGamma, isForIdleMode);
} else if (isValidMapping(luxLevels, brightnessLevelsBacklight) && !isForIdleMode) {
return new SimpleMappingStrategy(luxLevels, brightnessLevelsBacklight,
autoBrightnessAdjustmentMaxGamma, shortTermModelTimeout);
@@ -355,6 +355,12 @@
public abstract void dump(PrintWriter pw);
/**
+ * We can designate a mapping strategy to be used for idle screen brightness mode.
+ * @return whether this mapping strategy is to be used for idle screen brightness mode.
+ */
+ public abstract boolean isForIdleMode();
+
+ /**
* Check if the short term model should be reset given the anchor lux the last
* brightness change was made at and the current ambient lux.
*/
@@ -711,6 +717,11 @@
pw.println(" mUserBrightness=" + mUserBrightness);
}
+ @Override
+ public boolean isForIdleMode() {
+ return false;
+ }
+
private void computeSpline() {
Pair<float[], float[]> curve = getAdjustedCurve(mLux, mBrightness, mUserLux,
mUserBrightness, mAutoBrightnessAdjustment, mMaxGamma);
@@ -758,9 +769,10 @@
private float mAutoBrightnessAdjustment;
private float mUserLux;
private float mUserBrightness;
+ private final boolean mIsForIdleMode;
public PhysicalMappingStrategy(BrightnessConfiguration config, float[] nits,
- float[] brightness, float maxGamma) {
+ float[] brightness, float maxGamma, boolean isForIdleMode) {
Preconditions.checkArgument(nits.length != 0 && brightness.length != 0,
"Nits and brightness arrays must not be empty!");
@@ -772,6 +784,7 @@
Preconditions.checkArrayElementsInRange(brightness,
PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, "brightness");
+ mIsForIdleMode = isForIdleMode;
mMaxGamma = maxGamma;
mAutoBrightnessAdjustment = 0;
mUserLux = -1;
@@ -933,6 +946,11 @@
pw.println(" mBrightnessRangeAdjustmentApplied=" + mBrightnessRangeAdjustmentApplied);
}
+ @Override
+ public boolean isForIdleMode() {
+ return mIsForIdleMode;
+ }
+
private void computeNitsBrightnessSplines(float[] nits) {
mNitsToBrightnessSpline = Spline.createSpline(nits, mBrightness);
mBrightnessToNitsSpline = Spline.createSpline(mBrightness, nits);
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 77ab813..3f55848 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -42,6 +42,7 @@
import android.annotation.UserIdInt;
import android.app.AppOpsManager;
import android.app.compat.CompatChanges;
+import android.companion.virtual.IVirtualDevice;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.content.Context;
@@ -126,6 +127,7 @@
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.UiThread;
+import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import com.android.server.display.DisplayDeviceConfig.SensorData;
import com.android.server.display.utils.SensorUtils;
import com.android.server.wm.SurfaceAnimationThread;
@@ -244,9 +246,12 @@
public final SparseArray<CallbackRecord> mCallbacks =
new SparseArray<CallbackRecord>();
- /** All {@link DisplayWindowPolicyController}s indexed by {@link DisplayInfo#displayId}. */
- final SparseArray<DisplayWindowPolicyController> mDisplayWindowPolicyController =
- new SparseArray<>();
+ /**
+ * All {@link IVirtualDevice} and {@link DisplayWindowPolicyController}s indexed by
+ * {@link DisplayInfo#displayId}.
+ */
+ final SparseArray<Pair<IVirtualDevice, DisplayWindowPolicyController>>
+ mDisplayWindowPolicyControllers = new SparseArray<>();
// List of all currently registered display adapters.
private final ArrayList<DisplayAdapter> mDisplayAdapters = new ArrayList<DisplayAdapter>();
@@ -1180,8 +1185,8 @@
}
private int createVirtualDisplayInternal(VirtualDisplayConfig virtualDisplayConfig,
- IVirtualDisplayCallback callback, IMediaProjection projection, String packageName,
- DisplayWindowPolicyController controller) {
+ IVirtualDisplayCallback callback, IMediaProjection projection,
+ IVirtualDevice virtualDevice, String packageName) {
final int callingUid = Binder.getCallingUid();
if (!validatePackageName(callingUid, packageName)) {
throw new SecurityException("packageName must match the calling uid");
@@ -1226,6 +1231,14 @@
}
}
+ if (virtualDevice != null) {
+ final VirtualDeviceManagerInternal vdm =
+ getLocalService(VirtualDeviceManagerInternal.class);
+ if (!vdm.isValidVirtualDevice(virtualDevice)) {
+ throw new SecurityException("Invalid virtual device");
+ }
+ }
+
if (callingUid != Process.SYSTEM_UID
&& (flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) {
if (!canProjectVideo(projection)) {
@@ -1306,8 +1319,8 @@
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSyncRoot) {
- return createVirtualDisplayLocked(callback, projection, callingUid, packageName,
- surface, flags, virtualDisplayConfig, controller);
+ return createVirtualDisplayLocked(callback, projection, virtualDevice, callingUid,
+ packageName, surface, flags, virtualDisplayConfig);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -1315,9 +1328,9 @@
}
private int createVirtualDisplayLocked(IVirtualDisplayCallback callback,
- IMediaProjection projection, int callingUid, String packageName, Surface surface,
- int flags, VirtualDisplayConfig virtualDisplayConfig,
- DisplayWindowPolicyController controller) {
+ IMediaProjection projection, IVirtualDevice virtualDevice,
+ int callingUid, String packageName, Surface surface,
+ int flags, VirtualDisplayConfig virtualDisplayConfig) {
if (mVirtualDisplayAdapter == null) {
Slog.w(TAG, "Rejecting request to create private virtual display "
+ "because the virtual display adapter is not available.");
@@ -1344,10 +1357,16 @@
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device);
if (display != null) {
- if (controller != null) {
- mDisplayWindowPolicyController.put(display.getDisplayIdLocked(), controller);
+ final int displayId = display.getDisplayIdLocked();
+ if (virtualDevice != null) {
+ final VirtualDeviceManagerInternal vdm =
+ getLocalService(VirtualDeviceManagerInternal.class);
+ final DisplayWindowPolicyController controller =
+ vdm.onVirtualDisplayCreated(virtualDevice, displayId);
+ mDisplayWindowPolicyControllers.put(displayId,
+ Pair.create(virtualDevice, controller));
}
- return display.getDisplayIdLocked();
+ return displayId;
}
// Something weird happened and the logical display was not created.
@@ -1391,7 +1410,13 @@
if (device != null) {
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device);
if (display != null) {
- mDisplayWindowPolicyController.delete(display.getDisplayIdLocked());
+ final int displayId = display.getDisplayIdLocked();
+ if (mDisplayWindowPolicyControllers.contains(displayId)) {
+ Pair<IVirtualDevice, DisplayWindowPolicyController> pair =
+ mDisplayWindowPolicyControllers.removeReturnOld(displayId);
+ getLocalService(VirtualDeviceManagerInternal.class)
+ .onVirtualDisplayRemoved(pair.first, displayId);
+ }
}
// TODO: multi-display - handle virtual displays the same as other display adapters.
mDisplayDeviceRepo.onDisplayDeviceEvent(device,
@@ -2345,13 +2370,13 @@
pw.println();
mPersistentDataStore.dump(pw);
- final int displayWindowPolicyControllerCount = mDisplayWindowPolicyController.size();
+ final int displayWindowPolicyControllerCount = mDisplayWindowPolicyControllers.size();
pw.println();
pw.println("Display Window Policy Controllers: size="
+ displayWindowPolicyControllerCount);
for (int i = 0; i < displayWindowPolicyControllerCount; i++) {
- pw.print("Display " + mDisplayWindowPolicyController.keyAt(i) + ":");
- mDisplayWindowPolicyController.valueAt(i).dump(" ", pw);
+ pw.print("Display " + mDisplayWindowPolicyControllers.keyAt(i) + ":");
+ mDisplayWindowPolicyControllers.valueAt(i).second.dump(" ", pw);
}
}
pw.println();
@@ -2922,9 +2947,10 @@
@Override // Binder call
public int createVirtualDisplay(VirtualDisplayConfig virtualDisplayConfig,
- IVirtualDisplayCallback callback, IMediaProjection projection, String packageName) {
+ IVirtualDisplayCallback callback, IMediaProjection projection,
+ IVirtualDevice virtualDeviceToken, String packageName) {
return createVirtualDisplayInternal(virtualDisplayConfig, callback, projection,
- packageName, null /* controller */);
+ virtualDeviceToken, packageName);
}
@Override // Binder call
@@ -3690,17 +3716,12 @@
}
@Override
- public int createVirtualDisplay(VirtualDisplayConfig virtualDisplayConfig,
- IVirtualDisplayCallback callback, IMediaProjection projection, String packageName,
- DisplayWindowPolicyController controller) {
- return createVirtualDisplayInternal(virtualDisplayConfig, callback, projection,
- packageName, controller);
- }
-
- @Override
public DisplayWindowPolicyController getDisplayWindowPolicyController(int displayId) {
synchronized (mSyncRoot) {
- return mDisplayWindowPolicyController.get(displayId);
+ if (mDisplayWindowPolicyControllers.contains(displayId)) {
+ return mDisplayWindowPolicyControllers.get(displayId).second;
+ }
+ return null;
}
}
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 7f78cac..b46ee27 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -386,12 +386,8 @@
private Sensor mLightSensor;
- // The mapper between ambient lux, display backlight values, and display brightness.
- // This mapper holds the current one that is being used. We will switch between the idle
- // mapper and active mapper here.
- @Nullable
- private BrightnessMappingStrategy mCurrentBrightnessMapper;
-
+ // The mappers between ambient lux, display backlight values, and display brightness.
+ // We will switch between the idle mapper and active mapper in AutomaticBrightnessController.
// Mapper used for active (normal) screen brightness mode
@Nullable
private BrightnessMappingStrategy mInteractiveModeBrightnessMapper;
@@ -399,6 +395,11 @@
@Nullable
private BrightnessMappingStrategy mIdleModeBrightnessMapper;
+ // If these are both true, and mIdleModeBrightnessMapper != null,
+ // then we are in idle screen brightness mode.
+ private boolean mIsDreaming;
+ private boolean mIsDocked;
+
// The current brightness configuration.
@Nullable
private BrightnessConfiguration mBrightnessConfiguration;
@@ -610,8 +611,14 @@
}
private void handleRbcChanged(boolean strengthChanged, boolean justActivated) {
- if (mCurrentBrightnessMapper == null) {
- Log.w(TAG, "No brightness mapping available to recalculate splines");
+ if (mAutomaticBrightnessController == null) {
+ return;
+ }
+ if ((!mAutomaticBrightnessController.isInIdleMode()
+ && mInteractiveModeBrightnessMapper == null)
+ || (mAutomaticBrightnessController.isInIdleMode()
+ && mIdleModeBrightnessMapper == null)) {
+ Log.w(TAG, "No brightness mapping available to recalculate splines for this mode");
return;
}
@@ -619,7 +626,7 @@
for (int i = 0; i < mNitsRange.length; i++) {
adjustedNits[i] = mCdsi.getReduceBrightColorsAdjustedBrightnessNits(mNitsRange[i]);
}
- mCurrentBrightnessMapper.recalculateSplines(mCdsi.isReduceBrightColorsActivated(),
+ mAutomaticBrightnessController.recalculateSplines(mCdsi.isReduceBrightColorsActivated(),
adjustedNits);
mPendingRbcOnOrChanged = strengthChanged || justActivated;
@@ -886,9 +893,8 @@
mIdleModeBrightnessMapper = BrightnessMappingStrategy.createForIdleMode(resources,
mDisplayDeviceConfig);
}
- mCurrentBrightnessMapper = mInteractiveModeBrightnessMapper;
- if (mCurrentBrightnessMapper != null) {
+ if (mInteractiveModeBrightnessMapper != null) {
final float dozeScaleFactor = resources.getFraction(
com.android.internal.R.fraction.config_screenAutoBrightnessDozeScaleFactor,
1, 1);
@@ -939,12 +945,13 @@
mAutomaticBrightnessController.stop();
}
mAutomaticBrightnessController = new AutomaticBrightnessController(this,
- handler.getLooper(), mSensorManager, mLightSensor, mCurrentBrightnessMapper,
- lightSensorWarmUpTimeConfig, PowerManager.BRIGHTNESS_MIN,
- PowerManager.BRIGHTNESS_MAX, dozeScaleFactor, lightSensorRate,
- initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce,
- autoBrightnessResetAmbientLuxAfterWarmUp, ambientBrightnessThresholds,
- screenBrightnessThresholds, mContext, mHbmController);
+ handler.getLooper(), mSensorManager, mLightSensor,
+ mInteractiveModeBrightnessMapper, lightSensorWarmUpTimeConfig,
+ PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, dozeScaleFactor,
+ lightSensorRate, initialLightSensorRate, brighteningLightDebounce,
+ darkeningLightDebounce, autoBrightnessResetAmbientLuxAfterWarmUp,
+ ambientBrightnessThresholds, screenBrightnessThresholds, mContext,
+ mHbmController, mIdleModeBrightnessMapper);
} else {
mUseSoftwareAutoBrightnessConfig = false;
}
@@ -974,6 +981,16 @@
}
}
+ public void setAutomaticScreenBrightnessMode(boolean isIdle) {
+ if (mAutomaticBrightnessController != null) {
+ if (isIdle) {
+ mAutomaticBrightnessController.switchToIdleMode();
+ } else {
+ mAutomaticBrightnessController.switchToInteractiveScreenBrightnessMode();
+ }
+ }
+ }
+
private final Animator.AnimatorListener mAnimatorListener = new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
@@ -1422,9 +1439,14 @@
}
}
- if (!brightnessIsTemporary) {
+ // Report brightness to brightnesstracker:
+ // If brightness is not temporary (ie the slider has been released)
+ // AND if we are not in idle screen brightness mode.
+ if (!brightnessIsTemporary
+ && (mAutomaticBrightnessController != null
+ && !mAutomaticBrightnessController.isInIdleMode())) {
if (userInitiatedChange && (mAutomaticBrightnessController == null
- || !mAutomaticBrightnessController.hasValidAmbientLux())) {
+ || !mAutomaticBrightnessController.hasValidAmbientLux())) {
// If we don't have a valid lux reading we can't report a valid
// slider event so notify as if the system changed the brightness.
userInitiatedChange = false;
@@ -2162,11 +2184,10 @@
}
private float convertToNits(float brightness) {
- if (mCurrentBrightnessMapper != null) {
- return mCurrentBrightnessMapper.convertToNits(brightness);
- } else {
- return -1.0f;
+ if (mAutomaticBrightnessController == null) {
+ return -1f;
}
+ return mAutomaticBrightnessController.convertToNits(brightness);
}
private void updatePendingProximityRequestsLocked() {
@@ -2315,11 +2336,6 @@
pw.println(" mReportedToPolicy=" +
reportedToPolicyToString(mReportedScreenStateToPolicy));
- if (mIdleModeBrightnessMapper != null) {
- pw.println(" mIdleModeBrightnessMapper= ");
- mIdleModeBrightnessMapper.dump(pw);
- }
-
if (mScreenBrightnessRampAnimator != null) {
pw.println(" mScreenBrightnessRampAnimator.isAnimating()=" +
mScreenBrightnessRampAnimator.isAnimating());
diff --git a/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java b/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java
new file mode 100644
index 0000000..b2bd47b
--- /dev/null
+++ b/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java
@@ -0,0 +1,615 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locales;
+
+import static android.os.UserHandle.USER_NULL;
+
+import static com.android.server.locales.LocaleManagerService.DEBUG;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.backup.BackupManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.os.BestClock;
+import android.os.Binder;
+import android.os.Environment;
+import android.os.HandlerThread;
+import android.os.LocaleList;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.content.PackageMonitor;
+import com.android.internal.util.XmlUtils;
+
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.StandardCharsets;
+import java.time.Clock;
+import java.time.Duration;
+import java.time.ZoneOffset;
+import java.util.HashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Helper class for managing backup and restore of app-specific locales.
+ */
+class LocaleManagerBackupHelper {
+ private static final String TAG = "LocaleManagerBkpHelper"; // must be < 23 chars
+
+ // Tags and attributes for xml.
+ private static final String LOCALES_XML_TAG = "locales";
+ private static final String PACKAGE_XML_TAG = "package";
+ private static final String ATTR_PACKAGE_NAME = "name";
+ private static final String ATTR_LOCALES = "locales";
+ private static final String ATTR_CREATION_TIME_MILLIS = "creationTimeMillis";
+
+ private static final String STAGE_FILE_NAME = "staged_locales";
+ private static final String SYSTEM_BACKUP_PACKAGE_KEY = "android";
+
+ private static final Pattern STAGE_FILE_NAME_PATTERN = Pattern.compile(
+ TextUtils.formatSimple("(^%s_)(\\d+)(\\.xml$)", STAGE_FILE_NAME));
+ private static final int USER_ID_GROUP_INDEX_IN_PATTERN = 2;
+ private static final Duration STAGE_FILE_RETENTION_PERIOD = Duration.ofDays(3);
+
+ private final LocaleManagerService mLocaleManagerService;
+ private final PackageManagerInternal mPackageManagerInternal;
+ private final File mStagedLocalesDir;
+ private final Clock mClock;
+ private final Context mContext;
+ private final Object mStagedDataLock = new Object();
+
+ // Staged data map keyed by user-id to handle multi-user scenario / work profiles. We are using
+ // SparseArray because it is more memory-efficient than a HashMap.
+ private final SparseArray<StagedData> mStagedData = new SparseArray<>();
+
+ private final PackageMonitor mPackageMonitor;
+ private final BroadcastReceiver mUserMonitor;
+
+ LocaleManagerBackupHelper(LocaleManagerService localeManagerService,
+ PackageManagerInternal pmInternal) {
+ this(localeManagerService.mContext, localeManagerService, pmInternal,
+ new File(Environment.getDataSystemCeDirectory(),
+ "app_locales"), getDefaultClock());
+ }
+
+ private static @NonNull Clock getDefaultClock() {
+ return new BestClock(ZoneOffset.UTC, SystemClock.currentNetworkTimeClock(),
+ Clock.systemUTC());
+ }
+
+ @VisibleForTesting LocaleManagerBackupHelper(Context context,
+ LocaleManagerService localeManagerService,
+ PackageManagerInternal pmInternal, File stagedLocalesDir, Clock clock) {
+ mContext = context;
+ mLocaleManagerService = localeManagerService;
+ mPackageManagerInternal = pmInternal;
+ mClock = clock;
+ mStagedLocalesDir = stagedLocalesDir;
+
+ loadAllStageFiles();
+
+ HandlerThread broadcastHandlerThread = new HandlerThread(TAG,
+ Process.THREAD_PRIORITY_BACKGROUND);
+ broadcastHandlerThread.start();
+
+ mPackageMonitor = new PackageMonitorImpl();
+ mPackageMonitor.register(context, broadcastHandlerThread.getLooper(),
+ UserHandle.ALL,
+ true);
+ mUserMonitor = new UserMonitor();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_USER_REMOVED);
+ context.registerReceiverAsUser(mUserMonitor, UserHandle.ALL, filter,
+ null, broadcastHandlerThread.getThreadHandler());
+ }
+
+ @VisibleForTesting
+ BroadcastReceiver getUserMonitor() {
+ return mUserMonitor;
+ }
+
+ @VisibleForTesting
+ PackageMonitor getPackageMonitor() {
+ return mPackageMonitor;
+ }
+
+ /**
+ * Loads the staged data into memory by reading all the files in the staged directory.
+ *
+ * <p><b>Note:</b> We don't ned to hold the lock here because this is only called in the
+ * constructor (before any broadcast receivers are registered).
+ */
+ private void loadAllStageFiles() {
+ File[] files = mStagedLocalesDir.listFiles();
+ if (files == null) {
+ return;
+ }
+ for (File file : files) {
+ String fileName = file.getName();
+ Matcher matcher = STAGE_FILE_NAME_PATTERN.matcher(fileName);
+ if (!matcher.matches()) {
+ file.delete();
+ Slog.w(TAG, TextUtils.formatSimple("Deleted %s. Reason: %s.", fileName,
+ "Unrecognized file"));
+ continue;
+ }
+ try {
+ final int userId = Integer.parseInt(matcher.group(USER_ID_GROUP_INDEX_IN_PATTERN));
+ StagedData stagedData = readStageFile(file);
+ if (stagedData != null) {
+ mStagedData.put(userId, stagedData);
+ } else {
+ file.delete();
+ Slog.w(TAG, TextUtils.formatSimple("Deleted %s. Reason: %s.", fileName,
+ "Could not read file"));
+ }
+ } catch (NumberFormatException e) {
+ file.delete();
+ Slog.w(TAG, TextUtils.formatSimple("Deleted %s. Reason: %s.", fileName,
+ "Could not parse user id from file name"));
+ }
+ }
+ }
+
+ /**
+ * Loads the stage file from the disk and parses it into a list of app backups.
+ */
+ private @Nullable StagedData readStageFile(@NonNull File file) {
+ InputStream stagedDataInputStream = null;
+ AtomicFile stageFile = new AtomicFile(file);
+ try {
+ stagedDataInputStream = stageFile.openRead();
+ final TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(stagedDataInputStream, StandardCharsets.UTF_8.name());
+
+ XmlUtils.beginDocument(parser, LOCALES_XML_TAG);
+ long creationTimeMillis = parser.getAttributeLong(/* namespace= */ null,
+ ATTR_CREATION_TIME_MILLIS);
+ return new StagedData(creationTimeMillis, readFromXml(parser));
+ } catch (IOException | XmlPullParserException e) {
+ Slog.e(TAG, "Could not parse stage file ", e);
+ } finally {
+ IoUtils.closeQuietly(stagedDataInputStream);
+ }
+ return null;
+ }
+
+ /**
+ * @see LocaleManagerInternal#getBackupPayload(int userId)
+ */
+ public byte[] getBackupPayload(int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "getBackupPayload invoked for user id " + userId);
+ }
+
+ synchronized (mStagedDataLock) {
+ cleanStagedDataForOldEntriesLocked();
+ }
+
+ HashMap<String, String> pkgStates = new HashMap<>();
+ for (ApplicationInfo appInfo : mPackageManagerInternal.getInstalledApplications(/*flags*/0,
+ userId, Binder.getCallingUid())) {
+ try {
+ LocaleList appLocales = mLocaleManagerService.getApplicationLocales(
+ appInfo.packageName,
+ userId);
+ // Backup locales only for apps which do have app-specific overrides.
+ if (!appLocales.isEmpty()) {
+ if (DEBUG) {
+ Slog.d(TAG, "Add package=" + appInfo.packageName + " locales="
+ + appLocales.toLanguageTags() + " to backup payload");
+ }
+ pkgStates.put(appInfo.packageName, appLocales.toLanguageTags());
+ }
+ } catch (RemoteException | IllegalArgumentException e) {
+ Slog.e(TAG, "Exception when getting locales for package: " + appInfo.packageName,
+ e);
+ }
+ }
+
+ if (pkgStates.isEmpty()) {
+ if (DEBUG) {
+ Slog.d(TAG, "Final payload=null");
+ }
+ // Returning null here will ensure deletion of the entry for LMS from the backup data.
+ return null;
+ }
+
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ try {
+ // Passing arbitrary value for creationTimeMillis since it is ignored when forStage
+ // is false.
+ writeToXml(out, pkgStates, /* forStage= */ false, /* creationTimeMillis= */ -1);
+ } catch (IOException e) {
+ Slog.e(TAG, "Could not write to xml for backup ", e);
+ return null;
+ }
+
+ if (DEBUG) {
+ try {
+ Slog.d(TAG, "Final payload=" + out.toString("UTF-8"));
+ } catch (UnsupportedEncodingException e) {
+ Slog.w(TAG, "Could not encode payload to UTF-8", e);
+ }
+ }
+ return out.toByteArray();
+ }
+
+ private void cleanStagedDataForOldEntriesLocked() {
+ for (int i = 0; i < mStagedData.size(); i++) {
+ int userId = mStagedData.keyAt(i);
+ StagedData stagedData = mStagedData.get(userId);
+ if (stagedData.mCreationTimeMillis
+ < mClock.millis() - STAGE_FILE_RETENTION_PERIOD.toMillis()) {
+ deleteStagedDataLocked(userId);
+ }
+ }
+ }
+
+ /**
+ * @see LocaleManagerInternal#stageAndApplyRestoredPayload(byte[] payload, int userId)
+ */
+ public void stageAndApplyRestoredPayload(byte[] payload, int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "stageAndApplyRestoredPayload user=" + userId + " payload="
+ + (payload != null ? new String(payload, StandardCharsets.UTF_8) : null));
+ }
+ if (payload == null) {
+ Slog.e(TAG, "stageAndApplyRestoredPayload: no payload to restore for user " + userId);
+ return;
+ }
+
+ final ByteArrayInputStream inputStream = new ByteArrayInputStream(payload);
+
+ HashMap<String, String> pkgStates = new HashMap<>();
+ try {
+ // Parse the input blob into a list of BackupPackageState.
+ final TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(inputStream, StandardCharsets.UTF_8.name());
+
+ XmlUtils.beginDocument(parser, LOCALES_XML_TAG);
+ pkgStates = readFromXml(parser);
+ } catch (IOException | XmlPullParserException e) {
+ Slog.e(TAG, "Could not parse payload ", e);
+ }
+
+ // We need a lock here to prevent race conditions when accessing the stage file.
+ // It might happen that a restore was triggered (manually using bmgr cmd) and at the same
+ // time a new package is added. We want to ensure that both these operations aren't
+ // performed simultaneously.
+ synchronized (mStagedDataLock) {
+ // Backups for apps which are yet to be installed.
+ mStagedData.put(userId, new StagedData(mClock.millis(), new HashMap<>()));
+
+ for (String pkgName : pkgStates.keySet()) {
+ String languageTags = pkgStates.get(pkgName);
+ // Check if the application is already installed for the concerned user.
+ if (isPackageInstalledForUser(pkgName, userId)) {
+ // Don't apply the restore if the locales have already been set for the app.
+ checkExistingLocalesAndApplyRestore(pkgName, languageTags, userId);
+ } else {
+ // Stage the data if the app isn't installed.
+ mStagedData.get(userId).mPackageStates.put(pkgName, languageTags);
+ if (DEBUG) {
+ Slog.d(TAG, "Add locales=" + languageTags
+ + " package=" + pkgName + " for lazy restore.");
+ }
+ }
+ }
+
+ writeStageFileLocked(userId);
+ }
+ }
+
+ /**
+ * Notifies the backup manager to include the "android" package in the next backup pass.
+ */
+ public void notifyBackupManager() {
+ BackupManager.dataChanged(SYSTEM_BACKUP_PACKAGE_KEY);
+ }
+
+ private boolean isPackageInstalledForUser(String packageName, int userId) {
+ PackageInfo pkgInfo = null;
+ try {
+ pkgInfo = mContext.getPackageManager().getPackageInfoAsUser(
+ packageName, /* flags= */ 0, userId);
+ } catch (PackageManager.NameNotFoundException e) {
+ if (DEBUG) {
+ Slog.d(TAG, "Could not get package info for " + packageName, e);
+ }
+ }
+ return pkgInfo != null;
+ }
+
+ /**
+ * Checks if locales already exist for the application and applies the restore accordingly.
+ * <p>
+ * The user might change the locales for an application before the restore is applied. In this
+ * case, we want to keep the user settings and discard the restore.
+ */
+ private void checkExistingLocalesAndApplyRestore(@NonNull String pkgName,
+ @NonNull String languageTags, int userId) {
+ try {
+ LocaleList currLocales = mLocaleManagerService.getApplicationLocales(
+ pkgName,
+ userId);
+ if (!currLocales.isEmpty()) {
+ return;
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Could not check for current locales before restoring", e);
+ }
+
+ // Restore the locale immediately
+ try {
+ mLocaleManagerService.setApplicationLocales(pkgName, userId,
+ LocaleList.forLanguageTags(languageTags));
+ if (DEBUG) {
+ Slog.d(TAG, "Restored locales=" + languageTags + " for package=" + pkgName);
+ }
+ } catch (RemoteException | IllegalArgumentException e) {
+ Slog.e(TAG, "Could not restore locales for " + pkgName, e);
+ }
+ }
+
+ /**
+ * Converts the list of app backups into xml and writes it onto the disk.
+ */
+ private void writeStageFileLocked(int userId) {
+ StagedData stagedData = mStagedData.get(userId);
+ if (stagedData.mPackageStates.isEmpty()) {
+ deleteStagedDataLocked(userId);
+ return;
+ }
+
+ final FileOutputStream stagedDataOutputStream;
+ AtomicFile stageFile = new AtomicFile(
+ new File(mStagedLocalesDir,
+ TextUtils.formatSimple("%s_%d.xml", STAGE_FILE_NAME, userId)));
+ try {
+ stagedDataOutputStream = stageFile.startWrite();
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to save stage file");
+ return;
+ }
+
+ try {
+ writeToXml(stagedDataOutputStream, stagedData.mPackageStates, /* forStage= */ true,
+ stagedData.mCreationTimeMillis);
+ stageFile.finishWrite(stagedDataOutputStream);
+ if (DEBUG) {
+ Slog.d(TAG, "Stage file written.");
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Could not write stage file", e);
+ stageFile.failWrite(stagedDataOutputStream);
+ }
+ }
+
+ private void deleteStagedDataLocked(@UserIdInt int userId) {
+ AtomicFile stageFile = getStageFileIfExistsLocked(userId);
+ if (stageFile != null) {
+ stageFile.delete();
+ }
+ mStagedData.remove(userId);
+ }
+
+ private @Nullable AtomicFile getStageFileIfExistsLocked(@UserIdInt int userId) {
+ final File stageFile = new File(mStagedLocalesDir,
+ TextUtils.formatSimple("%s_%d.xml", STAGE_FILE_NAME, userId));
+ return stageFile.isFile() ? new AtomicFile(stageFile)
+ : null;
+ }
+
+ /**
+ * Parses the backup data from the serialized xml input stream.
+ */
+ private @NonNull HashMap<String, String> readFromXml(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ HashMap<String, String> packageStates = new HashMap<>();
+ int depth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, depth)) {
+ if (parser.getName().equals(PACKAGE_XML_TAG)) {
+ String packageName = parser.getAttributeValue(/* namespace= */ null,
+ ATTR_PACKAGE_NAME);
+ String languageTags = parser.getAttributeValue(/* namespace= */ null, ATTR_LOCALES);
+
+ if (!TextUtils.isEmpty(packageName) && !TextUtils.isEmpty(languageTags)) {
+ packageStates.put(packageName, languageTags);
+ }
+ }
+ }
+ return packageStates;
+ }
+
+ /**
+ * Converts the list of app backup data into a serialized xml stream.
+ *
+ * @param forStage Flag to indicate whether this method is called for the purpose of
+ * staging the data. Note that if this is false, {@code creationTimeMillis} is ignored because
+ * we only need it for the stage data.
+ * @param creationTimeMillis The timestamp when the stage data was created. This is required
+ * to determine when to delete the stage data.
+ */
+ private static void writeToXml(OutputStream stream,
+ @NonNull HashMap<String, String> pkgStates, boolean forStage, long creationTimeMillis)
+ throws IOException {
+ if (pkgStates.isEmpty()) {
+ // No need to write anything at all if pkgStates is empty.
+ return;
+ }
+
+ TypedXmlSerializer out = Xml.newFastSerializer();
+ out.setOutput(stream, StandardCharsets.UTF_8.name());
+ out.startDocument(/* encoding= */ null, /* standalone= */ true);
+ out.startTag(/* namespace= */ null, LOCALES_XML_TAG);
+
+ if (forStage) {
+ out.attribute(/* namespace= */ null, ATTR_CREATION_TIME_MILLIS,
+ Long.toString(creationTimeMillis));
+ }
+
+ for (String pkg : pkgStates.keySet()) {
+ out.startTag(/* namespace= */ null, PACKAGE_XML_TAG);
+ out.attribute(/* namespace= */ null, ATTR_PACKAGE_NAME, pkg);
+ out.attribute(/* namespace= */ null, ATTR_LOCALES, pkgStates.get(pkg));
+ out.endTag(/*namespace= */ null, PACKAGE_XML_TAG);
+ }
+
+ out.endTag(/* namespace= */ null, LOCALES_XML_TAG);
+ out.endDocument();
+ }
+
+ private static class StagedData {
+ final long mCreationTimeMillis;
+ final HashMap<String, String> mPackageStates;
+
+ StagedData(long creationTimeMillis, HashMap<String, String> pkgStates) {
+ mCreationTimeMillis = creationTimeMillis;
+ mPackageStates = pkgStates;
+ }
+ }
+
+ /**
+ * Broadcast listener to capture user removed event.
+ *
+ * <p>The stage file is deleted when a user is removed.
+ */
+ private final class UserMonitor extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ try {
+ String action = intent.getAction();
+ if (action.equals(Intent.ACTION_USER_REMOVED)) {
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
+ synchronized (mStagedDataLock) {
+ deleteStagedDataLocked(userId);
+ }
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, "Exception in user monitor.", e);
+ }
+ }
+ }
+
+ /**
+ * Helper to monitor package states.
+ *
+ * <p>We're interested in package added, package data cleared and package removed events.
+ */
+ private final class PackageMonitorImpl extends PackageMonitor {
+ @Override
+ public void onPackageAdded(String packageName, int uid) {
+ try {
+ synchronized (mStagedDataLock) {
+ int userId = UserHandle.getUserId(uid);
+ if (mStagedData.contains(userId)) {
+ // Perform lazy restore only if the staged data exists.
+ doLazyRestoreLocked(packageName, userId);
+ }
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, "Exception in onPackageAdded.", e);
+ }
+ }
+
+ @Override
+ public void onPackageDataCleared(String packageName, int uid) {
+ try {
+ notifyBackupManager();
+ } catch (Exception e) {
+ Slog.e(TAG, "Exception in onPackageDataCleared.", e);
+ }
+ }
+
+ @Override
+ public void onPackageRemoved(String packageName, int uid) {
+ try {
+ notifyBackupManager();
+ } catch (Exception e) {
+ Slog.e(TAG, "Exception in onPackageRemoved.", e);
+ }
+ }
+ }
+
+ /**
+ * Performs lazy restore from the staged data.
+ *
+ * <p>This is invoked by the package monitor on the package added callback.
+ */
+ private void doLazyRestoreLocked(String packageName, int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "doLazyRestore package=" + packageName + " user=" + userId);
+ }
+
+ // Check if the package is installed indeed
+ if (!isPackageInstalledForUser(packageName, userId)) {
+ Slog.e(TAG, packageName + " not installed for user " + userId
+ + ". Could not restore locales from stage file");
+ return;
+ }
+
+ StagedData stagedData = mStagedData.get(userId);
+ for (String pkgName : stagedData.mPackageStates.keySet()) {
+ String languageTags = stagedData.mPackageStates.get(pkgName);
+
+ if (pkgName.equals(packageName)) {
+
+ checkExistingLocalesAndApplyRestore(pkgName, languageTags, userId);
+
+ // Remove the restored entry from the staged data list.
+ stagedData.mPackageStates.remove(pkgName);
+ // Update the file on the disk.
+ writeStageFileLocked(userId);
+
+ // No need to loop further after restoring locales because the staged data will
+ // contain at most one entry for the newly added package.
+ break;
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/locales/LocaleManagerInternal.java b/services/core/java/com/android/server/locales/LocaleManagerInternal.java
new file mode 100644
index 0000000..2db92a5
--- /dev/null
+++ b/services/core/java/com/android/server/locales/LocaleManagerInternal.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locales;
+
+import android.annotation.Nullable;
+
+/**
+ * System-server internal interface to the {@link LocaleManagerService}.
+ *
+ * @hide Only for use within the system server.
+ */
+public abstract class LocaleManagerInternal {
+ /**
+ * Returns the app-specific locales to be backed up as a data-blob.
+ */
+ public abstract @Nullable byte[] getBackupPayload(int userId);
+
+ /**
+ * Restores the app-locales that were previously backed up.
+ *
+ * <p>This method will parse the input data blob and restore the locales for apps which are
+ * present on the device. It will stage the locale data for the apps which are not installed
+ * at the time this is called, to be referenced later when the app is installed.
+ */
+ public abstract void stageAndApplyRestoredPayload(byte[] payload, int userId);
+}
diff --git a/services/core/java/com/android/server/locales/LocaleManagerService.java b/services/core/java/com/android/server/locales/LocaleManagerService.java
index 457c2fd..6aabdb5 100644
--- a/services/core/java/com/android/server/locales/LocaleManagerService.java
+++ b/services/core/java/com/android/server/locales/LocaleManagerService.java
@@ -20,6 +20,7 @@
import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManagerInternal;
import android.app.ILocaleManager;
@@ -29,6 +30,7 @@
import android.content.pm.PackageManagerInternal;
import android.os.Binder;
import android.os.LocaleList;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
@@ -51,11 +53,14 @@
*/
public class LocaleManagerService extends SystemService {
private static final String TAG = "LocaleManagerService";
- private final Context mContext;
+ final Context mContext;
private final LocaleManagerService.LocaleManagerBinderService mBinderService;
private ActivityTaskManagerInternal mActivityTaskManagerInternal;
private ActivityManagerInternal mActivityManagerInternal;
private PackageManagerInternal mPackageManagerInternal;
+
+ private LocaleManagerBackupHelper mBackupHelper;
+
public static final boolean DEBUG = false;
public LocaleManagerService(Context context) {
@@ -65,23 +70,48 @@
mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+ mBackupHelper = new LocaleManagerBackupHelper(this,
+ mPackageManagerInternal);
}
@VisibleForTesting
LocaleManagerService(Context context, ActivityTaskManagerInternal activityTaskManagerInternal,
ActivityManagerInternal activityManagerInternal,
- PackageManagerInternal packageManagerInternal) {
+ PackageManagerInternal packageManagerInternal,
+ LocaleManagerBackupHelper localeManagerBackupHelper) {
super(context);
mContext = context;
mBinderService = new LocaleManagerBinderService();
mActivityTaskManagerInternal = activityTaskManagerInternal;
mActivityManagerInternal = activityManagerInternal;
mPackageManagerInternal = packageManagerInternal;
+ mBackupHelper = localeManagerBackupHelper;
}
@Override
public void onStart() {
publishBinderService(Context.LOCALE_SERVICE, mBinderService);
+ LocalServices.addService(LocaleManagerInternal.class, new LocaleManagerInternalImpl());
+ }
+
+ private final class LocaleManagerInternalImpl extends LocaleManagerInternal {
+
+ @Override
+ public @Nullable byte[] getBackupPayload(int userId) {
+ checkCallerIsSystem();
+ return mBackupHelper.getBackupPayload(userId);
+ }
+
+ @Override
+ public void stageAndApplyRestoredPayload(byte[] payload, int userId) {
+ mBackupHelper.stageAndApplyRestoredPayload(payload, userId);
+ }
+
+ private void checkCallerIsSystem() {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("Caller is not system.");
+ }
+ }
}
private final class LocaleManagerBinderService extends ILocaleManager.Stub {
@@ -110,6 +140,7 @@
(new LocaleManagerShellCommand(mBinderService))
.exec(this, in, out, err, args, callback, resultReceiver);
}
+
}
/**
@@ -161,6 +192,8 @@
notifyAppWhoseLocaleChanged(appPackageName, userId, locales);
notifyInstallerOfAppWhoseLocaleChanged(appPackageName, userId, locales);
notifyRegisteredReceivers(appPackageName, userId, locales);
+
+ mBackupHelper.notifyBackupManager();
}
}
diff --git a/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java b/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
index 22a675a..5e38bca 100644
--- a/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
@@ -23,6 +23,8 @@
import static com.android.server.location.LocationManagerService.TAG;
import static com.android.server.location.eventlog.LocationEventLog.EVENT_LOG;
+import static java.lang.Math.max;
+
import android.annotation.Nullable;
import android.location.Location;
import android.location.LocationResult;
@@ -53,6 +55,7 @@
implements DeviceIdleHelper.DeviceIdleListener, DeviceIdleInternal.StationaryListener {
private static final long MAX_STATIONARY_LOCATION_AGE_MS = 30000;
+ private static final long MIN_INTERVAL_MS = 1000;
final Object mLock = new Object();
@@ -179,7 +182,7 @@
&& mLastLocation != null
&& mLastLocation.getElapsedRealtimeAgeMillis(mDeviceStationaryRealtimeMs)
<= MAX_STATIONARY_LOCATION_AGE_MS) {
- throttlingIntervalMs = mIncomingRequest.getIntervalMillis();
+ throttlingIntervalMs = max(mIncomingRequest.getIntervalMillis(), MIN_INTERVAL_MS);
}
ProviderRequest newRequest;
diff --git a/services/core/java/com/android/server/notification/PermissionHelper.java b/services/core/java/com/android/server/notification/PermissionHelper.java
index e64ec77..99cb6f0 100644
--- a/services/core/java/com/android/server/notification/PermissionHelper.java
+++ b/services/core/java/com/android/server/notification/PermissionHelper.java
@@ -27,6 +27,7 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
+import android.os.Binder;
import android.os.RemoteException;
import android.permission.IPermissionManager;
import android.util.ArrayMap;
@@ -73,7 +74,12 @@
*/
public boolean hasPermission(int uid) {
assertFlag();
- return mPmi.checkUidPermission(uid, NOTIFICATION_PERMISSION) == PERMISSION_GRANTED;
+ final long callingId = Binder.clearCallingIdentity();
+ try {
+ return mPmi.checkUidPermission(uid, NOTIFICATION_PERMISSION) == PERMISSION_GRANTED;
+ } finally {
+ Binder.restoreCallingIdentity(callingId);
+ }
}
/**
@@ -185,27 +191,37 @@
public boolean isPermissionFixed(String packageName, @UserIdInt int userId) {
assertFlag();
+ final long callingId = Binder.clearCallingIdentity();
try {
- int flags = mPermManager.getPermissionFlags(packageName, NOTIFICATION_PERMISSION,
- userId);
- return (flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0
- || (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0;
- } catch (RemoteException e) {
- Slog.e(TAG, "Could not reach system server", e);
+ try {
+ int flags = mPermManager.getPermissionFlags(packageName, NOTIFICATION_PERMISSION,
+ userId);
+ return (flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0
+ || (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Could not reach system server", e);
+ }
+ return false;
+ } finally {
+ Binder.restoreCallingIdentity(callingId);
}
- return false;
}
boolean isPermissionUserSet(String packageName, @UserIdInt int userId) {
assertFlag();
+ final long callingId = Binder.clearCallingIdentity();
try {
- int flags = mPermManager.getPermissionFlags(packageName, NOTIFICATION_PERMISSION,
- userId);
- return (flags & PackageManager.FLAG_PERMISSION_USER_SET) != 0;
- } catch (RemoteException e) {
- Slog.e(TAG, "Could not reach system server", e);
+ try {
+ int flags = mPermManager.getPermissionFlags(packageName, NOTIFICATION_PERMISSION,
+ userId);
+ return (flags & PackageManager.FLAG_PERMISSION_USER_SET) != 0;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Could not reach system server", e);
+ }
+ return false;
+ } finally {
+ Binder.restoreCallingIdentity(callingId);
}
- return false;
}
private void assertFlag() {
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 328a55f..0f3b4bc 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -143,8 +143,9 @@
UserManager.DISALLOW_CAMERA_TOGGLE,
UserManager.DISALLOW_CHANGE_WIFI_STATE,
UserManager.DISALLOW_WIFI_TETHERING,
- UserManager.DISALLOW_SHARING_ADMIN_CONFIGURED_WIFI
-
+ UserManager.DISALLOW_SHARING_ADMIN_CONFIGURED_WIFI,
+ UserManager.DISALLOW_WIFI_DIRECT,
+ UserManager.DISALLOW_ADD_WIFI_CONFIG
});
public static final Set<String> DEPRECATED_USER_RESTRICTIONS = Sets.newArraySet(
@@ -190,7 +191,9 @@
UserManager.DISALLOW_MICROPHONE_TOGGLE,
UserManager.DISALLOW_CAMERA_TOGGLE,
UserManager.DISALLOW_CHANGE_WIFI_STATE,
- UserManager.DISALLOW_WIFI_TETHERING
+ UserManager.DISALLOW_WIFI_TETHERING,
+ UserManager.DISALLOW_WIFI_DIRECT,
+ UserManager.DISALLOW_ADD_WIFI_CONFIG
);
/**
@@ -227,7 +230,9 @@
UserManager.DISALLOW_CONFIG_DATE_TIME,
UserManager.DISALLOW_CONFIG_PRIVATE_DNS,
UserManager.DISALLOW_CHANGE_WIFI_STATE,
- UserManager.DISALLOW_WIFI_TETHERING
+ UserManager.DISALLOW_WIFI_TETHERING,
+ UserManager.DISALLOW_WIFI_DIRECT,
+ UserManager.DISALLOW_ADD_WIFI_CONFIG
);
/**
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index 6628802..e508260 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -243,21 +243,24 @@
@Override
public boolean requestFrontend(@NonNull TunerFrontendRequest request,
- @NonNull int[] frontendHandle) throws RemoteException {
+ @NonNull int[] frontendHandle) {
enforceTunerAccessPermission("requestFrontend");
enforceTrmAccessPermission("requestFrontend");
if (frontendHandle == null) {
- throw new RemoteException("frontendHandle can't be null");
+ Slog.e(TAG, "frontendHandle can't be null");
+ return false;
}
synchronized (mLock) {
if (!checkClientExists(request.clientId)) {
- throw new RemoteException("Request frontend from unregistered client: "
+ Slog.e(TAG, "Request frontend from unregistered client: "
+ request.clientId);
+ return false;
}
// If the request client is holding or sharing a frontend, throw an exception.
if (!getClientProfile(request.clientId).getInUseFrontendHandles().isEmpty()) {
- throw new RemoteException("Release frontend before requesting another one. "
- + "Client id: " + request.clientId);
+ Slog.e(TAG, "Release frontend before requesting another one. Client id: "
+ + request.clientId);
+ return false;
}
return requestFrontendInternal(request, frontendHandle);
}
@@ -1153,7 +1156,8 @@
ClientProfile ownerClient = getClientProfile(fe.getOwnerClientId());
if (ownerClient != null) {
for (int shareOwnerId : ownerClient.getShareFeClientIds()) {
- clearFrontendAndClientMapping(getClientProfile(shareOwnerId));
+ reclaimResource(shareOwnerId,
+ TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND);
}
}
}
diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
index 7b26fe0..4576957 100644
--- a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
+++ b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
@@ -226,9 +226,9 @@
.getSystemService(TelephonyManager.class)
.createForSubscriptionId(subId);
- if (!networkPriority.getAllowedPlmnIds().isEmpty()) {
+ if (!networkPriority.getAllowedOperatorPlmnIds().isEmpty()) {
final String plmnId = subIdSpecificTelephonyMgr.getNetworkOperator();
- if (!networkPriority.getAllowedPlmnIds().contains(plmnId)) {
+ if (!networkPriority.getAllowedOperatorPlmnIds().contains(plmnId)) {
return false;
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 2b28478..447f4be 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -157,6 +157,7 @@
import static com.android.server.wm.ActivityRecordProto.NUM_INTERESTING_WINDOWS;
import static com.android.server.wm.ActivityRecordProto.PIP_AUTO_ENTER_ENABLED;
import static com.android.server.wm.ActivityRecordProto.PROC_ID;
+import static com.android.server.wm.ActivityRecordProto.PROVIDES_MAX_BOUNDS;
import static com.android.server.wm.ActivityRecordProto.REPORTED_DRAWN;
import static com.android.server.wm.ActivityRecordProto.REPORTED_VISIBLE;
import static com.android.server.wm.ActivityRecordProto.STARTING_DISPLAYED;
@@ -258,6 +259,7 @@
import android.content.LocusId;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.ConstrainDisplayApisConfig;
import android.content.pm.PackageManager;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
@@ -585,6 +587,8 @@
*/
private CompatDisplayInsets mCompatDisplayInsets;
+ private static ConstrainDisplayApisConfig sConstrainDisplayApisConfig;
+
boolean pendingVoiceInteractionStart; // Waiting for activity-invoked voice session
IVoiceInteractionSession voiceSession; // Voice interaction session for this activity
@@ -1153,8 +1157,10 @@
if (info.configChanges != 0) {
pw.println(prefix + "configChanges=0x" + Integer.toHexString(info.configChanges));
}
- pw.println(prefix + "neverSandboxDisplayApis=" + info.neverSandboxDisplayApis());
- pw.println(prefix + "alwaysSandboxDisplayApis=" + info.alwaysSandboxDisplayApis());
+ pw.println(prefix + "neverSandboxDisplayApis=" + info.neverSandboxDisplayApis(
+ sConstrainDisplayApisConfig));
+ pw.println(prefix + "alwaysSandboxDisplayApis=" + info.alwaysSandboxDisplayApis(
+ sConstrainDisplayApisConfig));
}
if (mLastParentBeforePip != null) {
pw.println(prefix + "lastParentTaskIdBeforePip=" + mLastParentBeforePip.mTaskId);
@@ -1731,6 +1737,10 @@
info.windowLayout.windowLayoutAffinity =
uid + ":" + info.windowLayout.windowLayoutAffinity;
}
+ // Initialize once, when we know all system services are available.
+ if (sConstrainDisplayApisConfig == null) {
+ sConstrainDisplayApisConfig = new ConstrainDisplayApisConfig();
+ }
stateNotNeeded = (aInfo.flags & FLAG_STATE_NOT_NEEDED) != 0;
nonLocalizedLabel = aInfo.nonLocalizedLabel;
labelRes = aInfo.labelRes;
@@ -7330,8 +7340,8 @@
+ "should create compatDisplayInsets = %s",
getUid(),
mTmpBounds,
- info.neverSandboxDisplayApis(),
- info.alwaysSandboxDisplayApis(),
+ info.neverSandboxDisplayApis(sConstrainDisplayApisConfig),
+ info.alwaysSandboxDisplayApis(sConstrainDisplayApisConfig),
!matchParentBounds(),
mCompatDisplayInsets != null,
shouldCreateCompatDisplayInsets());
@@ -7892,11 +7902,11 @@
return false;
}
// Never apply sandboxing to an app that should be explicitly excluded from the config.
- if (info != null && info.neverSandboxDisplayApis()) {
+ if (info.neverSandboxDisplayApis(sConstrainDisplayApisConfig)) {
return false;
}
// Always apply sandboxing to an app that should be explicitly included from the config.
- if (info != null && info.alwaysSandboxDisplayApis()) {
+ if (info.alwaysSandboxDisplayApis(sConstrainDisplayApisConfig)) {
return true;
}
// Max bounds should be sandboxed when an activity should have compatDisplayInsets, and it
@@ -8899,6 +8909,9 @@
proto.write(PIP_AUTO_ENTER_ENABLED, pictureInPictureArgs.isAutoEnterEnabled());
proto.write(IN_SIZE_COMPAT_MODE, inSizeCompatMode());
proto.write(MIN_ASPECT_RATIO, getMinAspectRatio());
+ // Only record if max bounds sandboxing is applied, if the caller has the necessary
+ // permission to access the device configs.
+ proto.write(PROVIDES_MAX_BOUNDS, providesMaxBounds());
}
@Override
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 471b4ce..ba96590 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -72,6 +72,8 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.ActivityTaskManagerService.ANIMATE;
+import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_ALLOW;
+import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_FG_ONLY;
import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME;
import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
@@ -1288,7 +1290,10 @@
// This is used to block background activity launch even if the app is still
// visible to user after user clicking home button.
- final boolean appSwitchAllowed = mService.getBalAppSwitchesAllowed();
+ final int appSwitchState = mService.getBalAppSwitchesState();
+ final boolean appSwitchAllowed = appSwitchState == APP_SWITCH_ALLOW;
+ final boolean appSwitchAllowedOrFg =
+ appSwitchState == APP_SWITCH_ALLOW || appSwitchState == APP_SWITCH_FG_ONLY;
// don't abort if the callingUid has a visible window or is a persistent system process
final int callingUidProcState = mService.mActiveUids.getUidState(callingUid);
@@ -1301,7 +1306,7 @@
// Normal apps with visible app window will be allowed to start activity if app switching
// is allowed, or apps like live wallpaper with non app visible window will be allowed.
- if (((appSwitchAllowed || mService.mActiveUids.hasNonAppVisibleWindow(callingUid))
+ if (((appSwitchAllowedOrFg || mService.mActiveUids.hasNonAppVisibleWindow(callingUid))
&& callingUidHasAnyVisibleWindow)
|| isCallingUidPersistentSystemProcess) {
if (DEBUG_ACTIVITY_STARTS) {
@@ -1458,7 +1463,7 @@
// anything that has fallen through would currently be aborted
Slog.w(TAG, "Background activity start [callingPackage: " + callingPackage
+ "; callingUid: " + callingUid
- + "; appSwitchAllowed: " + appSwitchAllowed
+ + "; appSwitchState: " + appSwitchState
+ "; isCallingUidForeground: " + isCallingUidForeground
+ "; callingUidHasAnyVisibleWindow: " + callingUidHasAnyVisibleWindow
+ "; callingUidProcState: " + DebugUtils.valueToString(ActivityManager.class,
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index bbccac4..be01646 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -505,7 +505,27 @@
* Whether normal application switches are allowed; a call to {@link #stopAppSwitches()
* disables this.
*/
- private volatile boolean mAppSwitchesAllowed = true;
+ private volatile int mAppSwitchesState = APP_SWITCH_ALLOW;
+
+ // The duration of resuming foreground app switch from disallow.
+ private static final long RESUME_FG_APP_SWITCH_MS = 500;
+
+ /** App switch is not allowed. */
+ static final int APP_SWITCH_DISALLOW = 0;
+
+ /** App switch is allowed only if the activity launch was requested by a foreground app. */
+ static final int APP_SWITCH_FG_ONLY = 1;
+
+ /** App switch is allowed. */
+ static final int APP_SWITCH_ALLOW = 2;
+
+ @IntDef({
+ APP_SWITCH_DISALLOW,
+ APP_SWITCH_FG_ONLY,
+ APP_SWITCH_ALLOW,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface AppSwitchState {}
/**
* Last stop app switches time, apps finished before this time cannot start background activity
@@ -1250,7 +1270,7 @@
if (topFocusedRootTask != null && topFocusedRootTask.getTopResumedActivity() != null
&& topFocusedRootTask.getTopResumedActivity().info.applicationInfo.uid
== Binder.getCallingUid()) {
- mAppSwitchesAllowed = true;
+ mAppSwitchesState = APP_SWITCH_ALLOW;
}
}
return pir.sendInner(0, fillInIntent, resolvedType, allowlistToken, null, null,
@@ -2169,8 +2189,8 @@
/**
* Return true if app switching is allowed.
*/
- boolean getBalAppSwitchesAllowed() {
- return mAppSwitchesAllowed;
+ @AppSwitchState int getBalAppSwitchesState() {
+ return mAppSwitchesState;
}
/** Register an {@link AnrController} to control the ANR dialog behavior */
@@ -3691,8 +3711,10 @@
public void stopAppSwitches() {
mAmInternal.enforceCallingPermission(STOP_APP_SWITCHES, "stopAppSwitches");
synchronized (mGlobalLock) {
- mAppSwitchesAllowed = false;
+ mAppSwitchesState = APP_SWITCH_DISALLOW;
mLastStopAppSwitchesTime = SystemClock.uptimeMillis();
+ mH.removeMessages(H.RESUME_FG_APP_SWITCH_MSG);
+ mH.sendEmptyMessageDelayed(H.RESUME_FG_APP_SWITCH_MSG, RESUME_FG_APP_SWITCH_MS);
}
}
@@ -3700,7 +3722,8 @@
public void resumeAppSwitches() {
mAmInternal.enforceCallingPermission(STOP_APP_SWITCHES, "resumeAppSwitches");
synchronized (mGlobalLock) {
- mAppSwitchesAllowed = true;
+ mAppSwitchesState = APP_SWITCH_ALLOW;
+ mH.removeMessages(H.RESUME_FG_APP_SWITCH_MSG);
}
}
@@ -5193,6 +5216,7 @@
static final int REPORT_TIME_TRACKER_MSG = 1;
static final int UPDATE_PROCESS_ANIMATING_STATE = 2;
static final int END_POWER_MODE_UNKNOWN_VISIBILITY_MSG = 3;
+ static final int RESUME_FG_APP_SWITCH_MSG = 4;
static final int FIRST_ACTIVITY_TASK_MSG = 100;
static final int FIRST_SUPERVISOR_TASK_MSG = 200;
@@ -5230,6 +5254,14 @@
}
}
break;
+ case RESUME_FG_APP_SWITCH_MSG: {
+ synchronized (mGlobalLock) {
+ if (mAppSwitchesState == APP_SWITCH_DISALLOW) {
+ mAppSwitchesState = APP_SWITCH_FG_ONLY;
+ }
+ }
+ }
+ break;
}
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 71ab5b6..4768b27 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -45,7 +45,6 @@
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
@@ -846,20 +845,6 @@
}
/**
- * Only trusted overlays are allowed to use FLAG_SLIPPERY.
- */
- static int sanitizeFlagSlippery(int flags, int privateFlags, String name) {
- if ((flags & FLAG_SLIPPERY) == 0) {
- return flags;
- }
- if ((privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0) {
- return flags;
- }
- Slog.w(TAG, "Removing FLAG_SLIPPERY for non-trusted overlay " + name);
- return flags & ~FLAG_SLIPPERY;
- }
-
- /**
* Sanitize the layout parameters coming from a client. Allows the policy
* to do things like ensure that windows of a specific type can't take
* input focus.
@@ -942,8 +927,6 @@
} else if (mRoundedCornerWindow == win) {
mRoundedCornerWindow = null;
}
-
- attrs.flags = sanitizeFlagSlippery(attrs.flags, attrs.privateFlags, win.getName());
}
/**
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index ea99781..3e55811 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3115,14 +3115,6 @@
}
@Override
- boolean fillsParent() {
- // From the perspective of policy, we still want to report that this task fills parent
- // in fullscreen windowing mode even it doesn't match parent bounds because there will be
- // letterbox around its real content.
- return getWindowingMode() == WINDOWING_MODE_FULLSCREEN || matchParentBounds();
- }
-
- @Override
void forAllLeafTasks(Consumer<Task> callback, boolean traverseTopToBottom) {
final int count = mChildren.size();
boolean isLeafTask = true;
@@ -4534,14 +4526,15 @@
}
super.setWindowingMode(windowingMode);
- // Try reparent pinned activity back to its original task after onConfigurationChanged
- // cascade finishes. This is done on Task level instead of
- // {@link ActivityRecord#onConfigurationChanged(Configuration)} since when we exit PiP,
- // we set final windowing mode on the ActivityRecord first and then on its Task when
- // the exit PiP transition finishes. Meanwhile, the exit transition is always
- // performed on its original task, reparent immediately in ActivityRecord breaks it.
- if (currentMode == WINDOWING_MODE_PINNED) {
- if (topActivity != null && topActivity.getLastParentBeforePip() != null) {
+ if (currentMode == WINDOWING_MODE_PINNED && topActivity != null) {
+ // Try reparent pinned activity back to its original task after
+ // onConfigurationChanged cascade finishes. This is done on Task level instead of
+ // {@link ActivityRecord#onConfigurationChanged(Configuration)} since when we exit
+ // PiP, we set final windowing mode on the ActivityRecord first and then on its
+ // Task when the exit PiP transition finishes. Meanwhile, the exit transition is
+ // always performed on its original task, reparent immediately in ActivityRecord
+ // breaks it.
+ if (topActivity.getLastParentBeforePip() != null) {
// Do not reparent if the pinned task is in removal, indicated by the
// force hidden flag.
if (!isForceHidden()) {
@@ -4554,6 +4547,11 @@
}
}
}
+ // Resume app-switches-allowed flag when exiting from pinned mode since
+ // it does not follow the ActivityStarter path.
+ if (topActivity.shouldBeVisible()) {
+ mAtmService.resumeAppSwitches();
+ }
}
if (creating) {
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index fdaa2fc..c845dca 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -2342,6 +2342,14 @@
return true;
}
+ @Override
+ boolean fillsParent() {
+ // From the perspective of policy, we still want to report that this task fills parent
+ // in fullscreen windowing mode even it doesn't match parent bounds because there will be
+ // letterbox around its real content.
+ return getWindowingMode() == WINDOWING_MODE_FULLSCREEN || matchParentBounds();
+ }
+
boolean dump(String prefix, FileDescriptor fd, PrintWriter pw, boolean dumpAll,
boolean dumpClient, String dumpPackage, final boolean needSep, Runnable header) {
boolean printed = false;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index e986fd2..58860de 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1652,6 +1652,7 @@
final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
displayPolicy.adjustWindowParamsLw(win, win.mAttrs);
+ attrs.flags = sanitizeFlagSlippery(attrs.flags, win.getName(), callingUid, callingPid);
win.setRequestedVisibilities(requestedVisibilities);
res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid);
@@ -2207,6 +2208,7 @@
if (attrs != null) {
displayPolicy.adjustWindowParamsLw(win, attrs);
win.mToken.adjustWindowParams(win, attrs);
+ attrs.flags = sanitizeFlagSlippery(attrs.flags, win.getName(), uid, pid);
int disableFlags =
(attrs.systemUiVisibility | attrs.subtreeSystemUiVisibility) & DISABLE_MASK;
if (disableFlags != 0 && !hasStatusBarPermission(pid, uid)) {
@@ -8220,6 +8222,23 @@
}
/**
+ * You need ALLOW_SLIPPERY_TOUCHES permission to be able to set FLAG_SLIPPERY.
+ */
+ private int sanitizeFlagSlippery(int flags, String windowName, int callingUid, int callingPid) {
+ if ((flags & FLAG_SLIPPERY) == 0) {
+ return flags;
+ }
+ final int permissionResult = mContext.checkPermission(
+ android.Manifest.permission.ALLOW_SLIPPERY_TOUCHES, callingPid, callingUid);
+ if (permissionResult != PackageManager.PERMISSION_GRANTED) {
+ Slog.w(TAG, "Removing FLAG_SLIPPERY from '" + windowName
+ + "' because it doesn't have ALLOW_SLIPPERY_TOUCHES permission");
+ return flags & ~FLAG_SLIPPERY;
+ }
+ return flags;
+ }
+
+ /**
* Assigns an InputChannel to a SurfaceControl and configures it to receive
* touch input according to it's on-screen geometry.
*
@@ -8258,7 +8277,7 @@
h.setWindowToken(window);
h.name = name;
- flags = DisplayPolicy.sanitizeFlagSlippery(flags, privateFlags, name);
+ flags = sanitizeFlagSlippery(flags, name, callingUid, callingPid);
final int sanitizedFlags = flags & (LayoutParams.FLAG_NOT_TOUCHABLE
| FLAG_SLIPPERY | LayoutParams.FLAG_NOT_FOCUSABLE);
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 1cfbe07..90a5d69e 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -37,6 +37,7 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_ALLOW;
import static com.android.server.wm.ActivityTaskManagerService.INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MILLIS;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
@@ -512,7 +513,7 @@
*/
@HotPath(caller = HotPath.START_SERVICE)
public boolean areBackgroundFgsStartsAllowed() {
- return areBackgroundActivityStartsAllowed(mAtm.getBalAppSwitchesAllowed(),
+ return areBackgroundActivityStartsAllowed(mAtm.getBalAppSwitchesState() == APP_SWITCH_ALLOW,
true /* isCheckingForFgsStart */);
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java
index 17d7c51..d6db1b2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java
@@ -123,6 +123,8 @@
doNothing().when(mContextSpy).enforceCallingPermission(
eq(Manifest.permission.WRITE_COMMUNAL_STATE), anyString());
+ doNothing().when(mContextSpy).enforceCallingPermission(
+ eq(Manifest.permission.READ_COMMUNAL_STATE), anyString());
mService = new CommunalManagerService(mContextSpy);
mService.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
@@ -203,6 +205,18 @@
}
@Test
+ public void testIsCommunalMode_isTrue() throws RemoteException {
+ mBinder.setCommunalViewShowing(true);
+ assertThat(mBinder.isCommunalMode()).isTrue();
+ }
+
+ @Test
+ public void testIsCommunalMode_isFalse() throws RemoteException {
+ mBinder.setCommunalViewShowing(false);
+ assertThat(mBinder.isCommunalMode()).isFalse();
+ }
+
+ @Test
public void testIntercept_unlocked_communalOff_appNotEnabled_showWhenLockedOff() {
when(mKeyguardManager.isKeyguardLocked()).thenReturn(false);
mAInfo.flags = 0;
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java
index 4d6f49e..4eba219 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java
@@ -90,6 +90,19 @@
}
@Test
+ public void testThrottle_lowInterval() {
+ ProviderRequest request = new ProviderRequest.Builder().setIntervalMillis(0).build();
+
+ mProvider.getController().setRequest(request);
+ mDelegateProvider.reportLocation(createLocationResult("test_provider", mRandom));
+ verify(mListener, times(1)).onReportLocation(any(LocationResult.class));
+
+ mInjector.getDeviceStationaryHelper().setStationary(true);
+ mInjector.getDeviceIdleHelper().setIdle(true);
+ verify(mListener, after(1500).times(2)).onReportLocation(any(LocationResult.class));
+ }
+
+ @Test
public void testThrottle_stationaryExit() {
ProviderRequest request = new ProviderRequest.Builder().setIntervalMillis(50).build();
@@ -104,17 +117,16 @@
mInjector.getDeviceIdleHelper().setIdle(true);
verify(mDelegate).onSetRequest(ProviderRequest.EMPTY_REQUEST);
- verify(mListener, timeout(75).times(2)).onReportLocation(any(LocationResult.class));
- verify(mListener, timeout(75).times(3)).onReportLocation(any(LocationResult.class));
+ verify(mListener, timeout(1100).times(2)).onReportLocation(any(LocationResult.class));
mInjector.getDeviceStationaryHelper().setStationary(false);
verify(mDelegate, times(2)).onSetRequest(request);
- verify(mListener, after(75).times(3)).onReportLocation(any(LocationResult.class));
+ verify(mListener, after(1000).times(2)).onReportLocation(any(LocationResult.class));
}
@Test
public void testThrottle_idleExit() {
- ProviderRequest request = new ProviderRequest.Builder().setIntervalMillis(50).build();
+ ProviderRequest request = new ProviderRequest.Builder().setIntervalMillis(1000).build();
mProvider.getController().setRequest(request);
verify(mDelegate).onSetRequest(request);
@@ -127,17 +139,16 @@
mInjector.getDeviceStationaryHelper().setStationary(true);
verify(mDelegate).onSetRequest(ProviderRequest.EMPTY_REQUEST);
- verify(mListener, timeout(75).times(2)).onReportLocation(any(LocationResult.class));
- verify(mListener, timeout(75).times(3)).onReportLocation(any(LocationResult.class));
+ verify(mListener, timeout(1100).times(2)).onReportLocation(any(LocationResult.class));
mInjector.getDeviceIdleHelper().setIdle(false);
verify(mDelegate, times(2)).onSetRequest(request);
- verify(mListener, after(75).times(3)).onReportLocation(any(LocationResult.class));
+ verify(mListener, after(1000).times(2)).onReportLocation(any(LocationResult.class));
}
@Test
public void testThrottle_NoInitialLocation() {
- ProviderRequest request = new ProviderRequest.Builder().setIntervalMillis(50).build();
+ ProviderRequest request = new ProviderRequest.Builder().setIntervalMillis(1000).build();
mProvider.getController().setRequest(request);
verify(mDelegate).onSetRequest(request);
@@ -149,11 +160,11 @@
mDelegateProvider.reportLocation(createLocationResult("test_provider", mRandom));
verify(mListener, times(1)).onReportLocation(any(LocationResult.class));
verify(mDelegate, times(1)).onSetRequest(ProviderRequest.EMPTY_REQUEST);
- verify(mListener, timeout(75).times(2)).onReportLocation(any(LocationResult.class));
+ verify(mListener, timeout(1100).times(2)).onReportLocation(any(LocationResult.class));
mInjector.getDeviceStationaryHelper().setStationary(false);
verify(mDelegate, times(2)).onSetRequest(request);
- verify(mListener, after(75).times(2)).onReportLocation(any(LocationResult.class));
+ verify(mListener, after(1000).times(2)).onReportLocation(any(LocationResult.class));
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
index 2eb9e34..a0d86c9 100644
--- a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
@@ -187,6 +187,30 @@
}
@Test
+ public void testGetEmergencyGesturePowerButtonCooldownPeriodMs_enabled() {
+ withEmergencyGesturePowerButtonCooldownPeriodMsValue(4000);
+ assertEquals(4000,
+ mGestureLauncherService.getEmergencyGesturePowerButtonCooldownPeriodMs(mContext,
+ FAKE_USER_ID));
+ }
+
+ @Test
+ public void testGetEmergencyGesturePowerButtonCooldownPeriodMs_disabled() {
+ withEmergencyGesturePowerButtonCooldownPeriodMsValue(0);
+ assertEquals(0,
+ mGestureLauncherService.getEmergencyGesturePowerButtonCooldownPeriodMs(mContext,
+ FAKE_USER_ID));
+ }
+
+ @Test
+ public void testGetEmergencyGesturePowerButtonCooldownPeriodMs_cappedAtMaximum() {
+ withEmergencyGesturePowerButtonCooldownPeriodMsValue(10000);
+ assertEquals(GestureLauncherService.EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_MAX,
+ mGestureLauncherService.getEmergencyGesturePowerButtonCooldownPeriodMs(mContext,
+ FAKE_USER_ID));
+ }
+
+ @Test
public void testHandleCameraLaunchGesture_userSetupComplete() {
withUserSetupCompleteValue(true);
@@ -645,6 +669,211 @@
}
@Test
+ public void testInterceptPowerKeyDown_triggerEmergency_singleTaps_cooldownTriggered() {
+ // Enable power button cooldown
+ withEmergencyGesturePowerButtonCooldownPeriodMsValue(3000);
+ mGestureLauncherService.updateEmergencyGesturePowerButtonCooldownPeriodMs();
+
+ // Trigger emergency by tapping button 5 times
+ long eventTime = triggerEmergencyGesture();
+
+ // Add enough interval to reset consecutive tap count
+ long interval = GestureLauncherService.POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS + 1;
+ eventTime += interval;
+
+ // Subsequent single tap is intercepted, but should not trigger any gesture
+ KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+ IGNORED_REPEAT);
+ boolean interactive = true;
+ MutableBoolean outLaunched = new MutableBoolean(true);
+ boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+ outLaunched);
+ assertTrue(intercepted);
+ assertFalse(outLaunched.value);
+
+ // Add enough interval to reset consecutive tap count
+ interval = GestureLauncherService.POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS + 1;
+ eventTime += interval;
+
+ // Another single tap should be the same (intercepted but should not trigger gesture)
+ keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+ IGNORED_REPEAT);
+ interactive = true;
+ outLaunched = new MutableBoolean(true);
+ intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+ outLaunched);
+ assertTrue(intercepted);
+ assertFalse(outLaunched.value);
+ }
+
+ @Test
+ public void
+ testInterceptPowerKeyDown_triggerEmergency_cameraGestureEnabled_doubleTap_cooldownTriggered() {
+ // Enable camera double tap gesture
+ withCameraDoubleTapPowerEnableConfigValue(true);
+ withCameraDoubleTapPowerDisableSettingValue(0);
+ mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+
+ // Enable power button cooldown
+ withEmergencyGesturePowerButtonCooldownPeriodMsValue(3000);
+ mGestureLauncherService.updateEmergencyGesturePowerButtonCooldownPeriodMs();
+
+ // Trigger emergency by tapping button 5 times
+ long eventTime = triggerEmergencyGesture();
+
+ // Add enough interval to reset consecutive tap count
+ long interval = GestureLauncherService.POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS + 1;
+ eventTime += interval;
+
+ // Subsequent double tap is intercepted, but should not trigger any gesture
+ for (int i = 0; i < 2; i++) {
+ KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION,
+ IGNORED_CODE, IGNORED_REPEAT);
+ boolean interactive = true;
+ MutableBoolean outLaunched = new MutableBoolean(true);
+ boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent,
+ interactive, outLaunched);
+ assertTrue(intercepted);
+ assertFalse(outLaunched.value);
+ interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ eventTime += interval;
+ }
+ }
+
+ @Test
+ public void testInterceptPowerKeyDown_triggerEmergency_fiveTaps_cooldownTriggered() {
+ // Enable power button cooldown
+ withEmergencyGesturePowerButtonCooldownPeriodMsValue(3000);
+ mGestureLauncherService.updateEmergencyGesturePowerButtonCooldownPeriodMs();
+
+ // Trigger emergency by tapping button 5 times
+ long eventTime = triggerEmergencyGesture();
+
+ // Add enough interval to reset consecutive tap count
+ long interval = GestureLauncherService.POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS + 1;
+ eventTime += interval;
+
+ // Subsequent 5 taps are intercepted, but should not trigger any gesture
+ for (int i = 0; i < 5; i++) {
+ KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION,
+ IGNORED_CODE, IGNORED_REPEAT);
+ boolean interactive = true;
+ MutableBoolean outLaunched = new MutableBoolean(true);
+ boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent,
+ interactive, outLaunched);
+ assertTrue(intercepted);
+ assertFalse(outLaunched.value);
+ interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ eventTime += interval;
+ }
+ }
+
+ @Test
+ public void testInterceptPowerKeyDown_triggerEmergency_longPress_cooldownTriggered() {
+ // Enable power button cooldown
+ withEmergencyGesturePowerButtonCooldownPeriodMsValue(3000);
+ mGestureLauncherService.updateEmergencyGesturePowerButtonCooldownPeriodMs();
+
+ // Trigger emergency by tapping button 5 times
+ long eventTime = triggerEmergencyGesture();
+
+ // Add enough interval to reset consecutive tap count
+ long interval = GestureLauncherService.POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS + 1;
+ eventTime += interval;
+
+ // Subsequent long press is intercepted, but should not trigger any gesture
+ KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+ IGNORED_REPEAT, IGNORED_META_STATE, IGNORED_DEVICE_ID, IGNORED_SCANCODE,
+ KeyEvent.FLAG_LONG_PRESS);
+
+ boolean interactive = true;
+ MutableBoolean outLaunched = new MutableBoolean(true);
+ boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+ outLaunched);
+ assertTrue(intercepted);
+ assertFalse(outLaunched.value);
+ }
+
+ @Test
+ public void testInterceptPowerKeyDown_triggerEmergency_cooldownDisabled_cooldownNotTriggered() {
+ // Disable power button cooldown by setting cooldown period to 0
+ withEmergencyGesturePowerButtonCooldownPeriodMsValue(0);
+ mGestureLauncherService.updateEmergencyGesturePowerButtonCooldownPeriodMs();
+
+ // Trigger emergency by tapping button 5 times
+ long eventTime = triggerEmergencyGesture();
+
+ // Add enough interval to reset consecutive tap count
+ long interval = GestureLauncherService.POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS + 1;
+ eventTime += interval;
+
+ // Subsequent single tap is NOT intercepted
+ KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+ IGNORED_REPEAT);
+ boolean interactive = true;
+ MutableBoolean outLaunched = new MutableBoolean(true);
+ boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+ outLaunched);
+ assertFalse(intercepted);
+ assertFalse(outLaunched.value);
+
+ // Add enough interval to reset consecutive tap count
+ interval = GestureLauncherService.POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS + 1;
+ eventTime += interval;
+
+ // Long press also NOT intercepted
+ keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+ IGNORED_REPEAT, IGNORED_META_STATE, IGNORED_DEVICE_ID, IGNORED_SCANCODE,
+ KeyEvent.FLAG_LONG_PRESS);
+ interactive = true;
+ outLaunched = new MutableBoolean(true);
+ intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+ outLaunched);
+ assertFalse(intercepted);
+ assertFalse(outLaunched.value);
+ }
+
+ @Test
+ public void
+ testInterceptPowerKeyDown_triggerEmergency_outsideCooldownPeriod_cooldownNotTriggered() {
+ // Enable power button cooldown
+ withEmergencyGesturePowerButtonCooldownPeriodMsValue(5000);
+ mGestureLauncherService.updateEmergencyGesturePowerButtonCooldownPeriodMs();
+
+ // Trigger emergency by tapping button 5 times
+ long eventTime = triggerEmergencyGesture();
+
+ // Add enough interval to be outside of cooldown period
+ long interval = 5001;
+ eventTime += interval;
+
+ // Subsequent single tap is NOT intercepted
+ KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+ IGNORED_REPEAT);
+ boolean interactive = true;
+ MutableBoolean outLaunched = new MutableBoolean(true);
+ boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+ outLaunched);
+ assertFalse(intercepted);
+ assertFalse(outLaunched.value);
+
+ // Add enough interval to reset consecutive tap count
+ interval = GestureLauncherService.POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS + 1;
+ eventTime += interval;
+
+ // Long press also NOT intercepted
+ keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+ IGNORED_REPEAT, IGNORED_META_STATE, IGNORED_DEVICE_ID, IGNORED_SCANCODE,
+ KeyEvent.FLAG_LONG_PRESS);
+ interactive = true;
+ outLaunched = new MutableBoolean(true);
+ intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+ outLaunched);
+ assertFalse(intercepted);
+ assertFalse(outLaunched.value);
+ }
+
+ @Test
public void testInterceptPowerKeyDown_longpress() {
withCameraDoubleTapPowerEnableConfigValue(true);
withCameraDoubleTapPowerDisableSettingValue(0);
@@ -1153,6 +1382,45 @@
assertEquals(1, tapCounts.get(1).intValue());
}
+ /**
+ * Helper method to trigger emergency gesture by pressing button for 5 times.
+ * @return last event time.
+ */
+ private long triggerEmergencyGesture() {
+ // Enable emergency power gesture
+ withEmergencyGestureEnabledConfigValue(true);
+ withEmergencyGestureEnabledSettingValue(true);
+ mGestureLauncherService.updateEmergencyGestureEnabled();
+ withUserSetupCompleteValue(true);
+
+ // 4 button presses
+ long eventTime = INITIAL_EVENT_TIME_MILLIS;
+ boolean interactive = true;
+ KeyEvent keyEvent;
+ MutableBoolean outLaunched = new MutableBoolean(false);
+ for (int i = 0; i < 4; i++) {
+ keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+ IGNORED_REPEAT);
+ mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive, outLaunched);
+ final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ eventTime += interval;
+ }
+
+ // 5th button press should trigger the emergency flow
+ keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+ IGNORED_REPEAT);
+ outLaunched.value = false;
+ boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+ outLaunched);
+ assertTrue(outLaunched.value);
+ assertTrue(intercepted);
+ verify(mUiEventLogger, times(1))
+ .log(GestureLauncherService.GestureLauncherEvent.GESTURE_EMERGENCY_TAP_POWER);
+ verify(mStatusBarManagerInternal).onEmergencyActionLaunchGestureDetected();
+
+ return eventTime;
+ }
+
private void withCameraDoubleTapPowerEnableConfigValue(boolean enableConfigValue) {
when(mResources.getBoolean(
com.android.internal.R.bool.config_cameraDoubleTapPowerGestureEnabled))
@@ -1181,6 +1449,14 @@
UserHandle.USER_CURRENT);
}
+ private void withEmergencyGesturePowerButtonCooldownPeriodMsValue(int period) {
+ Settings.Secure.putIntForUser(
+ mContentResolver,
+ Settings.Secure.EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS,
+ period,
+ UserHandle.USER_CURRENT);
+ }
+
private void withUserSetupCompleteValue(boolean userSetupComplete) {
int userSetupCompleteValue = userSetupComplete ? 1 : 0;
Settings.Secure.putIntForUser(
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/OWNERS b/services/tests/servicestests/src/com/android/server/biometrics/OWNERS
index 8765c9a..6a2192a 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/biometrics/OWNERS
@@ -1,7 +1 @@
-set noparent
-
-kchyn@google.com
-jaggies@google.com
-curtislb@google.com
-ilyamaty@google.com
-joshmccloskey@google.com
+include /services/core/java/com/android/server/biometrics/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index f664517..abe7d89 100644
--- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -21,7 +21,9 @@
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyFloat;
import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -63,6 +65,7 @@
@Mock SensorManager mSensorManager;
@Mock BrightnessMappingStrategy mBrightnessMappingStrategy;
+ @Mock BrightnessMappingStrategy mIdleBrightnessMappingStrategy;
@Mock HysteresisLevels mAmbientBrightnessThresholds;
@Mock HysteresisLevels mScreenBrightnessThresholds;
@Mock Handler mNoOpHandler;
@@ -100,7 +103,7 @@
INITIAL_LIGHT_SENSOR_RATE, BRIGHTENING_LIGHT_DEBOUNCE_CONFIG,
DARKENING_LIGHT_DEBOUNCE_CONFIG, RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG,
mAmbientBrightnessThresholds, mScreenBrightnessThresholds,
- mContext, mHbmController
+ mContext, mHbmController, mIdleBrightnessMappingStrategy
);
when(mHbmController.getCurrentBrightnessMax()).thenReturn(BRIGHTNESS_MAX_FLOAT);
@@ -231,4 +234,44 @@
// There should be a user data point added to the mapper.
verify(mBrightnessMappingStrategy).addUserDataPoint(1000f, 0.5f);
}
+
+ @Test
+ public void testSwitchToIdleMappingStrategy() throws Exception {
+ Sensor lightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor");
+ mController = setupController(lightSensor);
+
+ ArgumentCaptor<SensorEventListener> listenerCaptor =
+ ArgumentCaptor.forClass(SensorEventListener.class);
+ verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(lightSensor),
+ eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+ SensorEventListener listener = listenerCaptor.getValue();
+
+ // Sensor reads 1000 lux,
+ listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 1000));
+
+ // User sets brightness to 100
+ mController.configure(true /* enable */, null /* configuration */,
+ 0.5f /* brightness */, true /* userChangedBrightness */, 0 /* adjustment */,
+ false /* userChanged */, DisplayPowerRequest.POLICY_BRIGHT);
+
+ // There should be a user data point added to the mapper.
+ verify(mBrightnessMappingStrategy, times(1)).addUserDataPoint(1000f, 0.5f);
+ verify(mBrightnessMappingStrategy, times(2)).setBrightnessConfiguration(any());
+ verify(mBrightnessMappingStrategy, times(3)).getBrightness(anyFloat(), any(), anyInt());
+
+ // Now let's do the same for idle mode
+ mController.switchToIdleMode();
+ // Called once for init, and once when switching
+ verify(mBrightnessMappingStrategy, times(2)).isForIdleMode();
+ // Ensure, after switching, original BMS is not used anymore
+ verifyNoMoreInteractions(mBrightnessMappingStrategy);
+
+ // User sets idle brightness to 0.5
+ mController.configure(true /* enable */, null /* configuration */,
+ 0.5f /* brightness */, true /* userChangedBrightness */, 0 /* adjustment */,
+ false /* userChanged */, DisplayPowerRequest.POLICY_BRIGHT);
+
+ // Ensure we use the correct mapping strategy
+ verify(mIdleBrightnessMappingStrategy, times(1)).addUserDataPoint(1000f, 0.5f);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index 68e90fb..eaa271a 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -219,7 +219,7 @@
builder.setUniqueId(uniqueId);
builder.setFlags(flags);
int displayId = bs.createVirtualDisplay(builder.build(), mMockAppToken /* callback */,
- null /* projection */, PACKAGE_NAME);
+ null /* projection */, null /* virtualDeviceToken */, PACKAGE_NAME);
displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
@@ -341,7 +341,7 @@
builder.setFlags(flags);
builder.setUniqueId(uniqueId);
int displayId = bs.createVirtualDisplay(builder.build(), mMockAppToken /* callback */,
- null /* projection */, PACKAGE_NAME);
+ null /* projection */, null /* virtualDeviceToken */, PACKAGE_NAME);
displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
@@ -580,7 +580,8 @@
VIRTUAL_DISPLAY_NAME, width, height, dpi);
builder.setUniqueId(uniqueId);
final int firstDisplayId = binderService.createVirtualDisplay(builder.build(),
- mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME);
+ mMockAppToken /* callback */, null /* projection */, null /* virtualDeviceToken */,
+ PACKAGE_NAME);
// The second virtual display requests to mirror the first virtual display.
final String uniqueId2 = "uniqueId --- displayIdToMirrorTest #2";
@@ -590,7 +591,8 @@
builder2.setUniqueId(uniqueId2);
builder2.setDisplayIdToMirror(firstDisplayId);
final int secondDisplayId = binderService.createVirtualDisplay(builder2.build(),
- mMockAppToken2 /* callback */, null /* projection */, PACKAGE_NAME);
+ mMockAppToken2 /* callback */, null /* projection */,
+ null /* virtualDeviceToken */, PACKAGE_NAME);
displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
// flush the handler
@@ -628,7 +630,8 @@
builder.setSurface(surface);
builder.setUniqueId(uniqueId);
final int displayId = binderService.createVirtualDisplay(builder.build(),
- mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME);
+ mMockAppToken /* callback */, null /* projection */, null /* virtualDeviceToken */,
+ PACKAGE_NAME);
displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
@@ -1108,7 +1111,7 @@
builder.setUniqueId(uniqueId);
int displayId = bs.createVirtualDisplay(builder.build(), mMockAppToken /* callback */,
- null /* projection */, PACKAGE_NAME);
+ null /* projection */, null /* virtualDeviceToken */, PACKAGE_NAME);
displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
displayManager.getDisplayHandler().runWithScissors(() -> {}, 0 /* now */);
DisplayDeviceInfo ddi = displayManager.getDisplayDeviceInfoInternal(displayId);
diff --git a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java
new file mode 100644
index 0000000..70e78eb
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java
@@ -0,0 +1,740 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locales;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.os.Binder;
+import android.os.Environment;
+import android.os.LocaleList;
+import android.os.RemoteException;
+import android.os.SimpleClock;
+import android.util.AtomicFile;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.internal.content.PackageMonitor;
+import com.android.internal.util.XmlUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.time.Clock;
+import java.time.Duration;
+import java.time.ZoneOffset;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Unit tests for the {@link LocaleManagerInternal}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class LocaleManagerBackupRestoreTest {
+ private static final String DEFAULT_PACKAGE_NAME = "com.android.myapp";
+ private static final String DEFAULT_LOCALE_TAGS = "en-XC,ar-XB";
+ private static final String TEST_LOCALES_XML_TAG = "locales";
+ private static final int DEFAULT_USER_ID = 0;
+ private static final int WORK_PROFILE_USER_ID = 10;
+ private static final int DEFAULT_UID = Binder.getCallingUid() + 100;
+ private static final long DEFAULT_CREATION_TIME_MILLIS = 1000;
+ private static final Duration RETENTION_PERIOD = Duration.ofDays(3);
+ private static final LocaleList DEFAULT_LOCALES =
+ LocaleList.forLanguageTags(DEFAULT_LOCALE_TAGS);
+ private static final Map<String, String> DEFAULT_PACKAGE_LOCALES_MAP = Map.of(
+ DEFAULT_PACKAGE_NAME, DEFAULT_LOCALE_TAGS);
+ private static final File STAGED_LOCALES_DIR = new File(
+ Environment.getExternalStorageDirectory(), "lmsUnitTests");
+
+
+ private LocaleManagerBackupHelper mBackupHelper;
+ private long mCurrentTimeMillis;
+
+ @Mock
+ private Context mMockContext;
+ @Mock
+ private PackageManagerInternal mMockPackageManagerInternal;
+ @Mock
+ private PackageManager mMockPackageManager;
+ @Mock
+ private LocaleManagerService mMockLocaleManagerService;
+ BroadcastReceiver mUserMonitor;
+ PackageMonitor mPackageMonitor;
+
+ private final Clock mClock = new SimpleClock(ZoneOffset.UTC) {
+ @Override
+ public long millis() {
+ return currentTimeMillis();
+ }
+ };
+
+ private long currentTimeMillis() {
+ return mCurrentTimeMillis;
+ }
+
+ private void setCurrentTimeMillis(long currentTimeMillis) {
+ mCurrentTimeMillis = currentTimeMillis;
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mMockContext = mock(Context.class);
+ mMockPackageManagerInternal = mock(PackageManagerInternal.class);
+ mMockPackageManager = mock(PackageManager.class);
+ mMockLocaleManagerService = mock(LocaleManagerService.class);
+
+ doReturn(mMockPackageManager).when(mMockContext).getPackageManager();
+
+ mBackupHelper = spy(new ShadowLocaleManagerBackupHelper(mMockContext,
+ mMockLocaleManagerService, mMockPackageManagerInternal,
+ new File(Environment.getExternalStorageDirectory(), "lmsUnitTests"), mClock));
+ doNothing().when(mBackupHelper).notifyBackupManager();
+
+ mUserMonitor = mBackupHelper.getUserMonitor();
+ mPackageMonitor = mBackupHelper.getPackageMonitor();
+ setCurrentTimeMillis(DEFAULT_CREATION_TIME_MILLIS);
+ cleanStagedFiles();
+ }
+
+ @Test
+ public void testBackupPayload_noAppsInstalled_returnsNull() throws Exception {
+ doReturn(List.of()).when(mMockPackageManagerInternal)
+ .getInstalledApplications(anyLong(), anyInt(), anyInt());
+
+ assertNull(mBackupHelper.getBackupPayload(DEFAULT_USER_ID));
+ }
+
+ @Test
+ public void testBackupPayload_noAppLocalesSet_returnsNull() throws Exception {
+ setUpLocalesForPackage(DEFAULT_PACKAGE_NAME, LocaleList.getEmptyLocaleList());
+ setUpDummyAppForPackageManager(DEFAULT_PACKAGE_NAME);
+
+ assertNull(mBackupHelper.getBackupPayload(DEFAULT_USER_ID));
+ }
+
+ @Test
+ public void testBackupPayload_appLocalesSet_returnsNonNullBlob() throws Exception {
+ setUpLocalesForPackage(DEFAULT_PACKAGE_NAME, DEFAULT_LOCALES);
+ setUpDummyAppForPackageManager(DEFAULT_PACKAGE_NAME);
+
+ byte[] payload = mBackupHelper.getBackupPayload(DEFAULT_USER_ID);
+ verifyPayloadForAppLocales(DEFAULT_PACKAGE_LOCALES_MAP, payload);
+ }
+
+ @Test
+ public void testBackupPayload_exceptionInGetLocalesAllPackages_returnsNull() throws Exception {
+ setUpDummyAppForPackageManager(DEFAULT_PACKAGE_NAME);
+ doThrow(new RemoteException("mock")).when(mMockLocaleManagerService).getApplicationLocales(
+ anyString(), anyInt());
+
+ assertNull(mBackupHelper.getBackupPayload(DEFAULT_USER_ID));
+ }
+
+ @Test
+ public void testBackupPayload_exceptionInGetLocalesSomePackages_appsWithExceptionNotBackedUp()
+ throws Exception {
+ // Set up two apps.
+ ApplicationInfo defaultAppInfo = new ApplicationInfo();
+ ApplicationInfo anotherAppInfo = new ApplicationInfo();
+ defaultAppInfo.packageName = DEFAULT_PACKAGE_NAME;
+ anotherAppInfo.packageName = "com.android.anotherapp";
+ doReturn(List.of(defaultAppInfo, anotherAppInfo)).when(mMockPackageManagerInternal)
+ .getInstalledApplications(anyLong(), anyInt(), anyInt());
+
+ setUpLocalesForPackage(DEFAULT_PACKAGE_NAME, DEFAULT_LOCALES);
+ // Exception when getting locales for anotherApp.
+ doThrow(new RemoteException("mock")).when(mMockLocaleManagerService).getApplicationLocales(
+ eq(anotherAppInfo.packageName), anyInt());
+
+ byte[] payload = mBackupHelper.getBackupPayload(DEFAULT_USER_ID);
+ verifyPayloadForAppLocales(DEFAULT_PACKAGE_LOCALES_MAP, payload);
+ }
+
+ @Test
+ public void testRestore_nullPayload_nothingRestoredAndNoStageFile() throws Exception {
+ mBackupHelper.stageAndApplyRestoredPayload(/* payload= */ null, DEFAULT_USER_ID);
+
+ verifyNothingRestored();
+ checkStageFileDoesNotExist(DEFAULT_USER_ID);
+ }
+
+ @Test
+ public void testRestore_zeroLengthPayload_nothingRestoredAndNoStageFile() throws Exception {
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ mBackupHelper.stageAndApplyRestoredPayload(/* payload= */ out.toByteArray(),
+ DEFAULT_USER_ID);
+
+ verifyNothingRestored();
+ checkStageFileDoesNotExist(DEFAULT_USER_ID);
+ }
+
+ @Test
+ public void testRestore_allAppsInstalled_noStageFileCreated() throws Exception {
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ writeTestPayload(out, DEFAULT_PACKAGE_LOCALES_MAP);
+
+ setUpPackageInstalled(DEFAULT_PACKAGE_NAME);
+ setUpLocalesForPackage(DEFAULT_PACKAGE_NAME, LocaleList.getEmptyLocaleList());
+
+ mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID);
+
+ // Locales were restored
+ verify(mMockLocaleManagerService, times(1)).setApplicationLocales(DEFAULT_PACKAGE_NAME,
+ DEFAULT_USER_ID, DEFAULT_LOCALES);
+
+ // Stage file wasn't created.
+ checkStageFileDoesNotExist(DEFAULT_USER_ID);
+ }
+
+ @Test
+ public void testRestore_noAppsInstalled_everythingStaged() throws Exception {
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ writeTestPayload(out, DEFAULT_PACKAGE_LOCALES_MAP);
+
+ setUpPackageNotInstalled(DEFAULT_PACKAGE_NAME);
+
+ mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID);
+
+ verifyNothingRestored();
+ verifyStageFileContent(DEFAULT_PACKAGE_LOCALES_MAP,
+ getStageFileIfExists(DEFAULT_USER_ID), DEFAULT_CREATION_TIME_MILLIS);
+ }
+
+ @Test
+ public void testRestore_someAppsInstalled_partiallyStaged() throws Exception {
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ HashMap<String, String> pkgLocalesMap = new HashMap<>();
+
+ String pkgNameA = "com.android.myAppA", pkgNameB = "com.android.myAppB";
+ String langTagsA = "ru", langTagsB = "hi,fr";
+ pkgLocalesMap.put(pkgNameA, langTagsA);
+ pkgLocalesMap.put(pkgNameB, langTagsB);
+ writeTestPayload(out, pkgLocalesMap);
+
+ setUpPackageInstalled(pkgNameA);
+ setUpPackageNotInstalled(pkgNameB);
+ setUpLocalesForPackage(pkgNameA, LocaleList.getEmptyLocaleList());
+
+ mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID);
+
+ verify(mMockLocaleManagerService, times(1)).setApplicationLocales(pkgNameA, DEFAULT_USER_ID,
+ LocaleList.forLanguageTags(langTagsA));
+
+ pkgLocalesMap.remove(pkgNameA);
+ verifyStageFileContent(pkgLocalesMap, getStageFileIfExists(DEFAULT_USER_ID),
+ DEFAULT_CREATION_TIME_MILLIS);
+ }
+
+ @Test
+ public void testRestore_appLocalesAlreadySet_nothingRestoredAndNoStageFile() throws Exception {
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ writeTestPayload(out, DEFAULT_PACKAGE_LOCALES_MAP);
+
+ setUpPackageInstalled(DEFAULT_PACKAGE_NAME);
+ setUpLocalesForPackage(DEFAULT_PACKAGE_NAME, LocaleList.forLanguageTags("hi,mr"));
+
+ mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID);
+
+ // Since locales are already set, we should not restore anything for it.
+ verifyNothingRestored();
+ // Stage file wasn't created
+ checkStageFileDoesNotExist(DEFAULT_USER_ID);
+ }
+
+ @Test
+ public void testRestore_appLocalesSetForSomeApps_restoresOnlyForAppsHavingNoLocalesSet()
+ throws Exception {
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ HashMap<String, String> pkgLocalesMap = new HashMap<>();
+
+ String pkgNameA = "com.android.myAppA", pkgNameB = "com.android.myAppB", pkgNameC =
+ "com.android.myAppC";
+ String langTagsA = "ru", langTagsB = "hi,fr", langTagsC = "zh,es";
+ pkgLocalesMap.put(pkgNameA, langTagsA);
+ pkgLocalesMap.put(pkgNameB, langTagsB);
+ pkgLocalesMap.put(pkgNameC, langTagsC);
+ writeTestPayload(out, pkgLocalesMap);
+
+ // Both app A & B are installed on the device but A has locales already set.
+ setUpPackageInstalled(pkgNameA);
+ setUpPackageInstalled(pkgNameB);
+ setUpPackageNotInstalled(pkgNameC);
+ setUpLocalesForPackage(pkgNameA, LocaleList.forLanguageTags("mr,fr"));
+ setUpLocalesForPackage(pkgNameB, LocaleList.getEmptyLocaleList());
+ setUpLocalesForPackage(pkgNameC, LocaleList.getEmptyLocaleList());
+
+ mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID);
+
+ // Restore locales only for myAppB.
+ verify(mMockLocaleManagerService, times(0)).setApplicationLocales(eq(pkgNameA), anyInt(),
+ any());
+ verify(mMockLocaleManagerService, times(1)).setApplicationLocales(pkgNameB, DEFAULT_USER_ID,
+ LocaleList.forLanguageTags(langTagsB));
+ verify(mMockLocaleManagerService, times(0)).setApplicationLocales(eq(pkgNameC), anyInt(),
+ any());
+
+ // App C is staged.
+ pkgLocalesMap.remove(pkgNameA);
+ pkgLocalesMap.remove(pkgNameB);
+ verifyStageFileContent(pkgLocalesMap, getStageFileIfExists(DEFAULT_USER_ID),
+ DEFAULT_CREATION_TIME_MILLIS);
+ }
+
+ @Test
+ public void testRestore_restoreInvokedAgain_creationTimeChanged() throws Exception {
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ writeTestPayload(out, DEFAULT_PACKAGE_LOCALES_MAP);
+
+ setUpPackageNotInstalled(DEFAULT_PACKAGE_NAME);
+
+ mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID);
+
+ verifyStageFileContent(DEFAULT_PACKAGE_LOCALES_MAP, getStageFileIfExists(DEFAULT_USER_ID),
+ DEFAULT_CREATION_TIME_MILLIS);
+
+ final long newCreationTime = DEFAULT_CREATION_TIME_MILLIS + 100;
+ setCurrentTimeMillis(newCreationTime);
+ mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID);
+
+ verifyStageFileContent(DEFAULT_PACKAGE_LOCALES_MAP, getStageFileIfExists(DEFAULT_USER_ID),
+ newCreationTime);
+ }
+
+ @Test
+ public void testRestore_appInstalledAfterSUW_restoresFromStage() throws Exception {
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ HashMap<String, String> pkgLocalesMap = new HashMap<>();
+
+ String pkgNameA = "com.android.myAppA", pkgNameB = "com.android.myAppB";
+ String langTagsA = "ru", langTagsB = "hi,fr";
+ pkgLocalesMap.put(pkgNameA, langTagsA);
+ pkgLocalesMap.put(pkgNameB, langTagsB);
+ writeTestPayload(out, pkgLocalesMap);
+
+ setUpPackageNotInstalled(pkgNameA);
+ setUpPackageNotInstalled(pkgNameB);
+ setUpLocalesForPackage(pkgNameA, LocaleList.getEmptyLocaleList());
+ setUpLocalesForPackage(pkgNameB, LocaleList.getEmptyLocaleList());
+
+ mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID);
+
+ verifyNothingRestored();
+
+ setUpPackageInstalled(pkgNameA);
+
+ mPackageMonitor.onPackageAdded(pkgNameA, DEFAULT_UID);
+
+ verify(mMockLocaleManagerService, times(1)).setApplicationLocales(pkgNameA, DEFAULT_USER_ID,
+ LocaleList.forLanguageTags(langTagsA));
+
+ pkgLocalesMap.remove(pkgNameA);
+ verifyStageFileContent(pkgLocalesMap, getStageFileIfExists(DEFAULT_USER_ID),
+ DEFAULT_CREATION_TIME_MILLIS);
+
+ setUpPackageInstalled(pkgNameB);
+
+ mPackageMonitor.onPackageAdded(pkgNameB, DEFAULT_UID);
+
+ verify(mMockLocaleManagerService, times(1)).setApplicationLocales(pkgNameB, DEFAULT_USER_ID,
+ LocaleList.forLanguageTags(langTagsB));
+ checkStageFileDoesNotExist(DEFAULT_USER_ID);
+ }
+
+ @Test
+ public void testRestore_appInstalledAfterSUWAndLocalesAlreadySet_restoresNothing()
+ throws Exception {
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ writeTestPayload(out, DEFAULT_PACKAGE_LOCALES_MAP);
+
+ // Package is not present on the device when the SUW restore is going on.
+ setUpPackageNotInstalled(DEFAULT_PACKAGE_NAME);
+
+ mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID);
+
+ verifyNothingRestored();
+ verifyStageFileContent(DEFAULT_PACKAGE_LOCALES_MAP, getStageFileIfExists(DEFAULT_USER_ID),
+ DEFAULT_CREATION_TIME_MILLIS);
+
+ // App is installed later (post SUW).
+ setUpPackageInstalled(DEFAULT_PACKAGE_NAME);
+ setUpLocalesForPackage(DEFAULT_PACKAGE_NAME, LocaleList.forLanguageTags("hi,mr"));
+
+ mPackageMonitor.onPackageAdded(DEFAULT_PACKAGE_NAME, DEFAULT_UID);
+
+ // Since locales are already set, we should not restore anything for it.
+ verifyNothingRestored();
+ checkStageFileDoesNotExist(DEFAULT_USER_ID);
+ }
+
+ @Test
+ public void testStageFileDeletion_backupPassRunAfterRetentionPeriod_stageFileDeleted()
+ throws Exception {
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ writeTestPayload(out, DEFAULT_PACKAGE_LOCALES_MAP);
+
+ setUpPackageNotInstalled(DEFAULT_PACKAGE_NAME);
+
+ mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID);
+
+ verifyNothingRestored();
+ verifyStageFileContent(DEFAULT_PACKAGE_LOCALES_MAP, getStageFileIfExists(DEFAULT_USER_ID),
+ DEFAULT_CREATION_TIME_MILLIS);
+
+ // Retention period has not elapsed.
+ setCurrentTimeMillis(
+ DEFAULT_CREATION_TIME_MILLIS + RETENTION_PERIOD.minusHours(1).toMillis());
+ doReturn(List.of()).when(mMockPackageManagerInternal)
+ .getInstalledApplications(anyLong(), anyInt(), anyInt());
+ assertNull(mBackupHelper.getBackupPayload(DEFAULT_USER_ID));
+
+ // Stage file should NOT be deleted.
+ checkStageFileExists(DEFAULT_USER_ID);
+
+ // Exactly RETENTION_PERIOD amount of time has passed so stage file should still not be
+ // removed.
+ setCurrentTimeMillis(DEFAULT_CREATION_TIME_MILLIS + RETENTION_PERIOD.toMillis());
+ doReturn(List.of()).when(mMockPackageManagerInternal)
+ .getInstalledApplications(anyLong(), anyInt(), anyInt());
+ assertNull(mBackupHelper.getBackupPayload(DEFAULT_USER_ID));
+
+ // Stage file should NOT be deleted.
+ checkStageFileExists(DEFAULT_USER_ID);
+
+ // Retention period has now expired, stage file should be deleted.
+ setCurrentTimeMillis(
+ DEFAULT_CREATION_TIME_MILLIS + RETENTION_PERIOD.plusSeconds(1).toMillis());
+ doReturn(List.of()).when(mMockPackageManagerInternal)
+ .getInstalledApplications(anyLong(), anyInt(), anyInt());
+ assertNull(mBackupHelper.getBackupPayload(DEFAULT_USER_ID));
+
+ // Stage file should be deleted.
+ checkStageFileDoesNotExist(DEFAULT_USER_ID);
+ }
+
+ @Test
+ public void testUserRemoval_userRemoved_stageFileDeleted() throws Exception {
+ final ByteArrayOutputStream outDefault = new ByteArrayOutputStream();
+ writeTestPayload(outDefault, DEFAULT_PACKAGE_LOCALES_MAP);
+
+ final ByteArrayOutputStream outWorkProfile = new ByteArrayOutputStream();
+ String anotherPackage = "com.android.anotherapp";
+ String anotherLangTags = "mr,zh";
+ HashMap<String, String> pkgLocalesMapWorkProfile = new HashMap<>();
+ pkgLocalesMapWorkProfile.put(anotherPackage, anotherLangTags);
+ writeTestPayload(outWorkProfile, pkgLocalesMapWorkProfile);
+
+ // DEFAULT_PACKAGE_NAME is NOT installed on the device.
+ setUpPackageNotInstalled(DEFAULT_PACKAGE_NAME);
+ setUpPackageNotInstalled(anotherPackage);
+
+ mBackupHelper.stageAndApplyRestoredPayload(outDefault.toByteArray(), DEFAULT_USER_ID);
+ mBackupHelper.stageAndApplyRestoredPayload(outWorkProfile.toByteArray(),
+ WORK_PROFILE_USER_ID);
+
+ verifyNothingRestored();
+
+ // Verify stage file contents.
+ AtomicFile stageFileDefaultUser = getStageFileIfExists(DEFAULT_USER_ID);
+ verifyStageFileContent(DEFAULT_PACKAGE_LOCALES_MAP, stageFileDefaultUser,
+ DEFAULT_CREATION_TIME_MILLIS);
+
+ AtomicFile stageFileWorkProfile = getStageFileIfExists(WORK_PROFILE_USER_ID);
+ verifyStageFileContent(pkgLocalesMapWorkProfile, stageFileWorkProfile,
+ DEFAULT_CREATION_TIME_MILLIS);
+
+ Intent intent = new Intent();
+ intent.setAction(Intent.ACTION_USER_REMOVED);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, DEFAULT_USER_ID);
+ mUserMonitor.onReceive(mMockContext, intent);
+
+ // Stage file should be removed only for DEFAULT_USER_ID.
+ checkStageFileDoesNotExist(DEFAULT_USER_ID);
+ verifyStageFileContent(pkgLocalesMapWorkProfile, stageFileWorkProfile,
+ DEFAULT_CREATION_TIME_MILLIS);
+ }
+
+ @Test
+ public void testLoadStageFiles_invalidNameFormat_stageFileDeleted() throws Exception {
+ // Stage file name should be : staged_locales_<user_id_int>.xml
+ File stageFile = new File(STAGED_LOCALES_DIR, "xyz.xml");
+ assertTrue(stageFile.createNewFile());
+ assertTrue(stageFile.isFile());
+
+ // Putting valid xml data in file.
+ FileOutputStream out = new FileOutputStream(stageFile);
+ writeTestPayload(out, DEFAULT_PACKAGE_LOCALES_MAP, /* forStage= */
+ true, /* creationTimeMillis= */ 0);
+ out.flush();
+ out.close();
+
+ verifyStageFileContent(DEFAULT_PACKAGE_LOCALES_MAP,
+ new AtomicFile(stageFile), /* creationTimeMillis= */ 0);
+
+ mBackupHelper = new LocaleManagerBackupHelper(mMockContext, mMockLocaleManagerService,
+ mMockPackageManagerInternal, STAGED_LOCALES_DIR, mClock);
+ assertFalse(stageFile.isFile());
+ }
+
+ @Test
+ public void testLoadStageFiles_userIdNotParseable_stageFileDeleted() throws Exception {
+ // Stage file name should be : staged_locales_<user_id_int>.xml
+ File stageFile = new File(STAGED_LOCALES_DIR, "staged_locales_abc.xml");
+ assertTrue(stageFile.createNewFile());
+ assertTrue(stageFile.isFile());
+
+ // Putting valid xml data in file.
+ FileOutputStream out = new FileOutputStream(stageFile);
+ writeTestPayload(out, DEFAULT_PACKAGE_LOCALES_MAP, /* forStage= */
+ true, /* creationTimeMillis= */ 0);
+ out.flush();
+ out.close();
+
+ verifyStageFileContent(DEFAULT_PACKAGE_LOCALES_MAP,
+ new AtomicFile(stageFile), /* creationTimeMillis= */ 0);
+
+ mBackupHelper = new LocaleManagerBackupHelper(mMockContext, mMockLocaleManagerService,
+ mMockPackageManagerInternal, STAGED_LOCALES_DIR, mClock);
+ assertFalse(stageFile.isFile());
+ }
+
+ @Test
+ public void testLoadStageFiles_invalidContent_stageFileDeleted() throws Exception {
+ File stageFile = new File(STAGED_LOCALES_DIR, "staged_locales_0.xml");
+ assertTrue(stageFile.createNewFile());
+ assertTrue(stageFile.isFile());
+
+ FileOutputStream out = new FileOutputStream(stageFile);
+ out.write("some_non_xml_string".getBytes());
+ out.close();
+
+ mBackupHelper = new LocaleManagerBackupHelper(mMockContext, mMockLocaleManagerService,
+ mMockPackageManagerInternal, STAGED_LOCALES_DIR, mClock);
+ assertFalse(stageFile.isFile());
+ }
+
+ @Test
+ public void testLoadStageFiles_validContent_doesLazyRestore() throws Exception {
+ File stageFile = new File(STAGED_LOCALES_DIR, "staged_locales_0.xml");
+ assertTrue(stageFile.createNewFile());
+ assertTrue(stageFile.isFile());
+
+ // Putting valid xml data in file.
+ FileOutputStream out = new FileOutputStream(stageFile);
+ writeTestPayload(out, DEFAULT_PACKAGE_LOCALES_MAP, /* forStage= */
+ true, DEFAULT_CREATION_TIME_MILLIS);
+ out.flush();
+ out.close();
+
+ verifyStageFileContent(DEFAULT_PACKAGE_LOCALES_MAP,
+ new AtomicFile(stageFile), DEFAULT_CREATION_TIME_MILLIS);
+
+ mBackupHelper = new LocaleManagerBackupHelper(mMockContext, mMockLocaleManagerService,
+ mMockPackageManagerInternal, STAGED_LOCALES_DIR, mClock);
+ mPackageMonitor = mBackupHelper.getPackageMonitor();
+
+ // Stage file still exists.
+ assertTrue(stageFile.isFile());
+
+ // App is installed later.
+ setUpPackageInstalled(DEFAULT_PACKAGE_NAME);
+ setUpLocalesForPackage(DEFAULT_PACKAGE_NAME, LocaleList.getEmptyLocaleList());
+
+ mPackageMonitor.onPackageAdded(DEFAULT_PACKAGE_NAME, DEFAULT_UID);
+
+ verify(mMockLocaleManagerService, times(1)).setApplicationLocales(DEFAULT_PACKAGE_NAME,
+ DEFAULT_USER_ID, DEFAULT_LOCALES);
+
+ // Stage file gets deleted here because all staged locales have been applied.
+ assertFalse(stageFile.isFile());
+ }
+
+ private void setUpPackageInstalled(String packageName) throws Exception {
+ doReturn(new PackageInfo()).when(mMockPackageManager).getPackageInfoAsUser(
+ eq(packageName), anyInt(), anyInt());
+ }
+
+ private void setUpPackageNotInstalled(String packageName) throws Exception {
+ doReturn(null).when(mMockPackageManager).getPackageInfoAsUser(eq(packageName),
+ anyInt(), anyInt());
+ }
+
+ private void setUpLocalesForPackage(String packageName, LocaleList locales) throws Exception {
+ doReturn(locales).when(mMockLocaleManagerService).getApplicationLocales(
+ eq(packageName), anyInt());
+ }
+
+ private void setUpDummyAppForPackageManager(String packageName) {
+ ApplicationInfo dummyApp = new ApplicationInfo();
+ dummyApp.packageName = packageName;
+ doReturn(List.of(dummyApp)).when(mMockPackageManagerInternal)
+ .getInstalledApplications(anyLong(), anyInt(), anyInt());
+ }
+
+ /**
+ * Verifies that nothing was restored for any package.
+ *
+ * <p>If {@link LocaleManagerService#setApplicationLocales} is not invoked, we can conclude
+ * that nothing was restored.
+ */
+ private void verifyNothingRestored() throws Exception {
+ verify(mMockLocaleManagerService, times(0)).setApplicationLocales(anyString(), anyInt(),
+ any());
+ }
+
+
+ private static void verifyPayloadForAppLocales(Map<String, String> expectedPkgLocalesMap,
+ byte[] payload)
+ throws IOException, XmlPullParserException {
+ verifyPayloadForAppLocales(expectedPkgLocalesMap, payload, /* forStage= */ false, -1);
+ }
+
+ private static void verifyPayloadForAppLocales(Map<String, String> expectedPkgLocalesMap,
+ byte[] payload, boolean forStage, long expectedCreationTime)
+ throws IOException, XmlPullParserException {
+ final ByteArrayInputStream stream = new ByteArrayInputStream(payload);
+ final TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(stream, StandardCharsets.UTF_8.name());
+
+ Map<String, String> backupDataMap = new HashMap<>();
+ XmlUtils.beginDocument(parser, TEST_LOCALES_XML_TAG);
+ if (forStage) {
+ long actualCreationTime = parser.getAttributeLong(/* namespace= */ null,
+ "creationTimeMillis");
+ assertEquals(expectedCreationTime, actualCreationTime);
+ }
+ int depth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, depth)) {
+ if (parser.getName().equals("package")) {
+ String packageName = parser.getAttributeValue(null, "name");
+ String languageTags = parser.getAttributeValue(null, "locales");
+ backupDataMap.put(packageName, languageTags);
+ }
+ }
+
+ assertEquals(expectedPkgLocalesMap, backupDataMap);
+ }
+
+ private static void writeTestPayload(OutputStream stream, Map<String, String> pkgLocalesMap)
+ throws IOException {
+ writeTestPayload(stream, pkgLocalesMap, /* forStage= */ false, /* creationTimeMillis= */
+ -1);
+ }
+
+ private static void writeTestPayload(OutputStream stream, Map<String, String> pkgLocalesMap,
+ boolean forStage, long creationTimeMillis)
+ throws IOException {
+ if (pkgLocalesMap.isEmpty()) {
+ return;
+ }
+
+ TypedXmlSerializer out = Xml.newFastSerializer();
+ out.setOutput(stream, StandardCharsets.UTF_8.name());
+ out.startDocument(/* encoding= */ null, /* standalone= */ true);
+ out.startTag(/* namespace= */ null, TEST_LOCALES_XML_TAG);
+
+ if (forStage) {
+ out.attribute(/* namespace= */ null, "creationTimeMillis",
+ Long.toString(creationTimeMillis));
+ }
+
+ for (String pkg : pkgLocalesMap.keySet()) {
+ out.startTag(/* namespace= */ null, "package");
+ out.attribute(/* namespace= */ null, "name", pkg);
+ out.attribute(/* namespace= */ null, "locales", pkgLocalesMap.get(pkg));
+ out.endTag(/*namespace= */ null, "package");
+ }
+
+ out.endTag(/* namespace= */ null, TEST_LOCALES_XML_TAG);
+ out.endDocument();
+ }
+
+ private static void verifyStageFileContent(Map<String, String> expectedPkgLocalesMap,
+ AtomicFile stageFile,
+ long creationTimeMillis)
+ throws Exception {
+ assertNotNull(stageFile);
+ try (InputStream stagedDataInputStream = stageFile.openRead()) {
+ verifyPayloadForAppLocales(expectedPkgLocalesMap, stagedDataInputStream.readAllBytes(),
+ /* forStage= */ true, creationTimeMillis);
+ } catch (IOException | XmlPullParserException e) {
+ throw e;
+ }
+ }
+
+ private static void checkStageFileDoesNotExist(int userId) {
+ assertNull(getStageFileIfExists(userId));
+ }
+
+ private static void checkStageFileExists(int userId) {
+ assertNotNull(getStageFileIfExists(userId));
+ }
+
+ private static AtomicFile getStageFileIfExists(int userId) {
+ File file = new File(STAGED_LOCALES_DIR, String.format("staged_locales_%d.xml", userId));
+ if (file.isFile()) {
+ return new AtomicFile(file);
+ }
+ return null;
+ }
+
+ private static void cleanStagedFiles() {
+ File[] files = STAGED_LOCALES_DIR.listFiles();
+ if (files != null) {
+ for (File f : files) {
+ f.delete();
+ }
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java
index ddc58b2..658f8d5 100644
--- a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java
@@ -30,6 +30,7 @@
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.Manifest;
@@ -60,7 +61,7 @@
private static final int DEFAULT_USER_ID = 0;
private static final int DEFAULT_UID = Binder.getCallingUid() + 100;
private static final int INVALID_UID = -1;
- private static final String DEFAULT_LOCALE_TAGS = "en-XC, ar-XB";
+ private static final String DEFAULT_LOCALE_TAGS = "en-XC,ar-XB";
private static final LocaleList DEFAULT_LOCALES =
LocaleList.forLanguageTags(DEFAULT_LOCALE_TAGS);
private static final InstallSourceInfo DEFAULT_INSTALL_SOURCE_INFO = new InstallSourceInfo(
@@ -68,6 +69,7 @@
/* originatingPackageName = */ null, /* installingPackageName = */ null);
private LocaleManagerService mLocaleManagerService;
+ private LocaleManagerBackupHelper mMockBackupHelper;
@Mock
private Context mMockContext;
@@ -104,8 +106,9 @@
.handleIncomingUser(anyInt(), anyInt(), eq(DEFAULT_USER_ID), anyBoolean(), anyInt(),
anyString(), anyString());
+ mMockBackupHelper = mock(ShadowLocaleManagerBackupHelper.class);
mLocaleManagerService = new LocaleManagerService(mMockContext, mMockActivityTaskManager,
- mMockActivityManager, mMockPackageManagerInternal);
+ mMockActivityManager, mMockPackageManagerInternal, mMockBackupHelper);
}
@Test(expected = SecurityException.class)
@@ -122,6 +125,7 @@
verify(mMockContext).enforceCallingOrSelfPermission(
eq(android.Manifest.permission.CHANGE_CONFIGURATION),
anyString());
+ verify(mMockBackupHelper, times(0)).notifyBackupManager();
assertNoLocalesStored(mFakePackageConfigurationUpdater.getStoredLocales());
}
}
@@ -133,6 +137,7 @@
DEFAULT_USER_ID, LocaleList.getEmptyLocaleList());
fail("Expected NullPointerException");
} finally {
+ verify(mMockBackupHelper, times(0)).notifyBackupManager();
assertNoLocalesStored(mFakePackageConfigurationUpdater.getStoredLocales());
}
}
@@ -146,6 +151,7 @@
/* locales = */ null);
fail("Expected NullPointerException");
} finally {
+ verify(mMockBackupHelper, times(0)).notifyBackupManager();
assertNoLocalesStored(mFakePackageConfigurationUpdater.getStoredLocales());
}
}
@@ -163,6 +169,7 @@
DEFAULT_LOCALES);
assertEquals(DEFAULT_LOCALES, mFakePackageConfigurationUpdater.getStoredLocales());
+ verify(mMockBackupHelper, times(1)).notifyBackupManager();
}
@@ -175,6 +182,7 @@
DEFAULT_LOCALES);
assertEquals(DEFAULT_LOCALES, mFakePackageConfigurationUpdater.getStoredLocales());
+ verify(mMockBackupHelper, times(1)).notifyBackupManager();
}
@Test(expected = IllegalArgumentException.class)
@@ -187,6 +195,7 @@
fail("Expected IllegalArgumentException");
} finally {
assertNoLocalesStored(mFakePackageConfigurationUpdater.getStoredLocales());
+ verify(mMockBackupHelper, times(0)).notifyBackupManager();
}
}
@@ -217,7 +226,7 @@
.when(mMockActivityTaskManager).getApplicationConfig(anyString(), anyInt());
LocaleList locales = mLocaleManagerService.getApplicationLocales(
- DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
assertEquals(LocaleList.getEmptyLocaleList(), locales);
}
diff --git a/services/tests/servicestests/src/com/android/server/locales/ShadowLocaleManagerBackupHelper.java b/services/tests/servicestests/src/com/android/server/locales/ShadowLocaleManagerBackupHelper.java
new file mode 100644
index 0000000..93972c3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locales/ShadowLocaleManagerBackupHelper.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locales;
+
+import android.content.Context;
+import android.content.pm.PackageManagerInternal;
+
+import java.io.File;
+import java.time.Clock;
+
+/**
+ * Shadow for {@link LocaleManagerBackupHelper} to enable mocking it for tests.
+ *
+ * <p>{@link LocaleManagerBackupHelper} is a package private class and hence not mockable directly.
+ */
+public class ShadowLocaleManagerBackupHelper extends LocaleManagerBackupHelper {
+ ShadowLocaleManagerBackupHelper(Context context,
+ LocaleManagerService localeManagerService,
+ PackageManagerInternal pmInternal, File stagedLocalesDir, Clock clock) {
+ super(context, localeManagerService, pmInternal, stagedLocalesDir, clock);
+ }
+}
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 9fec96b..467084a 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -837,6 +837,17 @@
"android.telecom.extra.AUDIO_CODEC_BANDWIDTH_KHZ";
/**
+ * Last known cell identity key to be used to fill geo location header in case of an emergency
+ * call. This entry will not be filled if call is not identified as an emergency call.
+ * {@link Connection}. Only provided to the {@link ConnectionService} for the purpose
+ * of placing an emergency call; will not be present in the {@link InCallService} layer.
+ * The {@link ConnectionService}'s implementation will be logged for fine location access
+ * when an outgoing call is placed in this case.
+ */
+ public static final String EXTRA_LAST_KNOWN_CELL_IDENTITY =
+ "android.telecom.extra.LAST_KNOWN_CELL_IDENTITY";
+
+ /**
* Boolean connection extra key used to indicate whether device to device communication is
* available for the current call.
* @hide
diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
index 2b52af3..1ba997f 100644
--- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -576,6 +576,17 @@
}
/**
+ * Check if the caller (or self, if not processing an IPC) has ACCESS_LAST_KNOWN_CELL_ID
+ * permission
+ *
+ * @return true if caller has ACCESS_LAST_KNOWN_CELL_ID permission else false.
+ */
+ public static boolean checkLastKnownCellIdAccessPermission(Context context) {
+ return context.checkCallingOrSelfPermission("android.permission.ACCESS_LAST_KNOWN_CELL_ID")
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ /**
* Ensure the caller (or self, if not processing an IPC) has
* {@link android.Manifest.permission#READ_PHONE_STATE} or carrier privileges.
*
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 6f92c31..7d24b76 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1063,6 +1063,12 @@
"always_show_emergency_alert_onoff_bool";
/**
+ * Default mobile network MTU value, in bytes.
+ * @hide
+ */
+ public static final String KEY_DEFAULT_MTU_INT = "default_mtu_int";
+
+ /**
* The data call retry configuration for different types of APN.
* @hide
*/
@@ -2914,19 +2920,37 @@
"signal_strength_nr_nsa_use_lte_as_primary_bool";
/**
+ * String array of TCP buffer sizes per network type.
+ * The entries should be of the following form, with values in bytes:
+ * "network_name:read_min,read_default,read_max,write_min,write_default,write_max".
+ * For NR (5G), the following network names should be used:
+ * - NR_NSA: NR NSA, sub-6 frequencies
+ * - NR_NSA_MMWAVE: NR NSA, mmwave frequencies
+ * - NR_SA: NR SA, sub-6 frequencies
+ * - NR_SA_MMWAVE: NR SA, mmwave frequencies
+ * @hide
+ */
+ public static final String KEY_TCP_BUFFERS_STRING_ARRAY = "tcp_buffers_string_array";
+
+ /**
* String array of default bandwidth values per network type.
- * The entries should be of form "network_name:downstream,upstream", with values in Kbps.
+ * The entries should be of form: "network_name:downlink,uplink", with values in Kbps.
+ * For NR (5G), the following network names should be used:
+ * - NR_NSA: NR NSA, sub-6 frequencies
+ * - NR_NSA_MMWAVE: NR NSA, mmwave frequencies
+ * - NR_SA: NR SA, sub-6 frequencies
+ * - NR_SA_MMWAVE: NR SA, mmwave frequencies
* @hide
*/
public static final String KEY_BANDWIDTH_STRING_ARRAY = "bandwidth_string_array";
/**
* For NR (non-standalone), whether to use the LTE value instead of NR value as the default for
- * upstream bandwidth. Downstream bandwidth will still use the NR value as the default.
+ * uplink bandwidth. Downlink bandwidth will still use the NR value as the default.
* @hide
*/
- public static final String KEY_BANDWIDTH_NR_NSA_USE_LTE_VALUE_FOR_UPSTREAM_BOOL =
- "bandwidth_nr_nsa_use_lte_value_for_upstream_bool";
+ public static final String KEY_BANDWIDTH_NR_NSA_USE_LTE_VALUE_FOR_UPLINK_BOOL =
+ "bandwidth_nr_nsa_use_lte_value_for_uplink_bool";
/**
* Key identifying if voice call barring notification is required to be shown to the user.
@@ -3628,6 +3652,18 @@
public static final String KEY_5G_WATCHDOG_TIME_MS_LONG = "5g_watchdog_time_ms_long";
/**
+ * Which NR types are unmetered. A string array containing the following keys:
+ * NR_NSA - NR NSA is unmetered for sub-6 frequencies
+ * NR_NSA_MMWAVE - NR NSA is unmetered for mmwave frequencies
+ * NR_SA - NR SA is unmetered for sub-6 frequencies
+ * NR_SA_MMWAVE - NR SA is unmetered for mmwave frequencies
+ * TODO: remove other unmetered keys and replace with this
+ * @hide
+ */
+ public static final String KEY_UNMETERED_NETWORK_TYPES_STRING_ARRAY =
+ "unmetered_network_types_string_array";
+
+ /**
* Whether NR (non-standalone) should be unmetered for all frequencies.
* If either {@link #KEY_UNMETERED_NR_NSA_MMWAVE_BOOL} or
* {@link #KEY_UNMETERED_NR_NSA_SUB6_BOOL} are true, then this value will be ignored.
@@ -5483,6 +5519,7 @@
sDefaults.putBoolean(KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL, false);
sDefaults.putBoolean(KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL, false);
+ sDefaults.putInt(KEY_DEFAULT_MTU_INT, 1500);
sDefaults.putStringArray(KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS, new String[]{
"default:default_randomization=2000,5000,10000,20000,40000,80000:5000,160000:5000,"
+ "320000:5000,640000:5000,1280000:5000,1800000:5000",
@@ -5801,12 +5838,35 @@
CellSignalStrengthNr.USE_SSRSRP);
sDefaults.putBoolean(KEY_SIGNAL_STRENGTH_NR_NSA_USE_LTE_AS_PRIMARY_BOOL, true);
sDefaults.putStringArray(KEY_BANDWIDTH_STRING_ARRAY, new String[]{
- "GPRS:24,24", "EDGE:70,18", "UMTS:115,115", "CDMA-IS95A:14,14", "CDMA-IS95B:14,14",
- "1xRTT:30,30", "EvDo-rev.0:750,48", "EvDo-rev.A:950,550", "HSDPA:4300,620",
- "HSUPA:4300,1800", "HSPA:4300,1800", "EvDo-rev.B:1500,550", "eHRPD:750,48",
- "HSPAP:13000,3400", "TD-SCDMA:115,115", "LTE:30000,15000", "NR_NSA:47000,18000",
- "NR_NSA_MMWAVE:145000,60000", "NR_SA:145000,60000"});
- sDefaults.putBoolean(KEY_BANDWIDTH_NR_NSA_USE_LTE_VALUE_FOR_UPSTREAM_BOOL, false);
+ "GPRS:24,24", "EDGE:70,18", "UMTS:115,115", "CDMA:14,14",
+ "1xRTT:30,30", "EvDo_0:750,48", "EvDo_A:950,550", "HSDPA:4300,620",
+ "HSUPA:4300,1800", "HSPA:4300,1800", "EvDo_B:1500,550", "eHRPD:750,48",
+ "iDEN:14,14", "LTE:30000,15000", "HSPA+:13000,3400", "GSM:24,24",
+ "TD_SCDMA:115,115", "LTE_CA:30000,15000", "NR_NSA:47000,18000",
+ "NR_NSA_MMWAVE:145000,60000", "NR_SA:145000,60000", "NR_SA_MMWAVE:145000,60000"});
+ sDefaults.putStringArray(KEY_TCP_BUFFERS_STRING_ARRAY, new String[]{
+ "GPRS:4092,8760,48000,4096,8760,48000", "EDGE:4093,26280,70800,4096,16384,70800",
+ "UMTS:58254,349525,1048576,58254,349525,1048576",
+ "CDMA:4094,87380,262144,4096,16384,262144",
+ "1xRTT:16384,32768,131072,4096,16384,102400",
+ "EvDo_0:4094,87380,262144,4096,16384,262144",
+ "EvDo_A:4094,87380,262144,4096,16384,262144",
+ "HSDPA:61167,367002,1101005,8738,52429,262114",
+ "HSUPA:40778,244668,734003,16777,100663,301990",
+ "HSPA:40778,244668,734003,16777,100663,301990",
+ "EvDo_B:4094,87380,262144,4096,16384,262144",
+ "eHRPD:131072,262144,1048576,4096,16384,524288",
+ "iDEN:4094,87380,262144,4096,16384,262144",
+ "LTE:524288,1048576,2097152,262144,524288,1048576",
+ "HSPA+:122334,734003,2202010,32040,192239,576717",
+ "GSM:4092,8760,48000,4096,8760,48000",
+ "TD_SCDMA:58254,349525,1048576,58254,349525,1048576",
+ "LTE_CA:4096,6291456,12582912,4096,1048576,2097152",
+ "NR_NSA:2097152,6291456,16777216,512000,2097152,8388608",
+ "NR_NSA_MMWAVE:2097152,6291456,16777216,512000,2097152,8388608",
+ "NR_SA:2097152,6291456,16777216,512000,2097152,8388608",
+ "NR_SA_MMWAVE:2097152,6291456,16777216,512000,2097152,8388608"});
+ sDefaults.putBoolean(KEY_BANDWIDTH_NR_NSA_USE_LTE_VALUE_FOR_UPLINK_BOOL, false);
sDefaults.putString(KEY_WCDMA_DEFAULT_SIGNAL_STRENGTH_MEASUREMENT_STRING, "rssi");
sDefaults.putBoolean(KEY_CONFIG_SHOW_ORIG_DIAL_STRING_FOR_CDMA_BOOL, false);
sDefaults.putBoolean(KEY_SHOW_CALL_BLOCKING_DISABLED_NOTIFICATION_ALWAYS_BOOL, false);
@@ -5832,6 +5892,7 @@
sDefaults.putInt(KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT, 0);
sDefaults.putBoolean(KEY_ENABLE_NR_ADVANCED_WHILE_ROAMING_BOOL, true);
sDefaults.putBoolean(KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL, false);
+ sDefaults.putStringArray(KEY_UNMETERED_NETWORK_TYPES_STRING_ARRAY, new String[0]);
sDefaults.putBoolean(KEY_UNMETERED_NR_NSA_BOOL, false);
sDefaults.putBoolean(KEY_UNMETERED_NR_NSA_MMWAVE_BOOL, false);
sDefaults.putBoolean(KEY_UNMETERED_NR_NSA_SUB6_BOOL, false);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 0267b68..ae22247 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -16035,4 +16035,31 @@
ex.rethrowAsRuntimeException();
}
}
+
+ /**
+ * Get last known cell identity.
+ * Require {@link android.Manifest.permission#ACCESS_FINE_LOCATION} and
+ * com.android.phone.permission.ACCESS_LAST_KNOWN_CELL_ID, otherwise throws SecurityException.
+ * If there is current registered network this value will be same as the registered cell
+ * identity. If the device goes out of service the previous cell identity is cached and
+ * will be returned. If the cache age of the Cell identity is more than 24 hours
+ * it will be cleared and null will be returned.
+ * @return last known cell identity {@CellIdentity}.
+ * @hide
+ */
+ @RequiresPermission(allOf = {Manifest.permission.ACCESS_FINE_LOCATION,
+ "com.android.phone.permission.ACCESS_LAST_KNOWN_CELL_ID"})
+ public @Nullable CellIdentity getLastKnownCellIdentity() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony == null) {
+ throw new IllegalStateException("telephony service is null.");
+ }
+ return telephony.getLastKnownCellIdentity(getSubId(), getOpPackageName(),
+ getAttributionTag());
+ } catch (RemoteException ex) {
+ ex.rethrowAsRuntimeException();
+ }
+ return null;
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index d5c9ec4..1f336f7 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2493,4 +2493,16 @@
* Unregister an IMS connection state callback
*/
void unregisterImsStateCallback(in IImsStateCallback cb);
+
+ /**
+ * return last known cell identity
+ * @param subId user preferred subId.
+ * @param callingPackage the name of the package making the call.
+ * @param callingFeatureId The feature in the package.
+ */
+ CellIdentity getLastKnownCellIdentity(int subId, String callingPackage,
+ String callingFeatureId);
+
+ /** Check if telephony new data stack is enabled. */
+ boolean isUsingNewDataStack();
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index aec80ac..75f1337 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -141,6 +141,10 @@
*
* @param originalLayer Layer that should be visible at the start
* @param newLayer Layer that should be visible at the end
+ * @param ignoreEntriesWithRotationLayer If entries with a visible rotation layer should be ignored
+ * when checking the transition. If true we will not fail the assertion if a rotation layer is
+ * visible to fill the gap between the [originalLayer] being visible and the [newLayer] being
+ * visible.
* @param ignoreSnapshot If the snapshot layer should be ignored during the transition
* (useful mostly for app launch)
* @param ignoreSplashscreen If the splashscreen layer should be ignored during the transition.
@@ -150,20 +154,23 @@
fun FlickerTestParameter.replacesLayer(
originalLayer: FlickerComponentName,
newLayer: FlickerComponentName,
+ ignoreEntriesWithRotationLayer: Boolean = false,
ignoreSnapshot: Boolean = false,
ignoreSplashscreen: Boolean = true
) {
assertLayers {
val assertion = this.isVisible(originalLayer)
- if (ignoreSnapshot || ignoreSplashscreen) {
- assertion.then()
+
+ if (ignoreEntriesWithRotationLayer) {
+ assertion.then().isVisible(FlickerComponentName.ROTATION, isOptional = true)
}
if (ignoreSnapshot) {
- assertion.isVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
+ assertion.then().isVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
}
if (ignoreSplashscreen) {
- assertion.isSplashScreenVisibleFor(newLayer, isOptional = true)
+ assertion.then().isSplashScreenVisibleFor(newLayer, isOptional = true)
}
+
assertion.then().isVisible(newLayer)
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
index 62e3fa61..b5c81bb 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
@@ -166,7 +166,8 @@
* is replaced by [testApp], which remains visible until the end
*/
open fun appLayerReplacesLauncher() {
- testSpec.replacesLayer(LAUNCHER_COMPONENT, testApp.component, ignoreSnapshot = true)
+ testSpec.replacesLayer(LAUNCHER_COMPONENT, testApp.component,
+ ignoreEntriesWithRotationLayer = true, ignoreSnapshot = true)
}
/**
diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
index 1ab59a8..05e8bde 100644
--- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
+++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
@@ -106,26 +106,26 @@
* @param files the paths of files which might contain wildcards
*/
private void deleteFiles(String... files) throws Exception {
- boolean found = false;
- for (String file : files) {
- CommandResult result = getDevice().executeShellV2Command("ls " + file);
- if (result.getStatus() == CommandStatus.SUCCESS) {
- found = true;
- break;
+ try {
+ getDevice().enableAdbRoot();
+ boolean found = false;
+ for (String file : files) {
+ CommandResult result = getDevice().executeShellV2Command("ls " + file);
+ if (result.getStatus() == CommandStatus.SUCCESS) {
+ found = true;
+ break;
+ }
}
- }
- if (found) {
- try {
- getDevice().enableAdbRoot();
+ if (found) {
getDevice().remountSystemWritable();
for (String file : files) {
getDevice().executeShellCommand("rm -rf " + file);
}
- } finally {
- getDevice().disableAdbRoot();
+ getDevice().reboot();
}
- getDevice().reboot();
+ } finally {
+ getDevice().disableAdbRoot();
}
}
diff --git a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
index 926bf1b..f06fa81e 100644
--- a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
@@ -112,6 +112,10 @@
* @param files the paths of files which might contain wildcards
*/
private void deleteFiles(String... files) throws Exception {
+ if (!getDevice().isAdbRoot()) {
+ getDevice().enableAdbRoot();
+ }
+
boolean found = false;
for (String file : files) {
CommandResult result = getDevice().executeShellV2Command("ls " + file);
@@ -122,9 +126,6 @@
}
if (found) {
- if (!getDevice().isAdbRoot()) {
- getDevice().enableAdbRoot();
- }
getDevice().remountSystemWritable();
for (String file : files) {
getDevice().executeShellCommand("rm -rf " + file);
diff --git a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkPriorityTest.java b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkPriorityTest.java
index f7d36970..476be44 100644
--- a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkPriorityTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkPriorityTest.java
@@ -36,7 +36,7 @@
return new VcnCellUnderlyingNetworkPriority.Builder()
.setNetworkQuality(NETWORK_QUALITY_OK)
.setAllowMetered(true /* allowMetered */)
- .setAllowedPlmnIds(ALLOWED_PLMN_IDS)
+ .setAllowedOperatorPlmnIds(ALLOWED_PLMN_IDS)
.setAllowedSpecificCarrierIds(ALLOWED_CARRIER_IDS)
.setAllowRoaming(true /* allowRoaming */)
.setRequireOpportunistic(true /* requireOpportunistic */)
@@ -48,7 +48,7 @@
final VcnCellUnderlyingNetworkPriority networkPriority = getTestNetworkPriority();
assertEquals(NETWORK_QUALITY_OK, networkPriority.getNetworkQuality());
assertTrue(networkPriority.allowMetered());
- assertEquals(ALLOWED_PLMN_IDS, networkPriority.getAllowedPlmnIds());
+ assertEquals(ALLOWED_PLMN_IDS, networkPriority.getAllowedOperatorPlmnIds());
assertEquals(ALLOWED_CARRIER_IDS, networkPriority.getAllowedSpecificCarrierIds());
assertTrue(networkPriority.allowRoaming());
assertTrue(networkPriority.requireOpportunistic());
@@ -60,7 +60,7 @@
new VcnCellUnderlyingNetworkPriority.Builder().build();
assertEquals(NETWORK_QUALITY_ANY, networkPriority.getNetworkQuality());
assertFalse(networkPriority.allowMetered());
- assertEquals(new HashSet<String>(), networkPriority.getAllowedPlmnIds());
+ assertEquals(new HashSet<String>(), networkPriority.getAllowedOperatorPlmnIds());
assertEquals(new HashSet<Integer>(), networkPriority.getAllowedSpecificCarrierIds());
assertFalse(networkPriority.allowRoaming());
assertFalse(networkPriority.requireOpportunistic());
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index 724c33f..377f526 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -17,6 +17,8 @@
package android.net.vcn;
import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE;
+import static android.net.vcn.VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_PRIORITIES;
+import static android.net.vcn.VcnGatewayConnectionConfig.UNDERLYING_NETWORK_PRIORITIES_KEY;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -30,6 +32,7 @@
import android.net.ipsec.ike.IkeTunnelConnectionParams;
import android.net.vcn.persistablebundleutils.IkeSessionParamsUtilsTest;
import android.net.vcn.persistablebundleutils.TunnelConnectionParamsUtilsTest;
+import android.os.PersistableBundle;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -230,6 +233,16 @@
assertEquals(config, new VcnGatewayConnectionConfig(config.toPersistableBundle()));
}
+ @Test
+ public void testParsePersistableBundleWithoutVcnUnderlyingNetworkPriorities() {
+ PersistableBundle configBundle = buildTestConfig().toPersistableBundle();
+ configBundle.putPersistableBundle(UNDERLYING_NETWORK_PRIORITIES_KEY, null);
+
+ final VcnGatewayConnectionConfig config = new VcnGatewayConnectionConfig(configBundle);
+ assertEquals(
+ DEFAULT_UNDERLYING_NETWORK_PRIORITIES, config.getVcnUnderlyingNetworkPriorities());
+ }
+
private static IkeTunnelConnectionParams buildTunnelConnectionParams(String ikePsk) {
final IkeSessionParams ikeParams =
IkeSessionParamsUtilsTest.createBuilderMinimum()
@@ -271,4 +284,40 @@
assertNotEquals(tunnelParams, anotherTunnelParams);
assertNotEquals(config, anotherConfig);
}
+
+ private static VcnGatewayConnectionConfig buildTestConfigWithVcnUnderlyingNetworkPriorities(
+ LinkedHashSet<VcnUnderlyingNetworkPriority> networkPriorities) {
+ return buildTestConfigWithExposedCaps(
+ new VcnGatewayConnectionConfig.Builder(
+ "buildTestConfigWithVcnUnderlyingNetworkPriorities",
+ TUNNEL_CONNECTION_PARAMS)
+ .setVcnUnderlyingNetworkPriorities(networkPriorities),
+ EXPOSED_CAPS);
+ }
+
+ @Test
+ public void testVcnUnderlyingNetworkPrioritiesEquality() throws Exception {
+ final VcnGatewayConnectionConfig config =
+ buildTestConfigWithVcnUnderlyingNetworkPriorities(UNDERLYING_NETWORK_PRIORITIES);
+
+ final LinkedHashSet<VcnUnderlyingNetworkPriority> networkPrioritiesEqual =
+ new LinkedHashSet();
+ networkPrioritiesEqual.add(VcnCellUnderlyingNetworkPriorityTest.getTestNetworkPriority());
+ networkPrioritiesEqual.add(VcnWifiUnderlyingNetworkPriorityTest.getTestNetworkPriority());
+ final VcnGatewayConnectionConfig configEqual =
+ buildTestConfigWithVcnUnderlyingNetworkPriorities(networkPrioritiesEqual);
+
+ final LinkedHashSet<VcnUnderlyingNetworkPriority> networkPrioritiesNotEqual =
+ new LinkedHashSet();
+ networkPrioritiesNotEqual.add(
+ VcnWifiUnderlyingNetworkPriorityTest.getTestNetworkPriority());
+ final VcnGatewayConnectionConfig configNotEqual =
+ buildTestConfigWithVcnUnderlyingNetworkPriorities(networkPrioritiesNotEqual);
+
+ assertEquals(UNDERLYING_NETWORK_PRIORITIES, networkPrioritiesEqual);
+ assertEquals(config, configEqual);
+
+ assertNotEquals(UNDERLYING_NETWORK_PRIORITIES, networkPrioritiesNotEqual);
+ assertNotEquals(config, configNotEqual);
+ }
}
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
index 2e1aab6..46a614f 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
@@ -279,7 +279,7 @@
final String networkPriorityPlmnId = useMatchedPlmnId ? PLMN_ID : PLMN_ID_OTHER;
final VcnCellUnderlyingNetworkPriority networkPriority =
getCellNetworkPriorityBuilder()
- .setAllowedPlmnIds(Set.of(networkPriorityPlmnId))
+ .setAllowedOperatorPlmnIds(Set.of(networkPriorityPlmnId))
.build();
assertEquals(